Compare commits

..

3 Commits

Author SHA1 Message Date
fa2fa11449 deploy prod by promoting staging to be faster
Some checks failed
Build and push image for doorman-homeassistant / docker (push) Successful in 30s
Build and push Doorman UI / API / docker (push) Failing after 1m54s
Build and push image for doorman-homeassistant / deploy-gitainer (push) Successful in 5s
2025-06-08 19:31:41 -07:00
80dddd9a55 add test for not found msg 2025-06-08 19:22:41 -07:00
5595a29352 change err to msg 2025-06-08 19:19:30 -07:00
14 changed files with 41 additions and 35 deletions

View File

@ -52,10 +52,6 @@ jobs:
env:
ACCOUNT_SID: ${{ secrets.TWILIO_ACCOUNT_SID }}
AUTH_TOKEN: ${{ secrets.TWILIO_AUTH_TOKEN }}
AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
DISCORD_BOT_TOKEN: ${{ secrets.DISCORD_BOT_TOKEN }}
DISCORD_CLIENT_SECRET: ${{ secrets.DISCORD_CLIENT_SECRET }}
- name: Deploy Doorman Buzzer Client
run: bun run deploy-buzzer-client

View File

@ -9,7 +9,7 @@
"watch-build": "bun run --watch src/index.ts",
"start": "concurrently \"bun run watch-build\" \"bun run start-twilio\"",
"build": "bun run src/index.ts",
"deploy": "twilio-run deploy --load-system-env --env .env.example --service-name doorman --environment=prod --override-existing-project",
"deploy": "twilio-run promote --environment=staging --to=prod",
"deploy:staging": "twilio-run deploy --load-system-env --env .env.example --service-name doorman --environment=staging --override-existing-project"
},
"dependencies": {

View File

@ -12,6 +12,7 @@ import LokiTransport from "winston-loki";
import pTimeout, { TimeoutError } from "promise.timeout";
import { ZodError } from "zod";
import { fromError } from "zod-validation-error";
import { setResponseJson } from "../utils/responseUtils";
export type BaseEvent = { request: { cookies: {}; headers: {}; }; }
@ -248,13 +249,12 @@ export function withMetrics<T extends DoormanLambdaContext, U extends BaseEvent>
} else if (e instanceof ZodError) {
// global catch for validation errors
const response = new Twilio.Response();
response.setStatusCode(400);
response.setHeaders({
"Content-Type": "application/json"
});
// return nice to read error message from ZOD
response.setBody({ err: fromError(e).toString() });
setResponseJson(response, 400, {
msg: fromError(e).toString(),
});
callbackResult = [null, response];
}
}

View File

@ -36,7 +36,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, AuthRequestTwil
if (!config) {
getMetricFromRegistry<Counter>(metricsRegistry, AuthMetrics.DOOR_CONFIG_NOT_FOUND).inc({ door }, 1);
setResponseJson(response, 404, {
err: `Door ${door} not found`,
msg: `Door ${door} not found`,
});
return callback(null, response);
@ -57,7 +57,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, AuthRequestTwil
if (!method) {
setResponseJson(response, 401, {
err: "Invalid PIN",
msg: "Invalid PIN",
});
return callback(null, response);
}

View File

@ -71,7 +71,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, EditRequestTwil
if (!newConfigRaw) {
setResponseJson(response, 400, {
err: "Missing new config",
msg: "Missing new config",
});
return callback(null, response);
}
@ -82,7 +82,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, EditRequestTwil
if (!config) {
setResponseJson(response, 404, {
err: `Door not found ${door}`,
msg: `Door not found ${door}`,
});
return callback(null, response);
}
@ -95,7 +95,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, EditRequestTwil
// if nothing changed, we should throw since this is a pointless change
if (isDeepSubset(config, newConfig)) {
setResponseJson(response, 400, {
err: "Nothing changed in the new config",
msg: "Nothing changed in the new config",
});
return callback(null, response);

View File

@ -48,7 +48,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, InfoRequestTwil
.then(async (alias) => {
if (!alias) {
setResponseJson(response, 404, {
err: "This buzzer is not registered",
msg: "This buzzer is not registered",
});
return undefined;
}
@ -61,7 +61,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, InfoRequestTwil
if (!config) {
setResponseJson(response, 404, {
err: "This buzzer is not registered",
msg: "This buzzer is not registered",
});
} else {
if (buzzer) {

View File

@ -33,7 +33,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, NotifyRequestTw
if (req.key !== context.NOTIFY_SECRET_KEY) {
getMetricFromRegistry<Counter>(metricsRegistry, NotifyMetrics.UNAUTHENTICATED_CALL).inc(1);
setResponseJson(response, 401, {
err: "Unauthenticated call", event
msg: "Unauthenticated call", event
});
return callback(null, response);
}
@ -63,7 +63,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, NotifyRequestTw
);
} catch (e) {
console.error(e);
setResponseJson(response, 500, { err: e, event });
setResponseJson(response, 500, { msg: e, event });
return callback(null, response);
}

View File

@ -55,7 +55,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, OnboardRequestT
const config = await db.entities.onboardDoorConfig.findById(getOnboardDoorId(discordState.name));
if (!config || config.nonce !== discordState.id) {
setResponseJson(response, 404, { err: "approval not found" });
setResponseJson(response, 404, { msg: "approval not found" });
return callback(null, response);
}
@ -81,7 +81,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, OnboardRequestT
console.log(profileId);
if (!profileId) {
setResponseJson(response, 404, { err: "profile not found" });
setResponseJson(response, 404, { msg: "profile not found" });
return callback(null, response);
}
@ -144,7 +144,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, OnboardRequestT
getMetricFromRegistry<Counter>(metricsRegistry, OnboardMetrics.TRANSACTION_CONFLICT).inc(1);
console.error(e);
setResponseJson(response, 409, { err: "something went wrong during onboarding" });
setResponseJson(response, 409, { msg: "something went wrong during onboarding" });
}
return callback(null, response);
@ -153,7 +153,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, OnboardRequestT
let newConfig = req.newConfig;
if (!newConfig) {
setResponseJson(response, 400, { err: "missing newConfig" });
setResponseJson(response, 400, { msg: "missing newConfig" });
return callback(null, response);
}
@ -166,7 +166,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, OnboardRequestT
if (existingAlias) {
getMetricFromRegistry<Counter>(metricsRegistry, OnboardMetrics.BUZZER_CONFLICT).inc({ buzzer: newConfigObj.buzzer }, 1);
setResponseJson(response, 409, { err: `A buzzer is already registered with the number ${newConfigObj.buzzer}` });
setResponseJson(response, 409, { msg: `A buzzer is already registered with the number ${newConfigObj.buzzer}` });
return callback(null, response);
}
@ -174,7 +174,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, OnboardRequestT
if (existingConfig) {
getMetricFromRegistry<Counter>(metricsRegistry, OnboardMetrics.NAME_CONFLICT).inc({ name: newConfigObj.name }, 1);
setResponseJson(response, 409, { err: `A buzzer is already registered with the name ${newConfigObj.name}` });
setResponseJson(response, 409, { msg: `A buzzer is already registered with the name ${newConfigObj.name}` });
return callback(null, response);
}

View File

@ -3,6 +3,7 @@ import { waitForService, baseUrl, doorName, buzzerNumber, key, buzzerUrl } from
import { DoorStatus } from "../src/types/DoorStatus";
import { StatusResponse } from "../src/functions/api/door/status";
import { sleep } from "bun";
import { InfoResponseClient } from "../src/functions/api/door/info";
// these tests should only run locally
if (process.env.STAGE === 'staging') {
@ -21,6 +22,15 @@ describe("info path works", () => {
expect(resp.buzzer).toBe(buzzerNumber);
});
test("unknown info repsonds with a msg", async () => {
const resp = await fetch(baseUrl + `/api/door/info?door=baddoor`);
expect(resp.status).toBe(404);
const body = await resp.json() as any;
expect(body.msg).toContain("not registered");
})
test("info works from client", async () => {
const resp = await fetch(baseUrl + `/api/door/info?buzzer=${buzzerNumber}`).then(res => res.json()) as any;

View File

@ -8,7 +8,7 @@
"start-twilio": "twilio-run --live --port 4500",
"start": "concurrently \"bun run watch-build\" \"bun run start-twilio\"",
"build": "bun run src/index.ts",
"deploy": "twilio-run deploy --load-system-env --env .env.example --service-name buzzer --environment=prod --override-existing-project",
"deploy": "twilio-run promote --environment=staging --to=prod",
"deploy:staging": "twilio-run deploy --load-system-env --env .env.example --service-name buzzer --environment=staging --override-existing-project"
},
"dependencies": {

View File

@ -14,7 +14,7 @@ app.get("/api/door/info", async (c) => {
const door = c.req.query().door;
if (!door) {
return c.json({
err: "Must specify a door",
msg: "Must specify a door",
}, 400);
}
@ -24,7 +24,7 @@ app.get("/api/door/info", async (c) => {
if (!config) {
return c.json({
err: "This buzzer is not registered properly",
msg: "This buzzer is not registered properly",
}, 404);
}

View File

@ -14,17 +14,17 @@ export async function loader({ params, request }: any) {
return {};
}
const response = await fetchUrlEncoded('/api/door/info', {
const res = await fetchUrlEncoded('/api/door/info', {
door,
}).then(res => res.json());
console.log(response);
console.log(res);
if (response.err) {
if (res.msg) {
throw new Error("Not a valid door");
}
return response as DoorResponse;
return res as DoorResponse;
}
export function DoorPage() {

View File

@ -116,8 +116,8 @@ export const EditPage = ({ isOnboarding }: EditPageProps) => {
fetchUrlEncoded(apiRoute, body)
.then(res => res.json())
.then(res => {
if (res.err) {
addAlert("error", res.err);
if (res.msg) {
addAlert("error", res.msg);
return;
}
if (!isOnboarding) {

View File

@ -18,7 +18,7 @@ export const RedirectPage = () => {
fetch(state.apiRedirect + '?' + params.toString())
.then(res => res.json())
.then(res => {
if (res.err) {
if (res.msg) {
console.error(res);
window.location.href = '/';
return;