Skip to content
Merged

V1.1 #133

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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ notifyDMMessage(thread, sender) // → the other thread participant
```typescript
export async function runMaintenanceJobs()
// Invoked by GET /cron/maintenance from Vercel Cron every five minutes.
// Runs pod expiry, one-hour meetup reminders, recap prompts, and stale
// Runs pod expiry, one-hour meetup reminders, and stale
// waitlist promotion outside the request-serving process.
```

Expand Down
8 changes: 6 additions & 2 deletions backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,17 @@ OPENAI_MODERATION_MODEL=omni-moderation-latest
# "required" fails closed if moderation is unavailable; production defaults to required.
MODERATION_ENFORCEMENT=required

# Public base URL of this API, used in emailed links (web account deletion).
# Defaults to https://api.theovalapp.com when unset.
# API_PUBLIC_URL=https://api.theovalapp.com

# Universal/App Links
IOS_APP_ID=com.bradyvb.ovalapp
APPLE_TEAM_ID=687FPU46UV
ANDROID_PACKAGE=com.bradyvb.ovalapp
ANDROID_PACKAGE=com.theovalapp.app
# Add after the corresponding store listing exists. Until then, invite pages show
# a coming-soon message instead of a dead store link.
# IOS_APP_STORE_URL=https://apps.apple.com/us/app/oval/id1234567890
# ANDROID_STORE_URL=https://play.google.com/store/apps/details?id=com.bradyvb.ovalapp
# ANDROID_STORE_URL=https://play.google.com/store/apps/details?id=com.theovalapp.app
# Add after the Android production keystore exists.
# ANDROID_SHA256_CERT_FINGERPRINT=AA:BB:CC:...
32 changes: 22 additions & 10 deletions backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
-- Add an independently managed club cover image.
ALTER TABLE "Club" ADD COLUMN "coverUrl" TEXT;
ALTER TABLE "ClubMember" ADD COLUMN "permissions" TEXT;

-- Keep a durable audit trail for high-impact ownership handoffs.
CREATE TABLE "ClubOwnershipTransfer" (
"id" TEXT NOT NULL,
"clubId" TEXT NOT NULL,
"fromUserId" TEXT,
"toUserId" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

CONSTRAINT "ClubOwnershipTransfer_pkey" PRIMARY KEY ("id")
);

CREATE INDEX "ClubOwnershipTransfer_clubId_createdAt_idx"
ON "ClubOwnershipTransfer"("clubId", "createdAt");

CREATE INDEX "ClubOwnershipTransfer_fromUserId_idx"
ON "ClubOwnershipTransfer"("fromUserId");

CREATE INDEX "ClubOwnershipTransfer_toUserId_idx"
ON "ClubOwnershipTransfer"("toUserId");

ALTER TABLE "ClubOwnershipTransfer"
ADD CONSTRAINT "ClubOwnershipTransfer_clubId_fkey"
FOREIGN KEY ("clubId") REFERENCES "Club"("id") ON DELETE CASCADE ON UPDATE CASCADE;

ALTER TABLE "ClubOwnershipTransfer"
ADD CONSTRAINT "ClubOwnershipTransfer_fromUserId_fkey"
FOREIGN KEY ("fromUserId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;

ALTER TABLE "ClubOwnershipTransfer"
ADD CONSTRAINT "ClubOwnershipTransfer_toUserId_fkey"
FOREIGN KEY ("toUserId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;

-- Club membership roles are the runtime authorization source. Enforce the
-- invariant in PostgreSQL so concurrent requests cannot create co-owners.
CREATE UNIQUE INDEX "ClubMember_single_owner_per_club_idx"
ON "ClubMember"("clubId")
WHERE "role" = 'OWNER';
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
ALTER TABLE "ClubRole" ADD COLUMN "position" INTEGER NOT NULL DEFAULT 0;

WITH ranked AS (
SELECT
"id",
ROW_NUMBER() OVER (
PARTITION BY "clubId"
ORDER BY "createdAt" ASC, "name" ASC
) - 1 AS next_position
FROM "ClubRole"
)
UPDATE "ClubRole"
SET "position" = ranked.next_position
FROM ranked
WHERE "ClubRole"."id" = ranked."id";

CREATE INDEX "ClubRole_clubId_position_idx" ON "ClubRole"("clubId", "position");
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
ALTER TABLE "ClubAnnouncement"
ADD COLUMN "meetingId" TEXT,
ADD COLUMN "notifyMembers" BOOLEAN NOT NULL DEFAULT true;

ALTER TABLE "ClubAnnouncement"
ADD CONSTRAINT "ClubAnnouncement_meetingId_fkey"
FOREIGN KEY ("meetingId") REFERENCES "ClubMeeting"("id")
ON DELETE SET NULL ON UPDATE CASCADE;

CREATE INDEX "ClubAnnouncement_meetingId_idx" ON "ClubAnnouncement"("meetingId");
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
ALTER TABLE "ClubMessage" ADD COLUMN "replyToId" TEXT;

ALTER TABLE "ClubMessage"
ADD CONSTRAINT "ClubMessage_replyToId_fkey"
FOREIGN KEY ("replyToId") REFERENCES "ClubMessage"("id")
ON DELETE SET NULL ON UPDATE CASCADE;

CREATE TABLE "ClubMessageReaction" (
"id" TEXT NOT NULL,
"messageId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"emoji" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "ClubMessageReaction_pkey" PRIMARY KEY ("id")
);

ALTER TABLE "ClubMessageReaction"
ADD CONSTRAINT "ClubMessageReaction_messageId_fkey"
FOREIGN KEY ("messageId") REFERENCES "ClubMessage"("id")
ON DELETE CASCADE ON UPDATE CASCADE;

ALTER TABLE "ClubMessageReaction"
ADD CONSTRAINT "ClubMessageReaction_userId_fkey"
FOREIGN KEY ("userId") REFERENCES "User"("id")
ON DELETE CASCADE ON UPDATE CASCADE;

CREATE UNIQUE INDEX "ClubMessageReaction_messageId_userId_emoji_key"
ON "ClubMessageReaction"("messageId", "userId", "emoji");
CREATE INDEX "ClubMessageReaction_messageId_idx" ON "ClubMessageReaction"("messageId");
CREATE INDEX "ClubMessageReaction_userId_idx" ON "ClubMessageReaction"("userId");
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE "ClubMessage"
ADD COLUMN "imageUrl" TEXT;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ALTER TABLE "Pod"
ADD COLUMN "title" TEXT,
ADD COLUMN "note" TEXT,
ADD COLUMN "locationAddress" TEXT;

ALTER TABLE "Club"
ADD COLUMN "discoveryPreference" TEXT NOT NULL DEFAULT 'CAMPUS';
Loading
Loading