recreate backend with lambdas
This commit is contained in:
parent
9de9dcb59b
commit
79b03574ff
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"dotenv.enableAutocloaking": false
|
||||||
|
}
|
||||||
@ -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",
|
||||||
|
|||||||
@ -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");
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
1
packages/serverless/.gitignore
vendored
1
packages/serverless/.gitignore
vendored
@ -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
|
||||||
|
|||||||
56
packages/serverless/functions/api/door/auth.js
Normal file
56
packages/serverless/functions/api/door/auth.js
Normal 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);
|
||||||
|
});
|
||||||
|
};
|
||||||
44
packages/serverless/functions/api/door/info.js
Normal file
44
packages/serverless/functions/api/door/info.js
Normal 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);
|
||||||
|
});
|
||||||
|
};
|
||||||
54
packages/serverless/functions/api/door/status.js
Normal file
54
packages/serverless/functions/api/door/status.js
Normal 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);
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -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"
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user