@shophost/rest-api 2.0.20 → 2.0.21

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/README.md CHANGED
@@ -42,6 +42,30 @@ import { createNextHandler } from "@shophost/rest-api/next";
42
42
  import { ManufacturerSchema } from "@shophost/rest-api/schemas";
43
43
  ```
44
44
 
45
+ ## Database Schema Sync
46
+
47
+ The published package now includes a small CLI so a fresh project can push the packaged Prisma schema to its own database.
48
+
49
+ 1. Set one of these environment variables in your app:
50
+ - `DATABASE_URL`
51
+ - `POSTGRES_PRISMA_URL`
52
+ 2. Run:
53
+
54
+ ```bash
55
+ pnpm exec shophost-rest-api db push
56
+ # or
57
+ npx shophost-rest-api db push
58
+ ```
59
+
60
+ You can pass regular Prisma `db push` flags through as well:
61
+
62
+ ```bash
63
+ pnpm exec shophost-rest-api db push --accept-data-loss
64
+ pnpm exec shophost-rest-api db push --url "postgres://..."
65
+ ```
66
+
67
+ The CLI uses the `schema.prisma` and `prisma.config.ts` that ship with `@shophost/rest-api`, so consumers do not need to copy the schema into their own project first.
68
+
45
69
  ## Development
46
70
 
47
71
  To build this package:
package/package.json CHANGED
@@ -1,7 +1,10 @@
1
1
  {
2
2
  "name": "@shophost/rest-api",
3
- "version": "2.0.20",
3
+ "version": "2.0.21",
4
4
  "type": "module",
5
+ "bin": {
6
+ "shophost-rest-api": "./scripts/shophost-rest-api.mjs"
7
+ },
5
8
  "main": "./src/index.js",
6
9
  "types": "./src/index.d.ts",
7
10
  "exports": {
@@ -38,6 +41,9 @@
38
41
  },
39
42
  "files": [
40
43
  "src",
44
+ "scripts",
45
+ "schema.prisma",
46
+ "prisma.config.ts",
41
47
  "next.d.ts",
42
48
  "db.d.ts",
43
49
  "schemas.d.ts",
@@ -64,6 +70,7 @@
64
70
  "@prisma/adapter-neon": "^7.4.2",
65
71
  "@prisma/client": "^7.4.2",
66
72
  "@prisma/client-runtime-utils": "^7.4.2",
73
+ "prisma": "^7.4.2",
67
74
  "@react-email/components": "^0.0.36",
68
75
  "@vercel/blob": "^0.27.2",
69
76
  "axios": "^1.8.2",
@@ -0,0 +1,16 @@
1
+ import "dotenv/config";
2
+ import { defineConfig } from "prisma/config";
3
+
4
+ function firstDefined(...names: string[]) {
5
+ return names.find((name) => process.env[name])?.trim();
6
+ }
7
+
8
+ export default defineConfig({
9
+ datasource: {
10
+ url: firstDefined("POSTGRES_PRISMA_URL", "DATABASE_URL"),
11
+ shadowDatabaseUrl: firstDefined(
12
+ "POSTGRES_URL_NON_POOLING",
13
+ "DIRECT_DATABASE_URL"
14
+ ),
15
+ },
16
+ });
package/schema.prisma ADDED
@@ -0,0 +1,768 @@
1
+ generator client {
2
+ provider = "prisma-client"
3
+ previewFeatures = ["driverAdapters"]
4
+ output = "src/core/db/__generated__/client"
5
+ binaryTargets = ["native", "rhel-openssl-3.0.x", "debian-openssl-3.0.x"]
6
+
7
+ runtime = "nodejs"
8
+ moduleFormat = "esm"
9
+ generatedFileExtension = "ts"
10
+ importFileExtension = ""
11
+ engineType = "client"
12
+ }
13
+
14
+ datasource db {
15
+ provider = "postgresql"
16
+ }
17
+
18
+ enum FileUploadStatus {
19
+ pending
20
+ completed
21
+ failed
22
+ }
23
+
24
+ enum Locale {
25
+ af // Afrikaans
26
+ sq // Albanian
27
+ ar_dz // Arabic (Algeria)
28
+ ar_bh // Arabic (Bahrain)
29
+ ar_eg // Arabic (Egypt)
30
+ ar_iq // Arabic (Iraq)
31
+ ar_jo // Arabic (Jordan)
32
+ ar_kw // Arabic (Kuwait)
33
+ ar_lb // Arabic (Lebanon)
34
+ ar_ly // Arabic (Libya)
35
+ ar_ma // Arabic (Morocco)
36
+ ar_om // Arabic (Oman)
37
+ ar_qa // Arabic (Qatar)
38
+ ar_sa // Arabic (Saudi Arabia)
39
+ ar_sy // Arabic (Syria)
40
+ ar_tn // Arabic (Tunisia)
41
+ ar_ae // Arabic (U.A.E.)
42
+ ar_ye // Arabic (Yemen)
43
+ eu // Basque
44
+ be // Belarusian
45
+ bg // Bulgarian
46
+ ca // Catalan
47
+ zh_hk // Chinese (Hong Kong)
48
+ zh_cn // Chinese (PRC)
49
+ zh_sg // Chinese (Singapore)
50
+ zh_tw // Chinese (Taiwan)
51
+ hr // Croatian
52
+ cs // Czech
53
+ da // Danish
54
+ nl_be // Dutch (Belgium)
55
+ nl // Dutch (Standard)
56
+ en // English
57
+ en_au // English (Australia)
58
+ en_bz // English (Belize)
59
+ en_ca // English (Canada)
60
+ en_ie // English (Ireland)
61
+ en_jm // English (Jamaica)
62
+ en_nz // English (New Zealand)
63
+ en_za // English (South Africa)
64
+ en_tt // English (Trinidad)
65
+ en_gb // English (United Kingdom)
66
+ en_us // English (United States)
67
+ et // Estonian
68
+ fo // Faeroese
69
+ fa // Farsi
70
+ fi // Finnish
71
+ fr_be // French (Belgium)
72
+ fr_ca // French (Canada)
73
+ fr_lu // French (Luxembourg)
74
+ fr // French (Standard)
75
+ fr_ch // French (Switzerland)
76
+ gd // Gaelic (Scotland)
77
+ de_at // German (Austria)
78
+ de_li // German (Liechtenstein)
79
+ de_lu // German (Luxembourg)
80
+ de // German (Standard)
81
+ de_ch // German (Switzerland)
82
+ el // Greek
83
+ he // Hebrew
84
+ hi // Hindi
85
+ hu // Hungarian
86
+ is // Icelandic
87
+ id // Indonesian
88
+ ga // Irish
89
+ it // Italian (Standard)
90
+ it_ch // Italian (Switzerland)
91
+ ja // Japanese
92
+ ko // Korean
93
+ ku // Kurdish
94
+ lv // Latvian
95
+ lt // Lithuanian
96
+ mk // Macedonian (FYROM)
97
+ ml // Malayalam
98
+ ms // Malaysian
99
+ mt // Maltese
100
+ no // Norwegian
101
+ nb // Norwegian (Bokmål)
102
+ nn // Norwegian (Nynorsk)
103
+ pl // Polish
104
+ pt_br // Portuguese (Brazil)
105
+ pt // Portuguese (Portugal)
106
+ pa // Punjabi
107
+ rm // Rhaeto-Romanic
108
+ ro // Romanian
109
+ ro_md // Romanian (Republic of Moldova)
110
+ ru // Russian
111
+ ru_md // Russian (Republic of Moldova)
112
+ sr // Serbian
113
+ sk // Slovak
114
+ sl // Slovenian
115
+ sb // Sorbian
116
+ es_ar // Spanish (Argentina)
117
+ es_bo // Spanish (Bolivia)
118
+ es_cl // Spanish (Chile)
119
+ es_co // Spanish (Colombia)
120
+ es_cr // Spanish (Costa Rica)
121
+ es_do // Spanish (Dominican Republic)
122
+ es_ec // Spanish (Ecuador)
123
+ es_sv // Spanish (El Salvador)
124
+ es_gt // Spanish (Guatemala)
125
+ es_hn // Spanish (Honduras)
126
+ es_mx // Spanish (Mexico)
127
+ es // Spanish (Spain)
128
+ sv // Swedish
129
+ sv_fi // Swedish (Finland)
130
+ th // Thai
131
+ tr // Turkish
132
+ uk // Ukrainian
133
+ ur // Urdu
134
+ vi // Vietnamese
135
+ cy // Welsh
136
+ ji // Yiddish
137
+ zu // Zulu
138
+ }
139
+
140
+ enum Currency {
141
+ USD // United States Dollar
142
+ EUR // Euro
143
+ GBP // British Pound Sterling
144
+ JPY // Japanese Yen
145
+ AUD // Australian Dollar
146
+ CAD // Canadian Dollar
147
+ CHF // Swiss Franc
148
+ CNY // Chinese Yuan
149
+ SEK // Swedish Krona
150
+ NZD // New Zealand Dollar
151
+ MXN // Mexican Peso
152
+ SGD // Singapore Dollar
153
+ HKD // Hong Kong Dollar
154
+ NOK // Norwegian Krone
155
+ KRW // South Korean Won
156
+ TRY // Turkish Lira
157
+ RUB // Russian Ruble
158
+ INR // Indian Rupee
159
+ BRL // Brazilian Real
160
+ ZAR // South African Rand
161
+ DKK // Danish Krone
162
+ PLN // Polish Złoty
163
+ THB // Thai Baht
164
+ IDR // Indonesian Rupiah
165
+ HUF // Hungarian Forint
166
+ CZK // Czech Koruna
167
+ ILS // Israeli New Shekel
168
+ PHP // Philippine Peso
169
+ AED // United Arab Emirates Dirham
170
+ COP // Colombian Peso
171
+ SAR // Saudi Riyal
172
+ MYR // Malaysian Ringgit
173
+ RON // Romanian Leu
174
+ }
175
+
176
+ model User {
177
+ id String @id
178
+ name String
179
+ firstname String
180
+ lastname String
181
+ email String
182
+ emailVerified Boolean
183
+ image String?
184
+ createdAt DateTime
185
+ updatedAt DateTime
186
+ sessions Session[]
187
+ accounts Account[]
188
+ members Member[]
189
+ invitations Invitation[]
190
+ shippingAddresses Address[]
191
+ orders Order[]
192
+ reservations Reservation[]
193
+
194
+ @@unique([email])
195
+ }
196
+
197
+ model Session {
198
+ id String @id
199
+ expiresAt DateTime
200
+ token String
201
+ createdAt DateTime
202
+ updatedAt DateTime
203
+ ipAddress String?
204
+ userAgent String?
205
+ userId String
206
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
207
+ activeOrganizationId String?
208
+
209
+ @@unique([token])
210
+ }
211
+
212
+ model Account {
213
+ id String @id
214
+ accountId String
215
+ providerId String
216
+ userId String
217
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
218
+ accessToken String?
219
+ refreshToken String?
220
+ idToken String?
221
+ accessTokenExpiresAt DateTime?
222
+ refreshTokenExpiresAt DateTime?
223
+ scope String?
224
+ password String?
225
+ createdAt DateTime
226
+ updatedAt DateTime
227
+ }
228
+
229
+ model Verification {
230
+ id String @id
231
+ identifier String
232
+ value String
233
+ expiresAt DateTime
234
+ createdAt DateTime?
235
+ updatedAt DateTime?
236
+ }
237
+
238
+ model Member {
239
+ id String @id @default(cuid())
240
+ organizationId String
241
+ organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
242
+ userId String
243
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
244
+ role String
245
+ createdAt DateTime @default(now())
246
+ }
247
+
248
+ model Invitation {
249
+ id String @id
250
+ organizationId String
251
+ organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
252
+ email String
253
+ role String?
254
+ status String
255
+ expiresAt DateTime
256
+ inviterId String
257
+ user User @relation(fields: [inviterId], references: [id], onDelete: Cascade)
258
+ }
259
+
260
+ model Organization {
261
+ id String @id @default(cuid())
262
+ name String
263
+ slug String?
264
+ phone String?
265
+ email String?
266
+ legalEntityId String?
267
+ legalEntity LegalEntity? @relation(fields: [legalEntityId], references: [id])
268
+ addressId String
269
+ address Address @relation(fields: [addressId], references: [id])
270
+ configurationId String
271
+ configuration OrganizationConfiguration @relation(fields: [configurationId], references: [id])
272
+ logo String? // logo URL
273
+ logoId String? @unique
274
+ logoFile File? @relation("OrganizationLogo", fields: [logoId], references: [id])
275
+ files File[]
276
+ members Member[]
277
+ invitations Invitation[]
278
+ orders Order[]
279
+ campaigns Campaign[]
280
+ shippingMethods ShippingMethod[]
281
+ manufacturers Manufacturer[]
282
+ productCategories ProductCategory[]
283
+ products Product[]
284
+ reservations Reservation[]
285
+ createdAt DateTime @default(now())
286
+ createdBy String?
287
+ updatedAt DateTime @updatedAt
288
+ updatedBy String?
289
+ deletedAt DateTime?
290
+ deletedBy String?
291
+
292
+ @@unique([slug])
293
+ }
294
+
295
+ model OpeningTimes {
296
+ id String @id @default(cuid())
297
+ monday DateTime? @db.Time
298
+ tuesday DateTime? @db.Time
299
+ wednesday DateTime? @db.Time
300
+ thursday DateTime? @db.Time
301
+ friday DateTime? @db.Time
302
+ saturday DateTime? @db.Time
303
+ sunday DateTime? @db.Time
304
+ organizationConfiguration OrganizationConfiguration?
305
+ }
306
+
307
+ model ClosingTimes {
308
+ id String @id @default(cuid())
309
+ monday DateTime? @db.Time
310
+ tuesday DateTime? @db.Time
311
+ wednesday DateTime? @db.Time
312
+ thursday DateTime? @db.Time
313
+ friday DateTime? @db.Time
314
+ saturday DateTime? @db.Time
315
+ sunday DateTime? @db.Time
316
+ organizationConfiguration OrganizationConfiguration?
317
+ }
318
+
319
+ model OrganizationConfiguration {
320
+ id String @id @default(cuid())
321
+ hostname String?
322
+ countriesShipping String[] @default([])
323
+ stripeAccountId String?
324
+ enableHostCheckout Boolean
325
+ enableHostTracking Boolean
326
+ isAcceptingOrders Boolean? @default(false)
327
+ defaultCurrency Currency @default(USD)
328
+ defaultLocale Locale @default(en)
329
+ supportedLocales Locale[] @default([en])
330
+ openingTimesId String? @unique
331
+ openingTimes OpeningTimes? @relation(fields: [openingTimesId], references: [id])
332
+ closingTimesId String? @unique
333
+ closingTimes ClosingTimes? @relation(fields: [closingTimesId], references: [id])
334
+ facebookProfile String?
335
+ instagramProfile String?
336
+ xProfile String?
337
+ createdAt DateTime @default(now())
338
+ createdBy String?
339
+ updatedAt DateTime @default(now())
340
+ updatedBy String?
341
+ organizations Organization[]
342
+ }
343
+
344
+ model Address {
345
+ id String @id @default(cuid())
346
+ firstname String?
347
+ lastname String?
348
+ phone String?
349
+ addressLineOne String
350
+ doorNumber String?
351
+ addressLineTwo String?
352
+ zipCode String?
353
+ placeId String?
354
+ deliveryInstructions String?
355
+ city String
356
+ country String?
357
+ latitude Float?
358
+ longitude Float?
359
+ isDefault Boolean @default(false)
360
+ LegalEntity LegalEntity[]
361
+ organizations Organization[]
362
+ users User[]
363
+ orders Order[] @relation("OrderShippingAddress")
364
+ createdAt DateTime @default(now())
365
+ createdBy String?
366
+ updatedAt DateTime @updatedAt
367
+ updatedBy String?
368
+ deletedAt DateTime?
369
+ deletedBy String?
370
+ }
371
+
372
+ model LegalEntity {
373
+ id String @id @default(cuid())
374
+ name String
375
+ taxId String
376
+ addressId String
377
+ address Address @relation(fields: [addressId], references: [id])
378
+ email String?
379
+ phone String?
380
+ organizations Organization[]
381
+
382
+ // metadata
383
+ createdAt DateTime @default(now())
384
+ createdBy String?
385
+ updatedAt DateTime @updatedAt
386
+ updatedBy String?
387
+ deletedAt DateTime?
388
+ deletedBy String?
389
+ }
390
+
391
+ model File {
392
+ id String @id @default(cuid())
393
+ mimeType String?
394
+ size Int?
395
+ filename String?
396
+ url String?
397
+ status FileUploadStatus @default(pending)
398
+ organizationId String?
399
+ organization Organization? @relation(fields: [organizationId], references: [id])
400
+ manufacturerLogo Manufacturer? @relation("ManufacturerLogo")
401
+ productCategoryImage ProductCategory? @relation("ProductCategoryImage")
402
+ productImages Product[] @relation("ProductImages")
403
+ modifierImages Modifier[] @relation("ModifierImages")
404
+ shippingMethodLogoFor ShippingMethod? @relation("ShippingMethodProviderLogo")
405
+ organizationLogo Organization? @relation("OrganizationLogo")
406
+ orderItemImages OrderItem[] @relation("OrderItemImage")
407
+ // metadata
408
+ createdAt DateTime @default(now())
409
+ createdBy String?
410
+ updatedAt DateTime @updatedAt
411
+ updatedBy String?
412
+ deletedAt DateTime?
413
+ deletedBy String?
414
+ }
415
+
416
+ model Manufacturer {
417
+ id String @id @default(cuid())
418
+ name String
419
+ logoId String? @unique
420
+ logo File? @relation("ManufacturerLogo", fields: [logoId], references: [id])
421
+ organizationId String
422
+ organization Organization @relation(fields: [organizationId], references: [id])
423
+ createdAt DateTime @default(now())
424
+ createdBy String?
425
+ updatedAt DateTime @updatedAt
426
+ updatedBy String?
427
+ deletedAt DateTime?
428
+ deletedBy String?
429
+
430
+ products Product[]
431
+ translations ManufacturerTranslation[]
432
+ }
433
+
434
+ model ManufacturerTranslation {
435
+ id String @id @default(cuid())
436
+ locale String
437
+ about String?
438
+ manufacturerId String
439
+ manufacturer Manufacturer @relation(fields: [manufacturerId], references: [id])
440
+ createdAt DateTime @default(now())
441
+ createdBy String?
442
+ updatedAt DateTime @updatedAt
443
+ updatedBy String?
444
+
445
+ @@unique([locale, manufacturerId])
446
+ }
447
+
448
+ model ProductCategory {
449
+ id String @id @default(cuid())
450
+ slug String?
451
+ organizationId String
452
+ priority Int?
453
+ organization Organization @relation(fields: [organizationId], references: [id])
454
+ imageId String? @unique
455
+ image File? @relation("ProductCategoryImage", fields: [imageId], references: [id])
456
+ createdAt DateTime @default(now())
457
+ createdBy String?
458
+ updatedAt DateTime @default(now())
459
+ updatedBy String?
460
+ deletedAt DateTime?
461
+ deletedBy String?
462
+ publishedAt DateTime?
463
+ publishedBy String?
464
+ translations ProductCategoryTranslation[]
465
+ products Product[] @relation("ProductCategories")
466
+ }
467
+
468
+ model ProductCategoryTranslation {
469
+ id String @id @default(cuid())
470
+ locale String
471
+ title String?
472
+ description String?
473
+ productCategoryId String
474
+ productCategory ProductCategory @relation(fields: [productCategoryId], references: [id])
475
+ createdAt DateTime @default(now())
476
+ createdBy String?
477
+ updatedAt DateTime @updatedAt
478
+ updatedBy String?
479
+
480
+ @@unique([locale, productCategoryId])
481
+ }
482
+
483
+ model Product {
484
+ id String @id @default(cuid())
485
+ content String?
486
+ slug String?
487
+ sku String?
488
+ basePrice Float
489
+ discountedBasePrice Float?
490
+ currency String
491
+ images File[] @relation("ProductImages")
492
+ organizationId String
493
+ organization Organization @relation(fields: [organizationId], references: [id])
494
+ manufacturerId String?
495
+ manufacturer Manufacturer? @relation(fields: [manufacturerId], references: [id])
496
+ tags String[]
497
+ categories ProductCategory[] @relation("ProductCategories")
498
+ modifierGroups ModifierGroup[] @relation("ProductToModifierGroup")
499
+ translations ProductTranslation[]
500
+ metadata Json?
501
+ publishedAt DateTime?
502
+ publishedBy String?
503
+ deletedAt DateTime?
504
+ deletedBy String?
505
+ createdAt DateTime @default(now())
506
+ createdBy String?
507
+ updatedAt DateTime @default(now())
508
+ updatedBy String?
509
+ snapshots ProductSnapshot[]
510
+ latestSnapshotId String? @unique
511
+ latestSnapshot ProductSnapshot? @relation("LatestProductSnapshot", fields: [latestSnapshotId], references: [id])
512
+ }
513
+
514
+ model ProductSnapshot {
515
+ id String @id @default(cuid())
516
+ productId String
517
+ product Product @relation(fields: [productId], references: [id])
518
+ data Json
519
+ createdAt DateTime @default(now())
520
+ createdBy String?
521
+ latestForProduct Product? @relation("LatestProductSnapshot")
522
+ orderItems OrderItem[] @relation("OrderItemProductSnapshot")
523
+ }
524
+
525
+ model ProductTranslation {
526
+ id String @id @default(cuid())
527
+ locale String
528
+ title String?
529
+ description String?
530
+ productId String
531
+ product Product @relation(fields: [productId], references: [id])
532
+ createdAt DateTime @default(now())
533
+ createdBy String?
534
+ updatedAt DateTime @updatedAt
535
+ updatedBy String?
536
+
537
+ @@unique([locale, productId])
538
+ }
539
+
540
+ model ModifierGroup {
541
+ id String @id @default(cuid())
542
+ selectMin Int?
543
+ selectMax Int?
544
+ createdAt DateTime @default(now())
545
+ updatedAt DateTime @default(now())
546
+ modifiers Modifier[] @relation("ModifierToModifierGroup")
547
+ products Product[] @relation("ProductToModifierGroup")
548
+ translations ModifierGroupTranslation[] @relation("ModifierGroupToTranslation")
549
+ }
550
+
551
+ model ModifierGroupTranslation {
552
+ id String @id @default(cuid())
553
+ locale String
554
+ title String?
555
+ description String?
556
+ modifierGroupId String
557
+ modifierGroup ModifierGroup @relation("ModifierGroupToTranslation", fields: [modifierGroupId], references: [id], onDelete: Cascade)
558
+
559
+ @@unique([locale, modifierGroupId])
560
+ }
561
+
562
+ model Modifier {
563
+ id String @id @default(cuid())
564
+ title String
565
+ description String?
566
+ sku String?
567
+ price Float
568
+ modifierGroupId String
569
+ modifierGroup ModifierGroup @relation("ModifierToModifierGroup", fields: [modifierGroupId], references: [id], onDelete: Cascade)
570
+ images File[] @relation("ModifierImages")
571
+ }
572
+
573
+ enum OrderFulfilmentMethod {
574
+ delivery
575
+ pickup
576
+ }
577
+
578
+ enum OrderStatus {
579
+ draft
580
+ pending
581
+ confirmed
582
+ completed
583
+ cancelled
584
+ }
585
+
586
+ model Order {
587
+ id String @id @default(cuid())
588
+ organizationId String
589
+ organization Organization @relation(fields: [organizationId], references: [id])
590
+ userId String?
591
+ user User? @relation(fields: [userId], references: [id])
592
+ paymentId String? @unique
593
+ payment Payment? @relation("OrderPayment", fields: [paymentId], references: [id])
594
+ token String
595
+ expiresAt DateTime @default(dbgenerated("NOW() + INTERVAL '24 hours'"))
596
+ referenceId String
597
+ fulfilmentMethod OrderFulfilmentMethod?
598
+ shippingMethodId String?
599
+ shippingMethod ShippingMethod? @relation("OrderShippingMethod", fields: [shippingMethodId], references: [id])
600
+ shippingAddressId String?
601
+ shippingAddress Address? @relation("OrderShippingAddress", fields: [shippingAddressId], references: [id])
602
+ items OrderItem[]
603
+ dateOfExecution DateTime?
604
+ currency Currency @default(USD)
605
+ sourceIp String?
606
+ acceptedAt DateTime?
607
+ readyForDispatchAt DateTime?
608
+ dispatchedAt DateTime?
609
+ completedAt DateTime?
610
+ cancelledAt DateTime?
611
+ deletedAt DateTime?
612
+ deletedBy String?
613
+ createdAt DateTime @default(now())
614
+ updatedAt DateTime @updatedAt
615
+ }
616
+
617
+ model OrderItem {
618
+ id String @id @default(cuid())
619
+ quantity Int
620
+ unitPrice Float
621
+ totalPrice Float
622
+ orderId String
623
+ order Order @relation(fields: [orderId], references: [id])
624
+ rawData Json?
625
+ productSnapshotId String?
626
+ productSnapshot ProductSnapshot? @relation("OrderItemProductSnapshot", fields: [productSnapshotId], references: [id])
627
+ imageId String?
628
+ image File? @relation("OrderItemImage", fields: [imageId], references: [id])
629
+ translations OrderItemTranslation[]
630
+ }
631
+
632
+ model OrderItemTranslation {
633
+ id String @id @default(cuid())
634
+ locale String
635
+ title String
636
+ subtitle String?
637
+ orderItemId String
638
+ orderItem OrderItem @relation(fields: [orderItemId], references: [id])
639
+
640
+ @@unique([locale, orderItemId])
641
+ }
642
+
643
+ enum PaymentStatus {
644
+ pending
645
+ succeeded
646
+ refunded
647
+ failed
648
+ canceled
649
+ }
650
+
651
+ enum PaymentProvider {
652
+ stripe
653
+ revolut
654
+ payu
655
+ not_applicable
656
+ }
657
+
658
+ enum PaymentMethod {
659
+ card
660
+ blik
661
+ paypal
662
+ bank_transfer
663
+ cash_on_delivery
664
+ }
665
+
666
+ model Payment {
667
+ id String @id @default(cuid())
668
+ method PaymentMethod
669
+ provider PaymentProvider @default(not_applicable)
670
+ shipping Float @default(0)
671
+ subtotal Float @default(0)
672
+ total Float
673
+ discount Float?
674
+ currency Currency @default(USD)
675
+ status PaymentStatus @default(pending)
676
+ createdAt DateTime @default(now())
677
+ updatedAt DateTime @updatedAt
678
+ sessions PaymentSession[]
679
+ order Order? @relation("OrderPayment")
680
+ }
681
+
682
+ model PaymentSession {
683
+ id String @id @default(cuid())
684
+ referenceId String @unique
685
+ amount Float
686
+ currency Currency @default(USD)
687
+ rawData Json
688
+ capturedAt DateTime?
689
+ expiredAt DateTime?
690
+ createdAt DateTime @default(now())
691
+ updatedAt DateTime @default(now())
692
+ paymentId String
693
+ payment Payment @relation(fields: [paymentId], references: [id])
694
+ }
695
+
696
+ enum CampaignType {
697
+ discount
698
+ promotion
699
+ }
700
+
701
+ model Campaign {
702
+ id String @id @default(cuid())
703
+ title String
704
+ type CampaignType
705
+ validFrom DateTime
706
+ validTo DateTime
707
+ organizationId String
708
+ organization Organization @relation(fields: [organizationId], references: [id])
709
+ amount Float?
710
+ percentage Float?
711
+ publishedAt DateTime?
712
+ publishedBy String?
713
+ createdAt DateTime @default(now())
714
+ updatedAt DateTime @updatedAt
715
+ }
716
+
717
+ model ShippingMethod {
718
+ id String @id @default(cuid())
719
+ title String
720
+ providerName String
721
+ providerLogoId String? @unique
722
+ providerLogo File? @relation("ShippingMethodProviderLogo", fields: [providerLogoId], references: [id])
723
+ requireDateOfDelivery Boolean?
724
+ organizationId String
725
+ organization Organization @relation(fields: [organizationId], references: [id])
726
+ shippingZones ShippingZone[]
727
+ archivedAt DateTime?
728
+ archivedBy String?
729
+ publishedAt DateTime?
730
+ publishedBy String?
731
+ createdAt DateTime @default(now())
732
+ createdBy String?
733
+ updatedAt DateTime @updatedAt
734
+ updatedBy String?
735
+
736
+ orders Order[] @relation("OrderShippingMethod")
737
+ }
738
+
739
+ model ShippingZone {
740
+ id String @id @default(cuid())
741
+ title String
742
+ distanceUpto Float?
743
+ price Float
744
+ minimumOrderAmount Float?
745
+ shippingMethodId String
746
+ shippingMethod ShippingMethod @relation(fields: [shippingMethodId], references: [id])
747
+ eta String
748
+ createdAt DateTime @default(now())
749
+ updatedAt DateTime @updatedAt
750
+ }
751
+
752
+ model Reservation {
753
+ id String @id @default(cuid())
754
+ firstname String
755
+ lastname String
756
+ userId String?
757
+ user User? @relation(fields: [userId], references: [id])
758
+ guests Int
759
+ phone String
760
+ date DateTime
761
+ referenceId String
762
+ organizationId String
763
+ organization Organization @relation(fields: [organizationId], references: [id])
764
+ createdAt DateTime @default(now())
765
+ acceptedAt DateTime?
766
+ cancelledAt DateTime?
767
+ updatedAt DateTime @updatedAt
768
+ }
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawn } from "node:child_process";
4
+ import { existsSync } from "node:fs";
5
+ import { createRequire } from "node:module";
6
+ import { dirname, join } from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+ const packageRoot = join(__dirname, "..");
11
+ const schemaPath = join(packageRoot, "schema.prisma");
12
+ const configPath = join(packageRoot, "prisma.config.ts");
13
+
14
+ const require = createRequire(import.meta.url);
15
+ const RESERVED_FLAGS = ["--schema", "--config"];
16
+
17
+ function printHelp() {
18
+ console.log(`shophost-rest-api
19
+
20
+ Usage:
21
+ shophost-rest-api db push [prisma-db-push-options]
22
+
23
+ Examples:
24
+ shophost-rest-api db push
25
+ shophost-rest-api db push --accept-data-loss
26
+ shophost-rest-api db push --url "postgres://..."
27
+
28
+ Environment:
29
+ DATABASE_URL
30
+ POSTGRES_PRISMA_URL
31
+ `);
32
+ }
33
+
34
+ function getPrismaCliPath() {
35
+ try {
36
+ return require.resolve("prisma/build/index.js");
37
+ } catch (error) {
38
+ console.error(
39
+ "Could not resolve the Prisma CLI from @shophost/rest-api."
40
+ );
41
+ console.error(
42
+ "Reinstall dependencies and make sure the package was installed correctly."
43
+ );
44
+ if (error instanceof Error && error.message) {
45
+ console.error(error.message);
46
+ }
47
+ process.exit(1);
48
+ }
49
+ }
50
+
51
+ function getDbPushArgs(argv) {
52
+ if (argv[0] !== "db" || argv[1] !== "push") {
53
+ return null;
54
+ }
55
+
56
+ return argv.slice(2);
57
+ }
58
+
59
+ const argv = process.argv.slice(2);
60
+
61
+ if (
62
+ argv.length === 0 ||
63
+ (argv.length === 1 && (argv[0] === "--help" || argv[0] === "-h"))
64
+ ) {
65
+ printHelp();
66
+ process.exit(0);
67
+ }
68
+
69
+ const dbPushArgs = getDbPushArgs(argv);
70
+
71
+ if (!dbPushArgs) {
72
+ console.error(`Unknown command: ${argv.join(" ")}`);
73
+ printHelp();
74
+ process.exit(1);
75
+ }
76
+
77
+ const reservedFlag = dbPushArgs.find(
78
+ (arg) =>
79
+ RESERVED_FLAGS.includes(arg) ||
80
+ RESERVED_FLAGS.some((flag) => arg.startsWith(`${flag}=`))
81
+ );
82
+ const isDbPushHelp =
83
+ dbPushArgs.includes("--help") || dbPushArgs.includes("-h");
84
+
85
+ if (reservedFlag) {
86
+ console.error(
87
+ `${reservedFlag} is managed by this CLI. Use \`--url\` to point at a different database.`
88
+ );
89
+ process.exit(1);
90
+ }
91
+
92
+ if (!existsSync(schemaPath)) {
93
+ console.error(`Missing Prisma schema at ${schemaPath}`);
94
+ process.exit(1);
95
+ }
96
+
97
+ if (!existsSync(configPath)) {
98
+ console.error(`Missing Prisma config at ${configPath}`);
99
+ process.exit(1);
100
+ }
101
+
102
+ const hasUrlOverride = dbPushArgs.some(
103
+ (arg, index) =>
104
+ arg === "--url" ||
105
+ arg.startsWith("--url=") ||
106
+ (index > 0 && dbPushArgs[index - 1] === "--url")
107
+ );
108
+
109
+ if (
110
+ !isDbPushHelp &&
111
+ !hasUrlOverride &&
112
+ !process.env.DATABASE_URL &&
113
+ !process.env.POSTGRES_PRISMA_URL
114
+ ) {
115
+ console.error(
116
+ "Set DATABASE_URL or POSTGRES_PRISMA_URL before running `shophost-rest-api db push`."
117
+ );
118
+ process.exit(1);
119
+ }
120
+
121
+ const prismaCliPath = getPrismaCliPath();
122
+ const child = spawn(
123
+ process.execPath,
124
+ [
125
+ prismaCliPath,
126
+ "db",
127
+ "push",
128
+ "--schema",
129
+ schemaPath,
130
+ "--config",
131
+ configPath,
132
+ ...dbPushArgs,
133
+ ],
134
+ {
135
+ cwd: process.cwd(),
136
+ env: process.env,
137
+ stdio: "inherit",
138
+ }
139
+ );
140
+
141
+ child.on("exit", (code, signal) => {
142
+ if (signal) {
143
+ process.kill(process.pid, signal);
144
+ return;
145
+ }
146
+
147
+ process.exit(code ?? 1);
148
+ });
149
+
150
+ child.on("error", (error) => {
151
+ console.error(error);
152
+ process.exit(1);
153
+ });