From 24ca5f8e0be9a379d0a5ad3c516f07e7a18fc0b7 Mon Sep 17 00:00:00 2001 From: Martin Dimitrov Date: Sat, 26 Oct 2024 11:49:42 -0700 Subject: [PATCH] move packages --- Dockerfile | 20 ----- README.md | 12 ++- bun.lockb | Bin 434576 -> 396464 bytes docker-compose.yml | 14 ---- doorman.code-workspace | 14 ++-- package.json | 6 +- .../{serverless => doorman-api}/.env.example | 0 .../{serverless => doorman-api}/.gitignore | 0 .../{buzzer-client => doorman-api}/.nvmrc | 0 .../.twilioserverlessrc | 0 .../{serverless => doorman-api}/README.md | 4 +- .../functions/api/door/auth.js | 0 .../functions/api/door/info.js | 0 .../functions/api/door/status.js | 0 .../functions/common/ddb.private.js | 0 .../{serverless => doorman-api}/package.json | 2 +- .../.env.example | 0 .../.gitignore | 0 .../{serverless => doorman-client}/.nvmrc | 0 .../.twilioserverlessrc | 0 .../.vscode/settings.json | 0 .../{buzzer-client => doorman-client}/LICENSE | 0 .../README.md | 0 .../buzzer_welcome_boosted.protected.mp3 | Bin .../buzzer_welcome_boostedx2.protected.mp3 | Bin .../assets/buzzing_up_boosted.protected.mp3 | Bin .../bun.lockb | Bin .../functions/buzzer-activated.js | 0 .../functions/call-residents.js | 0 .../functions/door-open.js | 0 .../functions/text-me.js | 0 .../package-lock.json | 0 .../package.json | 2 +- packages/{client => doorman-ui}/README.md | 0 packages/{client => doorman-ui}/index.html | 0 packages/{client => doorman-ui}/package.json | 2 +- .../public/Insulin_Nation_Low_Kramer.gif | Bin .../{client => doorman-ui}/public/global.css | 0 .../public/manifest.json | 0 .../{client => doorman-ui}/public/robots.txt | 0 packages/{client => doorman-ui}/src/App.tsx | 0 .../src/components/AuthComponent.tsx | 0 .../src/components/CountdownBar.tsx | 0 packages/{client => doorman-ui}/src/index.tsx | 0 .../src/pages/DoorPage.tsx | 0 .../src/types/Action.ts | 0 packages/{client => doorman-ui}/tsconfig.json | 0 packages/{client => doorman-ui}/vite-env.d.ts | 0 .../{client => doorman-ui}/vite.config.ts | 0 packages/server/package.json | 28 ------- .../server/src/clients/db/AbstractDbClient.ts | 31 -------- .../server/src/clients/db/RedisDbClient.ts | 69 ------------------ .../server/src/clients/db/RedisDbProvider.ts | 15 ---- .../server/src/middlewares/DoorAuthModes.ts | 66 ----------------- .../src/middlewares/TimeLockMiddleware.ts | 17 ----- packages/server/src/routers/DoorRouter.ts | 65 ----------------- packages/server/src/server.ts | 34 --------- packages/server/src/types/IAccessControl.ts | 4 - .../server/src/types/IAuthCallbackQuery.ts | 7 -- packages/server/src/types/IAuthMode.ts | 4 - packages/server/src/types/IDoorConfig.ts | 7 -- packages/server/src/types/IDoorResponse.ts | 5 -- packages/server/src/types/IDoorStatus.ts | 5 -- packages/server/src/types/RedisKeys.ts | 16 ---- packages/server/src/util/EnvConfigUtil.ts | 55 -------------- packages/server/src/util/RateLimits.ts | 14 ---- packages/server/tsconfig.json | 28 ------- 67 files changed, 28 insertions(+), 518 deletions(-) delete mode 100644 Dockerfile delete mode 100644 docker-compose.yml rename packages/{serverless => doorman-api}/.env.example (100%) rename packages/{serverless => doorman-api}/.gitignore (100%) rename packages/{buzzer-client => doorman-api}/.nvmrc (100%) rename packages/{buzzer-client => doorman-api}/.twilioserverlessrc (100%) rename packages/{serverless => doorman-api}/README.md (72%) rename packages/{serverless => doorman-api}/functions/api/door/auth.js (100%) rename packages/{serverless => doorman-api}/functions/api/door/info.js (100%) rename packages/{serverless => doorman-api}/functions/api/door/status.js (100%) rename packages/{serverless => doorman-api}/functions/common/ddb.private.js (100%) rename packages/{serverless => doorman-api}/package.json (95%) rename packages/{buzzer-client => doorman-client}/.env.example (100%) rename packages/{buzzer-client => doorman-client}/.gitignore (100%) rename packages/{serverless => doorman-client}/.nvmrc (100%) rename packages/{serverless => doorman-client}/.twilioserverlessrc (100%) rename packages/{buzzer-client => doorman-client}/.vscode/settings.json (100%) rename packages/{buzzer-client => doorman-client}/LICENSE (100%) rename packages/{buzzer-client => doorman-client}/README.md (100%) rename packages/{buzzer-client => doorman-client}/assets/buzzer_welcome_boosted.protected.mp3 (100%) rename packages/{buzzer-client => doorman-client}/assets/buzzer_welcome_boostedx2.protected.mp3 (100%) rename packages/{buzzer-client => doorman-client}/assets/buzzing_up_boosted.protected.mp3 (100%) rename packages/{buzzer-client => doorman-client}/bun.lockb (100%) rename packages/{buzzer-client => doorman-client}/functions/buzzer-activated.js (100%) rename packages/{buzzer-client => doorman-client}/functions/call-residents.js (100%) rename packages/{buzzer-client => doorman-client}/functions/door-open.js (100%) rename packages/{buzzer-client => doorman-client}/functions/text-me.js (100%) rename packages/{buzzer-client => doorman-client}/package-lock.json (100%) rename packages/{buzzer-client => doorman-client}/package.json (95%) rename packages/{client => doorman-ui}/README.md (100%) rename packages/{client => doorman-ui}/index.html (100%) rename packages/{client => doorman-ui}/package.json (97%) rename packages/{client => doorman-ui}/public/Insulin_Nation_Low_Kramer.gif (100%) rename packages/{client => doorman-ui}/public/global.css (100%) rename packages/{client => doorman-ui}/public/manifest.json (100%) rename packages/{client => doorman-ui}/public/robots.txt (100%) rename packages/{client => doorman-ui}/src/App.tsx (100%) rename packages/{client => doorman-ui}/src/components/AuthComponent.tsx (100%) rename packages/{client => doorman-ui}/src/components/CountdownBar.tsx (100%) rename packages/{client => doorman-ui}/src/index.tsx (100%) rename packages/{client => doorman-ui}/src/pages/DoorPage.tsx (100%) rename packages/{client => doorman-ui}/src/types/Action.ts (100%) rename packages/{client => doorman-ui}/tsconfig.json (100%) rename packages/{client => doorman-ui}/vite-env.d.ts (100%) rename packages/{client => doorman-ui}/vite.config.ts (100%) delete mode 100644 packages/server/package.json delete mode 100644 packages/server/src/clients/db/AbstractDbClient.ts delete mode 100644 packages/server/src/clients/db/RedisDbClient.ts delete mode 100644 packages/server/src/clients/db/RedisDbProvider.ts delete mode 100644 packages/server/src/middlewares/DoorAuthModes.ts delete mode 100644 packages/server/src/middlewares/TimeLockMiddleware.ts delete mode 100644 packages/server/src/routers/DoorRouter.ts delete mode 100644 packages/server/src/server.ts delete mode 100644 packages/server/src/types/IAccessControl.ts delete mode 100644 packages/server/src/types/IAuthCallbackQuery.ts delete mode 100644 packages/server/src/types/IAuthMode.ts delete mode 100644 packages/server/src/types/IDoorConfig.ts delete mode 100644 packages/server/src/types/IDoorResponse.ts delete mode 100644 packages/server/src/types/IDoorStatus.ts delete mode 100644 packages/server/src/types/RedisKeys.ts delete mode 100644 packages/server/src/util/EnvConfigUtil.ts delete mode 100644 packages/server/src/util/RateLimits.ts delete mode 100644 packages/server/tsconfig.json diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 31e17af..0000000 --- a/Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM oven/bun - -ADD packages packages -ADD bun.lockb bun.lockb -ADD package.json package.json -ADD tsconfig.json tsconfig.json - -# install all deps -RUN bun install - -# client build -WORKDIR /home/bun/app/packages/client -RUN bun run build - -# move built client to server -RUN mv dist ../server/ -WORKDIR /home/bun/app/packages/server - -# start server -CMD bun run ./src/server.ts \ No newline at end of file diff --git a/README.md b/README.md index a5bc215..cac9fbf 100644 --- a/README.md +++ b/README.md @@ -14,13 +14,23 @@ bun run index.ts This project was created using `bun init` in bun v1.0.3. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. +## Deployments + +They are configured to happen in gitea actions for CI/CD. If you need to deploy manually, it should be possible as long as you source the requisite envs first. + ## to deploy Doorman API / UI ```bash bun run deploy-serverless ``` -## homeassistant integration +## to deploy Doorman Buzzer client + +```bash +bun run deploy-buzzer-client +``` + +## homeassistant integration poller in configuration.yaml diff --git a/bun.lockb b/bun.lockb index 7e70e09f2eb477d7687b6c36210adc3a4b0cd52c..55ec6193c1b8b9c4d636becdde1c286845b693a9 100755 GIT binary patch delta 74670 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 delta 95849 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 G-v0ms$@HfH diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 646677f..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,14 +0,0 @@ -version: "3" - -services: - doorman: - container_name: doorman - image: gitea.chromart.dedyn.io/martin/doorman:latest - environment: - - CHALLENGE_EXPIRE_MS=105000 - - BASE_DOMAIN=gitea.chromart.dedyn.io - - REDIS_CONNECT_URL=redis://@redis:6379 - depends_on: - - redis - redis: - image: redis:latest \ No newline at end of file diff --git a/doorman.code-workspace b/doorman.code-workspace index 3b9c664..516e575 100644 --- a/doorman.code-workspace +++ b/doorman.code-workspace @@ -1,16 +1,20 @@ { "folders": [ { - "path": "packages/client" + "name": "doorman-ui", + "path": "packages/doorman-ui" }, { - "path": "packages/server" + "name": "doorman-api", + "path": "packages/doorman-api" }, { + "name": "doorman-client", + "path": "packages/doorman-client" + }, + { + "name": "doorman", "path": "." - }, - { - "path": "packages/serverless" } ], "settings": { diff --git a/package.json b/package.json index 35a5157..973cf63 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,9 @@ "bun-types": "latest" }, "scripts": { - "prepare-client-serverless": "bun --filter 'doorman-client' build && rm -rf packages/serverless/assets/* && mkdir -p packages/serverless/assets/assets && cp -fr packages/client/dist/* packages/serverless/assets/ && cp -f packages/serverless/assets/index.html packages/serverless/assets/assets/index.html", - "deploy-serverless": "bun run prepare-client-serverless && bun --filter 'serverless' deploy", - "deploy-buzzer-client": "bun --filter 'buzzer-client' deploy" + "prepare-ui": "bun --filter 'doorman-ui' build && rm -rf packages/doorman-api/assets/* && mkdir -p packages/doorman-api/assets/assets && cp -fr packages/doorman-ui/dist/* packages/doorman-api/assets/ && cp -f packages/doorman-api/assets/index.html packages/doorman-api/assets/assets/index.html", + "deploy-serverless": "bun run prepare-ui && bun --filter 'doorman-api' deploy", + "deploy-buzzer-client": "bun --filter 'doorman-client' deploy" }, "peerDependencies": { "typescript": "^5.0.0" diff --git a/packages/serverless/.env.example b/packages/doorman-api/.env.example similarity index 100% rename from packages/serverless/.env.example rename to packages/doorman-api/.env.example diff --git a/packages/serverless/.gitignore b/packages/doorman-api/.gitignore similarity index 100% rename from packages/serverless/.gitignore rename to packages/doorman-api/.gitignore diff --git a/packages/buzzer-client/.nvmrc b/packages/doorman-api/.nvmrc similarity index 100% rename from packages/buzzer-client/.nvmrc rename to packages/doorman-api/.nvmrc diff --git a/packages/buzzer-client/.twilioserverlessrc b/packages/doorman-api/.twilioserverlessrc similarity index 100% rename from packages/buzzer-client/.twilioserverlessrc rename to packages/doorman-api/.twilioserverlessrc diff --git a/packages/serverless/README.md b/packages/doorman-api/README.md similarity index 72% rename from packages/serverless/README.md rename to packages/doorman-api/README.md index aa40868..fa24b87 100644 --- a/packages/serverless/README.md +++ b/packages/doorman-api/README.md @@ -2,8 +2,8 @@ this project deploys the UI and API to twilio functions https://doorman-6741-prod.twil.io -It uses a cloud redis cache +It uses DDB for the backend -After the twilio functions I have setup a cloudflare worker at https://doorman.chromart.workers.dev +After the twilio functions I have setup a cloudflare worker at https://doorman.chromart.cc to proxy the requests to the twilio lambda The cloudflare worker just proxies requests so the endpoint is a bit nicer diff --git a/packages/serverless/functions/api/door/auth.js b/packages/doorman-api/functions/api/door/auth.js similarity index 100% rename from packages/serverless/functions/api/door/auth.js rename to packages/doorman-api/functions/api/door/auth.js diff --git a/packages/serverless/functions/api/door/info.js b/packages/doorman-api/functions/api/door/info.js similarity index 100% rename from packages/serverless/functions/api/door/info.js rename to packages/doorman-api/functions/api/door/info.js diff --git a/packages/serverless/functions/api/door/status.js b/packages/doorman-api/functions/api/door/status.js similarity index 100% rename from packages/serverless/functions/api/door/status.js rename to packages/doorman-api/functions/api/door/status.js diff --git a/packages/serverless/functions/common/ddb.private.js b/packages/doorman-api/functions/common/ddb.private.js similarity index 100% rename from packages/serverless/functions/common/ddb.private.js rename to packages/doorman-api/functions/common/ddb.private.js diff --git a/packages/serverless/package.json b/packages/doorman-api/package.json similarity index 95% rename from packages/serverless/package.json rename to packages/doorman-api/package.json index 67c1249..15b4d70 100644 --- a/packages/serverless/package.json +++ b/packages/doorman-api/package.json @@ -1,5 +1,5 @@ { - "name": "serverless", + "name": "doorman-api", "version": "0.0.0", "private": true, "scripts": { diff --git a/packages/buzzer-client/.env.example b/packages/doorman-client/.env.example similarity index 100% rename from packages/buzzer-client/.env.example rename to packages/doorman-client/.env.example diff --git a/packages/buzzer-client/.gitignore b/packages/doorman-client/.gitignore similarity index 100% rename from packages/buzzer-client/.gitignore rename to packages/doorman-client/.gitignore diff --git a/packages/serverless/.nvmrc b/packages/doorman-client/.nvmrc similarity index 100% rename from packages/serverless/.nvmrc rename to packages/doorman-client/.nvmrc diff --git a/packages/serverless/.twilioserverlessrc b/packages/doorman-client/.twilioserverlessrc similarity index 100% rename from packages/serverless/.twilioserverlessrc rename to packages/doorman-client/.twilioserverlessrc diff --git a/packages/buzzer-client/.vscode/settings.json b/packages/doorman-client/.vscode/settings.json similarity index 100% rename from packages/buzzer-client/.vscode/settings.json rename to packages/doorman-client/.vscode/settings.json diff --git a/packages/buzzer-client/LICENSE b/packages/doorman-client/LICENSE similarity index 100% rename from packages/buzzer-client/LICENSE rename to packages/doorman-client/LICENSE diff --git a/packages/buzzer-client/README.md b/packages/doorman-client/README.md similarity index 100% rename from packages/buzzer-client/README.md rename to packages/doorman-client/README.md diff --git a/packages/buzzer-client/assets/buzzer_welcome_boosted.protected.mp3 b/packages/doorman-client/assets/buzzer_welcome_boosted.protected.mp3 similarity index 100% rename from packages/buzzer-client/assets/buzzer_welcome_boosted.protected.mp3 rename to packages/doorman-client/assets/buzzer_welcome_boosted.protected.mp3 diff --git a/packages/buzzer-client/assets/buzzer_welcome_boostedx2.protected.mp3 b/packages/doorman-client/assets/buzzer_welcome_boostedx2.protected.mp3 similarity index 100% rename from packages/buzzer-client/assets/buzzer_welcome_boostedx2.protected.mp3 rename to packages/doorman-client/assets/buzzer_welcome_boostedx2.protected.mp3 diff --git a/packages/buzzer-client/assets/buzzing_up_boosted.protected.mp3 b/packages/doorman-client/assets/buzzing_up_boosted.protected.mp3 similarity index 100% rename from packages/buzzer-client/assets/buzzing_up_boosted.protected.mp3 rename to packages/doorman-client/assets/buzzing_up_boosted.protected.mp3 diff --git a/packages/buzzer-client/bun.lockb b/packages/doorman-client/bun.lockb similarity index 100% rename from packages/buzzer-client/bun.lockb rename to packages/doorman-client/bun.lockb diff --git a/packages/buzzer-client/functions/buzzer-activated.js b/packages/doorman-client/functions/buzzer-activated.js similarity index 100% rename from packages/buzzer-client/functions/buzzer-activated.js rename to packages/doorman-client/functions/buzzer-activated.js diff --git a/packages/buzzer-client/functions/call-residents.js b/packages/doorman-client/functions/call-residents.js similarity index 100% rename from packages/buzzer-client/functions/call-residents.js rename to packages/doorman-client/functions/call-residents.js diff --git a/packages/buzzer-client/functions/door-open.js b/packages/doorman-client/functions/door-open.js similarity index 100% rename from packages/buzzer-client/functions/door-open.js rename to packages/doorman-client/functions/door-open.js diff --git a/packages/buzzer-client/functions/text-me.js b/packages/doorman-client/functions/text-me.js similarity index 100% rename from packages/buzzer-client/functions/text-me.js rename to packages/doorman-client/functions/text-me.js diff --git a/packages/buzzer-client/package-lock.json b/packages/doorman-client/package-lock.json similarity index 100% rename from packages/buzzer-client/package-lock.json rename to packages/doorman-client/package-lock.json diff --git a/packages/buzzer-client/package.json b/packages/doorman-client/package.json similarity index 95% rename from packages/buzzer-client/package.json rename to packages/doorman-client/package.json index f4310c6..75dd944 100644 --- a/packages/buzzer-client/package.json +++ b/packages/doorman-client/package.json @@ -1,5 +1,5 @@ { - "name": "buzzer-client", + "name": "doorman-client", "version": "0.0.0", "private": true, "scripts": { diff --git a/packages/client/README.md b/packages/doorman-ui/README.md similarity index 100% rename from packages/client/README.md rename to packages/doorman-ui/README.md diff --git a/packages/client/index.html b/packages/doorman-ui/index.html similarity index 100% rename from packages/client/index.html rename to packages/doorman-ui/index.html diff --git a/packages/client/package.json b/packages/doorman-ui/package.json similarity index 97% rename from packages/client/package.json rename to packages/doorman-ui/package.json index 94ac5fc..a160063 100644 --- a/packages/client/package.json +++ b/packages/doorman-ui/package.json @@ -1,5 +1,5 @@ { - "name": "doorman-client", + "name": "doorman-ui", "version": "0.1.0", "private": true, "dependencies": { diff --git a/packages/client/public/Insulin_Nation_Low_Kramer.gif b/packages/doorman-ui/public/Insulin_Nation_Low_Kramer.gif similarity index 100% rename from packages/client/public/Insulin_Nation_Low_Kramer.gif rename to packages/doorman-ui/public/Insulin_Nation_Low_Kramer.gif diff --git a/packages/client/public/global.css b/packages/doorman-ui/public/global.css similarity index 100% rename from packages/client/public/global.css rename to packages/doorman-ui/public/global.css diff --git a/packages/client/public/manifest.json b/packages/doorman-ui/public/manifest.json similarity index 100% rename from packages/client/public/manifest.json rename to packages/doorman-ui/public/manifest.json diff --git a/packages/client/public/robots.txt b/packages/doorman-ui/public/robots.txt similarity index 100% rename from packages/client/public/robots.txt rename to packages/doorman-ui/public/robots.txt diff --git a/packages/client/src/App.tsx b/packages/doorman-ui/src/App.tsx similarity index 100% rename from packages/client/src/App.tsx rename to packages/doorman-ui/src/App.tsx diff --git a/packages/client/src/components/AuthComponent.tsx b/packages/doorman-ui/src/components/AuthComponent.tsx similarity index 100% rename from packages/client/src/components/AuthComponent.tsx rename to packages/doorman-ui/src/components/AuthComponent.tsx diff --git a/packages/client/src/components/CountdownBar.tsx b/packages/doorman-ui/src/components/CountdownBar.tsx similarity index 100% rename from packages/client/src/components/CountdownBar.tsx rename to packages/doorman-ui/src/components/CountdownBar.tsx diff --git a/packages/client/src/index.tsx b/packages/doorman-ui/src/index.tsx similarity index 100% rename from packages/client/src/index.tsx rename to packages/doorman-ui/src/index.tsx diff --git a/packages/client/src/pages/DoorPage.tsx b/packages/doorman-ui/src/pages/DoorPage.tsx similarity index 100% rename from packages/client/src/pages/DoorPage.tsx rename to packages/doorman-ui/src/pages/DoorPage.tsx diff --git a/packages/client/src/types/Action.ts b/packages/doorman-ui/src/types/Action.ts similarity index 100% rename from packages/client/src/types/Action.ts rename to packages/doorman-ui/src/types/Action.ts diff --git a/packages/client/tsconfig.json b/packages/doorman-ui/tsconfig.json similarity index 100% rename from packages/client/tsconfig.json rename to packages/doorman-ui/tsconfig.json diff --git a/packages/client/vite-env.d.ts b/packages/doorman-ui/vite-env.d.ts similarity index 100% rename from packages/client/vite-env.d.ts rename to packages/doorman-ui/vite-env.d.ts diff --git a/packages/client/vite.config.ts b/packages/doorman-ui/vite.config.ts similarity index 100% rename from packages/client/vite.config.ts rename to packages/doorman-ui/vite.config.ts diff --git a/packages/server/package.json b/packages/server/package.json deleted file mode 100644 index ddeefff..0000000 --- a/packages/server/package.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "doorman-server", - "version": "1.0.0", - "main": "index.js", - "license": "MIT", - "scripts": { - "start": "bun --hot run src/server.ts", - "build": "bun tsc" - }, - "dependencies": { - "express": "^4.18.2", - "express-fileupload": "^1.4.0", - "express-rate-limit": "^6.10.0", - "lnurl": "^0.25.1", - "qrcode": "^1.5.3", - "redis": "^4.6.8", - "ts-node": "^10.9.1", - "ts-node-dev": "^2.0.0", - "typescript": "^5.2.2" - }, - "devDependencies": { - "@types/express": "^4.17.17", - "@types/express-fileupload": "^1.4.1", - "@types/express-rate-limit": "^6.0.0", - "@types/node": "^20.6.4", - "@types/qrcode": "^1.5.2" - } -} diff --git a/packages/server/src/clients/db/AbstractDbClient.ts b/packages/server/src/clients/db/AbstractDbClient.ts deleted file mode 100644 index 1d7237f..0000000 --- a/packages/server/src/clients/db/AbstractDbClient.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { IAccessControl } from "../../types/IAccessControl"; - -export abstract class AbstractDbClient { - /** - * Checks if the given challenge already exists in the db - * @param challenge - challenge to check against db - * @returns true if challenge exists in DB - */ - public abstract doesChallengeExist(challenge: string): Promise; - - // access control methods - /** - * Set an entry in the DB to mark that a particular challenge was completed by a certain key. - * A future operation which presents this challenge will be authenticated by the key associated in this operation - * @param challenge - the challenge which was completed - * @param key - the key which was part of the authentication test - */ - public abstract markChallengeSuccess(challenge: string, key: string): Promise; - - /** - * Calls into the DB to see if a particular challege has been completed. If it has, return the relevant access control metadata. - * This method will also decrement the access count, and remove it if it falls below the number of remaining access tokens on this challenge - * @param challenge - 32 byte hex challenge - */ - public abstract accessFromChallenge(challenge: string): Promise; - - public abstract connect(): Promise; - - public abstract put(key: string, value: string): Promise; - public abstract exists(key: string): Promise; -} \ No newline at end of file diff --git a/packages/server/src/clients/db/RedisDbClient.ts b/packages/server/src/clients/db/RedisDbClient.ts deleted file mode 100644 index 9a738a2..0000000 --- a/packages/server/src/clients/db/RedisDbClient.ts +++ /dev/null @@ -1,69 +0,0 @@ -import type { RedisClientOptions, RedisClientType, RedisFunctions, RedisModules, RedisScripts } from "redis"; -import { createClient } from "redis"; -import type { IAccessControl } from "../../types/IAccessControl"; -import { AbstractDbClient } from "./AbstractDbClient"; -import { RedisKeys } from "../../types/RedisKeys"; - -export class RedisDbClient extends AbstractDbClient { - private client: RedisClientType; - - private timers: { [challenge: string]: NodeJS.Timeout } = {}; - - constructor(onError: (err: any) => void, options?: RedisClientOptions) { - super(); - this.client = createClient(options); - this.client.on("error", onError); - } - - public async connect(): Promise> { - return this.client.connect(); - } - - public doesChallengeExist(challenge: string): Promise { - return this.client.sIsMember(RedisKeys.CHALLENGES, challenge); - } - - public async removeChallenge(challenge: string): Promise { - let res: number = await this.client.sRem(RedisKeys.CHALLENGES, challenge); - clearTimeout(this.timers[challenge]); - delete this.timers[challenge]; - return res > 0; - } - - public async markChallengeSuccess(challenge: string, key: string): Promise { - await this.client.set(challenge, key); - } - - public async accessFromChallenge(challenge: string): Promise { - let key: string | null = await this.client.getDel(challenge); - - if (key == null) { - return null; - } - - return { - key, - remainingAccess: 0, - } - } - - public async put(key: string, value: string): Promise { - return this.client.set(key, value); - } - - public async exists(key: string): Promise { - return this.client.exists(key); - } - - public async get(key: string): Promise { - return this.client.get(key); - } - - public async remove(key: string): Promise { - return this.client.getDel(key); - } - - public getClient() { - return this.client; - } -} \ No newline at end of file diff --git a/packages/server/src/clients/db/RedisDbProvider.ts b/packages/server/src/clients/db/RedisDbProvider.ts deleted file mode 100644 index 7306b1b..0000000 --- a/packages/server/src/clients/db/RedisDbProvider.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { getEnv } from "../../util/EnvConfigUtil"; -import { RedisDbClient } from "./RedisDbClient"; - - -let client: RedisDbClient; - - -export async function getRedisClient(): Promise> { - if (!client) { - client = new RedisDbClient((err) => console.error(err), { url: getEnv("REDIS_CONNECT_URL") }); - await client.connect() - } - - return client; -} \ No newline at end of file diff --git a/packages/server/src/middlewares/DoorAuthModes.ts b/packages/server/src/middlewares/DoorAuthModes.ts deleted file mode 100644 index 235598a..0000000 --- a/packages/server/src/middlewares/DoorAuthModes.ts +++ /dev/null @@ -1,66 +0,0 @@ -import type { Request, RequestHandler } from "express"; -import { getRedisClient } from "../clients/db/RedisDbProvider"; -import { getAllDoorNames, getAuthModes, getDoorSettingString, getEnv } from "../util/EnvConfigUtil"; -import { IAuthMode } from "../types/IAuthMode"; -import { IDoorConfig } from "../types/IDoorConfig"; -import { doorRotatingKey } from "../types/RedisKeys"; -import crypto from "crypto"; -import fetch from "node-fetch"; - -const client = await getRedisClient(); - -export const HandleAuthMode: RequestHandler = async (req, res, next) => { - const authModes = getAuthModes(req.params.id); - - if (authModes.length === 0) { - res.status(404).json({ msg: `Unknown door ${req.params.id}` }); - return; - } - - const checkAuth = async (mode: IAuthMode): Promise => { - switch(mode) { - case IAuthMode.FIXED_PIN: return handleFixedPinAuth(req) - case IAuthMode.RANDOM_ROTATING_KEY: return await handleRandomRotatingKeyAuth(req) - default: return false; - } - } - - const isAuthorized = (await Promise.all(authModes.map((mode) => checkAuth(mode)))).some(b => b); - - if (!isAuthorized) { - res.status(401).json({ msg: 'Unauthorized' }); - return; - } - - next(); -} - -const handleFixedPinAuth = (req: Request): boolean => { - const fixedPin = getDoorSettingString(req.params.id, IDoorConfig.FIXED_PIN); - return fixedPin !== undefined && req.query['key'] === fixedPin; -} - -const handleRandomRotatingKeyAuth = async (req: Request): Promise => { - const currentPin = await client.get(doorRotatingKey(req.params.id)); - - if (currentPin === req.query['rotatingKey']) { - await replaceDoorRandomKey(req.params.id); - return true; - } - return false; -} - -export const initializeRandomDoorPins = () => { - const doors = getAllDoorNames(); - doors.forEach(replaceDoorRandomKey); -} - -export const replaceDoorRandomKey = async (door: string) => { - const newKey = crypto.randomBytes(20).toString('hex'); - - await client.put(doorRotatingKey(door), newKey); - - const message = `New key for door ${door}! Unlock link: ${getEnv("BASE_DOMAIN")}/door/${door}?rotatingKey=${newKey}`; - console.log(message); - await fetch(getEnv("ROTATING_KEY_NTFY"), { method: "POST", body: message }); -} \ No newline at end of file diff --git a/packages/server/src/middlewares/TimeLockMiddleware.ts b/packages/server/src/middlewares/TimeLockMiddleware.ts deleted file mode 100644 index 78b8da8..0000000 --- a/packages/server/src/middlewares/TimeLockMiddleware.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { RequestHandler } from "express"; -import { getDoorSettingTimeLock } from "../util/EnvConfigUtil"; -import { IDoorStatus } from "../types/IDoorStatus"; - -export const TimeLockVerify: RequestHandler = async (req, res, next) => { - const timeLock = getDoorSettingTimeLock(req.params.id); - - const timeHr = (new Date()).getHours(); - - - if (timeHr >= timeLock[0] || timeHr <= timeLock[1]) { - res.status(410).json({ status: IDoorStatus.TIME_LOCK, msg: 'Sorry! This door is locked at this hour, try again later' }); - return; - } - - next(); -} \ No newline at end of file diff --git a/packages/server/src/routers/DoorRouter.ts b/packages/server/src/routers/DoorRouter.ts deleted file mode 100644 index f23229a..0000000 --- a/packages/server/src/routers/DoorRouter.ts +++ /dev/null @@ -1,65 +0,0 @@ -import express from "express"; -import { getRedisClient } from "../clients/db/RedisDbProvider"; -import { doorStatusKey } from "../types/RedisKeys"; -import { HandleAuthMode } from "../middlewares/DoorAuthModes"; -import { getAllDoorNames, getAuthModes, getDoorSettingNumber, getDoorSettingString, getEnv } from "../util/EnvConfigUtil"; -import { IDoorConfig } from "../types/IDoorConfig"; -import { TimeLockVerify } from "../middlewares/TimeLockMiddleware"; -import { IDoorStatus } from "../types/IDoorStatus"; - -const router = express.Router(); -const client = await getRedisClient(); - -router.get('/', async (req, res) => { - res.redirect(`/api/door/${getEnv("DEFAULT_DOOR") || getAllDoorNames()[0]}`); -}); - -router.get('/:id', async(req, res) => { - const doorId = req.params.id; - const authModes = getAuthModes(doorId); - const timeout = getDoorSettingNumber(doorId, IDoorConfig.OPEN_TIMEOUT)|| 60; - - if (authModes.length === 0) { - res.status(404).json({ msg: `Door ${doorId} not found` }); - return; - } - - const status = await client.get(doorStatusKey(doorId)) ? IDoorStatus.OPEN: IDoorStatus.CLOSED; - - res.status(200).json({ id: doorId, authModes, timeout, status }); -}); - -router.get('/:id/status', TimeLockVerify, async(req, res) => { - const isOpen = await client.get(doorStatusKey(req.params.id)); - - if (isOpen) { - const fingerprint = JSON.parse(isOpen); - res.status(200).json({ status: IDoorStatus.OPEN, fingerprint }); - - if (getDoorSettingString(req.params.id, IDoorConfig.CLOSE_AFTER_POLL)) { - await client.remove(doorStatusKey(req.params.id)); - } - return; - } - - res.status(401).json({ status: IDoorStatus.CLOSED }); -}); - -router.delete('/:id/status', async(req, res) => { - await client.remove(doorStatusKey(req.params.id)); - res.status(200).json({ msg: `Closed the door ${req.params.id}` }); -}); - -router.all('/:id/auth', HandleAuthMode, async(req, res) => { - const statusKey = doorStatusKey(req.params.id); - - const fingerprint = (req as any).fingerprint; - - const timeout = getDoorSettingNumber(req.params.id, IDoorConfig.OPEN_TIMEOUT) || 60; - - await client.put(statusKey, JSON.stringify(fingerprint)); - await client.getClient().expire(statusKey, timeout); - res.status(200).json({ msg: `Opened the door "${req.params.id}" for ${timeout}s` }); -}); - -export default router; \ No newline at end of file diff --git a/packages/server/src/server.ts b/packages/server/src/server.ts deleted file mode 100644 index df9594c..0000000 --- a/packages/server/src/server.ts +++ /dev/null @@ -1,34 +0,0 @@ -import express from "express"; -import DoorRouter from "./routers/DoorRouter"; -import { initializeRandomDoorPins } from "./middlewares/DoorAuthModes"; -import path from "path"; - -const Fingerprint = require('express-fingerprint'); -const app = express(); - -app.set('trust proxy', 1); - -app.use(Fingerprint({ - parameters: [ - Fingerprint.useragent, - Fingerprint.geoip - ] -})); - -app.use((req, res, next) => { - (req as any).fingerprint.ip = req.ip; - next(); -}); - -app.use(express.json()); -app.use(express.static("dist")); -app.use('/api/door', DoorRouter); - -app.get("*", (req, res) => { - res.sendFile(path.join(__dirname, "..", "dist", "index.html")); -}); - -app.listen(5000, async () => { - console.log("listening on port 5000"); - initializeRandomDoorPins(); -}); \ No newline at end of file diff --git a/packages/server/src/types/IAccessControl.ts b/packages/server/src/types/IAccessControl.ts deleted file mode 100644 index 17301c2..0000000 --- a/packages/server/src/types/IAccessControl.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface IAccessControl { - key: string; - remainingAccess?: number; -} \ No newline at end of file diff --git a/packages/server/src/types/IAuthCallbackQuery.ts b/packages/server/src/types/IAuthCallbackQuery.ts deleted file mode 100644 index b102b42..0000000 --- a/packages/server/src/types/IAuthCallbackQuery.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default interface IAuthCallbackQuery { - action: string; - tag: string; - k1: string; - sig: string; - key: string -} \ No newline at end of file diff --git a/packages/server/src/types/IAuthMode.ts b/packages/server/src/types/IAuthMode.ts deleted file mode 100644 index 4d1ad2a..0000000 --- a/packages/server/src/types/IAuthMode.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum IAuthMode { - FIXED_PIN = "FIXED_PIN", - RANDOM_ROTATING_KEY = "RANDOM_ROTATING_KEY", -} \ No newline at end of file diff --git a/packages/server/src/types/IDoorConfig.ts b/packages/server/src/types/IDoorConfig.ts deleted file mode 100644 index 91acec9..0000000 --- a/packages/server/src/types/IDoorConfig.ts +++ /dev/null @@ -1,7 +0,0 @@ -export enum IDoorConfig { - AUTH_MODES = "AUTH_MODES", - OPEN_TIMEOUT = "OPEN_TIMEOUT", - FIXED_PIN = "FIXED_PIN", - CLOSE_AFTER_POLL="CLOSE_AFTER_POLL", - ALWAYS_LOCKED_TIME="ALWAYS_LOCKED_TIME", -} \ No newline at end of file diff --git a/packages/server/src/types/IDoorResponse.ts b/packages/server/src/types/IDoorResponse.ts deleted file mode 100644 index bd77640..0000000 --- a/packages/server/src/types/IDoorResponse.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface IDoorResponse { - id: string, - timeout: number; - buzzerCode: string; -}; diff --git a/packages/server/src/types/IDoorStatus.ts b/packages/server/src/types/IDoorStatus.ts deleted file mode 100644 index 5857a4e..0000000 --- a/packages/server/src/types/IDoorStatus.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum IDoorStatus { - OPEN="OPEN", - CLOSED="CLOSED", - TIME_LOCK="TIME_LOCK" -} \ No newline at end of file diff --git a/packages/server/src/types/RedisKeys.ts b/packages/server/src/types/RedisKeys.ts deleted file mode 100644 index 8da098d..0000000 --- a/packages/server/src/types/RedisKeys.ts +++ /dev/null @@ -1,16 +0,0 @@ -export enum RedisKeys { - CHALLENGES = "challenges", - DOORS = "doors" -} - -export function doorStatusKey(id: string) { - return concatKeys(RedisKeys.DOORS, id, 'open'); -} - -export function doorRotatingKey(id: string) { - return concatKeys(RedisKeys.DOORS, id, 'rotatingKey'); -} - -export function concatKeys(...keys: String[]) { - return keys.join(':'); -} \ No newline at end of file diff --git a/packages/server/src/util/EnvConfigUtil.ts b/packages/server/src/util/EnvConfigUtil.ts deleted file mode 100644 index 45cd1cc..0000000 --- a/packages/server/src/util/EnvConfigUtil.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { IAuthMode } from "../types/IAuthMode"; -import { IDoorConfig } from "../types/IDoorConfig"; - -export const getEnv = (key: string): string => { - return process.env[key] || ""; -} - -const doorToEnv = (door: string): string => { - return door.toUpperCase().replaceAll(' ', '_').replaceAll('-', '_'); -}; - -export const getAuthModes = (door: string): IAuthMode[] => { - const config = getDoorSettingString(door, IDoorConfig.AUTH_MODES); - - if (config) { - return JSON.parse(config); - } - - return []; -}; - -export const getDoorSettingString = (door: string, setting: IDoorConfig): string | undefined => { - return getEnv(`${setting}_${doorToEnv(door)}`); -}; - -export const getDoorSettingNumber = (door: string, setting: IDoorConfig): number => { - return parseInt(getDoorSettingString(door, setting) || "0"); -}; - -export const getDoorSettingTimeLock = (door: string): number[] => { - const config = getDoorSettingString(door, IDoorConfig.ALWAYS_LOCKED_TIME); - - if (config) { - try { - return config.split(',').map(n => parseInt(n)); - } catch (e) { - console.warn(`Config ${IDoorConfig.ALWAYS_LOCKED_TIME} for door ${door} is invalid`); - } - } - - // never locked (always -1 < hr < 25) - return [25, -1]; -} - -export const getAllDoorNames = (): string[] => { - const names: string[] = []; - - Object.keys(process.env).forEach(key => { - if (key.startsWith(IDoorConfig.AUTH_MODES)) { - names.push(key.replace(IDoorConfig.AUTH_MODES + "_", "").toLowerCase()); - } - }); - - return names; -} \ No newline at end of file diff --git a/packages/server/src/util/RateLimits.ts b/packages/server/src/util/RateLimits.ts deleted file mode 100644 index 9cfd135..0000000 --- a/packages/server/src/util/RateLimits.ts +++ /dev/null @@ -1,14 +0,0 @@ -import rateLimit from "express-rate-limit"; - - -export const uploadDownloadLimiter = rateLimit({ - windowMs: 5 * 60 * 1000, - max: 5, - skipFailedRequests: true, -}); - -export const challengeLimiter = rateLimit({ - windowMs: 5 * 60 * 1000, - max: 10, - skipFailedRequests: true, -}); \ No newline at end of file diff --git a/packages/server/tsconfig.json b/packages/server/tsconfig.json deleted file mode 100644 index 8899e03..0000000 --- a/packages/server/tsconfig.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "compilerOptions": { - // Enable latest features - "lib": ["ESNext"], - "target": "ESNext", - "module": "ESNext", - "moduleDetection": "force", - "jsx": "react-jsx", - "allowJs": true, - "outDir": "build", - - // Bundler mode - "moduleResolution": "bundler", - // "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, - "noEmit": false, - - // Best practices - "strict": false, - "skipLibCheck": true, - "noFallthroughCasesInSwitch": true, - - // // Some stricter flags - // "noUnusedLocals": true, - // "noUnusedParameters": true, - // "noPropertyAccessFromIndexSignature": true - } -} \ No newline at end of file