-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
30 changed files
with
1,204 additions
and
440 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# dev | ||
.yarn/ | ||
!.yarn/releases | ||
.vscode/* | ||
!.vscode/launch.json | ||
!.vscode/*.code-snippets | ||
.idea/workspace.xml | ||
.idea/usage.statistics.xml | ||
.idea/shelf | ||
|
||
# deps | ||
node_modules/ | ||
|
||
# env | ||
.env | ||
.env.production | ||
|
||
.turbo/ | ||
|
||
# logs | ||
logs/ | ||
*.log | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
pnpm-debug.log* | ||
lerna-debug.log* | ||
|
||
# misc | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# dev | ||
.yarn/ | ||
!.yarn/releases | ||
.vscode/* | ||
!.vscode/launch.json | ||
!.vscode/*.code-snippets | ||
.idea/workspace.xml | ||
.idea/usage.statistics.xml | ||
.idea/shelf | ||
|
||
# deps | ||
node_modules/ | ||
|
||
# env | ||
.env | ||
.env.production | ||
|
||
# logs | ||
logs/ | ||
*.log | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
pnpm-debug.log* | ||
lerna-debug.log* | ||
|
||
# misc | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# syntax=docker/dockerfile:1 | ||
|
||
# Stage 1: Base with Node.js and PNPM installed | ||
ARG NODE_VERSION=20.12.1 | ||
FROM node:${NODE_VERSION}-slim as base | ||
|
||
LABEL fly_launch_runtime="Node.js" | ||
|
||
WORKDIR /app | ||
|
||
ENV NODE_ENV=production | ||
|
||
ARG PNPM_VERSION=9.0.4 | ||
RUN npm install -g pnpm@$PNPM_VERSION | ||
|
||
# Stage 2: Build Stage | ||
FROM base as build | ||
|
||
ENV NODE_ENV=development | ||
|
||
RUN apt-get update -qq && \ | ||
apt-get install --no-install-recommends -y build-essential node-gyp pkg-config python-is-python3 && \ | ||
rm -rf /var/lib/apt/lists/* | ||
|
||
COPY --link . . | ||
|
||
RUN pnpm --filter=ars deploy pruned-ars && \ | ||
cd pruned-ars && \ | ||
pnpm install && \ | ||
pnpm run build && \ | ||
pnpm prune --prod | ||
|
||
ENV NODE_ENV=production | ||
|
||
# Stage 3: Production Image | ||
FROM base | ||
|
||
COPY --from=build /app/pruned-ars /app | ||
|
||
EXPOSE 4444 | ||
|
||
CMD ["pnpm", "run", "start"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# ARS (Advanced Registration System) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# fly.toml app configuration file generated for ragger-snowy-sound-6162 on 2024-08-30T01:38:21+02:00 | ||
# | ||
# See https://fly.io/docs/reference/configuration/ for information about how to use this file. | ||
# | ||
|
||
app = 'ragger-snowy-sound-6162' | ||
primary_region = 'arn' | ||
|
||
[build] | ||
|
||
[http_service] | ||
internal_port = 4444 | ||
force_https = true | ||
auto_stop_machines = 'stop' | ||
auto_start_machines = true | ||
min_machines_running = 0 | ||
processes = ['app'] | ||
|
||
[[vm]] | ||
memory = '1gb' | ||
cpu_kind = 'shared' | ||
cpus = 1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
{ | ||
"name": "@echo-webkom/ars", | ||
"scripts": { | ||
"start": "pnpm with-env node dist/index.js", | ||
"build": "pnpm with-env esbuild src/**/*.ts --bundle --platform=node --outdir=dist", | ||
"dev": "pnpm with-env tsx watch src/index.ts", | ||
"test": "vitest", | ||
"clean": "rm -rf .turbo node_modules", | ||
"lint": "eslint", | ||
"typecheck": "tsc --noEmit", | ||
"with-env": "dotenv -e ../../.env --" | ||
}, | ||
"dependencies": { | ||
"@echo-webkom/db": "workspace:*", | ||
"@hono/node-server": "1.12.2", | ||
"date-fns": "3.6.0", | ||
"drizzle-orm": "0.33.0", | ||
"hono": "4.5.9", | ||
"zod": "3.23.8" | ||
}, | ||
"devDependencies": { | ||
"@flydotio/dockerfile": "0.5.8", | ||
"@types/node": "20.16.1", | ||
"dotenv-cli": "7.4.2", | ||
"esbuild": "0.23.1", | ||
"tsx": "4.17.0", | ||
"typescript": "5.5.4", | ||
"vitest": "2.0.5" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
import { isFuture, isPast } from "date-fns"; | ||
|
||
import { getCorrectSpotrange } from "./correct-spot-range"; | ||
import { createApp } from "./create-app"; | ||
import { isUserBanned } from "./is-user-banned"; | ||
import { isUserComplete } from "./is-user-complete"; | ||
import { doesIntersect } from "./list"; | ||
import { logger } from "./logger"; | ||
import { auth } from "./middleware"; | ||
import { | ||
getExisitingRegistration, | ||
getHappening, | ||
getHostGroups, | ||
getUser, | ||
registerUserToHappening, | ||
} from "./queries"; | ||
import { registerJsonSchema } from "./schema"; | ||
|
||
const app = createApp(); | ||
|
||
app.post("/", auth, async (c) => { | ||
const json = await c.req.json().catch(() => {}); | ||
const { success, data } = registerJsonSchema.safeParse(json); | ||
|
||
if (!success) { | ||
logger.error("Invalid JSON", json); | ||
return c.json({ success: false, message: "Ugyldig JSON" }); | ||
} | ||
|
||
const { userId, happeningId, questions } = data; | ||
|
||
const user = await getUser(userId); | ||
|
||
if (!user) { | ||
logger.error("User not found", { | ||
userId, | ||
}); | ||
return c.json({ success: false, message: "Brukeren finnes ikke" }); | ||
} | ||
|
||
const happening = await getHappening(happeningId); | ||
|
||
if (!happening) { | ||
logger.error("Happening not found", { | ||
happeningId, | ||
}); | ||
return c.json({ success: false, message: "Arrangementet finnes ikke" }); | ||
} | ||
|
||
if (!isUserComplete(user)) { | ||
logger.error("User is not complete", { | ||
userId, | ||
}); | ||
return c.json({ success: false, message: "Brukeren er ikke fullført" }); | ||
} | ||
|
||
const isBanned = await isUserBanned(user, happening); | ||
if (isBanned) { | ||
logger.error("User is banned", { | ||
userId, | ||
}); | ||
return c.json({ success: false, message: "Brukeren er utestengt" }); | ||
} | ||
|
||
const exisitingRegistration = await getExisitingRegistration(userId, happeningId); | ||
|
||
if (exisitingRegistration) { | ||
logger.error("Registration already exists", { | ||
userId, | ||
happeningId, | ||
}); | ||
|
||
const status = | ||
exisitingRegistration.status === "registered" | ||
? "Du er allerede påmeldt dette arrangementet" | ||
: "Du er allerede på venteliste til dette arrangementet"; | ||
|
||
return c.json({ | ||
success: false, | ||
message: status, | ||
}); | ||
} | ||
|
||
const canEarlyRegister = doesIntersect( | ||
happening.registrationGroups ?? [], | ||
user.memberships.map((membership) => membership.groupId), | ||
); | ||
|
||
if (!canEarlyRegister && happening.registrationStart && isFuture(happening.registrationStart)) { | ||
logger.error("Registration is not open", { | ||
userId, | ||
happeningId, | ||
}); | ||
return c.json({ success: false, message: "Påmeldingen har ikke startet" }); | ||
} | ||
|
||
if (!canEarlyRegister && !happening.registrationStart) { | ||
logger.error("Registration is not open", { | ||
userId, | ||
happeningId, | ||
}); | ||
return c.json({ success: false, message: "Påmelding er bare for inviterte undergrupper" }); | ||
} | ||
|
||
if (happening.registrationEnd && isPast(happening.registrationEnd)) { | ||
logger.error("Registration is closed", { | ||
userId, | ||
happeningId, | ||
}); | ||
return c.json({ success: false, message: "Påmeldingen har allerede stengt" }); | ||
} | ||
|
||
const hostGroups = await getHostGroups(happeningId); | ||
|
||
const canSkipSpotRange = doesIntersect( | ||
hostGroups, | ||
user.memberships.map((membership) => membership.groupId), | ||
); | ||
|
||
const userSpotRange = getCorrectSpotrange(user.year, happening.spotRanges, canSkipSpotRange); | ||
|
||
if (!userSpotRange) { | ||
logger.error("User is not in any spot range", { | ||
userId, | ||
happeningId, | ||
}); | ||
return c.json({ success: false, message: "Brukeren er ikke i en plassering" }); | ||
} | ||
|
||
const allQuestionsAnswered = happening.questions.every((question) => { | ||
const questionExists = questions.find((q) => q.questionId === question.id); | ||
const questionAnswer = questionExists?.answer; | ||
|
||
return question.required ? !!questionAnswer : true; | ||
}); | ||
|
||
if (!allQuestionsAnswered) { | ||
logger.error("Not all questions are answered", { | ||
userId, | ||
happeningId, | ||
}); | ||
return c.json({ success: false, message: "Du må svare på alle spørsmålene" }); | ||
} | ||
|
||
const status = await registerUserToHappening(userId, happeningId, userSpotRange); | ||
|
||
if (status === "error") { | ||
logger.error("Failed to update registration", { | ||
userId, | ||
happeningId, | ||
}); | ||
return c.json({ success: false, message: "Noe gikk galt" }); | ||
} | ||
|
||
const message = | ||
status === "waitlisted" ? "Du er nå på venteliste" : "Du er nå påmeldt arrangementet"; | ||
|
||
return c.json({ | ||
success: true, | ||
message, | ||
}); | ||
}); | ||
|
||
export default app; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { type SpotRange } from "@echo-webkom/db/schemas"; | ||
|
||
export const getCorrectSpotrange = ( | ||
year: number, | ||
spotRanges: Array<SpotRange>, | ||
canSkipSpotRange: boolean, | ||
) => { | ||
return ( | ||
spotRanges.find((spotRange) => { | ||
if (canSkipSpotRange) { | ||
return true; | ||
} | ||
|
||
return year >= spotRange.minYear && year <= spotRange.maxYear; | ||
}) ?? null | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { Hono } from "hono"; | ||
|
||
export type Bindings = { | ||
ADMIN_KEY: string; | ||
}; | ||
|
||
export const createApp = () => { | ||
const app = new Hono<{ | ||
Bindings: Bindings; | ||
}>(); | ||
|
||
return app; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { serve } from "@hono/node-server"; | ||
|
||
import app from "./app"; | ||
|
||
const PORT = 4444; | ||
console.log(`Server is running on port ${PORT}`); | ||
|
||
serve({ | ||
fetch: app.fetch, | ||
port: PORT, | ||
}); |
Oops, something went wrong.