Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@

A lightweight yet robust event point tracking system, built around college clubs.

It utilizies Apple Wallet & Google Wallet mobile passes with unique QR codes to enable students to earn points for attending events.

Penpoint is built with the Svelte 5, utilizing remote functions for a typesafe API.

> [!NOTE]
> Penpoint is under active development! It's expected to be mostly complete by the end of 2025.
> Penpoint is under active development! It's expected to be mostly complete by the fall of 2026.

## Setup
Penpoint was made with ease of setup in mind. Svelte, the framework used for this site, combines the website and backend, so you'll only have to setup this website..
Penpoint was made with ease of setup in mind. Svelte, the framework used for this site, combines the website and backend, so you'll only have to setup this website.
With that in mind, you will need an Apple Developer account and a Google Wallet API key.

> [!CAUTION]
> The setup guide is a work in progress, as is Penpoint.
292 changes: 194 additions & 98 deletions bun.lock

Large diffs are not rendered by default.

50 changes: 25 additions & 25 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,50 @@
"name": "penpoint",
"version": "0.1.0",
"devDependencies": {
"@internationalized/date": "^3.12.0",
"@lucide/svelte": "^0.561.0",
"@internationalized/date": "^3.12.2",
"@lucide/svelte": "^1.22.0",
"@sveltejs/adapter-auto": "^7.0.0",
"@sveltejs/adapter-node": "5.5.1",
"@sveltejs/kit": "2.53.0",
"@sveltejs/vite-plugin-svelte": "7.0.0",
"@tailwindcss/typography": "^0.5.19",
"@tailwindcss/vite": "^4.2.1",
"@sveltejs/adapter-node": "5.5.7",
"@sveltejs/kit": "2.68.0",
"@sveltejs/vite-plugin-svelte": "7.1.2",
"@tailwindcss/typography": "^0.5.20",
"@tailwindcss/vite": "^4.3.2",
"@tanstack/table-core": "^8.21.3",
"@types/bcrypt": "^6.0.0",
"@types/d3-scale": "^4.0.9",
"@types/d3-shape": "^3.1.8",
"@types/humanize-duration": "^3.27.4",
"@types/jsonwebtoken": "^9.0.10",
"@types/nodemailer": "^7.0.4",
"@vitest/browser": "^4.0.17",
"@vitest/browser-playwright": "^4.0.17",
"@types/nodemailer": "^8.0.1",
"@vitest/browser": "^4.1.9",
"@vitest/browser-playwright": "^4.1.9",
"bcrypt": "^6.0.0",
"bits-ui": "^2.16.3",
"bits-ui": "^2.18.1",
"clsx": "^2.1.1",
"d3-scale": "^4.0.2",
"d3-shape": "^3.2.0",
"humanize-duration": "^3.33.1",
"humanize-duration": "^3.34.0",
"jsonwebtoken": "^9.0.2",
"mdsvex": "^0.12.6",
"mdsvex": "^0.12.7",
"mode-watcher": "^1.1.0",
"nodemailer": "8.0.1",
"nodemailer": "9.0.3",
"paneforge": "^1.0.2",
"postgres": "^3.4.7",
"prettier": "^3.6.2",
"prettier-plugin-svelte": "^3.4.0",
"postgres": "^3.4.9",
"prettier": "^3.9.4",
"prettier-plugin-svelte": "^4.1.1",
"rehype-autolink-headings": "^7.1.0",
"rehype-slug": "^6.0.0",
"svelte": "5.53.3",
"svelte-check": "^4.3.2",
"svelte-sonner": "^1.0.5",
"tailwind-merge": "^3.3.1",
"svelte": "5.56.4",
"svelte-check": "^4.7.1",
"svelte-sonner": "^1.1.1",
"tailwind-merge": "^3.6.0",
"tailwind-variants": "^3.1.1",
"tailwindcss": "^4.2.1",
"tailwindcss": "^4.3.2",
"tw-animate-css": "^1.4.0",
"typescript": "^5.9.2",
"typescript": "^6.0.3",
"vaul-svelte": "^1.0.0-next.7",
"vite": "^7.1.7",
"zod": "^4.1.12"
"vite": "^8.1.1",
"zod": "^4.4.3"
},
"private": true,
"scripts": {
Expand Down
15 changes: 3 additions & 12 deletions src/hooks.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,18 @@ export const init: ServerInit = async () => {
if (privateEnv.PENPOINT_INIT) {
const { email, password } = privateEnv.PENPOINT_INIT;
const init_name = "Penny Point";
const init_id = "0000000";
const init_role = "admin"; // Don't change

async function init() {
const hashedPassword = await hashPassword(password);

await sql`
INSERT INTO students (student_id)
VALUES (${init_id})
ON CONFLICT DO NOTHING
`;

const result = await sql`
INSERT INTO users (email, student_id, name, role, password_hash, request_reason)
INSERT INTO users (email, name, role, password_hash)
VALUES (
${email},
${init_id},
${init_name},
${init_role},
${hashedPassword},
'Initial Penpoint admin user.'
${hashedPassword}
)
ON CONFLICT DO NOTHING
RETURNING *
Expand Down Expand Up @@ -95,7 +86,7 @@ export const auth = (async ({ event, resolve }) => {
const users = await sql`
SELECT *
FROM users u
WHERE u.id = ${payload.sub} AND u.role IS DISTINCT FROM 'unapproved'
WHERE u.id = ${payload.sub} AND u.role IS DISTINCT FROM 'inactive'
LIMIT 1
`.catch(() => []);

Expand Down
Loading
Loading