feat: initial commit — backend API + student cabinet frontend
- Go backend: auth (JWT), points earn/spend, QR token generation, partners, admin grant/stats endpoints with chi router - Next.js 14 frontend: login, student dashboard, transaction history, QR display, partners list - PostgreSQL migrations (4 tables), Redis cache, Docker Compose - CORS middleware, role-based route protection, Zustand auth store Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE users (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
-- student_id is NULL for partner and admin accounts
|
||||
student_id TEXT UNIQUE,
|
||||
password_hash TEXT NOT NULL,
|
||||
role TEXT NOT NULL CHECK (role IN ('student', 'partner', 'admin')),
|
||||
-- balance is denormalised for read performance; always updated atomically with transactions table
|
||||
balance INTEGER NOT NULL DEFAULT 0 CHECK (balance >= 0),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX ON users(email);
|
||||
CREATE INDEX ON users(role);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS users;
|
||||
@@ -0,0 +1,17 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE partners (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
-- user_id references the cashier/partner account in users table
|
||||
user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
name TEXT NOT NULL,
|
||||
address TEXT NOT NULL,
|
||||
-- max_spend_pct: maximum percentage of a purchase total that can be paid with points
|
||||
max_spend_pct INTEGER NOT NULL DEFAULT 50 CHECK (max_spend_pct BETWEEN 1 AND 100),
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX ON partners(is_active);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS partners;
|
||||
@@ -0,0 +1,21 @@
|
||||
-- +goose Up
|
||||
-- Append-only ledger of all point movements. Never delete rows from this table.
|
||||
CREATE TABLE transactions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id),
|
||||
-- partner_id is NULL for earn and admin_grant transactions
|
||||
partner_id UUID REFERENCES partners(id),
|
||||
-- amount > 0 means earn/grant; amount < 0 means spend/expire
|
||||
amount INTEGER NOT NULL,
|
||||
type TEXT NOT NULL CHECK (type IN ('earn', 'spend', 'admin_grant', 'expire')),
|
||||
description TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Primary query pattern: a student's history ordered by time
|
||||
CREATE INDEX ON transactions(user_id, created_at DESC);
|
||||
-- Secondary pattern: a partner's redemption history
|
||||
CREATE INDEX ON transactions(partner_id, created_at DESC);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS transactions;
|
||||
@@ -0,0 +1,21 @@
|
||||
-- +goose Up
|
||||
-- Configurable rules that determine how many points a student earns per trigger event.
|
||||
-- Managed by administrators through the admin dashboard.
|
||||
CREATE TABLE earning_rules (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name TEXT NOT NULL,
|
||||
points_amount INTEGER NOT NULL CHECK (points_amount > 0),
|
||||
-- trigger_type matches the source system or event that initiates earning
|
||||
trigger_type TEXT NOT NULL CHECK (trigger_type IN ('attendance', 'assignment', 'referral', 'admin')),
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX ON earning_rules(trigger_type, is_active);
|
||||
|
||||
-- Seed default admin-grant rule so administrators can grant points immediately after deploy
|
||||
INSERT INTO earning_rules (name, points_amount, trigger_type, is_active)
|
||||
VALUES ('Manual admin grant', 1, 'admin', TRUE);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS earning_rules;
|
||||
Reference in New Issue
Block a user