diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..cdd16c0 --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,4 @@ +node_modules +.wrangler +dist +.env diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000..ce3e063 --- /dev/null +++ b/backend/package.json @@ -0,0 +1,15 @@ +{ + "name": "app-s01qyw", + "type": "module", + "scripts": { + "dev": "wrangler dev" + }, + "dependencies": { + "hono": "latest" + }, + "devDependencies": { + "wrangler": "^4.129.0", + "typescript": "^5.0.0", + "@cloudflare/workers-types": "^4.0.0" + } +} \ No newline at end of file diff --git a/backend/tsconfig.json b/backend/tsconfig.json new file mode 100644 index 0000000..9ec9117 --- /dev/null +++ b/backend/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "lib": [ + "ESNext" + ], + "types": [ + "@cloudflare/workers-types" + ], + "strict": true, + "skipLibCheck": true + } +} \ No newline at end of file diff --git a/database/01_init.sql b/database/01_init.sql index f04cf75..d483343 100644 --- a/database/01_init.sql +++ b/database/01_init.sql @@ -1,13 +1,13 @@ CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY, email TEXT NOT NULL UNIQUE, - full_name TEXT, - role TEXT NOT NULL DEFAULT 'passenger', + full_name TEXT NOT NULL, username TEXT UNIQUE, + role TEXT NOT NULL DEFAULT 'passenger' CHECK (role IN ('passenger', 'driver', 'admin', 'member')), mauth_token TEXT, phone TEXT, citizen_id TEXT, - is_banned INTEGER DEFAULT 0, + is_banned INTEGER NOT NULL DEFAULT 0, created_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP ); @@ -15,29 +15,29 @@ CREATE TABLE IF NOT EXISTS companion_safety_profiles ( user_email TEXT PRIMARY KEY, persona_label TEXT NOT NULL, safety_consent INTEGER NOT NULL DEFAULT 0, - background_check_status TEXT NOT NULL DEFAULT 'not_started', + background_check_status TEXT NOT NULL DEFAULT 'not_started' CHECK (background_check_status IN ('not_started', 'pending', 'approved', 'rejected')), reviewed_at TEXT, created_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (user_email) REFERENCES users(email) + FOREIGN KEY (user_email) REFERENCES users(email) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS companion_circles ( id TEXT PRIMARY KEY, name TEXT NOT NULL, - cadence TEXT NOT NULL, - status TEXT NOT NULL DEFAULT 'forming', + cadence TEXT NOT NULL CHECK (cadence IN ('weekly', 'biweekly', 'monthly')), + status TEXT NOT NULL DEFAULT 'forming' CHECK (status IN ('forming', 'active', 'paused')), created_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS companion_circle_members ( circle_id TEXT NOT NULL, user_email TEXT NOT NULL, - status TEXT NOT NULL DEFAULT 'invited', + status TEXT NOT NULL DEFAULT 'invited' CHECK (status IN ('invited', 'accepted', 'declined', 'left')), created_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (circle_id, user_email), - FOREIGN KEY (circle_id) REFERENCES companion_circles(id), - FOREIGN KEY (user_email) REFERENCES users(email) + FOREIGN KEY (circle_id) REFERENCES companion_circles(id) ON DELETE CASCADE, + FOREIGN KEY (user_email) REFERENCES users(email) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS companion_activities ( id TEXT PRIMARY KEY, @@ -48,21 +48,21 @@ CREATE TABLE IF NOT EXISTS companion_activities ( location_label TEXT NOT NULL, starts_at TEXT NOT NULL, max_attendees INTEGER, - status TEXT NOT NULL DEFAULT 'planned', + status TEXT NOT NULL DEFAULT 'planned' CHECK (status IN ('planned', 'cancelled', 'completed')), created_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (circle_id) REFERENCES companion_circles(id), - FOREIGN KEY (created_by_email) REFERENCES users(email) + FOREIGN KEY (circle_id) REFERENCES companion_circles(id) ON DELETE CASCADE, + FOREIGN KEY (created_by_email) REFERENCES users(email) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS companion_activity_rsvps ( activity_id TEXT NOT NULL, user_email TEXT NOT NULL, - status TEXT NOT NULL DEFAULT 'going', + status TEXT NOT NULL DEFAULT 'going' CHECK (status IN ('going', 'maybe', 'not_going')), created_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (activity_id, user_email), - FOREIGN KEY (activity_id) REFERENCES companion_activities(id), - FOREIGN KEY (user_email) REFERENCES users(email) + FOREIGN KEY (activity_id) REFERENCES companion_activities(id) ON DELETE CASCADE, + FOREIGN KEY (user_email) REFERENCES users(email) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS driver_profiles ( id TEXT PRIMARY KEY, @@ -76,7 +76,7 @@ CREATE TABLE IF NOT EXISTS driver_profiles ( current_lng REAL, created_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (user_email) REFERENCES users(email) + FOREIGN KEY (user_email) REFERENCES users(email) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS rides ( id TEXT PRIMARY KEY, @@ -88,14 +88,14 @@ CREATE TABLE IF NOT EXISTS rides ( destination_location TEXT NOT NULL, destination_lat REAL NOT NULL, destination_lng REAL NOT NULL, - status TEXT NOT NULL DEFAULT 'requested', + status TEXT NOT NULL DEFAULT 'requested' CHECK (status IN ('requested', 'accepted', 'picked_up', 'completed', 'cancelled')), driver_lat REAL, driver_lng REAL, rating INTEGER, created_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (passenger_email) REFERENCES users(email), - FOREIGN KEY (driver_email) REFERENCES users(email) + FOREIGN KEY (passenger_email) REFERENCES users(email) ON DELETE CASCADE, + FOREIGN KEY (driver_email) REFERENCES users(email) ON DELETE SET NULL ); CREATE TABLE IF NOT EXISTS orders ( order_id TEXT PRIMARY KEY, @@ -106,28 +106,31 @@ CREATE TABLE IF NOT EXISTS orders ( payment_session_id TEXT NOT NULL, created_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (user_email) REFERENCES users(email) + FOREIGN KEY (user_email) REFERENCES users(email) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS notifications ( id TEXT PRIMARY KEY, recipient_email TEXT NOT NULL, - type TEXT NOT NULL DEFAULT 'info', + type TEXT NOT NULL DEFAULT 'info' CHECK (type IN ('info', 'success', 'warning', 'mention', 'system')), title TEXT NOT NULL, body TEXT, link TEXT, actor_email TEXT, is_read INTEGER NOT NULL DEFAULT 0, created_at TEXT DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (recipient_email) REFERENCES users(email), - FOREIGN KEY (actor_email) REFERENCES users(email) + FOREIGN KEY (recipient_email) REFERENCES users(email) ON DELETE CASCADE, + FOREIGN KEY (actor_email) REFERENCES users(email) ON DELETE SET NULL ); CREATE INDEX IF NOT EXISTS idx_users_email ON users(email); +CREATE INDEX IF NOT EXISTS idx_users_username ON users(username); CREATE INDEX IF NOT EXISTS idx_companion_circle_members_user ON companion_circle_members(user_email); CREATE INDEX IF NOT EXISTS idx_companion_activities_circle ON companion_activities(circle_id); CREATE INDEX IF NOT EXISTS idx_companion_activity_rsvps_user ON companion_activity_rsvps(user_email); CREATE INDEX IF NOT EXISTS idx_driver_profiles_user ON driver_profiles(user_email); +CREATE INDEX IF NOT EXISTS idx_driver_profiles_coords ON driver_profiles(current_lat, current_lng); CREATE INDEX IF NOT EXISTS idx_rides_passenger ON rides(passenger_email); CREATE INDEX IF NOT EXISTS idx_rides_driver ON rides(driver_email); +CREATE INDEX IF NOT EXISTS idx_rides_status ON rides(status); CREATE INDEX IF NOT EXISTS idx_orders_user ON orders(user_email); CREATE INDEX IF NOT EXISTS idx_notifications_recipient ON notifications(recipient_email, is_read); CREATE UNIQUE INDEX IF NOT EXISTS idx_users_email ON users(email);