Skip to content

A public event pinboard webapp for your local community!

License

Notifications You must be signed in to change notification settings

Denperidge/community-pinboard

Repository files navigation

Community Pinboard

Repository - Docker image - Development/bug tracker

A public event pinboard webapp for your local community, meant to be even lighter on the client devices than on the server!

A screenshot of the community pinboard application. It says 'Custom pinboard title!' on the top, with 3 pins on the board and one item previewed on the right. The 3 pins are: 'Gloss concert, 4/30/2024, Brussels, Cat' and 'Queer punk DIY day!, 4/27/2024, Ash's place, Ash' and 'going out, 4/30/2024, near Rue Royale 236, Bruxelles, Quinn'. On the right is the 'Pin view' panel, with the gloss concert pin background image displayed fully, the title + location shown, and the time/day entry is now expanded to include both date and time

A portrait version of the landscape screenshot of before, displaying the pinboard in full width with the same title

  • To get this project up and running, you can view How-to
  • For the design decision behind this project, see Explanation
  • For technical info, see Reference

Note: this is - at the point of writing - an application meant for small scale deployments. Please ensure you read Security Considerations

How-to

Run using Docker Compose

Recommended for: production

Pre-requirements: Docker, Docker Compose

  1. Create docker-compose.yml with the following content:
    # docker-compose.yml
    services:
      community-pinboard:
        # Pull the Docker image from https://hub.docker.com/r/denperidge/community-pinboard
        image: denperidge/community-pinboard:latest
        volumes:
          - ./data/:/app/data  # Mount data directory in ./data
        ports:
          - 3000:3000  # Expose port 3000
        # Optionally, load environment variables from .env
        env_file:
          - path: .env
            required: false
  2. Optionally, create a .env file to configure environment variables
  3. Run docker compose up --detach

You can now access the server from localhost:3000, and any pins & uploads in the data/ directory

Run using Docker

Recommended for: production

Pre-requirements: Docker

# Pull & run image
docker run -p 3000:3000 denperidge/community-pinboard:latest

You can now access the server from localhost:3000

Note: this example does not automatically bind ./data to /app/data

Install, run and test using Node.js

Recommended for: development

Pre-requirements: Node.js, Yarn

# Clone the repository, navigate to it and install dependencies
git clone https://github.com/Denperidge/community-pinboard.git
cd community-pinboard
yarn install

yarn build  # Build to dist/
yarn prod  # Run from dist/

You can now access the server from localhost:3000, and any pins & uploads in the data/ directory

Alternative commands:

Check the other scripts defined in package.json

Build and run using Docker

Recommended for: development

Pre-requirements: Docker

# Clone the repository & navigate to it
git clone https://github.com/Denperidge/community-pinboard.git
cd community-pinboard
docker build -t community-pinboard .

# Run image
docker run -p 3000:3000 community-pinboard

You can now access the server from localhost:3000

Note: this example does not automatically bind ./data to /app/data

Build and run using Docker-Compose

Recommended for: development

# Clone the repository & navigate to it
git clone https://github.com/Denperidge/community-pinboard.git
cd community-pinboard

# Build and run image
docker compose up --build

You can now access the server from localhost:3000, and any pins & uploads in the data/ directory

Configuration/environment variables

For customisation, please set the environment variables in your shell, docker-compose or a .env file in the root directory (the same directory this file, README.md is in)

For all configuration options, please refer to Reference: Environment variables.

Explanation

Base design decisions

This application was built to replace fragmented organising through multiple Facebook organisations, Discord servers and Whatsapp pinned messages. This means that by design, the systems built here have to go against this. Below are some of what I have found to be failings of the above tactics

  • Non-account-based: Making new accounts feels like second nature to a good portion of the internets users, but it is a hassle, especially for those with less technological skills. Whatsapp circumvented this by using phone numbers, but it still requires an install, and it's still a product of Meta (a company known for an intrinsic disrespect to the privacy of its users). If authentication becomes an element, something simple should be used during pin creation/editing, but a full login and registering system is out of scope.
  • Lightweight: Some apps like Messenger, Facebook and Discord or even some websites have forgotten that having your mobile phone isn't sending 500 requests and 20 animations simultaneously may slow down its performance. This website should be as streamlined as possible, and requires as little javascript as possible. All the heavy lifting - if any - should be done on the server side.
  • Easy setup & administration: While you might need some technological know-how to get it Community Pinboard up and running at first, the server side is kept intentionally compact. The dependencies and devDepedencies in package.json should be split to ensure that the production version does not get overbloated. The decision to use a simple file-based hierarchy is also in support of this, so that file-based management can be done in case of mistakes or problems.
  • Cross-platform: Everything has an app, but not everything should be. People barely get around to installing a calendar/scheduling app for people they live or work with, let alone this! A simple, clearview website.
  • Accessible: Care should be put into the accessibility of the project. Mandatory image descriptions is a measure that - even though it might have to get a toggle down the line - an attempt at a step to making user-generated content more accessible, or at least thought about. Further care should also be put in providing as well-polished accessibility from the get-go.

Security considerations

Obviously, I developed this application with security in mind; following recommended practices wherever I could find. But I'm a programmer and not a cybersecurity expert! Please review the used practices and check with your own security standards. And if you have a suggestion/improvement, please feel free to open an issue/PR!

Randomised express-session secret

In app.ts, the secret is randomly generated on startup. This is to minimize configuration by not requiring a custom string to be set, whilst ensuring the string is secure, whilst not having any noticeable impact on the user experience (as the logins are meant to be temporary).

Test timezone setting

Currently the tests should be ran with the TZ environment variable set to the same timezone your machine is Europe/Brussels. This is because JavaScript's new Date().getTimezoneOffset() (which is used to determine correct resulting hours) does not allow custom timezone insertion.

Timezone handling

HTML Forms

For native HTML date(time) input, you have...

  • date (which doesn't include time)
  • datetime-local (which doesn't include timezone information)

No timezone-based datetime is available. To ensure compatibility with more devices and lower client-side footprint, the native datetime-local input is used (as opposed to a JS library solution).

  • This returns no timezone information. The returned value is in the format of YYYY-MM-DDTHH:MM
  • This means that the input values will be from the users timezone point of view

Javascript Date()

  • JavaScript Date() also includes time
  • Stored in UTC, specifically ms since January 1, 1970 00:00:00 UTC (see w3schools)
  • I haven't have it gotten to consistently parse UTC strings with timezone information. This might be a goof on my end, though.
  • new Date().getTimezoneOffset() is based on TZ environment variable.
  • JavaScripts UTC constructor can deal with too many hours being provided. new Date(Date.UTC(2024, 1, 1, 25, 0)).toISOString() returns "2024-02-02T01:00:00.000Z"

dayjs

  • Wrapper for JS Date(), but with better parsing and formatting.
  • Should output according to the WEBSITE_LOCALE & WEBSITE_TIMEZONE environment variables

Node.js

  • process.env.tz Returns a Area/City format. See the Node.js docs for more information

add-to-calendar-button

ics.js/ics feed

TODO

Reference

Environment variables

Key Explanation default default (Docker)
HOST_DOMAIN Which domain the website/server will be reachable on localhost:3000 not set
DATA_DIR Where to store data uploaded by users data/ /app/data/
WEBSITE_TITLE The title for your website, displayed in HTML, OpenGraph, views/index h1 Community Pinboard! not set
WEBSITE_DESCRIPTION The description for your website, displayed in OpenGraph A public event pinboard for your local community! not set
* WEBSITE_LOCALE The locale for your website. This will determine in what format datetimes are displayed in the rendered HTML/within app/Pin.ts nl-be not set
* TZ The timezone in Area/City notation (see TZ identifier on Wikipedia list of database tz time zones). This will determine in what timezone datetimes are added to calendar. Additionally, view the process.env.TZ Node.js docs Europe/Brussels not set
NODE_ENV Node.js env variable to set environment yarn prod: production / yarn test: test** production
ADMIN_PASSWORDS Valid passwords for users to log in with. Passwords are separated using a pipe character '|' not set not set
MAX_TITLE Max character length for pin title 80 not set
MAX_DESCRIPTION Max character length for pin description 400 not set
MAX_LOCATION Max character length for pin location 150 not set
MAX_POSTEDBY Max character length for pin posted by 50 not set

(*: recommended) (**: set by Jest)

For an example setup, see .env.example

Project structure

Notice

The non-edited post-it SVG is from Openclipart, licensed under the Public Domain. More information on the SVG can be found here.

The current (placeholder) background is from the Nookipedia assets page.

The cork.jpg material is...

  • Created using Cork001 from ambientCG.com, licensed under the Creative Commons CC0 1.0 Universal License.
  • Modified by @hynet-mel.

License

This project is licensed under the MIT License.