@bash-app/bash-common 30.96.0 → 30.97.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/definitions.d.ts +5 -2
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +3 -2
- package/dist/definitions.js.map +1 -1
- package/dist/extendedSchemas.d.ts +257 -1
- package/dist/extendedSchemas.d.ts.map +1 -1
- package/dist/extendedSchemas.js +22 -0
- package/dist/extendedSchemas.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/bashCashPaymentUtils.d.ts +72 -0
- package/dist/utils/bashCashPaymentUtils.d.ts.map +1 -0
- package/dist/utils/bashCashPaymentUtils.js +115 -0
- package/dist/utils/bashCashPaymentUtils.js.map +1 -0
- package/dist/utils/service/apiServiceBookingApiUtils.d.ts.map +1 -1
- package/dist/utils/service/apiServiceBookingApiUtils.js +1 -0
- package/dist/utils/service/apiServiceBookingApiUtils.js.map +1 -1
- package/dist/utils/service/frontendServiceBookingUtils.d.ts +1 -0
- package/dist/utils/service/frontendServiceBookingUtils.d.ts.map +1 -1
- package/dist/utils/service/frontendServiceBookingUtils.js +1 -0
- package/dist/utils/service/frontendServiceBookingUtils.js.map +1 -1
- package/package.json +1 -1
- package/prisma/MIGRATION-CHECKLIST.md +198 -0
- package/prisma/manual-migration-add-missing-columns.sql +182 -0
- package/prisma/quick-migration.sql +47 -0
- package/prisma/schema.prisma +177 -66
- package/src/definitions.ts +6 -1
- package/src/extendedSchemas.ts +40 -0
- package/src/index.ts +1 -0
- package/src/utils/bashCashPaymentUtils.ts +146 -0
- package/src/utils/service/apiServiceBookingApiUtils.ts +1 -0
- package/src/utils/service/frontendServiceBookingUtils.ts +2 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
-- ============================================================================
|
|
2
|
+
-- BASH DATABASE MIGRATION - Missing Columns
|
|
3
|
+
-- Date: November 14, 2025
|
|
4
|
+
-- Description: Add missing columns from Prisma schema to production database
|
|
5
|
+
-- ============================================================================
|
|
6
|
+
|
|
7
|
+
-- Issue: Prisma schema has been updated but migrations weren't run
|
|
8
|
+
-- This SQL adds all missing columns that are causing runtime errors
|
|
9
|
+
|
|
10
|
+
-- ============================================================================
|
|
11
|
+
-- 1. Service Table - Add acceptsBashCash column
|
|
12
|
+
-- ============================================================================
|
|
13
|
+
-- This column controls whether a service accepts BashCash payments
|
|
14
|
+
-- Default: true (most services should accept BashCash)
|
|
15
|
+
|
|
16
|
+
ALTER TABLE "Service"
|
|
17
|
+
ADD COLUMN IF NOT EXISTS "acceptsBashCash" BOOLEAN NOT NULL DEFAULT true;
|
|
18
|
+
|
|
19
|
+
COMMENT ON COLUMN "Service"."acceptsBashCash" IS 'Whether this service accepts BashCash as payment';
|
|
20
|
+
|
|
21
|
+
-- ============================================================================
|
|
22
|
+
-- 2. User Table - Add isLightweightAccount column
|
|
23
|
+
-- ============================================================================
|
|
24
|
+
-- This column marks accounts created automatically via task invitation acceptance
|
|
25
|
+
-- These accounts need password changes and may have limited permissions initially
|
|
26
|
+
|
|
27
|
+
ALTER TABLE "User"
|
|
28
|
+
ADD COLUMN IF NOT EXISTS "isLightweightAccount" BOOLEAN NOT NULL DEFAULT false;
|
|
29
|
+
|
|
30
|
+
COMMENT ON COLUMN "User"."isLightweightAccount" IS 'True if account was auto-created via task invitation (needs password change)';
|
|
31
|
+
|
|
32
|
+
-- ============================================================================
|
|
33
|
+
-- 3. TaskComment Table - Create if not exists
|
|
34
|
+
-- ============================================================================
|
|
35
|
+
-- This table stores comments on event tasks
|
|
36
|
+
-- Used for host-volunteer communication about task details
|
|
37
|
+
|
|
38
|
+
CREATE TABLE IF NOT EXISTS "TaskComment" (
|
|
39
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
40
|
+
"taskId" TEXT NOT NULL,
|
|
41
|
+
"authorId" TEXT NOT NULL,
|
|
42
|
+
"content" TEXT NOT NULL,
|
|
43
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
44
|
+
CONSTRAINT "TaskComment_taskId_fkey" FOREIGN KEY ("taskId") REFERENCES "EventTask"("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
|
45
|
+
CONSTRAINT "TaskComment_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
-- Create indexes for TaskComment
|
|
49
|
+
CREATE INDEX IF NOT EXISTS "TaskComment_taskId_idx" ON "TaskComment"("taskId");
|
|
50
|
+
CREATE INDEX IF NOT EXISTS "TaskComment_authorId_idx" ON "TaskComment"("authorId");
|
|
51
|
+
CREATE INDEX IF NOT EXISTS "TaskComment_createdAt_idx" ON "TaskComment"("createdAt");
|
|
52
|
+
|
|
53
|
+
COMMENT ON TABLE "TaskComment" IS 'Comments on event tasks for host-volunteer communication';
|
|
54
|
+
|
|
55
|
+
-- ============================================================================
|
|
56
|
+
-- 4. TaskInvitation Table - Create if not exists
|
|
57
|
+
-- ============================================================================
|
|
58
|
+
-- This table stores email invitations for non-users to accept event tasks
|
|
59
|
+
-- When accepted, creates lightweight user accounts automatically
|
|
60
|
+
|
|
61
|
+
CREATE TABLE IF NOT EXISTS "TaskInvitation" (
|
|
62
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
63
|
+
"taskId" TEXT NOT NULL,
|
|
64
|
+
"invitedEmail" TEXT NOT NULL,
|
|
65
|
+
"token" TEXT NOT NULL UNIQUE,
|
|
66
|
+
"invitedName" TEXT,
|
|
67
|
+
"personalMessage" TEXT,
|
|
68
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
69
|
+
"expiresAt" TIMESTAMP(3) NOT NULL,
|
|
70
|
+
"acceptedAt" TIMESTAMP(3),
|
|
71
|
+
"acceptedById" TEXT,
|
|
72
|
+
"createdById" TEXT NOT NULL,
|
|
73
|
+
CONSTRAINT "TaskInvitation_taskId_fkey" FOREIGN KEY ("taskId") REFERENCES "EventTask"("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
|
74
|
+
CONSTRAINT "TaskInvitation_acceptedById_fkey" FOREIGN KEY ("acceptedById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE,
|
|
75
|
+
CONSTRAINT "TaskInvitation_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
-- Create indexes for TaskInvitation
|
|
79
|
+
CREATE INDEX IF NOT EXISTS "TaskInvitation_token_idx" ON "TaskInvitation"("token");
|
|
80
|
+
CREATE INDEX IF NOT EXISTS "TaskInvitation_invitedEmail_idx" ON "TaskInvitation"("invitedEmail");
|
|
81
|
+
CREATE INDEX IF NOT EXISTS "TaskInvitation_taskId_idx" ON "TaskInvitation"("taskId");
|
|
82
|
+
CREATE INDEX IF NOT EXISTS "TaskInvitation_createdById_idx" ON "TaskInvitation"("createdById");
|
|
83
|
+
CREATE INDEX IF NOT EXISTS "TaskInvitation_acceptedById_idx" ON "TaskInvitation"("acceptedById");
|
|
84
|
+
|
|
85
|
+
COMMENT ON TABLE "TaskInvitation" IS 'Email invitations for non-users to accept event tasks';
|
|
86
|
+
COMMENT ON COLUMN "TaskInvitation"."token" IS 'Unique CUID token for invitation link';
|
|
87
|
+
COMMENT ON COLUMN "TaskInvitation"."invitedName" IS 'Optional name hint from host';
|
|
88
|
+
COMMENT ON COLUMN "TaskInvitation"."personalMessage" IS 'Optional personal message from host';
|
|
89
|
+
COMMENT ON COLUMN "TaskInvitation"."expiresAt" IS 'Event date + 1 day';
|
|
90
|
+
|
|
91
|
+
-- ============================================================================
|
|
92
|
+
-- 5. Update User Table - Add missing task-related relations
|
|
93
|
+
-- ============================================================================
|
|
94
|
+
-- These columns should already exist if EventTask table exists, but verify:
|
|
95
|
+
|
|
96
|
+
-- Check if comments column is needed (it's a relation, not a physical column)
|
|
97
|
+
-- Check if invitations columns are needed (they're relations, not physical columns)
|
|
98
|
+
|
|
99
|
+
-- ============================================================================
|
|
100
|
+
-- 6. Verification Queries
|
|
101
|
+
-- ============================================================================
|
|
102
|
+
-- Run these to verify the migration was successful:
|
|
103
|
+
|
|
104
|
+
-- Check Service table
|
|
105
|
+
-- SELECT column_name, data_type, column_default
|
|
106
|
+
-- FROM information_schema.columns
|
|
107
|
+
-- WHERE table_name = 'Service' AND column_name = 'acceptsBashCash';
|
|
108
|
+
|
|
109
|
+
-- Check User table
|
|
110
|
+
-- SELECT column_name, data_type, column_default
|
|
111
|
+
-- FROM information_schema.columns
|
|
112
|
+
-- WHERE table_name = 'User' AND column_name = 'isLightweightAccount';
|
|
113
|
+
|
|
114
|
+
-- Check TaskComment table exists
|
|
115
|
+
-- SELECT table_name FROM information_schema.tables WHERE table_name = 'TaskComment';
|
|
116
|
+
|
|
117
|
+
-- Check TaskInvitation table exists
|
|
118
|
+
-- SELECT table_name FROM information_schema.tables WHERE table_name = 'TaskInvitation';
|
|
119
|
+
|
|
120
|
+
-- ============================================================================
|
|
121
|
+
-- 7. Rollback (if needed)
|
|
122
|
+
-- ============================================================================
|
|
123
|
+
-- DO NOT RUN UNLESS ROLLING BACK!
|
|
124
|
+
|
|
125
|
+
-- -- Remove acceptsBashCash
|
|
126
|
+
-- ALTER TABLE "Service" DROP COLUMN IF EXISTS "acceptsBashCash";
|
|
127
|
+
|
|
128
|
+
-- -- Remove isLightweightAccount
|
|
129
|
+
-- ALTER TABLE "User" DROP COLUMN IF EXISTS "isLightweightAccount";
|
|
130
|
+
|
|
131
|
+
-- -- Drop TaskComment table
|
|
132
|
+
-- DROP TABLE IF EXISTS "TaskComment" CASCADE;
|
|
133
|
+
|
|
134
|
+
-- -- Drop TaskInvitation table
|
|
135
|
+
-- DROP TABLE IF EXISTS "TaskInvitation" CASCADE;
|
|
136
|
+
|
|
137
|
+
-- ============================================================================
|
|
138
|
+
-- 8. Post-Migration Tasks
|
|
139
|
+
-- ============================================================================
|
|
140
|
+
-- After running this migration:
|
|
141
|
+
-- 1. Restart API server
|
|
142
|
+
-- 2. Clear any Redis/cache
|
|
143
|
+
-- 3. Run `npx prisma generate` in bash-common
|
|
144
|
+
-- 4. Test the /api/public-services/Sponsors endpoint
|
|
145
|
+
-- 5. Test task invitation flow
|
|
146
|
+
-- 6. Test task comment system
|
|
147
|
+
|
|
148
|
+
-- ============================================================================
|
|
149
|
+
-- EXECUTION INSTRUCTIONS
|
|
150
|
+
-- ============================================================================
|
|
151
|
+
--
|
|
152
|
+
-- To apply this migration to your PostgreSQL database:
|
|
153
|
+
--
|
|
154
|
+
-- Option 1: Using psql command line
|
|
155
|
+
-- $ psql -U your_username -d your_database -f add_missing_columns.sql
|
|
156
|
+
--
|
|
157
|
+
-- Option 2: Using Prisma
|
|
158
|
+
-- $ cd bash-common
|
|
159
|
+
-- $ npx prisma db push
|
|
160
|
+
--
|
|
161
|
+
-- Option 3: Copy-paste into Vercel Postgres Dashboard
|
|
162
|
+
-- 1. Go to Vercel Dashboard > Storage > Your Postgres DB > Query
|
|
163
|
+
-- 2. Copy-paste the SQL statements (exclude comments if needed)
|
|
164
|
+
-- 3. Execute
|
|
165
|
+
--
|
|
166
|
+
-- Option 4: Using TablePlus or another GUI
|
|
167
|
+
-- 1. Connect to your database
|
|
168
|
+
-- 2. Open SQL query window
|
|
169
|
+
-- 3. Copy-paste and execute
|
|
170
|
+
--
|
|
171
|
+
-- ============================================================================
|
|
172
|
+
-- IMPORTANT NOTES
|
|
173
|
+
-- ============================================================================
|
|
174
|
+
--
|
|
175
|
+
-- 1. This migration is IDEMPOTENT - safe to run multiple times
|
|
176
|
+
-- 2. Uses "IF NOT EXISTS" to prevent errors on re-run
|
|
177
|
+
-- 3. All new columns have DEFAULT values to avoid breaking existing data
|
|
178
|
+
-- 4. Foreign keys use CASCADE for proper cleanup
|
|
179
|
+
-- 5. Indexes are created for query performance
|
|
180
|
+
--
|
|
181
|
+
-- ============================================================================
|
|
182
|
+
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
-- Quick Migration SQL - Copy and paste into Vercel Postgres Query
|
|
2
|
+
-- Adds missing columns that are causing errors
|
|
3
|
+
|
|
4
|
+
-- 1. Add acceptsBashCash to Service table
|
|
5
|
+
ALTER TABLE "Service" ADD COLUMN IF NOT EXISTS "acceptsBashCash" BOOLEAN NOT NULL DEFAULT true;
|
|
6
|
+
|
|
7
|
+
-- 2. Add isLightweightAccount to User table
|
|
8
|
+
ALTER TABLE "User" ADD COLUMN IF NOT EXISTS "isLightweightAccount" BOOLEAN NOT NULL DEFAULT false;
|
|
9
|
+
|
|
10
|
+
-- 3. Create TaskComment table
|
|
11
|
+
CREATE TABLE IF NOT EXISTS "TaskComment" (
|
|
12
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
13
|
+
"taskId" TEXT NOT NULL,
|
|
14
|
+
"authorId" TEXT NOT NULL,
|
|
15
|
+
"content" TEXT NOT NULL,
|
|
16
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
17
|
+
CONSTRAINT "TaskComment_taskId_fkey" FOREIGN KEY ("taskId") REFERENCES "EventTask"("id") ON DELETE CASCADE,
|
|
18
|
+
CONSTRAINT "TaskComment_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE CASCADE
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
CREATE INDEX IF NOT EXISTS "TaskComment_taskId_idx" ON "TaskComment"("taskId");
|
|
22
|
+
CREATE INDEX IF NOT EXISTS "TaskComment_authorId_idx" ON "TaskComment"("authorId");
|
|
23
|
+
|
|
24
|
+
-- 4. Create TaskInvitation table
|
|
25
|
+
CREATE TABLE IF NOT EXISTS "TaskInvitation" (
|
|
26
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
27
|
+
"taskId" TEXT NOT NULL,
|
|
28
|
+
"invitedEmail" TEXT NOT NULL,
|
|
29
|
+
"token" TEXT NOT NULL UNIQUE,
|
|
30
|
+
"invitedName" TEXT,
|
|
31
|
+
"personalMessage" TEXT,
|
|
32
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
33
|
+
"expiresAt" TIMESTAMP(3) NOT NULL,
|
|
34
|
+
"acceptedAt" TIMESTAMP(3),
|
|
35
|
+
"acceptedById" TEXT,
|
|
36
|
+
"createdById" TEXT NOT NULL,
|
|
37
|
+
CONSTRAINT "TaskInvitation_taskId_fkey" FOREIGN KEY ("taskId") REFERENCES "EventTask"("id") ON DELETE CASCADE,
|
|
38
|
+
CONSTRAINT "TaskInvitation_acceptedById_fkey" FOREIGN KEY ("acceptedById") REFERENCES "User"("id") ON DELETE SET NULL,
|
|
39
|
+
CONSTRAINT "TaskInvitation_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "User"("id") ON DELETE CASCADE
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
CREATE INDEX IF NOT EXISTS "TaskInvitation_token_idx" ON "TaskInvitation"("token");
|
|
43
|
+
CREATE INDEX IF NOT EXISTS "TaskInvitation_invitedEmail_idx" ON "TaskInvitation"("invitedEmail");
|
|
44
|
+
CREATE INDEX IF NOT EXISTS "TaskInvitation_taskId_idx" ON "TaskInvitation"("taskId");
|
|
45
|
+
|
|
46
|
+
-- Done! Restart your API server after running this.
|
|
47
|
+
|
package/prisma/schema.prisma
CHANGED
|
@@ -3,9 +3,8 @@ generator client {
|
|
|
3
3
|
}
|
|
4
4
|
|
|
5
5
|
datasource db {
|
|
6
|
-
provider
|
|
7
|
-
url
|
|
8
|
-
directUrl = env("POSTGRES_URL_NON_POOLING")
|
|
6
|
+
provider = "postgresql"
|
|
7
|
+
url = env("POSTGRES_PRISMA_URL")
|
|
9
8
|
}
|
|
10
9
|
|
|
11
10
|
model Club {
|
|
@@ -54,16 +53,25 @@ model BashComment {
|
|
|
54
53
|
}
|
|
55
54
|
|
|
56
55
|
model Competition {
|
|
57
|
-
id
|
|
58
|
-
name
|
|
59
|
-
description
|
|
60
|
-
userId
|
|
61
|
-
bashEventId
|
|
62
|
-
numberOfPrizes
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
56
|
+
id String @id @default(cuid())
|
|
57
|
+
name String
|
|
58
|
+
description String @db.Text
|
|
59
|
+
userId String
|
|
60
|
+
bashEventId String
|
|
61
|
+
numberOfPrizes Int
|
|
62
|
+
competitionType CompetitionType? // Main category
|
|
63
|
+
subtype String? // Subcategory
|
|
64
|
+
rules String? @db.Text // Detailed rules/disclaimers
|
|
65
|
+
judgingType JudgingType? // How winner is determined
|
|
66
|
+
createdAt DateTime @default(now())
|
|
67
|
+
updatedAt DateTime @updatedAt
|
|
68
|
+
bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
|
|
69
|
+
owner User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
70
|
+
sponsor CompetitionSponsor[]
|
|
71
|
+
prizes Prize[]
|
|
72
|
+
|
|
73
|
+
@@index([bashEventId])
|
|
74
|
+
@@index([competitionType])
|
|
67
75
|
}
|
|
68
76
|
|
|
69
77
|
model CompetitionSponsor {
|
|
@@ -77,18 +85,51 @@ model CompetitionSponsor {
|
|
|
77
85
|
}
|
|
78
86
|
|
|
79
87
|
model EventTask {
|
|
80
|
-
id String
|
|
88
|
+
id String @id @default(cuid())
|
|
81
89
|
creatorId String
|
|
82
90
|
bashEventId String
|
|
83
91
|
title String
|
|
84
92
|
description String?
|
|
85
93
|
assignedToId String?
|
|
86
94
|
status TaskStatus?
|
|
87
|
-
createdAt DateTime?
|
|
88
|
-
assignedTo User?
|
|
89
|
-
bashEvent BashEvent
|
|
90
|
-
creator User
|
|
95
|
+
createdAt DateTime? @default(now())
|
|
96
|
+
assignedTo User? @relation("TasksAssignedToMe", fields: [assignedToId], references: [id], onDelete: Cascade)
|
|
97
|
+
bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
|
|
98
|
+
creator User @relation(fields: [creatorId], references: [id], onDelete: Cascade)
|
|
91
99
|
notificationsReferencingMe Notification[]
|
|
100
|
+
comments TaskComment[]
|
|
101
|
+
invitations TaskInvitation[]
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
model TaskComment {
|
|
105
|
+
id String @id @default(cuid())
|
|
106
|
+
taskId String
|
|
107
|
+
authorId String
|
|
108
|
+
content String @db.Text
|
|
109
|
+
createdAt DateTime @default(now())
|
|
110
|
+
task EventTask @relation(fields: [taskId], references: [id], onDelete: Cascade)
|
|
111
|
+
author User @relation("TaskCommentsAuthored", fields: [authorId], references: [id], onDelete: Cascade)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
model TaskInvitation {
|
|
115
|
+
id String @id @default(cuid())
|
|
116
|
+
taskId String
|
|
117
|
+
invitedEmail String
|
|
118
|
+
token String @unique @default(cuid()) // CUID for security
|
|
119
|
+
invitedName String? // Optional name hint from host
|
|
120
|
+
personalMessage String? // Optional message from host
|
|
121
|
+
createdAt DateTime @default(now())
|
|
122
|
+
expiresAt DateTime // Event date + 1 day
|
|
123
|
+
acceptedAt DateTime?
|
|
124
|
+
acceptedById String? // User who accepted (might be newly created)
|
|
125
|
+
createdById String // Host who sent invitation
|
|
126
|
+
task EventTask @relation(fields: [taskId], references: [id], onDelete: Cascade)
|
|
127
|
+
acceptedBy User? @relation("TaskInvitationsAccepted", fields: [acceptedById], references: [id], onDelete: SetNull)
|
|
128
|
+
createdBy User @relation("TaskInvitationsSent", fields: [createdById], references: [id], onDelete: Cascade)
|
|
129
|
+
|
|
130
|
+
@@index([token])
|
|
131
|
+
@@index([invitedEmail])
|
|
132
|
+
@@index([taskId])
|
|
92
133
|
}
|
|
93
134
|
|
|
94
135
|
model Reminder {
|
|
@@ -699,14 +740,20 @@ model Prize {
|
|
|
699
740
|
id String @id @default(cuid())
|
|
700
741
|
prizeWorth Float?
|
|
701
742
|
prizeType PrizeType
|
|
702
|
-
name String
|
|
743
|
+
name String // Prize name (e.g., "$100 Cash", "Backpack")
|
|
744
|
+
placeName String? // Display name (e.g., "1st Place", "Winner")
|
|
703
745
|
prizeLevel Int
|
|
704
746
|
description String
|
|
705
747
|
competitionId String?
|
|
706
748
|
paidOn String?
|
|
707
749
|
winnerUserId String?
|
|
708
|
-
|
|
750
|
+
wonAt DateTime? // When the prize was awarded
|
|
751
|
+
claimedAt DateTime? // When the prize was claimed
|
|
752
|
+
competition Competition? @relation(fields: [competitionId], references: [id], onDelete: Cascade)
|
|
709
753
|
winner User? @relation(fields: [winnerUserId], references: [id], onDelete: Cascade)
|
|
754
|
+
|
|
755
|
+
@@index([competitionId])
|
|
756
|
+
@@index([winnerUserId]) // For fetching user's wins
|
|
710
757
|
}
|
|
711
758
|
|
|
712
759
|
model Review {
|
|
@@ -774,16 +821,22 @@ model SocialMediaProfile {
|
|
|
774
821
|
}
|
|
775
822
|
|
|
776
823
|
model User {
|
|
777
|
-
id
|
|
778
|
-
username
|
|
779
|
-
email
|
|
780
|
-
createdOn
|
|
781
|
-
stripeCustomerId
|
|
782
|
-
stripeAccountId
|
|
783
|
-
isSuperUser
|
|
784
|
-
isSuspended
|
|
785
|
-
intent
|
|
786
|
-
googleCalendarAccess
|
|
824
|
+
id String @id @default(cuid())
|
|
825
|
+
username String? @unique
|
|
826
|
+
email String @unique
|
|
827
|
+
createdOn DateTime @default(now())
|
|
828
|
+
stripeCustomerId String? @unique
|
|
829
|
+
stripeAccountId String? @unique
|
|
830
|
+
isSuperUser Boolean @default(false)
|
|
831
|
+
isSuspended Boolean @default(false)
|
|
832
|
+
intent UserIntent?
|
|
833
|
+
googleCalendarAccess String?
|
|
834
|
+
|
|
835
|
+
// Google OAuth tokens for API access (Contacts, Calendar, etc.)
|
|
836
|
+
googleAccessToken String?
|
|
837
|
+
googleRefreshToken String?
|
|
838
|
+
googleTokenExpiry DateTime?
|
|
839
|
+
|
|
787
840
|
givenName String?
|
|
788
841
|
familyName String?
|
|
789
842
|
nameChangeCount Int @default(0)
|
|
@@ -868,8 +921,9 @@ model User {
|
|
|
868
921
|
|
|
869
922
|
// Security Fields
|
|
870
923
|
accountLockedUntil DateTime? // Account lockout timestamp for failed login protection
|
|
871
|
-
|
|
872
|
-
|
|
924
|
+
revokedTokens RevokedToken[] @relation("RevokedTokens") // Revoked JWT tokens
|
|
925
|
+
auditLogs AuditLog[] @relation("UserAuditLogs")
|
|
926
|
+
auditLogsPerformed AuditLog[] @relation("PerformedByUser")
|
|
873
927
|
consents UserConsent[]
|
|
874
928
|
|
|
875
929
|
associatedBashes AssociatedBash[]
|
|
@@ -902,6 +956,9 @@ model User {
|
|
|
902
956
|
documentID DocumentID?
|
|
903
957
|
eventTasksAssignedToMe EventTask[] @relation("TasksAssignedToMe")
|
|
904
958
|
eventTasks EventTask[]
|
|
959
|
+
taskCommentsAuthored TaskComment[] @relation("TaskCommentsAuthored")
|
|
960
|
+
taskInvitationsSent TaskInvitation[] @relation("TaskInvitationsSent")
|
|
961
|
+
taskInvitationsAccepted TaskInvitation[] @relation("TaskInvitationsAccepted")
|
|
905
962
|
exhibitorBookingRequestsAsHost ExhibitorBookingRequest[] @relation("ExhibitorBookingHost")
|
|
906
963
|
investments Investment[]
|
|
907
964
|
invitationsCreatedByMe Invitation[] @relation("InvitationsCreatedByMe")
|
|
@@ -1007,6 +1064,7 @@ model UserPreferences {
|
|
|
1007
1064
|
showEmailPublicly Boolean @default(true)
|
|
1008
1065
|
showPhonePublicly Boolean @default(false)
|
|
1009
1066
|
showAddressPublicly Boolean @default(false)
|
|
1067
|
+
showCompetitionWins Boolean @default(true) // Display competition wins on public profile
|
|
1010
1068
|
theme String @default("system")
|
|
1011
1069
|
language String @default("en")
|
|
1012
1070
|
timeZone String @default("America/New_York")
|
|
@@ -1303,6 +1361,7 @@ model Service {
|
|
|
1303
1361
|
isFreeFirstListing Boolean @default(false)
|
|
1304
1362
|
monthlyPrice Decimal @default(0)
|
|
1305
1363
|
serviceListingStripeSubscriptionId String?
|
|
1364
|
+
acceptsBashCash Boolean @default(true) // NEW: Allow BashCash payments
|
|
1306
1365
|
associatedServicesReferencingMe AssociatedService[]
|
|
1307
1366
|
exhibitorBookingRequests ExhibitorBookingRequest[] @relation("ExhibitorBookingService")
|
|
1308
1367
|
notification Notification[]
|
|
@@ -1826,40 +1885,42 @@ model ServiceBookingCheckout {
|
|
|
1826
1885
|
}
|
|
1827
1886
|
|
|
1828
1887
|
model ServiceBooking {
|
|
1829
|
-
id
|
|
1830
|
-
creatorId
|
|
1831
|
-
forUserId
|
|
1832
|
-
daysTotalATBCents
|
|
1833
|
-
serviceId
|
|
1834
|
-
rateOption
|
|
1835
|
-
flatRateCents
|
|
1836
|
-
status
|
|
1837
|
-
bookingType
|
|
1838
|
-
timezone
|
|
1839
|
-
requestedOn
|
|
1840
|
-
requestDecisionOn
|
|
1841
|
-
bookedOn
|
|
1842
|
-
canceledOn
|
|
1843
|
-
isFreeGuest
|
|
1844
|
-
allowPromiseToPay
|
|
1845
|
-
totalATBCents
|
|
1846
|
-
subtotalATBCents
|
|
1847
|
-
bashEventId
|
|
1848
|
-
isVendorBid
|
|
1849
|
-
vendorBidAmountCents
|
|
1850
|
-
vendorEventDetails
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1888
|
+
id String @id @default(cuid())
|
|
1889
|
+
creatorId String
|
|
1890
|
+
forUserId String
|
|
1891
|
+
daysTotalATBCents Int
|
|
1892
|
+
serviceId String
|
|
1893
|
+
rateOption ServiceBookingRateOption @default(TimeBased)
|
|
1894
|
+
flatRateCents Int?
|
|
1895
|
+
status ServiceBookingStatus @default(Pending)
|
|
1896
|
+
bookingType ServiceBookingType @default(Request)
|
|
1897
|
+
timezone String
|
|
1898
|
+
requestedOn DateTime?
|
|
1899
|
+
requestDecisionOn DateTime?
|
|
1900
|
+
bookedOn DateTime?
|
|
1901
|
+
canceledOn DateTime?
|
|
1902
|
+
isFreeGuest Boolean @default(false)
|
|
1903
|
+
allowPromiseToPay Boolean @default(false)
|
|
1904
|
+
totalATBCents Int
|
|
1905
|
+
subtotalATBCents Int @default(0)
|
|
1906
|
+
bashEventId String?
|
|
1907
|
+
isVendorBid Boolean @default(false)
|
|
1908
|
+
vendorBidAmountCents Int?
|
|
1909
|
+
vendorEventDetails String?
|
|
1910
|
+
bashCashApplied Int? // BashCash credits applied to this booking
|
|
1911
|
+
bashCashTransactionId String? // Link to BashCreditTransaction
|
|
1912
|
+
notification Notification[]
|
|
1913
|
+
bashEvent BashEvent? @relation(fields: [bashEventId], references: [id])
|
|
1914
|
+
creator User @relation("BookingCreator", fields: [creatorId], references: [id])
|
|
1915
|
+
forUser User @relation("BookingForUser", fields: [forUserId], references: [id])
|
|
1916
|
+
service Service @relation(fields: [serviceId], references: [id])
|
|
1917
|
+
addOns ServiceBookingAddOn[]
|
|
1918
|
+
checkout ServiceBookingCheckout?
|
|
1919
|
+
bookedDays ServiceBookingDay[]
|
|
1920
|
+
additionalFees ServiceBookingFee[]
|
|
1921
|
+
messages ServiceBookingMessage[]
|
|
1922
|
+
packages ServiceBookingPackage[]
|
|
1923
|
+
commissions BookingCommission[] @relation("BookingCommissions")
|
|
1863
1924
|
|
|
1864
1925
|
@@index([status])
|
|
1865
1926
|
@@index([creatorId])
|
|
@@ -2756,6 +2817,31 @@ enum PrizeType {
|
|
|
2756
2817
|
Combo
|
|
2757
2818
|
}
|
|
2758
2819
|
|
|
2820
|
+
enum CompetitionType {
|
|
2821
|
+
RAFFLE // Raffles & Drawings
|
|
2822
|
+
SKILL // Skill-Based Contests
|
|
2823
|
+
PHYSICAL // Physical & Outdoor
|
|
2824
|
+
INTERACTIVE // Interactive Audience
|
|
2825
|
+
SOCIAL_MEDIA // Social Media-Driven
|
|
2826
|
+
KIDS_FAMILY // Kids & Family
|
|
2827
|
+
FOOD_DRINK // Food & Drink
|
|
2828
|
+
VENDOR_BOOTH // Vendor Booth
|
|
2829
|
+
BUSINESS_PROFESSIONAL // Business / Professional
|
|
2830
|
+
RANDOMIZED // Randomized Participation
|
|
2831
|
+
SEASONAL_HOLIDAY // Seasonal or Holiday
|
|
2832
|
+
AUDIENCE_VOTING // Audience Voting-Based
|
|
2833
|
+
}
|
|
2834
|
+
|
|
2835
|
+
enum JudgingType {
|
|
2836
|
+
RANDOM_DRAW // Raffle-style random selection
|
|
2837
|
+
JUDGES // Panel of judges decides
|
|
2838
|
+
AUDIENCE_VOTE // Attendees vote
|
|
2839
|
+
METRICS // Based on measurable metrics
|
|
2840
|
+
HOST_CHOOSES // Host/organizer picks winner
|
|
2841
|
+
FIRST_COME // First X people win
|
|
2842
|
+
HIGHEST_BID // Auction-style
|
|
2843
|
+
}
|
|
2844
|
+
|
|
2759
2845
|
enum Sex {
|
|
2760
2846
|
Male
|
|
2761
2847
|
Female
|
|
@@ -4201,6 +4287,25 @@ model AuditLog {
|
|
|
4201
4287
|
@@index([ipAddress])
|
|
4202
4288
|
}
|
|
4203
4289
|
|
|
4290
|
+
/// Token Revocation System
|
|
4291
|
+
/// Tracks revoked JWT tokens to prevent their reuse
|
|
4292
|
+
/// Required for secure logout, password changes, and account termination
|
|
4293
|
+
model RevokedToken {
|
|
4294
|
+
id String @id @default(cuid())
|
|
4295
|
+
userId String // Owner of the revoked token
|
|
4296
|
+
jti String @unique // JWT ID from token payload
|
|
4297
|
+
revokedAt DateTime @default(now())
|
|
4298
|
+
expiresAt DateTime // When the original token would have expired (for cleanup)
|
|
4299
|
+
reason String? // Why it was revoked: "logout", "password_change", "suspicious_activity", etc.
|
|
4300
|
+
|
|
4301
|
+
// Relation to user
|
|
4302
|
+
user User @relation("RevokedTokens", fields: [userId], references: [id], onDelete: Cascade)
|
|
4303
|
+
|
|
4304
|
+
@@index([jti]) // Fast lookup for token validation
|
|
4305
|
+
@@index([userId, revokedAt]) // User-specific revocation history
|
|
4306
|
+
@@index([expiresAt]) // For cleanup of old expired tokens
|
|
4307
|
+
}
|
|
4308
|
+
|
|
4204
4309
|
enum AuditAction {
|
|
4205
4310
|
// Authentication
|
|
4206
4311
|
LOGIN_SUCCESS
|
|
@@ -4261,6 +4366,11 @@ enum AuditAction {
|
|
|
4261
4366
|
DATA_IMPORTED
|
|
4262
4367
|
SENSITIVE_DATA_ACCESSED
|
|
4263
4368
|
|
|
4369
|
+
// Compliance
|
|
4370
|
+
CONSENT_GIVEN
|
|
4371
|
+
CONSENT_WITHDRAWN
|
|
4372
|
+
POLICY_ACCEPTED
|
|
4373
|
+
|
|
4264
4374
|
// Security
|
|
4265
4375
|
SUSPICIOUS_ACTIVITY_DETECTED
|
|
4266
4376
|
RATE_LIMIT_EXCEEDED
|
|
@@ -4278,6 +4388,7 @@ enum AuditCategory {
|
|
|
4278
4388
|
Tickets
|
|
4279
4389
|
AdminAction
|
|
4280
4390
|
DataAccess
|
|
4391
|
+
Compliance
|
|
4281
4392
|
Security
|
|
4282
4393
|
}
|
|
4283
4394
|
|
package/src/definitions.ts
CHANGED
|
@@ -2,8 +2,10 @@ import {
|
|
|
2
2
|
BashEventDressTags,
|
|
3
3
|
BashEventType,
|
|
4
4
|
BashEventVibeTags,
|
|
5
|
+
CompetitionType,
|
|
5
6
|
Contact,
|
|
6
7
|
EntertainmentServiceType,
|
|
8
|
+
JudgingType,
|
|
7
9
|
MembershipTier,
|
|
8
10
|
MusicGenreType,
|
|
9
11
|
Prisma,
|
|
@@ -292,6 +294,7 @@ export const MONTHS_PREVIOUS_THAT_STRIPE_ACCOUNTS_WILL_BE_SEARCHED_BY_EMAIL =
|
|
|
292
294
|
1 as const;
|
|
293
295
|
|
|
294
296
|
export const HTTP_CODE_OK = 200 as const;
|
|
297
|
+
export const HTTP_CODE_CREATED = 201 as const;
|
|
295
298
|
export const HTTP_CODE_TEMPORARY_REDIRECT = 307 as const;
|
|
296
299
|
export const HTTP_CODE_INTERNAL_SERVER_ERR = 500 as const;
|
|
297
300
|
export const HTTP_CODE_BAD_REQUEST = 400 as const;
|
|
@@ -592,6 +595,8 @@ export interface ApiResult<T, P extends ErrorDataType = ErrorDataType> {
|
|
|
592
595
|
errorType?: ApiErrorType;
|
|
593
596
|
errorData?: P;
|
|
594
597
|
rawResponse?: any;
|
|
598
|
+
requiresPayment?: boolean; // For service listing payment flow
|
|
599
|
+
serviceId?: string; // For service listing payment flow
|
|
595
600
|
}
|
|
596
601
|
|
|
597
602
|
export function removeDataFromResult<
|
|
@@ -874,7 +879,7 @@ export const YearsOfExperienceToString: { [key in YearsOfExperience]: string } =
|
|
|
874
879
|
};
|
|
875
880
|
|
|
876
881
|
// Export Prisma enums for use in frontend
|
|
877
|
-
export { YearsOfExperience, EntertainmentServiceType, MusicGenreType, ServiceTypes };
|
|
882
|
+
export { YearsOfExperience, EntertainmentServiceType, MusicGenreType, ServiceTypes, CompetitionType, JudgingType };
|
|
878
883
|
|
|
879
884
|
export type BashEventTypeToStringType = {
|
|
880
885
|
[key in BashEventType]: string;
|