recreate backend with lambdas

This commit is contained in:
Martin Dimitrov 2024-05-03 15:08:23 -07:00
parent 9de9dcb59b
commit 79b03574ff
10 changed files with 161 additions and 9 deletions

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"dotenv.enableAutocloaking": false
}

BIN
bun.lockb

Binary file not shown.

View File

@ -8,13 +8,13 @@
}, },
"scripts": { "scripts": {
"prepare-client-serverless": "bun --filter 'doorman-client' build && cp -fr packages/client/dist/* packages/serverless/assets/ && cp -f packages/serverless/assets/index.html packages/serverless/assets/assets/index.html", "prepare-client-serverless": "bun --filter 'doorman-client' build && cp -fr packages/client/dist/* packages/serverless/assets/ && cp -f packages/serverless/assets/index.html packages/serverless/assets/assets/index.html",
"prepare-server-serverless": "bun --filter 'doorman-server' build && cp -fr packages/server/build/* packages/serverless/functions/ && mv packages/serverless/functions/server.js packages/serverless/functions/api.js",
"deploy-serverless": "bun run prepare-client-serverless && bun --filter 'serverless' deploy" "deploy-serverless": "bun run prepare-client-serverless && bun --filter 'serverless' deploy"
}, },
"peerDependencies": { "peerDependencies": {
"typescript": "^5.0.0" "typescript": "^5.0.0"
}, },
"dependencies": { "dependencies": {
"@types/twilio": "^3.19.3",
"crypto": "^1.0.1", "crypto": "^1.0.1",
"express-fingerprint": "^1.2.2", "express-fingerprint": "^1.2.2",
"hono": "^4.3.0", "hono": "^4.3.0",

View File

@ -28,7 +28,7 @@ export const AuthComponent = ({ door, secret, authMode, onError, onUnlock, runCh
}, [runCheck]) }, [runCheck])
const onSubmit = () => { const onSubmit = () => {
fetch(`/api/door/${door}/auth?key=${key}&rotatingKey=${key}`) fetch(`/api/door/auth?key=${key}&rotatingKey=${key}&door=${door}`)
.then(async res => { .then(async res => {
if (res.status !== 200) { if (res.status !== 200) {
setError("Incorrect PIN"); setError("Incorrect PIN");

View File

@ -7,7 +7,7 @@ import type { IDoorResponse } from "../../../server/src/types/IDoorResponse";
import { CountdownBar } from "../components/CountdownBar"; import { CountdownBar } from "../components/CountdownBar";
export async function loader({ params }: any) { export async function loader({ params }: any) {
const response = await fetch(params.door ? `/api/door/${params.door}`: `/api/door`).then(res => res.json()); const response = await fetch(params.door ? `/api/door/info?door=${params.door}`: `/api/door/info`).then(res => res.json());
console.log(response); console.log(response);
@ -74,7 +74,7 @@ export function DoorPage() {
} }
const timer = setInterval(async () => { const timer = setInterval(async () => {
const response = await fetch(`/api/door/${door}`).then(res => res.json()); const response = await fetch(`/api/door/info?door=${door}`).then(res => res.json());
// polling assumes that the door was opened and whatever closed it was the buzzer system... // polling assumes that the door was opened and whatever closed it was the buzzer system...
// ie. state transition from OPEN to CLOSED before timeout means that twilio opened the door // ie. state transition from OPEN to CLOSED before timeout means that twilio opened the door

View File

@ -11,7 +11,6 @@ lerna-debug.log*
.pnpm-debug.log* .pnpm-debug.log*
assets assets
functions
# Diagnostic reports (https://nodejs.org/api/report.html) # Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

View File

@ -0,0 +1,56 @@
/**
* Try to unlock the door with auth mode
*/
const redis = require('redis');
function doorStatusKey(id) {
return concatKeys("doors", id, 'open');
}
function concatKeys(...keys) {
return keys.join(':');
}
exports.handler = function(context, event, callback) {
const response = new Twilio.Response();
let door = event.door;
let pin = event.key;
if (!door || !pin) {
response.setStatusCode(400);
return callback(null, response);
}
door = door.toUpperCase();
if (!context['FIXED_PIN_' + door]) {
response.setStatusCode(404);
return callback(null, response);
}
let correctPin = context['FIXED_PIN' + door];
if (correctPin !== pin) {
response.setStatusCode(401);
return callback(null, response);
}
let client = new redis.RedisDbClient((err) => console.error(err), { url: context.REDIS_CONNECT_URL });
client.connect()
.then(async () => {
const statusKey = doorStatusKey(door);
const fingerprint = { method: "PIN" };
const timeout = context['OPEN_TIMEOUT_' + door] || 60;
await client.put(statusKey, JSON.stringify(fingerprint));
await client.getClient().expire(statusKey, timeout);
response
.setStatusCode(200)
.appendHeader('Content-Type', 'application/json')
.setBody({ msg: `Opened the door "${door}" for ${timeout}s` });
return callback(null, response);
});
};

View File

@ -0,0 +1,44 @@
/**
* Try to get door info
*/
const redis = require('redis');
function doorStatusKey(id) {
return concatKeys("doors", id, 'open');
}
function concatKeys(...keys) {
return keys.join(':');
}
exports.handler = function(context, event, callback) {
const response = new Twilio.Response();
let door = event.door || context.DEFAULT_DOOR;
if (!door) {
response.setStatusCode(400);
return callback(null, response);
}
door = door.toUpperCase();
const timeout = context['OPEN_TIMEOUT_' + door] || 60;
res.status(200).json();
let client = new RedisDbClient((err) => console.error(err), { url: context.REDIS_CONNECT_URL });
client.connect()
.then(async () => {
const status = await client.get(doorStatusKey(door)) ? "OPEN": "CLOSED";
response
.setStatusCode(200)
.appendHeader('Content-Type', 'application/json')
.setBody({ id: doorId, timeout, status });
return callback(null, response);
});
};

View File

@ -0,0 +1,54 @@
/**
* Try to update door status
*/
const redis = require('redis');
function doorStatusKey(id) {
return concatKeys("doors", id, 'open');
}
function concatKeys(...keys) {
return keys.join(':');
}
exports.handler = function(context, event, callback) {
const response = new Twilio.Response();
let door = event.door;
if (!door) {
response.setStatusCode(400);
return callback(null, response);
}
let client = new RedisDbClient((err) => console.error(err), { url: context.REDIS_CONNECT_URL });
client.connect()
.then(async () => {
const isOpen = await client.get(doorStatusKey(door));
if (isOpen) {
const fingerprint = JSON.parse(isOpen);
response
.setStatusCode(200)
.appendHeader('Content-Type', 'application/json')
.setBody({
status: "OPEN",
fingerprint,
});
await client.remove(doorStatusKey(req.params.id));
return callback(null, response);
}
response
.setStatusCode(401)
.appendHeader('Content-Type', 'application/json')
.setBody({
status: "CLOSED",
});
return callback(null, response);
});
};

View File

@ -11,10 +11,6 @@
"dependencies": { "dependencies": {
"twilio": "^3.56", "twilio": "^3.56",
"@twilio/runtime-handler": "1.3.0", "@twilio/runtime-handler": "1.3.0",
"@codegenie/serverless-express": "^4.14.1",
"express": "^4.18.2",
"express-fileupload": "^1.4.0",
"express-rate-limit": "^6.10.0",
"qrcode": "^1.5.3", "qrcode": "^1.5.3",
"redis": "^4.6.8" "redis": "^4.6.8"
}, },