• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

maximunited / yomu / 30718640354

01 Aug 2026 09:12PM UTC coverage: 84.08%. First build
30718640354

Pull #137

github

web-flow
Merge e1bc1df48 into e08acc94e
Pull Request #137: feat: sync loyalty catalog with verification and reminders

665 of 915 branches covered (72.68%)

Branch coverage included in aggregate %.

228 of 292 new or added lines in 6 files covered. (78.08%)

9507 of 11183 relevant lines covered (85.01%)

34.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/app/api/admin/init-db/route.ts
1
import { NextResponse } from 'next/server';
×
2
import { prisma } from '@/lib/prisma';
×
3

×
4
// Database initialization endpoint - creates all tables
×
5
// IMPORTANT: This should be protected or removed after initial setup
×
6
export async function POST() {
×
7
  try {
×
8
    // Check if DATABASE_URL exists
×
9
    if (!process.env.DATABASE_URL) {
×
10
      return NextResponse.json(
×
11
        {
×
12
          message: 'DATABASE_URL environment variable not found',
×
13
          status: 'error',
×
14
        },
×
15
        { status: 500 }
×
16
      );
×
17
    }
×
18

×
19
    // Execute each CREATE TABLE statement separately
×
20
    // Prisma doesn't support multiple statements in one call
×
21
    const sqlStatements = [
×
22
      // User table
×
23
      `CREATE TABLE IF NOT EXISTS "User" (
×
24
        "id" TEXT NOT NULL PRIMARY KEY,
×
25
        "name" TEXT,
×
26
        "email" TEXT UNIQUE,
×
27
        "password" TEXT,
×
28
        "emailVerified" TIMESTAMP(3),
×
29
        "image" TEXT,
×
30
        "dateOfBirth" TIMESTAMP(3),
×
31
        "anniversaryDate" TIMESTAMP(3),
×
32
        "profilePicture" TEXT,
×
33
        "apiKey" TEXT NOT NULL DEFAULT 'key123',
×
34
        "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
×
35
        "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
×
36
      )`,
×
37

×
38
      // Account table
×
39
      `CREATE TABLE IF NOT EXISTS "Account" (
×
40
        "id" TEXT NOT NULL PRIMARY KEY,
×
41
        "userId" TEXT NOT NULL,
×
42
        "type" TEXT NOT NULL,
×
43
        "provider" TEXT NOT NULL,
×
44
        "providerAccountId" TEXT NOT NULL,
×
45
        "refresh_token" TEXT,
×
46
        "access_token" TEXT,
×
47
        "expires_at" INTEGER,
×
48
        "token_type" TEXT,
×
49
        "scope" TEXT,
×
50
        "id_token" TEXT,
×
51
        "session_state" TEXT,
×
52
        CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
×
53
        CONSTRAINT "Account_provider_providerAccountId_key" UNIQUE ("provider", "providerAccountId")
×
54
      )`,
×
55

×
56
      // Session table
×
57
      `CREATE TABLE IF NOT EXISTS "Session" (
×
58
        "id" TEXT NOT NULL PRIMARY KEY,
×
59
        "sessionToken" TEXT NOT NULL UNIQUE,
×
60
        "userId" TEXT NOT NULL,
×
61
        "expires" TIMESTAMP(3) NOT NULL,
×
62
        CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
×
63
      )`,
×
64

×
65
      // VerificationToken table
×
66
      `CREATE TABLE IF NOT EXISTS "VerificationToken" (
×
67
        "identifier" TEXT NOT NULL,
×
68
        "token" TEXT NOT NULL UNIQUE,
×
69
        "expires" TIMESTAMP(3) NOT NULL,
×
70
        CONSTRAINT "VerificationToken_identifier_token_key" UNIQUE ("identifier", "token")
×
71
      )`,
×
72

×
73
      // brands table
×
74
      `CREATE TABLE IF NOT EXISTS "brands" (
×
75
        "id" TEXT NOT NULL PRIMARY KEY,
×
76
        "name" TEXT NOT NULL,
×
77
        "logoUrl" TEXT NOT NULL,
×
78
        "website" TEXT NOT NULL,
×
79
        "description" TEXT,
×
80
        "category" TEXT NOT NULL,
×
81
        "actionUrl" TEXT,
×
82
        "actionType" TEXT,
×
83
        "actionLabel" TEXT,
×
84
        "isActive" BOOLEAN NOT NULL DEFAULT true,
×
85
        "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
×
86
        "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
×
87
      )`,
×
88

×
89
      // brand_partnerships table
×
90
      `CREATE TABLE IF NOT EXISTS "brand_partnerships" (
×
91
        "id" TEXT NOT NULL PRIMARY KEY,
×
92
        "brandAId" TEXT NOT NULL,
×
93
        "brandBId" TEXT NOT NULL,
×
94
        "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
×
95
        CONSTRAINT "brand_partnerships_brandAId_fkey" FOREIGN KEY ("brandAId") REFERENCES "brands" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
×
96
        CONSTRAINT "brand_partnerships_brandBId_fkey" FOREIGN KEY ("brandBId") REFERENCES "brands" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
×
97
        CONSTRAINT "brand_partnerships_brandAId_brandBId_key" UNIQUE ("brandAId", "brandBId")
×
98
      )`,
×
99

×
100
      // benefits table
×
101
      `CREATE TABLE IF NOT EXISTS "benefits" (
×
102
        "id" TEXT NOT NULL PRIMARY KEY,
×
103
        "brandId" TEXT NOT NULL,
×
104
        "title" TEXT NOT NULL,
×
105
        "description" TEXT NOT NULL,
×
106
        "termsAndConditions" TEXT,
×
107
        "redemptionMethod" TEXT NOT NULL,
×
108
        "promoCode" TEXT,
×
109
        "url" TEXT,
×
110
        "validityType" TEXT NOT NULL,
×
111
        "validityDuration" INTEGER,
×
112
        "isFree" BOOLEAN NOT NULL DEFAULT true,
×
113
        "isActive" BOOLEAN NOT NULL DEFAULT true,
×
NEW
114
        "lastChecked" TIMESTAMP(3),
×
NEW
115
        "verified" BOOLEAN NOT NULL DEFAULT false,
×
116
        "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
×
117
        "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
×
118
        CONSTRAINT "benefits_brandId_fkey" FOREIGN KEY ("brandId") REFERENCES "brands" ("id") ON DELETE CASCADE ON UPDATE CASCADE
×
119
      )`,
×
120

×
121
      // custom_memberships table
×
122
      `CREATE TABLE IF NOT EXISTS "custom_memberships" (
×
123
        "id" TEXT NOT NULL PRIMARY KEY,
×
124
        "userId" TEXT NOT NULL,
×
125
        "name" TEXT NOT NULL,
×
126
        "description" TEXT NOT NULL,
×
127
        "category" TEXT NOT NULL,
×
128
        "icon" TEXT NOT NULL DEFAULT '/images/brands/restaurant.svg',
×
129
        "type" TEXT NOT NULL DEFAULT 'free',
×
130
        "cost" TEXT,
×
131
        "isActive" BOOLEAN NOT NULL DEFAULT true,
×
132
        "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
×
133
        "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
×
134
        CONSTRAINT "custom_memberships_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
×
135
      )`,
×
136

×
137
      // custom_benefits table
×
138
      `CREATE TABLE IF NOT EXISTS "custom_benefits" (
×
139
        "id" TEXT NOT NULL PRIMARY KEY,
×
140
        "customMembershipId" TEXT NOT NULL,
×
141
        "title" TEXT NOT NULL,
×
142
        "description" TEXT NOT NULL,
×
143
        "termsAndConditions" TEXT,
×
144
        "redemptionMethod" TEXT NOT NULL,
×
145
        "promoCode" TEXT,
×
146
        "url" TEXT,
×
147
        "validityType" TEXT NOT NULL,
×
148
        "validityDuration" INTEGER,
×
149
        "isFree" BOOLEAN NOT NULL DEFAULT true,
×
150
        "isActive" BOOLEAN NOT NULL DEFAULT true,
×
151
        "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
×
152
        "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
×
153
        CONSTRAINT "custom_benefits_customMembershipId_fkey" FOREIGN KEY ("customMembershipId") REFERENCES "custom_memberships" ("id") ON DELETE CASCADE ON UPDATE CASCADE
×
154
      )`,
×
155

×
156
      // user_memberships table
×
157
      `CREATE TABLE IF NOT EXISTS "user_memberships" (
×
158
        "id" TEXT NOT NULL PRIMARY KEY,
×
159
        "userId" TEXT NOT NULL,
×
160
        "brandId" TEXT,
×
161
        "isActive" BOOLEAN NOT NULL DEFAULT true,
×
NEW
162
        "remindEnabled" BOOLEAN NOT NULL DEFAULT true,
×
163
        "joinedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
×
164
        "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
×
165
        "customMembershipId" TEXT,
×
166
        CONSTRAINT "user_memberships_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
×
167
        CONSTRAINT "user_memberships_brandId_fkey" FOREIGN KEY ("brandId") REFERENCES "brands" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
×
168
        CONSTRAINT "user_memberships_customMembershipId_fkey" FOREIGN KEY ("customMembershipId") REFERENCES "custom_memberships" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
×
169
        CONSTRAINT "user_memberships_userId_brandId_key" UNIQUE ("userId", "brandId"),
×
170
        CONSTRAINT "user_memberships_userId_customMembershipId_key" UNIQUE ("userId", "customMembershipId")
×
171
      )`,
×
172

×
173
      // notifications table
×
174
      `CREATE TABLE IF NOT EXISTS "notifications" (
×
175
        "id" TEXT NOT NULL PRIMARY KEY,
×
176
        "userId" TEXT NOT NULL,
×
177
        "benefitId" TEXT,
×
178
        "type" TEXT NOT NULL,
×
179
        "title" TEXT NOT NULL,
×
180
        "message" TEXT NOT NULL,
×
181
        "isRead" BOOLEAN NOT NULL DEFAULT false,
×
182
        "scheduledFor" TIMESTAMP(3),
×
183
        "sentAt" TIMESTAMP(3),
×
184
        "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
×
185
        CONSTRAINT "notifications_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
×
186
        CONSTRAINT "notifications_benefitId_fkey" FOREIGN KEY ("benefitId") REFERENCES "benefits" ("id") ON DELETE CASCADE ON UPDATE CASCADE
×
187
      )`,
×
188

×
189
      // used_benefits table
×
190
      `CREATE TABLE IF NOT EXISTS "used_benefits" (
×
191
        "id" TEXT NOT NULL PRIMARY KEY,
×
192
        "userId" TEXT NOT NULL,
×
193
        "benefitId" TEXT NOT NULL,
×
194
        "usedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
×
195
        "notes" TEXT,
×
196
        "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
×
197
        "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
×
198
        CONSTRAINT "used_benefits_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
×
199
        CONSTRAINT "used_benefits_benefitId_fkey" FOREIGN KEY ("benefitId") REFERENCES "benefits" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
×
200
        CONSTRAINT "used_benefits_userId_benefitId_key" UNIQUE ("userId", "benefitId")
×
201
      )`,
×
202
    ];
×
203

×
204
    // Execute each statement individually
×
205
    for (const sql of sqlStatements) {
×
206
      await prisma.$executeRawUnsafe(sql);
×
207
    }
×
208

×
NEW
209
    // Additive upgrades for DBs created before verification/remind columns existed
×
NEW
210
    // (CREATE TABLE IF NOT EXISTS does not alter existing tables)
×
NEW
211
    const alterStatements = [
×
NEW
212
      `ALTER TABLE "benefits" ADD COLUMN IF NOT EXISTS "lastChecked" TIMESTAMP(3)`,
×
NEW
213
      `ALTER TABLE "benefits" ADD COLUMN IF NOT EXISTS "verified" BOOLEAN NOT NULL DEFAULT false`,
×
NEW
214
      `ALTER TABLE "user_memberships" ADD COLUMN IF NOT EXISTS "remindEnabled" BOOLEAN NOT NULL DEFAULT true`,
×
NEW
215
    ];
×
NEW
216
    for (const sql of alterStatements) {
×
NEW
217
      await prisma.$executeRawUnsafe(sql);
×
NEW
218
    }
×
NEW
219

×
220
    return NextResponse.json({
×
221
      message: 'Database initialized successfully',
×
222
      status: 'success',
×
223
      tables: [
×
224
        'User',
×
225
        'Account',
×
226
        'Session',
×
227
        'VerificationToken',
×
228
        'brands',
×
229
        'brand_partnerships',
×
230
        'benefits',
×
231
        'custom_memberships',
×
232
        'custom_benefits',
×
233
        'user_memberships',
×
234
        'notifications',
×
235
        'used_benefits',
×
236
      ],
×
237
    });
×
238
  } catch (error) {
×
239
    console.error('Database initialization error:', error);
×
240

×
241
    return NextResponse.json(
×
242
      {
×
243
        message: 'Failed to initialize database',
×
244
        status: 'error',
×
245
        error: error instanceof Error ? error.message : 'Unknown error',
×
246
      },
×
247
      { status: 500 }
×
248
    );
×
249
  }
×
250
}
×
251

×
252
// GET endpoint to check if this API is available
×
253
export async function GET() {
×
254
  return NextResponse.json({
×
255
    message:
×
256
      'Database initialization endpoint. Send POST request to initialize database.',
×
257
    status: 'ready',
×
258
    hasDatabase: !!process.env.DATABASE_URL,
×
259
  });
×
260
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc