Skip to content

Commit

Permalink
Add prettier (#66)
Browse files Browse the repository at this point in the history
* feat: remove express dependency by reimplementing the parts I need

* lint: improve rules

* feat: add prettier and format everything

* fix:

* lint:

Co-authored-by: Michael Salaverry <[email protected]>
  • Loading branch information
barakplasma and barakplasma authored Sep 15, 2021
1 parent 082133f commit 183eb4e
Show file tree
Hide file tree
Showing 28 changed files with 1,294 additions and 452 deletions.
38 changes: 20 additions & 18 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
{
"env": {
"browser": true,
"commonjs": true,
"es2021": true,
"node": true
},
"extends": [
"google"
],
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module"
},
"rules": {
"no-unused-vars": "off",
"require-jsdoc": "off",
"max-len": "warn"
}
"env": {
"browser": true,
"commonjs": true,
"es2021": true,
"node": true,
"jest/globals": true,
"cypress/globals": true
},
"extends": ["eslint:recommended", "google"],
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module"
},
"rules": {
"no-unused-vars": "off",
"require-jsdoc": "off",
"no-inner-declarations": "off",
"max-len": "warn"
},
"plugins": ["cypress", "jest"]
}
2 changes: 1 addition & 1 deletion .github/workflows/deployFromMain.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Deploy from main branch

on:
push:
branches: [ main ]
branches: [main]

jobs:
build:
Expand Down
Empty file added .prettierrc.json
Empty file.
13 changes: 4 additions & 9 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,18 @@
{
"name": "Launch via NPM",
"request": "launch",
"runtimeArgs": [
"run-script",
"start"
],
"runtimeArgs": ["run-script", "start"],
"smartStep": true,
"runtimeExecutable": "npm",
"skipFiles": [
"<node_internals>/**"
],
"skipFiles": ["<node_internals>/**"],
"type": "pwa-node"
},
{
"name": "Debug Jest Tests",
"type": "node",
"request": "launch",
"env": {
"HEADLESS": "true",
"HEADLESS": "true"
// "PWDEBUG": "1",
},
"runtimeArgs": [
Expand All @@ -39,4 +34,4 @@
"port": 9229
}
]
}
}
64 changes: 42 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@
![Website](https://img.shields.io/website?down_color=lightgrey&down_message=offline&up_color=blue&up_message=online&url=https%3A%2F%2Fbarakplasma.github.io%2Fin-person-queue%2Fclient%2F)

# in-person-queue

- [Live version](https://barakplasma.github.io/in-person-queue/client/)
- [Repository](https://github.com/barakplasma/in-person-queue)
- [Development](#development)

## What is this?

TL:DR; This is a full stack website & server **solution for enabling arbitrary administrators to create location based queues with real-time updates**.

## Inspiration

Due to COVID-19, there is a worldwide effort to provide vaccinations to every person on earth. The vaccines currently available must be administered by medical professionals, typically in non-traditional environments (outdoors in places with enough space to socially distance). People are told to patiently queue up (line up) for their vaccine. **We can do better than forcing people to stand in line in order to get vaccinated.**

Instead, there should be a mobile website for people to keep track of their position in a queue. The mobile website should have real-time updates, should work on any internet-enabled phone, and should respect the user's privacy and data. This project aspires to fulfill this need.
Expand All @@ -21,7 +24,7 @@ If you'd like to keep reading see "Use cases part II" below

## Use cases part II

The Pfizer-BioNTech COVID-19 Vaccine 💉 has a shelf life of 2-8 hours after a carton of doses has been thawed [[citation]](https://www.fda.gov/media/144413/download). Typically, medical professionals thaw and prepare enough doses for everyone with an appointment. However, not everyone with an appointment is ultimately able to show up to their appointments. Thus, there are typically a number of leftover vaccine doses which go to waste every day.
The Pfizer-BioNTech COVID-19 Vaccine 💉 has a shelf life of 2-8 hours after a carton of doses has been thawed [[citation]](https://www.fda.gov/media/144413/download). Typically, medical professionals thaw and prepare enough doses for everyone with an appointment. However, not everyone with an appointment is ultimately able to show up to their appointments. Thus, there are typically a number of leftover vaccine doses which go to waste every day.

**It is better to make leftover and soon-to-expire vaccine doses available to a nearby waiting list of people desiring vaccination than it is to let them go to waste.**

Expand All @@ -34,29 +37,32 @@ An intended use case of this project is to enable medical professionals, or the
## User Guide

[Implemented: Create queue] Navigate to an instance of HisoonNumber, such as [https://barakplasma.github.io/in-person-queue/client/](https://barakplasma.github.io/in-person-queue/client/) and click on "Create queue at my location". By creating a queue, you gain access to administer that queue.
This prompts the browser to ask permission to do a geolocation check. This geolocation is used to generate an OpenLocationCode, which is used as the name of the queue. Only the queue admin must provide geolocation access.
This prompts the browser to ask permission to do a geolocation check. This geolocation is used to generate an OpenLocationCode, which is used as the name of the queue. Only the queue admin must provide geolocation access.

[Implemented: ADMIN URL]
Anyone with the admin URL can act as an admin. The admin URL for a queue is a secret for controlling the queue.

[Implemented: ADMIN MESSAGING] An admin can set and update a queue message / title to "shout" to people waiting in that queue. This is a one-to-many communication channel.

[Implemented: SEE NEARBY QUEUES]
People can click "Join a nearby queue" to see a list of nearby queues.
People can click "Join a nearby queue" to see a list of nearby queues.

[Implemented: open existing queue] Alternatively, they can navigate to a queue URL (for example https://barakplasma.github.io/in-person-queue/client/queue.html?location=8G4P3QJJ+56) to join that existing queue.

[TODO: QUEUE STATS] On the queue page, a user can see the current length of the queue. A user can see an estimated waiting time, and the configured capacity of the queue. (it isn't practical to provide an infinite queue with long wait times)

## Deployment / Hosting / Ops

This project is built to be self-hosted. There are no cloud dependencies. You'll need:
* Node.js server (v14 or greater)
* Redis database (v6 or greater)
* domain name or public IP (free dynamic dns is enough)

- Node.js server (v14 or greater)
- Redis database (v6 or greater)
- domain name or public IP (free dynamic dns is enough)

A $35 Raspberry Pi and a home internet connection can handle a significant amount of traffic, don't be afraid to use one.

A very easy way to get started is with flyctl (config is included in this repo). They have a generous free tier, and managed redis for you.

```sh
$ brew install superfly/tap/flyctl
$ flyctl auth signup
Expand All @@ -67,35 +73,42 @@ $ flyctl secrets set REDIS_CONNECTION_STRING=redis://YOUR CONNECTION STRING HERE
See the [development](#development) section for more details on getting started locally

### Environment Variables

To deploy this, you might need a [.env](https://www.npmjs.com/package/dotenv) file like this or the corresponding environment variables

```env
PORT=3000
# NODE_ENV "development" | "production"
NODE_ENV=development
REDIS_CONNECTION_STRING=redis://:PASSWORD@HOSTNAME:PORT
CORS_ORIGIN='["localhost:3000","https://barakplasma.github.io"]'
```

See also the docker-compose.yml or fly.toml file for details

## Development

### Goals
* The most important goal of this project is to enable an ordinary person to create a vaccine leftover queue extremely quickly and easily.
* This project should stay SIMPLE to use and implement. I want any beginner to be able to fork/hack this project to fit their needs. The only simpler alternative to this project should be a paper/pencil/clipboard and a loud voice. See http://boringtechnology.club/ for more details
* The front end must be **accessible**, fast, and work on almost any MOBILE browser.
* The backend should be easy to self-host, and scale nicely. The backend should be easy to host on a Raspberry Pi, a digital ocean droplet, or a full cluster on EC2 / K8s. This means the backend should be high performance, and simple.
* I respect DevOps, but this project should be NoOps. An operator should ideally be able to set it up on a brand new rasberry pi once and never login to it again. [TODO: Sensible defaults and limits]
* Lighthouse scores above 90 in every category on mobile (currently 94 performance, 91 accessibility, and 100 best practices while connected to the websocket queue)

- The most important goal of this project is to enable an ordinary person to create a vaccine leftover queue extremely quickly and easily.
- This project should stay SIMPLE to use and implement. I want any beginner to be able to fork/hack this project to fit their needs. The only simpler alternative to this project should be a paper/pencil/clipboard and a loud voice. See http://boringtechnology.club/ for more details
- The front end must be **accessible**, fast, and work on almost any MOBILE browser.
- The backend should be easy to self-host, and scale nicely. The backend should be easy to host on a Raspberry Pi, a digital ocean droplet, or a full cluster on EC2 / K8s. This means the backend should be high performance, and simple.
- I respect DevOps, but this project should be NoOps. An operator should ideally be able to set it up on a brand new rasberry pi once and never login to it again. [TODO: Sensible defaults and limits]
- Lighthouse scores above 90 in every category on mobile (currently 94 performance, 91 accessibility, and 100 best practices while connected to the websocket queue)

### Technical Design

Full stack. Vanilla HTML/Javascript/CSS front-end, and Node.js (Socket.io/Express) backend with a Redis datastore.

To enable real-time updates when the queue changes, this project is built on top of WebSockets via Socket.io. For consistancy and ease of development, any client-server communication which could be stuffed into `socket.emit()` was stuffed into `socket.emit()` and `socket.on()`.
To enable real-time updates when the queue changes, this project is built on top of WebSockets via Socket.io. For consistancy and ease of development, any client-server communication which could be stuffed into `socket.emit()` was stuffed into `socket.emit()` and `socket.on()`.

The client website can be hosted as static files ANYWHERE, and this means this shouldn't be a SPA. To be beginner friendly and future proof, this was written without any frameworks, and using the web platform. Additionally, this was **intentionally** built without a bundler, minifier, or any kind of transpilation to reduce complexity and keep this simple to hack on. This comes at the expense of a tiny bit of performance, and that's ok. In my day-to-day, I am a React/Redux developer, and I know very well why I want this to stay simple. The CSS is an afterthought, so I went with MVP.css which was a fantastic choice for speed and accessibility.

### Getting started with localhost
[Implemented: docker-compose.yml] All you need to do to get started using your localhost is

[Implemented: docker-compose.yml] All you need to do to get started using your localhost is

1. run `$ git clone https://github.com/barakplasma/in-person-queue.git`
2. run `$ cd in-person-queue`
3. run `$ docker-compose up` in this repository, or `$ npm install && npm start` with Redis running (and env vars set)
Expand All @@ -107,34 +120,41 @@ You could also grab a pre-built image from the Github actions "Deploy to Fly.io"
Using `caddy reverse-proxy --to http://localhost:3000` can help you test locally with https. Use with `/setBackend` route;

### Localhost Environment Variables

To test in your browser, set the following localstorage keys on the localhost:port combo you choose to use
Using `/setBackend` does this for you

```js
window.localStorage.setItem('env', 'test');
window.localStorage.setItem('test host', 'localhost:${port}');
window.localStorage.setItem("env", "test");
window.localStorage.setItem("test host", "localhost:${port}");
```

### Debug

Debug is easiest when running from localhost and not in docker-compose.
Start a local redis instance with redis locally or in docker,

```sh
$ docker run --rm -it -p 6379:6379 --name some-redis redis:alpine
```

Then use VS-Code and launch program via debug panel.

### Tests

There are Jest based tests here, but you need to run Redis on the same host for them to pass (like debug).

Use `npm test` to run all tests.
Use `npm run test:unit` to run all tests other than playwright end-to-end tests.
Use `npm run test:e2e` to only run playwright end-to-end tests.

#### Keywords / Buzzwords
* WebSockets
* Socket.io
* Redis
* Vanilla.js
* Docker
* Fly.io

- WebSockets
- Socket.io
- Redis
- Vanilla.js
- Docker
- Fly.io

<div>Some Icons made by <a href="https://www.freepik.com" title="Freepik">Freepik</a> from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a></div>
133 changes: 73 additions & 60 deletions client/admin.html
Original file line number Diff line number Diff line change
@@ -1,65 +1,78 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Admin of Queue</title>
<script
defer
src="https://cdn.jsdelivr.net/gh/google/[email protected]/js/src/openlocationcode.min.js"
integrity="sha256-VLEhs9CPhP4nrGyllTmF3KPGi04HVq5gFGKxGetzkYY="
crossorigin="anonymous"
></script>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/[email protected]/mvp.css"
integrity="sha256-WDlAJ4pTJXGoGscyx2aoJBdb4wYUljMrgaY3PRG936o="
crossorigin="anonymous"
/>
<script type="module" src="./admin.js"></script>
<script type="module" src="./user.js"></script>
</head>

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin of Queue</title>
<script defer src="https://cdn.jsdelivr.net/gh/google/[email protected]/js/src/openlocationcode.min.js"
integrity="sha256-VLEhs9CPhP4nrGyllTmF3KPGi04HVq5gFGKxGetzkYY=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/mvp.css"
integrity="sha256-WDlAJ4pTJXGoGscyx2aoJBdb4wYUljMrgaY3PRG936o=" crossorigin="anonymous">
<script type="module" src="./admin.js"></script>
<script type="module" src="./user.js"></script>
</head>

<body>
<p>HisoonNumber</p>
<main>
<section id="position-section">
<aside>
<h3>Current head of the queue</h3>
<p id="userIdContainer">UserId: <span id="userId">N/A</span></p>
</aside>
</section>
<section>
<p>
<button id="current-user-done">Current user done</button>
<button id="refresh-queue">Refresh</button>
</p>
</section>
<section>
<aside>
<p id="queueLengthContainer">Queue Length: <span id="queueLengthCount">N/A</span>
<body>
<p>HisoonNumber</p>
<main>
<section id="position-section">
<aside>
<h3>Current head of the queue</h3>
<p id="userIdContainer">UserId: <span id="userId">N/A</span></p>
</aside>
</section>
<section>
<p>
<button id="current-user-done">Current user done</button>
<button id="refresh-queue">Refresh</button>
</p>
<p id="locationContainer">Location: <a id="location">N/A</a></p>
<p id="shareLink"></p>
</aside>
</section>
<section>
<form action="">
<label for="edit-admin-message">Edit Admin Message:</label></summary>
<textarea name="edit-admin-message" rows="8"
placeholder="Use this form to give your queue users some details on what this queue is for, what to do when it is their turn, and who you are."
id="admin-message"
spellcheck="true" maxlength="1000"></textarea>
<button id="submit-admin-message" type="button">update admin message</button>
</form>
</section>
<style>
body {
text-align: center;
}

textarea[name="edit-admin-message"] {
text-overflow: ellipsis;
width: 90%;
max-width: 90%;
min-width: 90%;
}
</style>
<footer><a class="done">Quit queue admin</a></footer>
</main>
</body>
</section>
<section>
<aside>
<p id="queueLengthContainer">
Queue Length: <span id="queueLengthCount">N/A</span>
</p>
<p id="locationContainer">Location: <a id="location">N/A</a></p>
<p id="shareLink"></p>
</aside>
</section>
<section>
<form action="">
<label for="edit-admin-message">Edit Admin Message:</label>
<textarea
name="edit-admin-message"
rows="8"
placeholder="Use this form to give your queue users some details on what this queue is for, what to do when it is their turn, and who you are."
id="admin-message"
spellcheck="true"
maxlength="1000"
></textarea>
<button id="submit-admin-message" type="button">
update admin message
</button>
</form>
</section>
<style>
body {
text-align: center;
}

</html>
textarea[name="edit-admin-message"] {
text-overflow: ellipsis;
width: 90%;
max-width: 90%;
min-width: 90%;
}
</style>
<footer><a class="done">Quit queue admin</a></footer>
</main>
</body>
</html>
Loading

0 comments on commit 183eb4e

Please sign in to comment.