Compare commits
32
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
960673bbc6 | ||
|
|
a909db39b1 | ||
|
|
020289966d | ||
|
|
fa614b8c37 | ||
|
|
cf8fc7b719 | ||
|
|
848e646880 | ||
|
|
98d9b095d9 | ||
|
|
2bbee98594 | ||
|
|
4d929df604 | ||
|
|
10aad07e6d | ||
|
|
d16965dab6 | ||
|
|
7596937f30 | ||
|
|
0cb4b2148a | ||
|
|
9de0e2e25a | ||
|
|
9cd5397395 | ||
|
|
f6b8cad15e | ||
|
|
29d53af8dd | ||
|
|
809ced6d8a | ||
|
|
84c905c9ff | ||
|
|
ae8d661364 | ||
|
|
5ec3018f8b | ||
|
|
8a3c1587b4 | ||
|
|
e60d61e90a | ||
|
|
f1d80279e9 | ||
|
|
4979d955c2 | ||
|
|
0175cab0b4 | ||
|
|
1aecc51bbd | ||
|
|
f50eb34769 | ||
|
|
4a1777de87 | ||
|
|
d89fc6837c | ||
|
|
5e8851e070 | ||
|
|
91d162c746 |
+4
-1
@@ -5,5 +5,8 @@ GOOGLE_CLIENT_ID=6
|
|||||||
GOOGLE_CLIENT_SECRET=
|
GOOGLE_CLIENT_SECRET=
|
||||||
RESEND_API_KEY=
|
RESEND_API_KEY=
|
||||||
EMAIL_FROM=
|
EMAIL_FROM=
|
||||||
RESEND_TEMPLATE_ID=
|
# Optional. Resend template for the admin "set your password" invite email.
|
||||||
|
# Defaults to `registration-email`. Expected variables:
|
||||||
|
# `target_email_address`, `registration_link`.
|
||||||
|
RESEND_REGISTRATION_TEMPLATE_ID=
|
||||||
API_V2_ONBOARDING_SECRET=
|
API_V2_ONBOARDING_SECRET=
|
||||||
@@ -7,3 +7,6 @@ bun.lockb
|
|||||||
|
|
||||||
# Miscellaneous
|
# Miscellaneous
|
||||||
/static/
|
/static/
|
||||||
|
|
||||||
|
# Generated code
|
||||||
|
/src/lib/generated/
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ AGENTS.md
|
|||||||
at your project root:
|
at your project root:
|
||||||
|
|
||||||
````text
|
````text
|
||||||
# Rhemawave Portal AI Guide
|
# Radiant Wave Portal AI Guide
|
||||||
|
|
||||||
## Mission
|
## Mission
|
||||||
|
|
||||||
You are working on portal.Rhemawavetech.com.
|
You are working on portal.radiantwavetech.com.
|
||||||
|
|
||||||
Act as a senior engineer making complete, production-ready changes.
|
Act as a senior engineer making complete, production-ready changes.
|
||||||
|
|
||||||
@@ -97,9 +97,9 @@ Use narrower commands when appropriate.
|
|||||||
|
|
||||||
If validation fails:
|
If validation fails:
|
||||||
|
|
||||||
* Diagnose the failure.
|
- Diagnose the failure.
|
||||||
* Fix the issue.
|
- Fix the issue.
|
||||||
* Run validation again.
|
- Run validation again.
|
||||||
|
|
||||||
Never finish with known failures.
|
Never finish with known failures.
|
||||||
|
|
||||||
@@ -109,19 +109,19 @@ Never finish with known failures.
|
|||||||
|
|
||||||
Application:
|
Application:
|
||||||
|
|
||||||
portal.Rhemawavetech.com
|
portal.radiantwavetech.com
|
||||||
|
|
||||||
Stack:
|
Stack:
|
||||||
|
|
||||||
* SvelteKit 2
|
- SvelteKit 2
|
||||||
* Svelte 5
|
- Svelte 5
|
||||||
* TypeScript
|
- TypeScript
|
||||||
* Tailwind CSS v4
|
- Tailwind CSS v4
|
||||||
* Prisma 7
|
- Prisma 7
|
||||||
* PostgreSQL
|
- PostgreSQL
|
||||||
* Better Auth
|
- Better Auth
|
||||||
* Vitest
|
- Vitest
|
||||||
* Playwright
|
- Playwright
|
||||||
|
|
||||||
Package manager:
|
Package manager:
|
||||||
|
|
||||||
@@ -266,9 +266,9 @@ AdminPage.svelte
|
|||||||
|
|
||||||
Registry metadata exists for:
|
Registry metadata exists for:
|
||||||
|
|
||||||
* columns
|
- columns
|
||||||
* searches
|
- searches
|
||||||
* relations
|
- relations
|
||||||
|
|
||||||
Forms are manually implemented per route.
|
Forms are manually implemented per route.
|
||||||
|
|
||||||
@@ -309,8 +309,8 @@ Only load that file when modifying Svelte components or Svelte logic.
|
|||||||
|
|
||||||
Testing uses:
|
Testing uses:
|
||||||
|
|
||||||
* Vitest
|
- Vitest
|
||||||
* Playwright browser testing
|
- Playwright browser testing
|
||||||
|
|
||||||
For testing patterns, read:
|
For testing patterns, read:
|
||||||
|
|
||||||
@@ -338,10 +338,10 @@ Only load that file when working on API functionality.
|
|||||||
|
|
||||||
Prettier rules:
|
Prettier rules:
|
||||||
|
|
||||||
* tabs
|
- tabs
|
||||||
* single quotes
|
- single quotes
|
||||||
* no trailing commas
|
- no trailing commas
|
||||||
* 80 character print width
|
- 80 character print width
|
||||||
|
|
||||||
Follow existing formatting.
|
Follow existing formatting.
|
||||||
|
|
||||||
@@ -391,17 +391,14 @@ Confirm:
|
|||||||
2. Relevant tests/checks were run.
|
2. Relevant tests/checks were run.
|
||||||
3. Failures were fixed.
|
3. Failures were fixed.
|
||||||
4. The final response explains:
|
4. The final response explains:
|
||||||
|
- what changed
|
||||||
* what changed
|
- files modified
|
||||||
* files modified
|
- verification performed
|
||||||
* verification performed
|
|
||||||
|
|
||||||
Do not stop at:
|
Do not stop at:
|
||||||
|
|
||||||
* "I found the issue"
|
- "I found the issue"
|
||||||
* "Here is a plan"
|
- "Here is a plan"
|
||||||
* "The next step would be"
|
- "The next step would be"
|
||||||
|
|
||||||
Finish the work.
|
Finish the work.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
# portal.Rhemawavetech.com
|
# portal.radiantwavetech.com
|
||||||
|
|
||||||
SvelteKit 2 + Svelte 5 (runes) + Tailwind CSS v4 + Prisma 7 + PostgreSQL + Better-Auth.
|
SvelteKit 2 + Svelte 5 (runes) + Tailwind CSS v4 + Prisma 7 + PostgreSQL + Better-Auth.
|
||||||
|
|
||||||
@@ -332,7 +332,7 @@ The `$effect` resets `dirty = false` on mount (when the record prop changes).
|
|||||||
| Template ID | Variables | Subject (API-level) |
|
| Template ID | Variables | Subject (API-level) |
|
||||||
| ------------------------------ | --------------------------------------------------------------- | ------------------------------ |
|
| ------------------------------ | --------------------------------------------------------------- | ------------------------------ |
|
||||||
| `automated-user-registration` | `firstName`, `tempPassword`, `verifyUrl`, `loginUrl` | "Your account is ready" |
|
| `automated-user-registration` | `firstName`, `tempPassword`, `verifyUrl`, `loginUrl` | "Your account is ready" |
|
||||||
| `license-key-delivery` | `firstName`, `license_key` | "Your Rhemawave License Key" |
|
| `license-key-delivery` | `firstName`, `license_key` | "Your Radiant Wave License Key" |
|
||||||
| `email-verification-email` | `target_email_address`, `verification_link` | "Verify your email address" |
|
| `email-verification-email` | `target_email_address`, `verification_link` | "Verify your email address" |
|
||||||
|
|
||||||
Variables must be declared in the Resend template editor's Variables section. Undeclared variables render as literal `{{{varName}}}` text.
|
Variables must be declared in the Resend template editor's Variables section. Undeclared variables render as literal `{{{varName}}}` text.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Rhema Wave Technologies - User Portal
|
# Radiant Wave Technologies - User Portal
|
||||||
|
|
||||||
A combination application that combines the main Radiant Wave API with a user-facing account management application,
|
A combination application that combines the main Radiant Wave API with a user-facing account management application,
|
||||||
and an admin-facing platform management application. An all-in-one management platform for Radiant Wave and Radiant Wave Technologies.
|
and an admin-facing platform management application. An all-in-one management platform for Radiant Wave and Radiant Wave Technologies.
|
||||||
@@ -36,18 +36,17 @@ podman push registry.digitalocean.com/radiantwave-portal-container-registry/port
|
|||||||
|
|
||||||
## Admin
|
## Admin
|
||||||
|
|
||||||
Admin CRUD pages at `/admin`. 9 models have per-page routes with modals for create/edit:
|
Admin pages live under `/admin` (requires `roleId <= 3`); `/admin` redirects to
|
||||||
|
`/admin/profiles`. Each route owns its own form and modal logic, built on the shared
|
||||||
|
`DataTable.svelte` and `AdminModal.svelte` components. The generic `$lib/svelte-admin`
|
||||||
|
CRUD layer has been removed; see `docs/ADMIN_OVERHAUL.md` for the migration notes.
|
||||||
|
|
||||||
| Included | Excluded |
|
| Route | Notes |
|
||||||
| ------------------------ | --------------------------------- |
|
| -------------------------------- | ------------------------ |
|
||||||
| users | Session (Better Auth) |
|
| `/admin/users` | Name, Email, Role |
|
||||||
| profiles | Account (Better Auth) |
|
| `/admin/profiles` | Reference implementation |
|
||||||
| addresses | Verification (Better Auth) |
|
| `/admin/proclamations` | Super-only content edits |
|
||||||
| user-roles | UserProclamations (composite key) |
|
| `/admin/solfeggio-frequencies` | |
|
||||||
| proclamations | |
|
| `/admin/proclamation-categories` | Draggable reorder |
|
||||||
| solfeggio-frequencies | |
|
| `/admin/proclamation-groups` | Draggable reorder |
|
||||||
| proclamation-categories | |
|
| `/admin/license-keys` | List-only + rotate |
|
||||||
| license-keys (list-only) | |
|
|
||||||
| subscription-levels | |
|
|
||||||
|
|
||||||
See `src/lib/svelte-admin/README.md` for architecture and configuration docs.
|
|
||||||
|
|||||||
+2
-4
@@ -89,8 +89,8 @@ POST:
|
|||||||
Payload:
|
Payload:
|
||||||
|
|
||||||
{
|
{
|
||||||
"license_key": "...",
|
"license_key": "...",
|
||||||
"email_address": "..."
|
"email_address": "..."
|
||||||
}
|
}
|
||||||
|
|
||||||
Requires API key.
|
Requires API key.
|
||||||
@@ -153,7 +153,6 @@ Variables:
|
|||||||
- verifyUrl
|
- verifyUrl
|
||||||
- loginUrl
|
- loginUrl
|
||||||
|
|
||||||
|
|
||||||
license-key-delivery
|
license-key-delivery
|
||||||
|
|
||||||
Variables:
|
Variables:
|
||||||
@@ -161,7 +160,6 @@ Variables:
|
|||||||
- firstName
|
- firstName
|
||||||
- license_key
|
- license_key
|
||||||
|
|
||||||
|
|
||||||
email-verification-email
|
email-verification-email
|
||||||
|
|
||||||
Variables:
|
Variables:
|
||||||
|
|||||||
+13
-12
@@ -36,13 +36,13 @@ Always destructure $props() with defaults.
|
|||||||
Preferred pattern:
|
Preferred pattern:
|
||||||
|
|
||||||
let {
|
let {
|
||||||
record = {} as Record<string, unknown>,
|
record = {} as Record<string, unknown>,
|
||||||
fieldErrors = {} as Record<string, string>,
|
fieldErrors = {} as Record<string, string>,
|
||||||
isSuper = false
|
isSuper = false
|
||||||
}: {
|
}: {
|
||||||
record?: Record<string, unknown>;
|
record?: Record<string, unknown>;
|
||||||
fieldErrors?: Record<string, string>;
|
fieldErrors?: Record<string, string>;
|
||||||
isSuper?: boolean;
|
isSuper?: boolean;
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
Do not assume props exist.
|
Do not assume props exist.
|
||||||
@@ -66,7 +66,7 @@ Correct:
|
|||||||
let name = $state('');
|
let name = $state('');
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
name = String(record.name ?? '');
|
name = String(record.name ?? '');
|
||||||
});
|
});
|
||||||
|
|
||||||
Use $effect whenever component state needs to follow changing props.
|
Use $effect whenever component state needs to follow changing props.
|
||||||
@@ -88,11 +88,11 @@ Example:
|
|||||||
let dirty = $state(false);
|
let dirty = $state(false);
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
dirty = false;
|
dirty = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
name = String(record.name ?? '');
|
name = String(record.name ?? '');
|
||||||
});
|
});
|
||||||
|
|
||||||
This ensures a new record starts clean.
|
This ensures a new record starts clean.
|
||||||
@@ -116,9 +116,10 @@ This can fail to update correctly.
|
|||||||
Use:
|
Use:
|
||||||
|
|
||||||
<select onchange={(e) => frequency = e.currentTarget.value}>
|
<select onchange={(e) => frequency = e.currentTarget.value}>
|
||||||
<option value="1" selected={frequency === '1'}>
|
|
||||||
One
|
<option value="1" selected={frequency === '1'}>
|
||||||
</option>
|
One
|
||||||
|
</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
Use selected on options.
|
Use selected on options.
|
||||||
|
|||||||
+29
-29
@@ -30,13 +30,13 @@ Tests are colocated with source files.
|
|||||||
|
|
||||||
Client tests:
|
Client tests:
|
||||||
|
|
||||||
*.svelte.test.ts
|
_.svelte.test.ts
|
||||||
*.svelte.spec.ts
|
_.svelte.spec.ts
|
||||||
|
|
||||||
Server tests:
|
Server tests:
|
||||||
|
|
||||||
*.test.ts
|
_.test.ts
|
||||||
*.spec.ts
|
_.spec.ts
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ Node.
|
|||||||
Always reset mocks:
|
Always reset mocks:
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -109,15 +109,15 @@ Mock only the methods used.
|
|||||||
Example:
|
Example:
|
||||||
|
|
||||||
vi.mock('$lib/db', () => ({
|
vi.mock('$lib/db', () => ({
|
||||||
prisma: {
|
prisma: {
|
||||||
proclamation: {
|
proclamation: {
|
||||||
findMany: vi.fn(),
|
findMany: vi.fn(),
|
||||||
findUnique: vi.fn(),
|
findUnique: vi.fn(),
|
||||||
create: vi.fn(),
|
create: vi.fn(),
|
||||||
update: vi.fn(),
|
update: vi.fn(),
|
||||||
delete: vi.fn()
|
delete: vi.fn()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Do not create massive unused mocks.
|
Do not create massive unused mocks.
|
||||||
@@ -143,7 +143,7 @@ createLocals(mockUser() as any)
|
|||||||
Specific roles:
|
Specific roles:
|
||||||
|
|
||||||
createLocals(
|
createLocals(
|
||||||
mockUser({ roleId: 4 }) as any
|
mockUser({ roleId: 4 }) as any
|
||||||
)
|
)
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -172,15 +172,15 @@ Always use exact regex names.
|
|||||||
Preferred:
|
Preferred:
|
||||||
|
|
||||||
page.getByRole(
|
page.getByRole(
|
||||||
'textbox',
|
'textbox',
|
||||||
{ name: /^Name$/ }
|
{ name: /^Name$/ }
|
||||||
)
|
)
|
||||||
|
|
||||||
Avoid:
|
Avoid:
|
||||||
|
|
||||||
page.getByRole(
|
page.getByRole(
|
||||||
'textbox',
|
'textbox',
|
||||||
{ name: 'Name' }
|
{ name: 'Name' }
|
||||||
)
|
)
|
||||||
|
|
||||||
because substring matching can create strict-mode failures.
|
because substring matching can create strict-mode failures.
|
||||||
@@ -192,15 +192,15 @@ because substring matching can create strict-mode failures.
|
|||||||
Because selects use selected options instead of bind:value:
|
Because selects use selected options instead of bind:value:
|
||||||
|
|
||||||
const element =
|
const element =
|
||||||
await page
|
await page
|
||||||
.getByRole(
|
.getByRole(
|
||||||
'combobox',
|
'combobox',
|
||||||
{ name: /^Frequency$/ }
|
{ name: /^Frequency$/ }
|
||||||
)
|
)
|
||||||
.element();
|
.element();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
(element as HTMLSelectElement).value
|
(element as HTMLSelectElement).value
|
||||||
).toBe('2');
|
).toBe('2');
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -213,9 +213,9 @@ Use:
|
|||||||
|
|
||||||
vi.mock('$lib/components/example.svelte', () => ({
|
vi.mock('$lib/components/example.svelte', () => ({
|
||||||
default: ($anchor: Element) => {
|
default: ($anchor: Element) => {
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
$anchor.replaceWith(div);
|
$anchor.replaceWith(div);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Imports using mocked modules must come after vi.mock().
|
Imports using mocked modules must come after vi.mock().
|
||||||
|
|||||||
@@ -0,0 +1,467 @@
|
|||||||
|
openapi: 3.0.3
|
||||||
|
info:
|
||||||
|
title: Radiant Wave Portal API (v2)
|
||||||
|
version: 2.0.0
|
||||||
|
description: |
|
||||||
|
Read API used by the Radiant Wave kiosk plus the automation onboarding
|
||||||
|
webhook.
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
The v2 API is **not** API-key gated. The former bearer-token middleware in
|
||||||
|
`src/hooks.server.ts` is present but disabled, so v2 routes are served
|
||||||
|
without a global API key.
|
||||||
|
|
||||||
|
Two endpoints carry their own credential semantics:
|
||||||
|
|
||||||
|
- `GET /api/v2/proclamations` optionally accepts the paired
|
||||||
|
`x-license-key` / `x-email` headers to scope results to an entitled user.
|
||||||
|
Supplying neither returns the full catalogue; supplying only one, or a
|
||||||
|
pair that does not match, is rejected.
|
||||||
|
- `POST /api/v2/automations/create/account` is protected by an HMAC-SHA256
|
||||||
|
request signature (see the operation description).
|
||||||
|
servers:
|
||||||
|
- url: https://portal.radiantwavetech.com
|
||||||
|
description: Production
|
||||||
|
- url: http://localhost:5173
|
||||||
|
description: Local development
|
||||||
|
tags:
|
||||||
|
- name: Catalogue
|
||||||
|
description: Reference data for categories, groups, frequencies and palettes.
|
||||||
|
- name: Proclamations
|
||||||
|
description: Proclamation content, optionally scoped by license entitlement.
|
||||||
|
- name: License
|
||||||
|
description: License key validation.
|
||||||
|
- name: Automations
|
||||||
|
description: Signed machine-to-machine webhooks.
|
||||||
|
paths:
|
||||||
|
/api/v2/categories:
|
||||||
|
get:
|
||||||
|
tags: [Catalogue]
|
||||||
|
summary: List proclamation categories
|
||||||
|
operationId: listCategories
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: All proclamation categories.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Category'
|
||||||
|
/api/v2/proclamation-categories:
|
||||||
|
get:
|
||||||
|
tags: [Catalogue]
|
||||||
|
summary: List proclamation categories (dedicated alias)
|
||||||
|
description: Alias of `/api/v2/categories`; returns the identical payload.
|
||||||
|
operationId: listProclamationCategories
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: All proclamation categories.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Category'
|
||||||
|
/api/v2/groups:
|
||||||
|
get:
|
||||||
|
tags: [Catalogue]
|
||||||
|
summary: List proclamation groups
|
||||||
|
description: Groups are ordered by `sortOrder` ascending.
|
||||||
|
operationId: listGroups
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: All proclamation groups.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Group'
|
||||||
|
/api/v2/proclamation-groups:
|
||||||
|
get:
|
||||||
|
tags: [Catalogue]
|
||||||
|
summary: List proclamation groups (dedicated alias)
|
||||||
|
description: Alias of `/api/v2/groups`; returns the identical payload.
|
||||||
|
operationId: listProclamationGroups
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: All proclamation groups.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Group'
|
||||||
|
/api/v2/frequencies:
|
||||||
|
get:
|
||||||
|
tags: [Catalogue]
|
||||||
|
summary: List canonical solfeggio frequencies
|
||||||
|
description: |
|
||||||
|
Returns the nine canonical frequencies (ids 1-9) the kiosk expects.
|
||||||
|
These are static values, not the portal database autoincrement ids.
|
||||||
|
operationId: listFrequencies
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: The nine canonical frequencies.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
minItems: 9
|
||||||
|
maxItems: 9
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Frequency'
|
||||||
|
/api/v2/color-palettes:
|
||||||
|
get:
|
||||||
|
tags: [Catalogue]
|
||||||
|
summary: List color palettes per canonical frequency
|
||||||
|
description: |
|
||||||
|
Returns one 7-color palette for each canonical frequency id (1-9).
|
||||||
|
Palette rows stored in the database take precedence; ids without a row
|
||||||
|
fall back to the built-in canonical palette.
|
||||||
|
operationId: listColorPalettes
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Nine palettes, one per canonical frequency id.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
minItems: 9
|
||||||
|
maxItems: 9
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/ColorPalette'
|
||||||
|
/api/v2/proclamations:
|
||||||
|
get:
|
||||||
|
tags: [Proclamations]
|
||||||
|
summary: List proclamations
|
||||||
|
description: |
|
||||||
|
Without entitlement headers, returns the full proclamation catalogue.
|
||||||
|
|
||||||
|
With both `x-license-key` and `x-email`, returns only the proclamations
|
||||||
|
assigned to the matching user. Both headers must be present together and
|
||||||
|
the key must belong to the user.
|
||||||
|
|
||||||
|
Proclamations whose frequency has no canonical id (for example 936 Hz)
|
||||||
|
are omitted from the response.
|
||||||
|
operationId: listProclamations
|
||||||
|
parameters:
|
||||||
|
- in: header
|
||||||
|
name: x-license-key
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
description: License key. Must be supplied together with `x-email`.
|
||||||
|
- in: header
|
||||||
|
name: x-email
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: email
|
||||||
|
required: false
|
||||||
|
description: Account email. Must be supplied together with `x-license-key`.
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Proclamation records with canonical frequency ids and embedded palettes.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Proclamation'
|
||||||
|
'400':
|
||||||
|
description: |
|
||||||
|
Missing entitlement or the supplied key/email pair is invalid.
|
||||||
|
Message is `not-authorized`, or
|
||||||
|
`this key doesn't appear to belong to you` when the key does not
|
||||||
|
belong to the specified user.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ErrorMessage'
|
||||||
|
/api/v2/license:
|
||||||
|
post:
|
||||||
|
tags: [License]
|
||||||
|
summary: Validate a license key
|
||||||
|
description: |
|
||||||
|
Validates that the license key exists, is active, and belongs to the
|
||||||
|
account matching `email_address`. A successful validation records
|
||||||
|
`lastValidatedAt` on the key.
|
||||||
|
operationId: validateLicense
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/LicenseRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: The validated key and its owner email.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/LicenseResponse'
|
||||||
|
'400':
|
||||||
|
description: |
|
||||||
|
Validation failure or malformed body. Possible messages:
|
||||||
|
`content-type must be application/json`, `invalid JSON body`,
|
||||||
|
`request body is required`, `license_key is required`,
|
||||||
|
`email_address is required`, `license key not found`,
|
||||||
|
`license key is not active`, `user does not exist`,
|
||||||
|
`key does not belong to specified user`.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ErrorMessage'
|
||||||
|
'413':
|
||||||
|
description: The request body exceeded the 64 KiB limit.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ErrorMessage'
|
||||||
|
'429':
|
||||||
|
description: Rate limit exceeded (10 requests per minute per IP).
|
||||||
|
headers:
|
||||||
|
Retry-After:
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
description: Seconds to wait before retrying.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ErrorMessage'
|
||||||
|
/api/v2/automations/create/account:
|
||||||
|
post:
|
||||||
|
tags: [Automations]
|
||||||
|
summary: Create an account and license key (signed webhook)
|
||||||
|
description: |
|
||||||
|
Machine-to-machine onboarding webhook. The request must be signed with
|
||||||
|
HMAC-SHA256 using `API_V2_ONBOARDING_SECRET`.
|
||||||
|
|
||||||
|
The canonical string that is signed is the newline-joined concatenation
|
||||||
|
of:
|
||||||
|
|
||||||
|
```
|
||||||
|
POST
|
||||||
|
<path>
|
||||||
|
<x-timestamp>
|
||||||
|
<x-nonce>
|
||||||
|
<raw request body>
|
||||||
|
```
|
||||||
|
|
||||||
|
where `<path>` is `/api/v2/automations/create/account`. The signature is
|
||||||
|
the lowercase hex digest.
|
||||||
|
|
||||||
|
On success, creates the user if they do not already exist, generates a
|
||||||
|
license key, emails it, and returns the user id. Invalid or missing
|
||||||
|
signatures return `401 { "error": "invalid signature" }` (the same
|
||||||
|
response is used for every signature failure, including replay and
|
||||||
|
timestamp skew).
|
||||||
|
operationId: createAutomationAccount
|
||||||
|
parameters:
|
||||||
|
- in: header
|
||||||
|
name: x-timestamp
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Unix epoch seconds. Must be within 300 seconds of the server clock.
|
||||||
|
- in: header
|
||||||
|
name: x-nonce
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Unique value per request. Replays within the skew window are rejected.
|
||||||
|
- in: header
|
||||||
|
name: x-signature
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
pattern: '^[0-9a-f]{64}$'
|
||||||
|
description: Lowercase hex HMAC-SHA256 of the canonical request string.
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/OnboardingRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Account and license key created.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/OnboardingResponse'
|
||||||
|
'400':
|
||||||
|
description: |
|
||||||
|
Malformed or invalid request. Possible messages:
|
||||||
|
`request body is required`, `invalid JSON body`, or
|
||||||
|
`email is required`.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ErrorMessage'
|
||||||
|
'401':
|
||||||
|
description: Signature missing, malformed, expired, replayed, or incorrect.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
required: [error]
|
||||||
|
properties:
|
||||||
|
error:
|
||||||
|
type: string
|
||||||
|
example: invalid signature
|
||||||
|
'413':
|
||||||
|
description: The request body exceeded the 64 KiB limit.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ErrorMessage'
|
||||||
|
'429':
|
||||||
|
description: |
|
||||||
|
Rate limit exceeded. Limits are 5 requests per minute per IP and
|
||||||
|
3 requests per 24 hours per email address.
|
||||||
|
headers:
|
||||||
|
Retry-After:
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
description: Seconds to wait before retrying.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ErrorMessage'
|
||||||
|
'500':
|
||||||
|
description: The account could not be created.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ErrorMessage'
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
Category:
|
||||||
|
type: object
|
||||||
|
required: [id, name, sortOrder]
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
sortOrder:
|
||||||
|
type: integer
|
||||||
|
Group:
|
||||||
|
type: object
|
||||||
|
required: [id, name, sortOrder]
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
sortOrder:
|
||||||
|
type: integer
|
||||||
|
Frequency:
|
||||||
|
type: object
|
||||||
|
required: [id, name, value]
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
minimum: 1
|
||||||
|
maximum: 9
|
||||||
|
description: Canonical id used by the kiosk.
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
value:
|
||||||
|
type: integer
|
||||||
|
description: Frequency in Hz.
|
||||||
|
ColorPalette:
|
||||||
|
type: object
|
||||||
|
required: [solfeggioFrequencyId, colors]
|
||||||
|
properties:
|
||||||
|
solfeggioFrequencyId:
|
||||||
|
type: integer
|
||||||
|
minimum: 1
|
||||||
|
maximum: 9
|
||||||
|
colors:
|
||||||
|
type: array
|
||||||
|
description: Seven hex colors, ordered light to dark.
|
||||||
|
minItems: 1
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
pattern: '^#[0-9A-Fa-f]{6}$'
|
||||||
|
Proclamation:
|
||||||
|
type: object
|
||||||
|
required: [id, name, content, categoryId, groupId, solfeggioFrequencyId]
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
content:
|
||||||
|
type: string
|
||||||
|
description: HTML content.
|
||||||
|
categoryId:
|
||||||
|
type: integer
|
||||||
|
groupId:
|
||||||
|
type: integer
|
||||||
|
solfeggioFrequencyId:
|
||||||
|
type: integer
|
||||||
|
minimum: 1
|
||||||
|
maximum: 9
|
||||||
|
colorPalette:
|
||||||
|
type: array
|
||||||
|
description: Present only when a palette exists for the canonical frequency id.
|
||||||
|
minItems: 1
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
pattern: '^#[0-9A-Fa-f]{6}$'
|
||||||
|
LicenseRequest:
|
||||||
|
type: object
|
||||||
|
required: [license_key, email_address]
|
||||||
|
properties:
|
||||||
|
license_key:
|
||||||
|
type: string
|
||||||
|
pattern: '^[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$'
|
||||||
|
example: ABCD-EFGH-IJKL-MNOP
|
||||||
|
email_address:
|
||||||
|
type: string
|
||||||
|
format: email
|
||||||
|
maxLength: 254
|
||||||
|
LicenseResponse:
|
||||||
|
type: object
|
||||||
|
required: [key, email_address]
|
||||||
|
properties:
|
||||||
|
key:
|
||||||
|
type: string
|
||||||
|
example: ABCD-EFGH-IJKL-MNOP
|
||||||
|
email_address:
|
||||||
|
type: string
|
||||||
|
format: email
|
||||||
|
OnboardingRequest:
|
||||||
|
type: object
|
||||||
|
required: [email]
|
||||||
|
properties:
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
|
format: email
|
||||||
|
maxLength: 254
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
maxLength: 120
|
||||||
|
description: Optional display name. Defaults to the email local-part.
|
||||||
|
OnboardingResponse:
|
||||||
|
type: object
|
||||||
|
required: [success, message, user_id]
|
||||||
|
properties:
|
||||||
|
success:
|
||||||
|
type: boolean
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
example: Account and license key created
|
||||||
|
user_id:
|
||||||
|
type: string
|
||||||
|
ErrorMessage:
|
||||||
|
type: object
|
||||||
|
required: [message]
|
||||||
|
properties:
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="color-scheme" content="light dark" />
|
||||||
|
<title>Verify your email address</title>
|
||||||
|
</head>
|
||||||
|
<body
|
||||||
|
style="
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #f1f5f9;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
font-family:
|
||||||
|
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<!-- Preheader (hidden) -->
|
||||||
|
<div style="display: none; max-height: 0; overflow: hidden; opacity: 0; color: transparent">
|
||||||
|
Confirm your email address to activate your Radiant Wave account.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table
|
||||||
|
role="presentation"
|
||||||
|
width="100%"
|
||||||
|
cellpadding="0"
|
||||||
|
cellspacing="0"
|
||||||
|
style="background-color: #f1f5f9; padding: 32px 12px"
|
||||||
|
>
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<table
|
||||||
|
role="presentation"
|
||||||
|
width="600"
|
||||||
|
cellpadding="0"
|
||||||
|
cellspacing="0"
|
||||||
|
style="
|
||||||
|
width: 100%;
|
||||||
|
max-width: 600px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 28px 32px 0 32px">
|
||||||
|
<span
|
||||||
|
style="
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #4f46e5;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Radiant Wave
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 24px 32px 0 32px">
|
||||||
|
<h1
|
||||||
|
style="
|
||||||
|
margin: 0 0 12px 0;
|
||||||
|
color: #0f172a;
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 1.25;
|
||||||
|
font-weight: 700;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Confirm your email address
|
||||||
|
</h1>
|
||||||
|
<p style="margin: 0 0 8px 0; color: #334155; font-size: 16px; line-height: 1.6">
|
||||||
|
Thanks for signing up for Radiant Wave. Verify your email address to activate your
|
||||||
|
account.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 16px 32px 0 32px">
|
||||||
|
<table role="presentation" cellpadding="0" cellspacing="0">
|
||||||
|
<tr>
|
||||||
|
<td style="border-radius: 10px; background-color: #4f46e5">
|
||||||
|
<a
|
||||||
|
href="{{{verification_link}}}"
|
||||||
|
target="_blank"
|
||||||
|
style="
|
||||||
|
display: inline-block;
|
||||||
|
padding: 14px 28px;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Verify Email Address
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 16px 32px 0 32px">
|
||||||
|
<p style="margin: 0; color: #64748b; font-size: 13px; line-height: 1.6">
|
||||||
|
If the button doesn't work, copy and paste this link into your browser:
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
style="
|
||||||
|
margin: 6px 0 0 0;
|
||||||
|
word-break: break-all;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.6;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href="{{{verification_link}}}"
|
||||||
|
target="_blank"
|
||||||
|
style="color: #4f46e5; text-decoration: underline"
|
||||||
|
>{{{verification_link}}}</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 24px 32px 32px 32px">
|
||||||
|
<hr style="border: none; border-top: 1px solid #e2e8f0; margin: 0 0 16px 0" />
|
||||||
|
<p style="margin: 0; color: #94a3b8; font-size: 12px; line-height: 1.6">
|
||||||
|
This link expires in 1 hour. If you didn't create an account, you can safely
|
||||||
|
ignore this email.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<table
|
||||||
|
role="presentation"
|
||||||
|
width="600"
|
||||||
|
cellpadding="0"
|
||||||
|
cellspacing="0"
|
||||||
|
style="width: 100%; max-width: 600px"
|
||||||
|
>
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 20px 8px; text-align: center">
|
||||||
|
<p style="margin: 0; color: #94a3b8; font-size: 12px; line-height: 1.6">
|
||||||
|
Sent to {{{target_email_address}}}<br />
|
||||||
|
© Radiant Wave Technologies
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="color-scheme" content="light dark" />
|
||||||
|
<title>Your Radiant Wave License Key</title>
|
||||||
|
</head>
|
||||||
|
<body
|
||||||
|
style="
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #f1f5f9;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
font-family:
|
||||||
|
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<!-- Preheader (hidden) -->
|
||||||
|
<div style="display: none; max-height: 0; overflow: hidden; opacity: 0; color: transparent">
|
||||||
|
Here's your Radiant Wave license key.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table
|
||||||
|
role="presentation"
|
||||||
|
width="100%"
|
||||||
|
cellpadding="0"
|
||||||
|
cellspacing="0"
|
||||||
|
style="background-color: #f1f5f9; padding: 32px 12px"
|
||||||
|
>
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<table
|
||||||
|
role="presentation"
|
||||||
|
width="600"
|
||||||
|
cellpadding="0"
|
||||||
|
cellspacing="0"
|
||||||
|
style="
|
||||||
|
width: 100%;
|
||||||
|
max-width: 600px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 28px 32px 0 32px">
|
||||||
|
<span
|
||||||
|
style="
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #4f46e5;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Radiant Wave
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 24px 32px 0 32px">
|
||||||
|
<h1
|
||||||
|
style="
|
||||||
|
margin: 0 0 12px 0;
|
||||||
|
color: #0f172a;
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 1.25;
|
||||||
|
font-weight: 700;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Your license key
|
||||||
|
</h1>
|
||||||
|
<p style="margin: 0 0 8px 0; color: #334155; font-size: 16px; line-height: 1.6">
|
||||||
|
Hi {{{firstName}}}, your Radiant Wave account is ready. Here's your license key —
|
||||||
|
you'll need it to activate the app.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 20px 32px 0 32px">
|
||||||
|
<table
|
||||||
|
role="presentation"
|
||||||
|
width="100%"
|
||||||
|
cellpadding="0"
|
||||||
|
cellspacing="0"
|
||||||
|
style="background-color: #f8fafc; border: 1px dashed #cbd5e1; border-radius: 12px"
|
||||||
|
>
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 18px 20px; text-align: center">
|
||||||
|
<p
|
||||||
|
style="
|
||||||
|
margin: 0 0 6px 0;
|
||||||
|
color: #64748b;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Your License Key
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
style="
|
||||||
|
margin: 0;
|
||||||
|
color: #0f172a;
|
||||||
|
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
word-break: break-all;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{{license_key}}}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 20px 32px 0 32px">
|
||||||
|
<table role="presentation" cellpadding="0" cellspacing="0">
|
||||||
|
<tr>
|
||||||
|
<td style="border-radius: 10px; background-color: #4f46e5">
|
||||||
|
<a
|
||||||
|
href="https://portal.radiantwavetech.com/dashboard"
|
||||||
|
target="_blank"
|
||||||
|
style="
|
||||||
|
display: inline-block;
|
||||||
|
padding: 14px 28px;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Go to Your Dashboard
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 24px 32px 32px 32px">
|
||||||
|
<hr style="border: none; border-top: 1px solid #e2e8f0; margin: 0 0 16px 0" />
|
||||||
|
<p style="margin: 0; color: #94a3b8; font-size: 12px; line-height: 1.6">
|
||||||
|
Keep this key safe — anyone who has it can activate the app. If you didn't request
|
||||||
|
this, contact support.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<table
|
||||||
|
role="presentation"
|
||||||
|
width="600"
|
||||||
|
cellpadding="0"
|
||||||
|
cellspacing="0"
|
||||||
|
style="width: 100%; max-width: 600px"
|
||||||
|
>
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 20px 8px; text-align: center">
|
||||||
|
<p style="margin: 0; color: #94a3b8; font-size: 12px; line-height: 1.6">
|
||||||
|
© Radiant Wave Technologies
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="color-scheme" content="light dark" />
|
||||||
|
<title>Reset your Radiant Wave password</title>
|
||||||
|
</head>
|
||||||
|
<body
|
||||||
|
style="
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #f1f5f9;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
font-family:
|
||||||
|
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<!-- Preheader (hidden) -->
|
||||||
|
<div style="display: none; max-height: 0; overflow: hidden; opacity: 0; color: transparent">
|
||||||
|
Choose a new password for your Radiant Wave account.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table
|
||||||
|
role="presentation"
|
||||||
|
width="100%"
|
||||||
|
cellpadding="0"
|
||||||
|
cellspacing="0"
|
||||||
|
style="background-color: #f1f5f9; padding: 32px 12px"
|
||||||
|
>
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<table
|
||||||
|
role="presentation"
|
||||||
|
width="600"
|
||||||
|
cellpadding="0"
|
||||||
|
cellspacing="0"
|
||||||
|
style="
|
||||||
|
width: 100%;
|
||||||
|
max-width: 600px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 28px 32px 0 32px">
|
||||||
|
<span
|
||||||
|
style="
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #4f46e5;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Radiant Wave
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 24px 32px 0 32px">
|
||||||
|
<h1
|
||||||
|
style="
|
||||||
|
margin: 0 0 12px 0;
|
||||||
|
color: #0f172a;
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 1.25;
|
||||||
|
font-weight: 700;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Reset your password
|
||||||
|
</h1>
|
||||||
|
<p style="margin: 0 0 8px 0; color: #334155; font-size: 16px; line-height: 1.6">
|
||||||
|
We received a request to reset your Radiant Wave password. Click the button below
|
||||||
|
to choose a new one.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 16px 32px 0 32px">
|
||||||
|
<table role="presentation" cellpadding="0" cellspacing="0">
|
||||||
|
<tr>
|
||||||
|
<td style="border-radius: 10px; background-color: #4f46e5">
|
||||||
|
<a
|
||||||
|
href="{{{reset_link}}}"
|
||||||
|
target="_blank"
|
||||||
|
style="
|
||||||
|
display: inline-block;
|
||||||
|
padding: 14px 28px;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Reset Password
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 16px 32px 0 32px">
|
||||||
|
<p style="margin: 0; color: #64748b; font-size: 13px; line-height: 1.6">
|
||||||
|
If the button doesn't work, copy and paste this link into your browser:
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
style="
|
||||||
|
margin: 6px 0 0 0;
|
||||||
|
word-break: break-all;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.6;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href="{{{reset_link}}}"
|
||||||
|
target="_blank"
|
||||||
|
style="color: #4f46e5; text-decoration: underline"
|
||||||
|
>{{{reset_link}}}</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 24px 32px 32px 32px">
|
||||||
|
<hr style="border: none; border-top: 1px solid #e2e8f0; margin: 0 0 16px 0" />
|
||||||
|
<p style="margin: 0; color: #94a3b8; font-size: 12px; line-height: 1.6">
|
||||||
|
This link expires in 1 hour. If you didn't request a password reset, you can
|
||||||
|
safely ignore this email — your password won't change.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<table
|
||||||
|
role="presentation"
|
||||||
|
width="600"
|
||||||
|
cellpadding="0"
|
||||||
|
cellspacing="0"
|
||||||
|
style="width: 100%; max-width: 600px"
|
||||||
|
>
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 20px 8px; text-align: center">
|
||||||
|
<p style="margin: 0; color: #94a3b8; font-size: 12px; line-height: 1.6">
|
||||||
|
Sent to {{{target_email_address}}}<br />
|
||||||
|
© Radiant Wave Technologies
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="color-scheme" content="light dark" />
|
||||||
|
<title>Welcome to Radiant Wave</title>
|
||||||
|
</head>
|
||||||
|
<body
|
||||||
|
style="
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #f1f5f9;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
font-family:
|
||||||
|
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<!-- Preheader (hidden) -->
|
||||||
|
<div style="display: none; max-height: 0; overflow: hidden; opacity: 0; color: transparent">
|
||||||
|
Set your password to finish setting up your Radiant Wave account.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table
|
||||||
|
role="presentation"
|
||||||
|
width="100%"
|
||||||
|
cellpadding="0"
|
||||||
|
cellspacing="0"
|
||||||
|
style="background-color: #f1f5f9; padding: 32px 12px"
|
||||||
|
>
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<table
|
||||||
|
role="presentation"
|
||||||
|
width="600"
|
||||||
|
cellpadding="0"
|
||||||
|
cellspacing="0"
|
||||||
|
style="
|
||||||
|
width: 100%;
|
||||||
|
max-width: 600px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 28px 32px 0 32px">
|
||||||
|
<span
|
||||||
|
style="
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #4f46e5;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Radiant Wave
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 24px 32px 0 32px">
|
||||||
|
<h1
|
||||||
|
style="
|
||||||
|
margin: 0 0 12px 0;
|
||||||
|
color: #0f172a;
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 1.25;
|
||||||
|
font-weight: 700;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Welcome to Radiant Wave
|
||||||
|
</h1>
|
||||||
|
<p style="margin: 0 0 8px 0; color: #334155; font-size: 16px; line-height: 1.6">
|
||||||
|
An account has been created for you. Set a password to finish setting up your
|
||||||
|
account and access your dashboard.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 16px 32px 0 32px">
|
||||||
|
<table role="presentation" cellpadding="0" cellspacing="0">
|
||||||
|
<tr>
|
||||||
|
<td style="border-radius: 10px; background-color: #4f46e5">
|
||||||
|
<a
|
||||||
|
href="{{{registration_link}}}"
|
||||||
|
target="_blank"
|
||||||
|
style="
|
||||||
|
display: inline-block;
|
||||||
|
padding: 14px 28px;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Set Your Password
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 16px 32px 0 32px">
|
||||||
|
<p style="margin: 0; color: #64748b; font-size: 13px; line-height: 1.6">
|
||||||
|
If the button doesn't work, copy and paste this link into your browser:
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
style="
|
||||||
|
margin: 6px 0 0 0;
|
||||||
|
word-break: break-all;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.6;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href="{{{registration_link}}}"
|
||||||
|
target="_blank"
|
||||||
|
style="color: #4f46e5; text-decoration: underline"
|
||||||
|
>{{{registration_link}}}</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 24px 32px 0 32px">
|
||||||
|
<p style="margin: 0; color: #334155; font-size: 15px; line-height: 1.6">
|
||||||
|
After you set your password, sign in and complete your profile to get started.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 24px 32px 32px 32px">
|
||||||
|
<hr style="border: none; border-top: 1px solid #e2e8f0; margin: 0 0 16px 0" />
|
||||||
|
<p style="margin: 0; color: #94a3b8; font-size: 12px; line-height: 1.6">
|
||||||
|
This link expires in 1 hour. If you didn't expect this email, you can safely
|
||||||
|
ignore it.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<table
|
||||||
|
role="presentation"
|
||||||
|
width="600"
|
||||||
|
cellpadding="0"
|
||||||
|
cellspacing="0"
|
||||||
|
style="width: 100%; max-width: 600px"
|
||||||
|
>
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 20px 8px; text-align: center">
|
||||||
|
<p style="margin: 0; color: #94a3b8; font-size: 12px; line-height: 1.6">
|
||||||
|
Sent to {{{target_email_address}}}<br />
|
||||||
|
© Radiant Wave Technologies
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -37,5 +37,11 @@ export default defineConfig(
|
|||||||
svelteConfig
|
svelteConfig
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.spec.ts', '**/*.spec.js'],
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "portal.Rhemawavetech.com",
|
"name": "portal.radiantwavetech.com",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "portal.Rhemawavetech.com",
|
"name": "portal.radiantwavetech.com",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/adapter-pg": "^7.8.0",
|
"@prisma/adapter-pg": "^7.8.0",
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "portal.Rhemawavetech.com",
|
"name": "portal.radiantwavetech.com",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -67,4 +67,4 @@
|
|||||||
"sveltekit-superforms": "^2.30.2",
|
"sveltekit-superforms": "^2.30.2",
|
||||||
"zod": "^4.4.3"
|
"zod": "^4.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,756 +0,0 @@
|
|||||||
# RadiantWave Portal API v2 Schema
|
|
||||||
|
|
||||||
This document defines the **required** API surface for the RadiantWave kiosk
|
|
||||||
application and serves as the implementation contract for the portal
|
|
||||||
(`../portal`).
|
|
||||||
|
|
||||||
It supersedes `payloads.md` and `ApiUpdates.md`, which described a v1 API that
|
|
||||||
does not meet the application's requirements.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Background and constraints
|
|
||||||
|
|
||||||
- The kiosk application (`this repo`) talks to
|
|
||||||
`https://portal.radiantwavetech.com/api/v1`.
|
|
||||||
- The **existing v1 routes must not change**: previously shipped kiosk binaries
|
|
||||||
depend on them. The v1 routes and their exact request/response shapes stay
|
|
||||||
frozen.
|
|
||||||
- To bring the API into compliance with this application's expectations we
|
|
||||||
introduce a **new, versioned API under `/api/v2`**, and migrate the kiosk
|
|
||||||
client to call `/api/v2` instead of `/api/v1`.
|
|
||||||
- The kiosk client currently uses a single base URL
|
|
||||||
(`src/network/api_client.h` → `API_BASE_URL`). All endpoint paths are
|
|
||||||
relative to that base, so migrating the base URL is the only client-side
|
|
||||||
change required.
|
|
||||||
|
|
||||||
Compliance means: every endpoint the kiosk parses must return exactly the
|
|
||||||
fields the kiosk expects, in the shapes documented below. Deviations break
|
|
||||||
provisioning, content sync, or rendering.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Versioning strategy
|
|
||||||
|
|
||||||
| Route space | Status | Consumer |
|
|
||||||
|-------------|---------------------------|-------------------------------------------------------|
|
|
||||||
| `/api/v1/*` | Frozen, never modified | Old/shipped kiosk binaries, GoHighLevel automation |
|
|
||||||
| `/api/v2/*` | New (this document) | Current kiosk application (after base-URL migration) |
|
|
||||||
|
|
||||||
Rules:
|
|
||||||
|
|
||||||
1. Never change the behavior or response shape of an existing `/api/v1/*`
|
|
||||||
route. If a v1 fix is required, fix it in v2.
|
|
||||||
2. New capabilities ship under `/api/v2` only.
|
|
||||||
3. A consumer may pin to the major version it was built against. The kiosk
|
|
||||||
pins to `/api/v2`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## General conventions (v2)
|
|
||||||
|
|
||||||
- Base URL: `https://portal.radiantwavetech.com/api/v2`.
|
|
||||||
- All routes require HTTPS (TLS 1.2+; TLS 1.3 recommended).
|
|
||||||
- Success responses are JSON with `content-type: application/json`.
|
|
||||||
- Error bodies use the shape `{"message": "..."}` for route-handler errors and
|
|
||||||
`{"error": "..."}` for the authentication layer (see Security).
|
|
||||||
- Any response code outside the documented success code is a failure; the
|
|
||||||
kiosk surfaces `message`/`error` text when present.
|
|
||||||
- API keys are sent as `Authorization: Bearer sk_<hex>` and are **required on
|
|
||||||
every v2 route** except the signed onboarding route (below).
|
|
||||||
- Credentials (license keys, emails) must **never** be passed in the query
|
|
||||||
string. v1 accepted `?email=&key=`; v2 must not.
|
|
||||||
- Request body sizes are capped (see Security); oversized bodies are rejected
|
|
||||||
with `413`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Canonical data model: solfeggio frequencies
|
|
||||||
|
|
||||||
The kiosk uses a fixed 9-preset frequency scheme. Frequency ids must be
|
|
||||||
**canonical** — they appear on proclamations and color palettes and must match
|
|
||||||
the kiosk's own `frequencyIdForHz()` mapping:
|
|
||||||
|
|
||||||
| Canonical id | Hz | Frequency (kiosk label) |
|
|
||||||
|--------------|------|------------------------------|
|
|
||||||
| 1 | 174 | Grounding / Relief (285 Hz is an alias of id 1) |
|
|
||||||
| 2 | 396 | Root |
|
|
||||||
| 3 | 417 | Sacral |
|
|
||||||
| 4 | 432 | Clarity / Wellbeing |
|
|
||||||
| 5 | 528 | Solar Plexus (fallback palette) |
|
|
||||||
| 6 | 639 | Heart |
|
|
||||||
| 7 | 741 | Throat |
|
|
||||||
| 8 | 852 | 3rd Eye |
|
|
||||||
| 9 | 963 | Crown |
|
|
||||||
|
|
||||||
**Compliance gap in v1:** the portal's `SolfeggioFrequency` table stores 7
|
|
||||||
rows whose ids do **not** match this scheme (id 1 = 936 Hz, id 5 = 528 Hz,
|
|
||||||
id 7 = 396 Hz, etc.). Because proclamations and color palettes are keyed by
|
|
||||||
frequency id, returning portal ids would make the kiosk look up the wrong
|
|
||||||
palette (e.g. a 396 Hz proclamation would render with the 741 Hz palette).
|
|
||||||
|
|
||||||
**v2 requirement:** every `solfeggioFrequencyId` emitted by v2 must be a
|
|
||||||
canonical id (1–9) whose `value` resolves through the kiosk map above.
|
|
||||||
Frequencies that have no canonical id (e.g. 936 Hz) must not be referenced by
|
|
||||||
any v2 proclamation or palette. See Implementation notes for the reconciling
|
|
||||||
strategy.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Endpoints
|
|
||||||
|
|
||||||
### 1. `GET /api/v2/frequencies`
|
|
||||||
|
|
||||||
Lists the canonical solfeggio frequencies.
|
|
||||||
|
|
||||||
Request:
|
|
||||||
|
|
||||||
```
|
|
||||||
GET /api/v2/frequencies
|
|
||||||
Authorization: Bearer sk_<hex>
|
|
||||||
```
|
|
||||||
|
|
||||||
Response (200):
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{ "id": 1, "name": "Grounding / Relief", "value": 174 },
|
|
||||||
{ "id": 2, "name": "Root", "value": 396 },
|
|
||||||
{ "id": 3, "name": "Sacral", "value": 417 },
|
|
||||||
{ "id": 4, "name": "Clarity / Wellbeing", "value": 432 },
|
|
||||||
{ "id": 5, "name": "Solar Plexus", "value": 528 },
|
|
||||||
{ "id": 6, "name": "Heart", "value": 639 },
|
|
||||||
{ "id": 7, "name": "Throat", "value": 741 },
|
|
||||||
{ "id": 8, "name": "3rd Eye", "value": 852 },
|
|
||||||
{ "id": 9, "name": "Crown", "value": 963 }
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
Contract (kiosk `parseFrequenciesJson`):
|
|
||||||
|
|
||||||
- `id`: int, 1–9, **non-zero** (entries with id 0 are skipped).
|
|
||||||
- `name`: string.
|
|
||||||
- `value`: int, Hz. Should be a value the kiosk's `frequencyIdForHz()` map
|
|
||||||
recognizes (174, 285, 396, 417, 432, 528, 639, 741, 852, 963).
|
|
||||||
|
|
||||||
Errors:
|
|
||||||
|
|
||||||
| Code | Body | Meaning |
|
|
||||||
|------|------|---------|
|
|
||||||
| 200 | array of frequency objects | Success |
|
|
||||||
| 401 | `{"error": "..."}` | Missing/invalid API key |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. `GET /api/v2/categories`
|
|
||||||
|
|
||||||
Lists all proclamation categories (identical shape to v1).
|
|
||||||
|
|
||||||
Request:
|
|
||||||
|
|
||||||
```
|
|
||||||
GET /api/v2/categories
|
|
||||||
Authorization: Bearer sk_<hex>
|
|
||||||
```
|
|
||||||
|
|
||||||
Response (200):
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{ "id": 4, "name": "Spiritual", "sortOrder": 0, "parentId": null },
|
|
||||||
{ "id": 5, "name": "General", "sortOrder": 0, "parentId": 4 }
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
Contract (kiosk `parseCategoriesJson`):
|
|
||||||
|
|
||||||
- `id`: int, **non-zero** (id 0 entries are skipped).
|
|
||||||
- `name`: string.
|
|
||||||
- `parentId`: int for children, **`null` for root** categories (kiosk maps
|
|
||||||
null → 0).
|
|
||||||
- `sortOrder`: int.
|
|
||||||
|
|
||||||
Errors:
|
|
||||||
|
|
||||||
| Code | Body | Meaning |
|
|
||||||
|------|------|---------|
|
|
||||||
| 200 | array of category objects | Success |
|
|
||||||
| 401 | `{"error": "..."}` | Missing/invalid API key |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. `GET /api/v2/proclamations`
|
|
||||||
|
|
||||||
Returns the proclamation library. This is the kiosk's primary content sync.
|
|
||||||
|
|
||||||
Request (default: full library):
|
|
||||||
|
|
||||||
```
|
|
||||||
GET /api/v2/proclamations
|
|
||||||
Authorization: Bearer sk_<hex>
|
|
||||||
```
|
|
||||||
|
|
||||||
Optional entitlement filter (full records, see below):
|
|
||||||
|
|
||||||
```
|
|
||||||
GET /api/v2/proclamations
|
|
||||||
Authorization: Bearer sk_<hex>
|
|
||||||
X-License-Key: XXXX-XXXX-XXXX-XXXX
|
|
||||||
X-Email: user@example.com
|
|
||||||
```
|
|
||||||
|
|
||||||
> Header-based credentials replace the v1 `?email=&key=` query form. Do not
|
|
||||||
> read credentials from the URL.
|
|
||||||
|
|
||||||
Response (200) — every item is a **full proclamation record**:
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"id": "b40fdcb9-703c-420e-af32-a081964c6f08",
|
|
||||||
"name": "Peace & Relaxation",
|
|
||||||
"content": "<p>The text content, HTML formatted</p>",
|
|
||||||
"categoryId": 5,
|
|
||||||
"solfeggioFrequencyId": 5,
|
|
||||||
"subscriptionLevelId": 1,
|
|
||||||
"colorPalette": ["#32CD32", "#3CB371", "#90EE90", "#98FB98", "#ADFFA9", "#C8FFD2", "#F0FFF0"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
Contract (kiosk `parseProclamationsJson` + `parseProclamationPalettesJson`):
|
|
||||||
|
|
||||||
- `id`: string UUID, **required non-empty** (empty ids are skipped).
|
|
||||||
- `name`: string, used as the display title. Kiosk strips HTML from it.
|
|
||||||
- `content`: string, **HTML** (the kiosk strips tags and unescapes entities
|
|
||||||
before rendering). Plain text is also acceptable but HTML is the convention.
|
|
||||||
- `categoryId`: int, maps to a `/categories` id.
|
|
||||||
- `solfeggioFrequencyId`: int, **must be a canonical id (1–9)** — see the
|
|
||||||
canonical model above.
|
|
||||||
- `subscriptionLevelId`: int or `null`. The kiosk treats missing/null/≤0 as
|
|
||||||
level 1 (Basic).
|
|
||||||
- `colorPalette` (optional): array of exactly 7 `"#RRGGBB"` strings, indexed
|
|
||||||
color 0 (primary) through color 6 (tertiary). When present, the kiosk can
|
|
||||||
color a proclamation directly from this payload (first proclamation per
|
|
||||||
frequency wins for palette derivation). When absent, the kiosk falls back to
|
|
||||||
`/color-palettes`, then to its own 528 Hz seed.
|
|
||||||
|
|
||||||
With entitlement headers (`X-License-Key` + `X-Email`):
|
|
||||||
|
|
||||||
- Validate the key exists, is active, and belongs to the email's user.
|
|
||||||
- Return the **full proclamation records** the user may access (matching the
|
|
||||||
record shape above), **not** raw `UserProclamations` join-table rows.
|
|
||||||
(v1 returned join rows, which the kiosk parser cannot consume — this is the
|
|
||||||
primary v1 compliance defect this endpoint fixes.)
|
|
||||||
|
|
||||||
Errors:
|
|
||||||
|
|
||||||
| Code | Body | Meaning |
|
|
||||||
|------|------|---------|
|
|
||||||
| 200 | array of proclamation records | Success |
|
|
||||||
| 400 | `{"message": "not-authorized"}` | Entitlement headers supplied but key or email does not exist |
|
|
||||||
| 400 | `{"message": "this key doesn't appear to belong to you"}` | Key exists but owned by a different user |
|
|
||||||
| 401 | `{"error": "..."}` | Missing/invalid API key |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 4. `POST /api/v2/license`
|
|
||||||
|
|
||||||
Validates a license key / email pair and returns the full subscription level.
|
|
||||||
|
|
||||||
Request:
|
|
||||||
|
|
||||||
```
|
|
||||||
POST /api/v2/license
|
|
||||||
Content-Type: application/json
|
|
||||||
Authorization: Bearer sk_<hex>
|
|
||||||
```
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"license_key": "XXXX-XXXX-XXXX-XXXX",
|
|
||||||
"email_address": "user@example.com"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Response (200):
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"key": "XXXX-XXXX-XXXX-XXXX",
|
|
||||||
"email_address": "user@example.com",
|
|
||||||
"subscription_level": {
|
|
||||||
"id": 2,
|
|
||||||
"name": "Advanced",
|
|
||||||
"description": "General + Specific"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`subscription_level` is **always an object** `{ id, name, description }`. If
|
|
||||||
the license's level name does not match any `SubscriptionLevel` row, fall back
|
|
||||||
to a structurally consistent object:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"key": "XXXX-XXXX-XXXX-XXXX",
|
|
||||||
"email_address": "user@example.com",
|
|
||||||
"subscription_level": { "id": null, "name": "SomeCustomLevel", "description": null }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Contract (kiosk `parseLicenseJson`):
|
|
||||||
|
|
||||||
- `key`: string, **required non-empty**.
|
|
||||||
- `email_address`: string.
|
|
||||||
- `subscription_level.name`: string, **required** for a successful validation
|
|
||||||
(the kiosk persists id/name/description into its local `system_kv` table and
|
|
||||||
uses the level for proclamation gating and the Home footer).
|
|
||||||
|
|
||||||
Errors:
|
|
||||||
|
|
||||||
| Code | Body | Meaning |
|
|
||||||
|------|------|---------|
|
|
||||||
| 200 | success object | License valid and owned by the email |
|
|
||||||
| 400 | `{"message": "license_key is required"}` | Missing `license_key` |
|
|
||||||
| 400 | `{"message": "email_address is required"}` | Missing `email_address` |
|
|
||||||
| 400 | `{"message": "license key not found"}` | No key with that value |
|
|
||||||
| 400 | `{"message": "license key is not active"}` | Key exists but inactive |
|
|
||||||
| 400 | `{"message": "user does not exist"}` | No user with that email |
|
|
||||||
| 400 | `{"message": "key does not belong to specified user"}` | Key owned by a different user |
|
|
||||||
| 401 | `{"error": "..."}` | Missing/invalid API key |
|
|
||||||
|
|
||||||
> The kiosk client also tolerates the v1 string form and flat
|
|
||||||
> `subscription_level_id` / `subscription_level_description` fields. v2 should
|
|
||||||
> always send the object form; the fallbacks exist only for transitional
|
|
||||||
> compatibility.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 5. `GET /api/v2/subscription-levels`
|
|
||||||
|
|
||||||
Lists the subscription levels.
|
|
||||||
|
|
||||||
Request:
|
|
||||||
|
|
||||||
```
|
|
||||||
GET /api/v2/subscription-levels
|
|
||||||
Authorization: Bearer sk_<hex>
|
|
||||||
```
|
|
||||||
|
|
||||||
Response (200):
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{ "id": 1, "name": "Basic", "description": "General only" },
|
|
||||||
{ "id": 2, "name": "Advanced", "description": "General + Specific" },
|
|
||||||
{ "id": 3, "name": "Elite", "description": "General + Specific + Precise" }
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
Errors:
|
|
||||||
|
|
||||||
| Code | Body | Meaning |
|
|
||||||
|------|------|---------|
|
|
||||||
| 200 | array of `{id, name, description}` | Success |
|
|
||||||
| 401 | `{"error": "..."}` | Missing/invalid API key |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 6. `GET /api/v2/color-palettes`
|
|
||||||
|
|
||||||
Exposes the 7-color palette for every canonical frequency so the portal admin
|
|
||||||
can update colors without a kiosk code release.
|
|
||||||
|
|
||||||
Request:
|
|
||||||
|
|
||||||
```
|
|
||||||
GET /api/v2/color-palettes
|
|
||||||
Authorization: Bearer sk_<hex>
|
|
||||||
```
|
|
||||||
|
|
||||||
Response (200) — one entry per canonical frequency (1–9):
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"solfeggioFrequencyId": 1,
|
|
||||||
"colors": ["#8B6914", "#B8860B", "#DAA520", "#E8B64C", "#FFD700", "#FFE9A8", "#FFF4CC"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"solfeggioFrequencyId": 5,
|
|
||||||
"colors": ["#32CD32", "#3CB371", "#90EE90", "#98FB98", "#ADFFA9", "#C8FFD2", "#F0FFF0"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
Contract (kiosk `parseColorPalettesJson`):
|
|
||||||
|
|
||||||
- `solfeggioFrequencyId`: int, **non-zero**, canonical id.
|
|
||||||
- `colors`: array of `"#RRGGBB"` (or `"RRGGBB"`) strings, exactly 7.
|
|
||||||
|
|
||||||
Kiosk fallback: `db::getPaletteForFrequencyId()` falls back to the 528 Hz
|
|
||||||
palette for any frequency id with no rows; the startup seed backfills missing
|
|
||||||
ids with `INSERT OR IGNORE`, so portal values win after a successful sync.
|
|
||||||
|
|
||||||
Errors:
|
|
||||||
|
|
||||||
| Code | Body | Meaning |
|
|
||||||
|------|------|---------|
|
|
||||||
| 200 | array of `{solfeggioFrequencyId, colors}` | Success |
|
|
||||||
| 401 | `{"error": "..."}` | Missing/invalid API key |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 7. `POST /api/v2/automations/create/account`
|
|
||||||
|
|
||||||
Creates (or reuses) a user account and issues a brand-new active license key.
|
|
||||||
The key is **not returned** — it is emailed to the user. This is the only v2
|
|
||||||
route that does not require a bearer API key; instead it requires an
|
|
||||||
**HMAC-SHA256 signature** (see Security). It is used for first-run provisioning
|
|
||||||
and by the GoHighLevel automation.
|
|
||||||
|
|
||||||
Request:
|
|
||||||
|
|
||||||
```
|
|
||||||
POST /api/v2/automations/create/account
|
|
||||||
Content-Type: application/json
|
|
||||||
X-Timestamp: <unix seconds>
|
|
||||||
X-Nonce: <random uuid>
|
|
||||||
X-Signature: <hex HMAC-SHA256 of canonical request with the onboarding secret>
|
|
||||||
```
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"email": "user@example.com",
|
|
||||||
"name": "Optional Display Name",
|
|
||||||
"subscription_level": "Basic"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- `email` (required, string).
|
|
||||||
- `name` (optional, string; defaults to the local part of `email`).
|
|
||||||
- `subscription_level` (required, string) — one of `Basic`, `Advanced`,
|
|
||||||
`Elite` (must match a `SubscriptionLevel.name`).
|
|
||||||
|
|
||||||
Response (200):
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"success": true,
|
|
||||||
"message": "Account and license key created",
|
|
||||||
"user_id": "uuid"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Errors:
|
|
||||||
|
|
||||||
| Code | Body | Meaning |
|
|
||||||
|------|------|---------|
|
|
||||||
| 200 | success object | Account (+ license key) created |
|
|
||||||
| 400 | `{"message": "email is required"}` | Missing/empty `email` |
|
|
||||||
| 400 | `{"message": "subscription_level is required"}` | Missing/empty `subscription_level` |
|
|
||||||
| 400 | `{"message": "subscription_level \"<name>\" not found"}` | Unknown level name |
|
|
||||||
| 401 | `{"error": "invalid signature"}` | Bad/expired/replayed HMAC signature |
|
|
||||||
| 429 | `{"message": "rate limit exceeded"}` | Too many requests from this IP/email |
|
|
||||||
| 500 | `{"message": "Failed to create account"}` | User creation failed |
|
|
||||||
| 500 | `{"message": "Failed to find or create user"}` | User could not be located |
|
|
||||||
|
|
||||||
Behavior (unchanged from v1):
|
|
||||||
|
|
||||||
- If the email already belongs to a user, no new user is created — only a new
|
|
||||||
license key for that user.
|
|
||||||
- A new user receives a temporary-password/verify email and a license-key
|
|
||||||
email. Sending up to two emails, so this endpoint must be rate-limited
|
|
||||||
aggressively.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Compliance contract (kiosk → v2)
|
|
||||||
|
|
||||||
| Kiosk consumer | v2 endpoint | Required fields / shape |
|
|
||||||
|----------------|-------------|-------------------------|
|
|
||||||
| `sync::syncProclamationsFromApi` (Home + ProclamationOptions sync) | `GET /proclamations`, `GET /categories` | Full proclamation records; `id`, `name`, `content`, `categoryId`, `solfeggioFrequencyId` (canonical), `subscriptionLevelId` |
|
|
||||||
| `license::validateStoredLicense` (License Key provisioning page) | `POST /license` | `subscription_level` as `{id, name, description}` object |
|
|
||||||
| `api::fetchColorPalettes` / `sync::syncColorPalettesFromApi` (readiness, to be wired) | `GET /color-palettes` | `[{solfeggioFrequencyId, colors[7]}]` |
|
|
||||||
| `sync::syncProclamationPalettesFromApi` (readiness, to be wired) | `GET /proclamations` | Embedded `colorPalette` array per proclamation |
|
|
||||||
| `api::fetchFrequencies` (readiness) | `GET /frequencies` | Canonical id 1–9 + `value` in Hz |
|
|
||||||
| Future level-picker UI | `GET /subscription-levels` | `{id, name, description}` |
|
|
||||||
| Provisioning / GHL automation | `POST /automations/create/account` | `email`, `subscription_level`, optional `name` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Implementation instructions (portal, `../portal`)
|
|
||||||
|
|
||||||
### Route layout
|
|
||||||
|
|
||||||
Create the v2 route tree alongside the frozen v1 tree:
|
|
||||||
|
|
||||||
```
|
|
||||||
src/routes/api/v2/
|
|
||||||
frequencies/+server.ts
|
|
||||||
categories/+server.ts
|
|
||||||
proclamations/+server.ts
|
|
||||||
license/+server.ts
|
|
||||||
subscription-levels/+server.ts
|
|
||||||
color-palettes/+server.ts
|
|
||||||
automations/create/account/+server.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
### Auth gate (`src/hooks.server.ts`)
|
|
||||||
|
|
||||||
Extend the existing handler so v2 auth is **always enforced** (do not honor
|
|
||||||
`API_KEYS_ACTIVE` for v2):
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const isV2 = event.url.pathname.startsWith('/api/v2/');
|
|
||||||
const isSignedOnboarding =
|
|
||||||
event.url.pathname.startsWith('/api/v2/automations/');
|
|
||||||
|
|
||||||
if (isV2 && !isSignedOnboarding) {
|
|
||||||
// validateApiKey() ALWAYS runs for v2 — no env-var bypass.
|
|
||||||
const authHeader = event.request.headers.get('authorization');
|
|
||||||
if (!authHeader?.startsWith('Bearer ')) {
|
|
||||||
return new Response(JSON.stringify({ error: 'API key required' }), {
|
|
||||||
status: 401,
|
|
||||||
headers: { 'content-type': 'application/json' }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const record = await validateApiKey(authHeader.slice(7));
|
|
||||||
if (!record) {
|
|
||||||
return new Response(JSON.stringify({ error: 'Invalid or expired API key' }), {
|
|
||||||
status: 401,
|
|
||||||
headers: { 'content-type': 'application/json' }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return resolve(event);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Keep the existing v1 branch untouched.
|
|
||||||
|
|
||||||
### Shared serializer (`src/lib/server/v2-serializers.ts`)
|
|
||||||
|
|
||||||
Add one module that maps DB rows to the v2 wire format. The critical piece is
|
|
||||||
frequency-id reconciliation:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Canonical id <- Hz (kiosk frequencyIdForHz map).
|
|
||||||
const CANONICAL_ID_FOR_HZ: Record<number, number> = {
|
|
||||||
174: 1, 285: 1, 396: 2, 417: 3, 432: 4,
|
|
||||||
528: 5, 639: 6, 741: 7, 852: 8, 963: 9
|
|
||||||
};
|
|
||||||
|
|
||||||
function canonicalIdForFrequency(freq: { id: number; value: number; name: string }): number | null {
|
|
||||||
const id = CANONICAL_ID_FOR_HZ[freq.value];
|
|
||||||
return id ?? null; // e.g. 936 Hz has no canonical id -> excluded from v2
|
|
||||||
}
|
|
||||||
|
|
||||||
export function serializeProclamation(p: Proclamation & { solfeggioFrequency: { value: number } }) {
|
|
||||||
const solfeggioFrequencyId = CANONICAL_ID_FOR_HZ[p.solfeggioFrequency.value];
|
|
||||||
if (!solfeggioFrequencyId) return null; // drop proclamations with no canonical frequency
|
|
||||||
return {
|
|
||||||
id: p.id,
|
|
||||||
name: p.name,
|
|
||||||
content: p.content,
|
|
||||||
categoryId: p.categoryId,
|
|
||||||
solfeggioFrequencyId,
|
|
||||||
subscriptionLevelId: p.subscriptionLevelId,
|
|
||||||
// optional; read from the palette source below
|
|
||||||
colorPalette: paletteFor(p.solfeggioFrequency.value) // 7x "#RRGGBB" or omit
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Key points:
|
|
||||||
|
|
||||||
- Derive the emitted `solfeggioFrequencyId` from the joined frequency's
|
|
||||||
**Hz value**, never from the DB id. This keeps v1 rows (and their ids)
|
|
||||||
untouched while v2 emits canonical ids.
|
|
||||||
- Proclamations whose frequency has no canonical id (e.g. 936 Hz) are excluded
|
|
||||||
from v2 responses (or re-assigned a canonical frequency in data).
|
|
||||||
- Add the missing frequencies to the DB so content can be authored for them:
|
|
||||||
`174, 285, 432, 963` (by `value`). Do not delete or renumber the existing
|
|
||||||
rows that v1 depends on.
|
|
||||||
|
|
||||||
### Color palettes
|
|
||||||
|
|
||||||
Add a portal-editable palette source keyed by canonical frequency, for example
|
|
||||||
a `colorPalette` JSON column on `SolfeggioFrequency` or a
|
|
||||||
`ColorPalette`/`ColorPaletteColor` table:
|
|
||||||
|
|
||||||
```prisma
|
|
||||||
model ColorPalette {
|
|
||||||
solfeggioFrequencyId Int @id
|
|
||||||
colors Json // ["#RRGGBB", ... x7]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- Seed all 9 canonical palettes to match the kiosk's current seed
|
|
||||||
(`src/db/database_manager.cpp` → `seedColorPalettes()`).
|
|
||||||
- `GET /color-palettes` returns one entry per canonical id.
|
|
||||||
- `GET /proclamations` embeds the palette for the proclamation's canonical
|
|
||||||
frequency.
|
|
||||||
|
|
||||||
### License endpoint
|
|
||||||
|
|
||||||
In `license/+server.ts`, resolve the level object like v1 change #1 suggested:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const level = await prisma.subscriptionLevel.findFirst({
|
|
||||||
where: { name: storedKey.level }
|
|
||||||
});
|
|
||||||
|
|
||||||
subscription_level = level
|
|
||||||
? { id: level.id, name: level.name, description: level.description }
|
|
||||||
: { id: null, name: storedKey.level, description: null };
|
|
||||||
```
|
|
||||||
|
|
||||||
### Proclamations entitlement filter
|
|
||||||
|
|
||||||
When `X-License-Key` / `X-Email` headers are present, validate them exactly as
|
|
||||||
v1 does, but return **full proclamation records** (the user's entitled set via
|
|
||||||
`UserProclamations` + `Proclamation` include), never join-table rows.
|
|
||||||
|
|
||||||
### Tests
|
|
||||||
|
|
||||||
- Unit-test the serializers with fixtures matching the kiosk's offline tests
|
|
||||||
in `tests/test_api_client.cpp` (string vs object `subscription_level`,
|
|
||||||
`subscriptionLevelId: null`, `parentId: null`, 7-color palettes, canonical
|
|
||||||
frequency mapping incl. 936 → dropped).
|
|
||||||
- Live smoke tests against `/api/v2` mirroring the existing v1 live tests.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Security: restricting v2 to the kiosk application
|
|
||||||
|
|
||||||
These endpoints should be reachable only from enrolled kiosk devices. v1's
|
|
||||||
auth could be disabled with `API_KEYS_ACTIVE=false`; v2 must not offer that
|
|
||||||
bypass. Suggestions below are ordered by impact.
|
|
||||||
|
|
||||||
### 1. Mandatory bearer-key auth on every content route
|
|
||||||
|
|
||||||
- Every v2 route except the signed onboarding route requires
|
|
||||||
`Authorization: Bearer sk_<hex>`.
|
|
||||||
- Enforce it in `hooks.server.ts` unconditionally (no env toggle).
|
|
||||||
- Keys are generated with `generateApiKey()`, stored **SHA-256 hashed**, and
|
|
||||||
only the `sk_xxxxxxxxxx` prefix is ever displayed. Never log or return the
|
|
||||||
plaintext key.
|
|
||||||
- Use constant-time comparison / lookup by hash (already the case via
|
|
||||||
`validateApiKey`).
|
|
||||||
|
|
||||||
### 2. Per-device credentials (enroll each kiosk)
|
|
||||||
|
|
||||||
One shared API key is a single point of failure. Instead:
|
|
||||||
|
|
||||||
- Add a `Device` record (id, serial, model, owner, enrolledAt, apiKeyId) and
|
|
||||||
issue **one unique key per kiosk** at first boot (or at manufacturing).
|
|
||||||
- Bind the key to the device: include the device id in a custom header
|
|
||||||
(`X-Device-Id`) and verify the key belongs to that device.
|
|
||||||
- A compromised key is then revoked per-device without a global rotation, and
|
|
||||||
the portal can tell which device made each request.
|
|
||||||
|
|
||||||
### 3. Least privilege / scoped keys
|
|
||||||
|
|
||||||
- Kiosk keys are read-only for content (`/frequencies`, `/categories`,
|
|
||||||
`/proclamations`, `/color-palettes`, `/subscription-levels`) and may POST to
|
|
||||||
`/license`.
|
|
||||||
- Reject any other verb/route combination with 403. Admin and data-mutation
|
|
||||||
keys are issued separately to portal staff only.
|
|
||||||
|
|
||||||
### 4. Rate limiting per key and per IP
|
|
||||||
|
|
||||||
- Content GETs: ~60/min per key/IP.
|
|
||||||
- `/license`: ~10/min per key/IP.
|
|
||||||
- `/automations/create/account`: ~5/min per IP and ~3/day per email.
|
|
||||||
- Return `429` with `Retry-After`; the kiosk client should back off and retry.
|
|
||||||
- Slippery handling for repeated failures (rate-limit on failures too).
|
|
||||||
|
|
||||||
### 5. Replay protection / request signing
|
|
||||||
|
|
||||||
- The bearer key alone is replayable if captured. Add to the high-risk routes
|
|
||||||
(and the onboarding route) an HMAC-SHA256 signature over the canonical
|
|
||||||
request (method, path, `X-Timestamp`, `X-Nonce`, raw body) using a secret
|
|
||||||
known only to the device and the server.
|
|
||||||
- Reject when `|now − X-Timestamp| > 300s` or when a nonce has been seen
|
|
||||||
before. This defeats capture-and-replay attacks over the network.
|
|
||||||
- Implement the same scheme for the onboarding route using a shared secret
|
|
||||||
configured on the portal and the GoHighLevel automation (replacing the
|
|
||||||
current entirely unauthenticated `/automations/*` exemption).
|
|
||||||
|
|
||||||
### 6. Never put credentials in URLs
|
|
||||||
|
|
||||||
- v1 accepted license keys and emails in the query string, which land in
|
|
||||||
access logs, proxies, and referrers. v2 reads credentials from the
|
|
||||||
`Authorization` header and JSON body only.
|
|
||||||
- Redact `Authorization` values, license keys, and emails in application and
|
|
||||||
proxy logs.
|
|
||||||
|
|
||||||
### 7. Client fingerprinting and bot rejection
|
|
||||||
|
|
||||||
- The kiosk sends `User-Agent: RadiantWave-Kiosk/0.1`. Require it (optionally
|
|
||||||
with a signed `X-Client-Id`) and return `403` for generic browsers, `curl`,
|
|
||||||
and headless scanners.
|
|
||||||
- Reject request bodies that are not `application/json` where JSON is
|
|
||||||
expected; enforce request-size limits (reject with `413`).
|
|
||||||
- At the proxy layer: require TLS 1.2+, prefer 1.3, set HSTS; consider
|
|
||||||
restricting v2 to expected deployment geographies and blocking TOR/exit
|
|
||||||
nodes and known hosting/bot ASNs.
|
|
||||||
|
|
||||||
### 8. Input validation and uniform errors
|
|
||||||
|
|
||||||
- Validate each body against a strict schema: email ≤ 254 chars and a valid
|
|
||||||
shape, `license_key` must match `^[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$`,
|
|
||||||
`subscription_level` must equal a known level name, `name` ≤ 120 chars.
|
|
||||||
- Reject embedded HTML/script in string fields.
|
|
||||||
- Return uniform `{"message": ...}` bodies; never leak stack traces or SQL
|
|
||||||
errors. Avoid verbose error differences that enable credential enumeration
|
|
||||||
(see below).
|
|
||||||
|
|
||||||
### 9. License / onboarding abuse resistance
|
|
||||||
|
|
||||||
- `/license` success/failure is enumerable by design (a fixed key format). At
|
|
||||||
minimum rate-limit aggressively. Consider returning a generic
|
|
||||||
`{"message": "invalid credentials"}` (possibly plus a structured `code`
|
|
||||||
field for the kiosk) so attackers cannot distinguish "user exists" from
|
|
||||||
"key exists". Keep the specific messages only behind authenticated admin
|
|
||||||
tooling if they are needed for support.
|
|
||||||
- `/automations/create/account` mints real license keys and sends email, so
|
|
||||||
protect it hardest: HMAC signature (above), per-IP and per-email rate
|
|
||||||
limits, an email-domain/typo check, and a honeypot field. Never expose it
|
|
||||||
without signature as an unauthenticated "create account" funnel.
|
|
||||||
|
|
||||||
### 10. Monitoring, logging, alerting
|
|
||||||
|
|
||||||
- Log every v2 request as structured data: timestamp, endpoint, method, status,
|
|
||||||
key prefix, `X-Device-Id`, IP, latency. Never log full keys, emails, or
|
|
||||||
bodies.
|
|
||||||
- Alert on: error-rate spikes, repeated 401s, unexpected `/license` volume,
|
|
||||||
signup bursts, single-device request floods, and revoked-key usage.
|
|
||||||
- Automatically suspend a device that shows abusive patterns; require
|
|
||||||
re-enrollment.
|
|
||||||
|
|
||||||
### 11. Key lifecycle
|
|
||||||
|
|
||||||
- Rotate keys on a schedule; support the existing `rotateApiKey` 48-hour grace
|
|
||||||
and emergency revocation per device.
|
|
||||||
- Auto-expire keys that have not authenticated in N days.
|
|
||||||
- Consider an authenticated `/api/v2/auth/refresh` (or device re-enrollment)
|
|
||||||
so a kiosk can obtain a fresh key, turning a leaked key into a self-healing
|
|
||||||
problem.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Rollout checklist
|
|
||||||
|
|
||||||
1. Implement the v2 routes and serializers; add unit + live smoke tests.
|
|
||||||
2. Ship v2 alongside v1 (both live; v1 frozen).
|
|
||||||
3. Flip the kiosk base URL: `src/network/api_client.h` →
|
|
||||||
`https://portal.radiantwavetech.com/api/v2` (already done by this change).
|
|
||||||
4. Run the kiosk live tests with `RW_LIVE_API_KEY` set:
|
|
||||||
- `./build/test_api_client`
|
|
||||||
- `./build/test_license_service`
|
|
||||||
5. Update portal-admin color palettes and verify a fresh kiosk sync picks them
|
|
||||||
up (now wired via `/color-palettes`).
|
|
||||||
6. Keep `/api/v1` online until all deployed kiosks are on the v2 build;
|
|
||||||
monitor v1 traffic and sunset it only after the fleet is migrated.
|
|
||||||
@@ -5,6 +5,15 @@ import { defineConfig } from 'prisma/config';
|
|||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
schema: 'prisma/schema.prisma',
|
schema: 'prisma/schema.prisma',
|
||||||
|
// Legacy subscription tables are retained in the database but are no longer
|
||||||
|
// managed by Prisma Migrate. They must never be dropped (production may not
|
||||||
|
// own them and the data is kept for recovery).
|
||||||
|
experimental: {
|
||||||
|
externalTables: true
|
||||||
|
},
|
||||||
|
tables: {
|
||||||
|
external: ['public.SubscriptionLevel', 'public.ProclamationCategorySubscriptionLevel']
|
||||||
|
},
|
||||||
migrations: {
|
migrations: {
|
||||||
path: 'prisma/migrations',
|
path: 'prisma/migrations',
|
||||||
seed: 'tsx prisma/seed.ts'
|
seed: 'tsx prisma/seed.ts'
|
||||||
|
|||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
-- Flatten ProclamationCategory hierarchy into a flat list and introduce
|
||||||
|
-- ProclamationGroup (formerly child sub-categories).
|
||||||
|
--
|
||||||
|
-- This migration is intentionally NON-DESTRUCTIVE:
|
||||||
|
-- * ProclamationGroup / Proclamation.groupId are added additively.
|
||||||
|
-- * Former child categories are ARCHIVED to ProclamationCategoryLegacy
|
||||||
|
-- before the hierarchy (parentId) is removed, so no category data is lost.
|
||||||
|
-- * Subscription-level resources (SubscriptionLevel,
|
||||||
|
-- ProclamationCategorySubscriptionLevel, Proclamation.subscriptionLevelId,
|
||||||
|
-- LicenseKey.level) are LEFT IN PLACE as unmanaged legacy data. They are no
|
||||||
|
-- longer modeled by the Prisma schema and are ignored by the application,
|
||||||
|
-- but their data is preserved and no DROP is attempted on tables the deploy
|
||||||
|
-- role may not own.
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "ProclamationGroup" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"sortOrder" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
CONSTRAINT "ProclamationGroup_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "ProclamationGroup_name_key" ON "ProclamationGroup"("name");
|
||||||
|
|
||||||
|
-- Seed groups from every distinct former sub-category name
|
||||||
|
INSERT INTO "ProclamationGroup" ("name", "sortOrder")
|
||||||
|
SELECT "name",
|
||||||
|
ROW_NUMBER() OVER (ORDER BY "name") - 1
|
||||||
|
FROM (
|
||||||
|
SELECT DISTINCT "name" FROM "ProclamationCategory" WHERE "parentId" IS NOT NULL
|
||||||
|
) AS d;
|
||||||
|
|
||||||
|
-- AddColumn (nullable during backfill)
|
||||||
|
ALTER TABLE "Proclamation" ADD COLUMN "groupId" INTEGER;
|
||||||
|
|
||||||
|
-- Backfill: former child categories become the proclamation's group; the
|
||||||
|
-- parent becomes its flat category.
|
||||||
|
UPDATE "Proclamation" AS p
|
||||||
|
SET "groupId" = g."id",
|
||||||
|
"categoryId" = c."parentId"
|
||||||
|
FROM "ProclamationCategory" AS c
|
||||||
|
JOIN "ProclamationGroup" AS g ON g."name" = c."name"
|
||||||
|
WHERE p."categoryId" = c."id" AND c."parentId" IS NOT NULL;
|
||||||
|
|
||||||
|
-- Backfill: proclamations that had no group (e.g. 'Custom (Client Specific)')
|
||||||
|
-- default to the General group, if it exists.
|
||||||
|
UPDATE "Proclamation"
|
||||||
|
SET "groupId" = (SELECT "id" FROM "ProclamationGroup" WHERE "name" = 'General')
|
||||||
|
WHERE "groupId" IS NULL;
|
||||||
|
|
||||||
|
-- Safety net: any proclamation still unmapped takes the first group.
|
||||||
|
UPDATE "Proclamation"
|
||||||
|
SET "groupId" = (SELECT "id" FROM "ProclamationGroup" ORDER BY "sortOrder" LIMIT 1)
|
||||||
|
WHERE "groupId" IS NULL;
|
||||||
|
|
||||||
|
ALTER TABLE "Proclamation" ALTER COLUMN "groupId" SET NOT NULL;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Proclamation" ADD CONSTRAINT "Proclamation_groupId_fkey" FOREIGN KEY ("groupId") REFERENCES "ProclamationGroup"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- Archive former child categories before removing them, so their data is
|
||||||
|
-- preserved and recoverable. The legacy table also records each child's
|
||||||
|
-- parent for audit/recovery.
|
||||||
|
CREATE TABLE "ProclamationCategoryLegacy" (
|
||||||
|
"id" INTEGER NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"parentId" INTEGER,
|
||||||
|
"sortOrder" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
CONSTRAINT "ProclamationCategoryLegacy_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO "ProclamationCategoryLegacy" ("id", "name", "parentId", "sortOrder")
|
||||||
|
SELECT "id", "name", "parentId", "sortOrder"
|
||||||
|
FROM "ProclamationCategory"
|
||||||
|
WHERE "parentId" IS NOT NULL;
|
||||||
|
|
||||||
|
-- Archive category->subscription mappings for the child categories being
|
||||||
|
-- removed, so the cascade delete below does not destroy this (deprecated)
|
||||||
|
-- mapping data. Requires read access to the join table, but never ownership.
|
||||||
|
CREATE TABLE "ProclamationCategorySubscriptionLevelLegacy" (
|
||||||
|
"categoryId" INTEGER NOT NULL,
|
||||||
|
"subscriptionLevelId" INTEGER NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "ProclamationCategorySubscriptionLevelLegacy_pkey" PRIMARY KEY ("categoryId", "subscriptionLevelId")
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO "ProclamationCategorySubscriptionLevelLegacy" ("categoryId", "subscriptionLevelId")
|
||||||
|
SELECT j."categoryId", j."subscriptionLevelId"
|
||||||
|
FROM "ProclamationCategorySubscriptionLevel" AS j
|
||||||
|
JOIN "ProclamationCategory" AS c ON c."id" = j."categoryId" AND c."parentId" IS NOT NULL;
|
||||||
|
|
||||||
|
-- Remove former child categories (their data is archived above; the
|
||||||
|
-- subscription-level join rows for them are archived above and the
|
||||||
|
-- SubscriptionLevel table itself is left untouched).
|
||||||
|
DELETE FROM "ProclamationCategory" WHERE "parentId" IS NOT NULL;
|
||||||
|
|
||||||
|
-- Drop self-relation
|
||||||
|
ALTER TABLE "ProclamationCategory" DROP CONSTRAINT "ProclamationCategory_parentId_fkey";
|
||||||
|
|
||||||
|
-- DropIndex
|
||||||
|
DROP INDEX "ProclamationCategory_parentId_name_key";
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "ProclamationCategory" DROP COLUMN "parentId";
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "ProclamationCategory_name_key" ON "ProclamationCategory"("name");
|
||||||
|
|
||||||
|
-- Subscription-level resources are intentionally left in place (unmanaged).
|
||||||
|
-- No DROP TABLE / DROP COLUMN / DROP CONSTRAINT is performed here, so this
|
||||||
|
-- migration never requires ownership of pre-existing subscription tables and
|
||||||
|
-- never destroys subscription data.
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "LicenseKey" DROP CONSTRAINT "LicenseKey_userId_fkey";
|
||||||
|
|
||||||
|
-- DropIndex
|
||||||
|
DROP INDEX "Profile_userId_key";
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "LicenseKey" ADD COLUMN "email" TEXT,
|
||||||
|
ALTER COLUMN "userId" DROP NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Profile" ADD COLUMN "email" TEXT,
|
||||||
|
ALTER COLUMN "userId" DROP NOT NULL;
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "LicenseKey_userId_idx" ON "LicenseKey"("userId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "LicenseKey_email_idx" ON "LicenseKey"("email");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "Profile_userId_idx" ON "Profile"("userId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "Profile_email_idx" ON "Profile"("email");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "LicenseKey" ADD CONSTRAINT "LicenseKey_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "user" ALTER COLUMN "roleId" SET DEFAULT 4;
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
-- Retain the legacy subscription artifacts as externally managed data.
|
||||||
|
--
|
||||||
|
-- SubscriptionLevel and ProclamationCategorySubscriptionLevel are no longer
|
||||||
|
-- modeled by the application. They are declared as external tables in
|
||||||
|
-- prisma.config.ts so Prisma Migrate ignores them entirely and never drops
|
||||||
|
-- them (production may not own these tables, and their data is kept for
|
||||||
|
-- recovery).
|
||||||
|
--
|
||||||
|
-- This migration only touches Prisma-managed tables to decouple them from the
|
||||||
|
-- legacy FK. It is intentionally idempotent so it applies cleanly to
|
||||||
|
-- production (columns present, NOT NULL, FK present), a fresh database built
|
||||||
|
-- from migration history, and development databases that already removed the
|
||||||
|
-- legacy objects.
|
||||||
|
|
||||||
|
-- Ensure the legacy columns exist. On databases built from migration history
|
||||||
|
-- these already exist; this re-adds them on environments that dropped them.
|
||||||
|
ALTER TABLE "Proclamation" ADD COLUMN IF NOT EXISTS "subscriptionLevelId" INTEGER;
|
||||||
|
ALTER TABLE "LicenseKey" ADD COLUMN IF NOT EXISTS "level" TEXT;
|
||||||
|
|
||||||
|
-- Decouple from SubscriptionLevel: drop the FK and relax NOT NULL so the
|
||||||
|
-- application can keep inserting rows without a legacy subscription value.
|
||||||
|
ALTER TABLE "Proclamation" DROP CONSTRAINT IF EXISTS "Proclamation_subscriptionLevelId_fkey";
|
||||||
|
ALTER TABLE "Proclamation" ALTER COLUMN "subscriptionLevelId" DROP NOT NULL;
|
||||||
|
ALTER TABLE "LicenseKey" ALTER COLUMN "level" DROP NOT NULL;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "LicenseKey" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "LicenseKey" ADD COLUMN "lastValidatedAt" TIMESTAMP(3);
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "AuditLog" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"userId" TEXT,
|
||||||
|
"email" TEXT,
|
||||||
|
"roleId" INTEGER,
|
||||||
|
"roleName" TEXT,
|
||||||
|
"page" TEXT NOT NULL,
|
||||||
|
"action" TEXT NOT NULL,
|
||||||
|
"entity" TEXT,
|
||||||
|
"entityId" TEXT,
|
||||||
|
"before" JSONB,
|
||||||
|
"after" JSONB,
|
||||||
|
"ip" TEXT,
|
||||||
|
|
||||||
|
CONSTRAINT "AuditLog_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "AuditLog_createdAt_idx" ON "AuditLog"("createdAt");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "AuditLog_roleId_idx" ON "AuditLog"("roleId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "AuditLog_action_idx" ON "AuditLog"("action");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "AuditLog_page_idx" ON "AuditLog"("page");
|
||||||
+98
-49
@@ -21,11 +21,11 @@ model User {
|
|||||||
image String?
|
image String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
roleId Int?
|
roleId Int? @default(4) // New users default to "User"
|
||||||
role UserRole? @relation(fields: [roleId], references: [id])
|
role UserRole? @relation(fields: [roleId], references: [id])
|
||||||
sessions Session[]
|
sessions Session[]
|
||||||
accounts Account[]
|
accounts Account[]
|
||||||
profile Profile?
|
profiles Profile[]
|
||||||
licenseKeys LicenseKey[]
|
licenseKeys LicenseKey[]
|
||||||
userProclamations UserProclamations[]
|
userProclamations UserProclamations[]
|
||||||
|
|
||||||
@@ -37,28 +37,32 @@ model User {
|
|||||||
// a bunch of time trying to figure out how to customize the default Better-Auth model.
|
// a bunch of time trying to figure out how to customize the default Better-Auth model.
|
||||||
model Profile {
|
model Profile {
|
||||||
id String @id @default(uuid())
|
id String @id @default(uuid())
|
||||||
userId String @unique
|
userId String?
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
email String?
|
||||||
firstName String
|
firstName String
|
||||||
lastName String
|
lastName String
|
||||||
phoneNumber String
|
phoneNumber String
|
||||||
ghlContactId String? @map("ghl_contact_id")
|
ghlContactId String? @map("ghl_contact_id")
|
||||||
addresses Address[]
|
addresses Address[]
|
||||||
|
|
||||||
|
@@index([userId])
|
||||||
|
@@index([email])
|
||||||
}
|
}
|
||||||
|
|
||||||
model Address {
|
model Address {
|
||||||
id String @id @default(uuid())
|
id String @id @default(uuid())
|
||||||
profileId String
|
profileId String
|
||||||
profile Profile @relation(fields: [profileId], references: [id], onDelete: Cascade)
|
profile Profile @relation(fields: [profileId], references: [id], onDelete: Cascade)
|
||||||
street String
|
street String
|
||||||
city String
|
city String
|
||||||
state String
|
state String
|
||||||
postalCode String
|
postalCode String
|
||||||
country String
|
country String
|
||||||
type String @default("Mailing")
|
type String @default("Mailing")
|
||||||
isDefaultMailing Boolean @default(false)
|
isDefaultMailing Boolean @default(false)
|
||||||
isDefaultBilling Boolean @default(false)
|
isDefaultBilling Boolean @default(false)
|
||||||
isDefaultShipping Boolean @default(false)
|
isDefaultShipping Boolean @default(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
model Session {
|
model Session {
|
||||||
@@ -123,9 +127,19 @@ model Proclamation {
|
|||||||
solfeggioFrequency SolfeggioFrequency @relation(fields: [solfeggioFrequencyId], references: [id])
|
solfeggioFrequency SolfeggioFrequency @relation(fields: [solfeggioFrequencyId], references: [id])
|
||||||
categoryId Int
|
categoryId Int
|
||||||
category ProclamationCategory @relation(fields: [categoryId], references: [id])
|
category ProclamationCategory @relation(fields: [categoryId], references: [id])
|
||||||
|
groupId Int
|
||||||
|
group ProclamationGroup @relation(fields: [groupId], references: [id])
|
||||||
userProclamations UserProclamations[]
|
userProclamations UserProclamations[]
|
||||||
subscriptionLevelId Int // Keeping old FK for migration stability
|
// Legacy subscription-level FK. Retained in the database but unmanaged; the
|
||||||
subscriptionLevel SubscriptionLevel @relation(fields: [subscriptionLevelId], references: [id])
|
// FK constraint is dropped and the value is left NULL. Hidden from the client.
|
||||||
|
subscriptionLevelId Int? @ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
model ProclamationGroup {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name String @unique
|
||||||
|
sortOrder Int @default(0)
|
||||||
|
proclamations Proclamation[]
|
||||||
}
|
}
|
||||||
|
|
||||||
model UserProclamations {
|
model UserProclamations {
|
||||||
@@ -142,40 +156,59 @@ model UserProclamations {
|
|||||||
model SolfeggioFrequency {
|
model SolfeggioFrequency {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
name String @unique
|
name String @unique
|
||||||
value Int @unique
|
value Int @unique
|
||||||
proclamations Proclamation[]
|
proclamations Proclamation[]
|
||||||
}
|
}
|
||||||
|
|
||||||
model ProclamationCategory {
|
model ProclamationCategory {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
name String
|
name String @unique
|
||||||
sortOrder Int @default(0)
|
sortOrder Int @default(0)
|
||||||
parentId Int?
|
proclamations Proclamation[]
|
||||||
parent ProclamationCategory? @relation("CategoryHierarchy", fields: [parentId], references: [id])
|
|
||||||
children ProclamationCategory[] @relation("CategoryHierarchy")
|
|
||||||
proclamations Proclamation[]
|
|
||||||
subscriptionLevels ProclamationCategorySubscriptionLevel[]
|
|
||||||
|
|
||||||
@@unique([parentId, name])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Join table for ProclamationCategory -> SubscriptionLevel defaults
|
// Archive of the former child sub-categories removed when the
|
||||||
model ProclamationCategorySubscriptionLevel {
|
// ProclamationCategory hierarchy was flattened. Kept for recovery/audit.
|
||||||
categoryId Int
|
// `@@ignore` keeps it out of the generated client while Prisma remains aware
|
||||||
category ProclamationCategory @relation(fields: [categoryId], references: [id], onDelete: Cascade)
|
// of the table so `migrate dev` does not report it as an untracked table.
|
||||||
|
model ProclamationCategoryLegacy {
|
||||||
|
id Int @id
|
||||||
|
name String
|
||||||
|
parentId Int?
|
||||||
|
sortOrder Int @default(0)
|
||||||
|
|
||||||
|
@@map("ProclamationCategoryLegacy")
|
||||||
|
@@ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
// Archive of the category->subscription-level mappings for the child
|
||||||
|
// categories removed when the category hierarchy was flattened. Kept with
|
||||||
|
// `@@ignore` so Prisma remains aware of the table but it is excluded from the
|
||||||
|
// generated client and Prisma's own migrations.
|
||||||
|
model ProclamationCategorySubscriptionLevelLegacy {
|
||||||
|
categoryId Int
|
||||||
subscriptionLevelId Int
|
subscriptionLevelId Int
|
||||||
subscriptionLevel SubscriptionLevel @relation(fields: [subscriptionLevelId], references: [id], onDelete: Cascade)
|
|
||||||
|
|
||||||
@@id([categoryId, subscriptionLevelId])
|
@@id([categoryId, subscriptionLevelId])
|
||||||
|
@@map("ProclamationCategorySubscriptionLevelLegacy")
|
||||||
|
@@ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
model LicenseKey {
|
model LicenseKey {
|
||||||
id String @id @default(uuid())
|
id String @id @default(uuid())
|
||||||
key String @unique
|
key String @unique
|
||||||
active Boolean
|
active Boolean
|
||||||
level String
|
createdAt DateTime @default(now())
|
||||||
userId String
|
lastValidatedAt DateTime?
|
||||||
user User @relation(fields: [userId], references: [id])
|
userId String?
|
||||||
|
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
|
||||||
|
email String?
|
||||||
|
// Legacy subscription level. Retained in the database but unmanaged; hidden
|
||||||
|
// from the client and left NULL.
|
||||||
|
level String? @ignore
|
||||||
|
|
||||||
|
@@index([userId])
|
||||||
|
@@index([email])
|
||||||
}
|
}
|
||||||
|
|
||||||
model ApiKey {
|
model ApiKey {
|
||||||
@@ -187,14 +220,6 @@ model ApiKey {
|
|||||||
revokedAt DateTime?
|
revokedAt DateTime?
|
||||||
}
|
}
|
||||||
|
|
||||||
model SubscriptionLevel {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
name String
|
|
||||||
description String?
|
|
||||||
proclamations Proclamation[]
|
|
||||||
categoryLevels ProclamationCategorySubscriptionLevel[]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Portal-editable 7-color palette per canonical solfeggio frequency id (1-9).
|
// Portal-editable 7-color palette per canonical solfeggio frequency id (1-9).
|
||||||
// `solfeggioFrequencyId` is the canonical id the kiosk uses (see
|
// `solfeggioFrequencyId` is the canonical id the kiosk uses (see
|
||||||
// v2-serializers.ts), not the DB autoincrement id of `SolfeggioFrequency`.
|
// v2-serializers.ts), not the DB autoincrement id of `SolfeggioFrequency`.
|
||||||
@@ -203,4 +228,28 @@ model ColorPalette {
|
|||||||
colors Json // ["#RRGGBB", ... x7]
|
colors Json // ["#RRGGBB", ... x7]
|
||||||
|
|
||||||
@@map("ColorPalette")
|
@@map("ColorPalette")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Append-only audit trail of database mutations. Written centrally by the
|
||||||
|
// Prisma extension in src/lib/db.ts; read by the admin Audit Log page.
|
||||||
|
model AuditLog {
|
||||||
|
id String @id @default(uuid())
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
userId String?
|
||||||
|
email String?
|
||||||
|
roleId Int?
|
||||||
|
roleName String?
|
||||||
|
page String
|
||||||
|
action String
|
||||||
|
entity String?
|
||||||
|
entityId String?
|
||||||
|
before Json?
|
||||||
|
after Json?
|
||||||
|
ip String?
|
||||||
|
|
||||||
|
@@index([createdAt])
|
||||||
|
@@index([roleId])
|
||||||
|
@@index([action])
|
||||||
|
@@index([page])
|
||||||
|
@@map("AuditLog")
|
||||||
|
}
|
||||||
|
|||||||
+26
-43
@@ -72,43 +72,26 @@ const colorPalettes: { solfeggioFrequencyId: number; colors: string[] }[] = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const subscriptionLevels = [
|
const proclamationGroups = [
|
||||||
{ id: 1, name: 'Basic', description: 'General only' },
|
{ name: 'General', sortOrder: 0 },
|
||||||
{ id: 2, name: 'Advanced', description: 'General + Specific' },
|
{ name: 'Specific', sortOrder: 1 },
|
||||||
{ id: 3, name: 'Elite', description: 'General + Specific + Precise' }
|
{ name: 'Precise', sortOrder: 2 },
|
||||||
|
{ name: 'Prayers', sortOrder: 3 },
|
||||||
|
{ name: 'Scripture', sortOrder: 4 }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Flat category list (formerly hierarchical parents).
|
||||||
const proclamationCategories = [
|
const proclamationCategories = [
|
||||||
{ id: 4, name: 'Spiritual', parentId: null },
|
{ id: 4, name: 'Spiritual' },
|
||||||
{ id: 5, name: 'General', parentId: 4 },
|
{ id: 2, name: 'Physical' },
|
||||||
{ id: 9, name: 'Specific', parentId: 4 },
|
{ id: 16, name: 'Mental' },
|
||||||
{ id: 40, name: 'Precise', parentId: 4 },
|
{ id: 17, name: 'Custom (Client Specific)' },
|
||||||
{ id: 2, name: 'Physical', parentId: null },
|
{ id: 19, name: 'Financial' },
|
||||||
{ id: 3, name: 'General', parentId: 2 },
|
{ id: 20, name: 'Emotional' },
|
||||||
{ id: 10, name: 'Specific', parentId: 2 },
|
{ id: 21, name: 'Social' },
|
||||||
{ id: 32, name: 'Precise', parentId: 2 },
|
{ id: 41, name: 'Biblical' },
|
||||||
{ id: 16, name: 'Mental', parentId: null },
|
{ id: 42, name: 'Personal Growth & Development' },
|
||||||
{ id: 30, name: 'General', parentId: 16 },
|
{ id: 46, name: 'Sports' }
|
||||||
{ id: 31, name: 'Specific', parentId: 16 },
|
|
||||||
{ id: 38, name: 'Precise', parentId: 16 },
|
|
||||||
{ id: 17, name: 'Custom (Client Specific)', parentId: null },
|
|
||||||
{ id: 19, name: 'Financial', parentId: null },
|
|
||||||
{ id: 28, name: 'General', parentId: 19 },
|
|
||||||
{ id: 29, name: 'Specific', parentId: 19 },
|
|
||||||
{ id: 37, name: 'Precise', parentId: 19 },
|
|
||||||
{ id: 20, name: 'Emotional', parentId: null },
|
|
||||||
{ id: 24, name: 'General', parentId: 20 },
|
|
||||||
{ id: 26, name: 'Specific', parentId: 20 },
|
|
||||||
{ id: 45, name: 'Precise', parentId: 20 },
|
|
||||||
{ id: 21, name: 'Social', parentId: null },
|
|
||||||
{ id: 22, name: 'General', parentId: 21 },
|
|
||||||
{ id: 23, name: 'Specific', parentId: 21 },
|
|
||||||
{ id: 39, name: 'Precise', parentId: 21 },
|
|
||||||
{ id: 41, name: 'Biblical', parentId: null },
|
|
||||||
{ id: 42, name: 'Personal Growth & Development', parentId: null },
|
|
||||||
{ id: 43, name: 'Prayers', parentId: 41 },
|
|
||||||
{ id: 44, name: 'Scripture', parentId: 41 },
|
|
||||||
{ id: 46, name: 'Sports', parentId: null }
|
|
||||||
];
|
];
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
@@ -130,6 +113,15 @@ async function main() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('Seeding ProclamationGroup...');
|
||||||
|
for (const group of proclamationGroups) {
|
||||||
|
await prisma.proclamationGroup.upsert({
|
||||||
|
where: { name: group.name },
|
||||||
|
create: group,
|
||||||
|
update: group
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
console.log('Seeding ProclamationCategory...');
|
console.log('Seeding ProclamationCategory...');
|
||||||
for (const cat of proclamationCategories) {
|
for (const cat of proclamationCategories) {
|
||||||
await prisma.proclamationCategory.upsert({
|
await prisma.proclamationCategory.upsert({
|
||||||
@@ -139,15 +131,6 @@ async function main() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Seeding SubscriptionLevel...');
|
|
||||||
for (const level of subscriptionLevels) {
|
|
||||||
await prisma.subscriptionLevel.upsert({
|
|
||||||
where: { id: level.id },
|
|
||||||
create: level,
|
|
||||||
update: level
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Seeding ColorPalette...');
|
console.log('Seeding ColorPalette...');
|
||||||
for (const palette of colorPalettes) {
|
for (const palette of colorPalettes) {
|
||||||
await prisma.colorPalette.upsert({
|
await prisma.colorPalette.upsert({
|
||||||
|
|||||||
Vendored
+3
-3
@@ -1,11 +1,11 @@
|
|||||||
import type { Session, User } from 'better-auth';
|
import type { auth } from '$lib/auth';
|
||||||
import type { ApiKeyRecord } from '$lib/server/api-key';
|
import type { ApiKeyRecord } from '$lib/server/api-key';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
namespace App {
|
namespace App {
|
||||||
interface Locals {
|
interface Locals {
|
||||||
session: Session | null;
|
session: typeof auth.$Infer.Session.session | null;
|
||||||
user: User | null;
|
user: typeof auth.$Infer.Session.user | null;
|
||||||
apiKey?: ApiKeyRecord;
|
apiKey?: ApiKeyRecord;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-10
@@ -5,21 +5,21 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
const theme = localStorage.getItem('theme');
|
try {
|
||||||
if (
|
const theme = localStorage.getItem('theme');
|
||||||
theme === 'dark' ||
|
const dark =
|
||||||
(!theme && window.matchMedia('(prefers-color-scheme: dark)').matches)
|
theme === 'dark' ||
|
||||||
) {
|
(!theme && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
||||||
document.documentElement.classList.add('dark');
|
document.documentElement.classList.toggle('dark', dark);
|
||||||
} else {
|
} catch (e) {
|
||||||
document.documentElement.classList.remove('dark');
|
// ignore
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="m-0" data-sveltekit-preload-data="hover">
|
<body data-sveltekit-preload-data="hover">
|
||||||
<div class="flex h-dvh w-dvw flex-col">%sveltekit.body%</div>
|
<div>%sveltekit.body%</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+79
-58
@@ -3,71 +3,92 @@ import { svelteKitHandler } from 'better-auth/svelte-kit';
|
|||||||
import { building } from '$app/environment';
|
import { building } from '$app/environment';
|
||||||
import { env } from '$env/dynamic/private';
|
import { env } from '$env/dynamic/private';
|
||||||
import { validateApiKey } from '$lib/server/api-key';
|
import { validateApiKey } from '$lib/server/api-key';
|
||||||
|
import { runWithAuditContext, type AuditContext } from '$lib/server/audit-context';
|
||||||
|
import { clientIp } from '$lib/server/v2-helpers';
|
||||||
|
|
||||||
|
function contextFor(
|
||||||
|
event: { url: URL; request: Request },
|
||||||
|
session?: App.Locals['user']
|
||||||
|
): AuditContext {
|
||||||
|
return {
|
||||||
|
userId: session?.id ?? null,
|
||||||
|
email: session?.email ?? null,
|
||||||
|
roleId: session?.roleId ?? null,
|
||||||
|
page: event.url.pathname,
|
||||||
|
ip: clientIp(event.request)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export async function handle({ event, resolve }) {
|
export async function handle({ event, resolve }) {
|
||||||
// v2 auth is ALWAYS enforced — no API_KEYS_ACTIVE bypass. The only
|
return runWithAuditContext(contextFor(event), async () => {
|
||||||
// exceptions are the signed onboarding routes (HMAC-protected).
|
// v2 auth is ALWAYS enforced — no API_KEYS_ACTIVE bypass. The only
|
||||||
// TODO: re-enable when v2 API key auth is ready.
|
// exceptions are the signed onboarding routes (HMAC-protected).
|
||||||
// const isV2 = event.url.pathname.startsWith('/api/v2/');
|
// TODO: re-enable when v2 API key auth is ready.
|
||||||
// const isSignedOnboarding = event.url.pathname.startsWith('/api/v2/automations/');
|
// const isV2 = event.url.pathname.startsWith('/api/v2/');
|
||||||
//
|
// const isSignedOnboarding = event.url.pathname.startsWith('/api/v2/automations/');
|
||||||
// if (isV2 && !isSignedOnboarding) {
|
//
|
||||||
// const authHeader = event.request.headers.get('authorization');
|
// if (isV2 && !isSignedOnboarding) {
|
||||||
// if (!authHeader?.startsWith('Bearer ')) {
|
// const authHeader = event.request.headers.get('authorization');
|
||||||
// return new Response(JSON.stringify({ error: 'API key required' }), {
|
// if (!authHeader?.startsWith('Bearer ')) {
|
||||||
// status: 401,
|
// return new Response(JSON.stringify({ error: 'API key required' }), {
|
||||||
// headers: { 'content-type': 'application/json' }
|
// status: 401,
|
||||||
// });
|
// headers: { 'content-type': 'application/json' }
|
||||||
// }
|
// });
|
||||||
//
|
// }
|
||||||
// const key = authHeader.slice(7);
|
//
|
||||||
// const record = await validateApiKey(key);
|
// const key = authHeader.slice(7);
|
||||||
// if (!record) {
|
// const record = await validateApiKey(key);
|
||||||
// return new Response(JSON.stringify({ error: 'Invalid or expired API key' }), {
|
// if (!record) {
|
||||||
// status: 401,
|
// return new Response(JSON.stringify({ error: 'Invalid or expired API key' }), {
|
||||||
// headers: { 'content-type': 'application/json' }
|
// status: 401,
|
||||||
// });
|
// headers: { 'content-type': 'application/json' }
|
||||||
// }
|
// });
|
||||||
//
|
// }
|
||||||
// event.locals.apiKey = record;
|
//
|
||||||
//
|
// event.locals.apiKey = record;
|
||||||
// return resolve(event);
|
//
|
||||||
// }
|
// return resolve(event);
|
||||||
|
// }
|
||||||
|
|
||||||
if (
|
if (
|
||||||
event.url.pathname.startsWith('/api/v1/') &&
|
event.url.pathname.startsWith('/api/v1/') &&
|
||||||
!event.url.pathname.startsWith('/api/v1/automations/')
|
!event.url.pathname.startsWith('/api/v1/automations/')
|
||||||
) {
|
) {
|
||||||
if (env.API_KEYS_ACTIVE !== 'false') {
|
if (env.API_KEYS_ACTIVE !== 'false') {
|
||||||
const authHeader = event.request.headers.get('authorization');
|
const authHeader = event.request.headers.get('authorization');
|
||||||
if (!authHeader?.startsWith('Bearer ')) {
|
if (!authHeader?.startsWith('Bearer ')) {
|
||||||
return new Response(JSON.stringify({ error: 'API key required' }), {
|
return new Response(JSON.stringify({ error: 'API key required' }), {
|
||||||
status: 401,
|
status: 401,
|
||||||
headers: { 'content-type': 'application/json' }
|
headers: { 'content-type': 'application/json' }
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = authHeader.slice(7);
|
||||||
|
const record = await validateApiKey(key);
|
||||||
|
if (!record) {
|
||||||
|
return new Response(JSON.stringify({ error: 'Invalid or expired API key' }), {
|
||||||
|
status: 401,
|
||||||
|
headers: { 'content-type': 'application/json' }
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const key = authHeader.slice(7);
|
return resolve(event);
|
||||||
const record = await validateApiKey(key);
|
|
||||||
if (!record) {
|
|
||||||
return new Response(JSON.stringify({ error: 'Invalid or expired API key' }), {
|
|
||||||
status: 401,
|
|
||||||
headers: { 'content-type': 'application/json' }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return resolve(event);
|
const session = await auth.api.getSession({
|
||||||
}
|
headers: event.request.headers
|
||||||
|
});
|
||||||
|
|
||||||
const session = await auth.api.getSession({
|
if (session) {
|
||||||
headers: event.request.headers
|
event.locals.session = session.session;
|
||||||
|
event.locals.user = session.user;
|
||||||
|
|
||||||
|
return runWithAuditContext(contextFor(event, session.user), () =>
|
||||||
|
svelteKitHandler({ event, resolve, auth, building })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return svelteKitHandler({ event, resolve, auth, building });
|
||||||
});
|
});
|
||||||
|
|
||||||
if (session) {
|
|
||||||
event.locals.session = session.session;
|
|
||||||
event.locals.user = session.user;
|
|
||||||
}
|
|
||||||
|
|
||||||
return svelteKitHandler({ event, resolve, auth, building });
|
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-1
@@ -5,13 +5,40 @@ import { sveltekitCookies } from 'better-auth/svelte-kit';
|
|||||||
import { getRequestEvent } from '$app/server';
|
import { getRequestEvent } from '$app/server';
|
||||||
import { sendMail } from './server/resend';
|
import { sendMail } from './server/resend';
|
||||||
import { sendVerificationEmail } from './server/email-verification';
|
import { sendVerificationEmail } from './server/email-verification';
|
||||||
|
import { sendRegistrationEmail } from './server/registration-email';
|
||||||
import { env } from '$env/dynamic/private';
|
import { env } from '$env/dynamic/private';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An admin-created user is invited with a set-password link. That reuses the
|
||||||
|
* password-reset token flow but marks the request with `invite=1` so we send a
|
||||||
|
* welcome/registration email instead of a "you requested a reset" email.
|
||||||
|
*/
|
||||||
|
function isRegistrationInvite(url: string): boolean {
|
||||||
|
try {
|
||||||
|
const callbackURL = new URL(url).searchParams.get('callbackURL') ?? '';
|
||||||
|
return callbackURL.includes('invite=1');
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const auth = betterAuth({
|
export const auth = betterAuth({
|
||||||
baseURL: env.BETTER_AUTH_URL,
|
baseURL: env.BETTER_AUTH_URL,
|
||||||
database: prismaAdapter(prisma, {
|
database: prismaAdapter(prisma, {
|
||||||
provider: 'postgresql'
|
provider: 'postgresql'
|
||||||
}),
|
}),
|
||||||
|
user: {
|
||||||
|
// Expose our custom role column on the session user so role checks can
|
||||||
|
// read `locals.user.roleId` without a database round-trip.
|
||||||
|
additionalFields: {
|
||||||
|
roleId: {
|
||||||
|
type: 'number',
|
||||||
|
required: false,
|
||||||
|
defaultValue: 4,
|
||||||
|
input: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
emailVerification: {
|
emailVerification: {
|
||||||
sendOnSignUp: true,
|
sendOnSignUp: true,
|
||||||
sendVerificationEmail: async ({ user, token }) => {
|
sendVerificationEmail: async ({ user, token }) => {
|
||||||
@@ -27,8 +54,16 @@ export const auth = betterAuth({
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
requireEmailVerification: true,
|
requireEmailVerification: true,
|
||||||
sendResetPassword: async ({ user, url }) => {
|
sendResetPassword: async ({ user, url }) => {
|
||||||
|
if (isRegistrationInvite(url)) {
|
||||||
|
void sendRegistrationEmail({ email: user.email, url }).catch((e) => {
|
||||||
|
console.error('sendRegistrationEmail template email failed:', e);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
void sendMail({
|
void sendMail({
|
||||||
to: user.email,
|
to: user.email,
|
||||||
|
subject: 'Reset your Radiant Wave password',
|
||||||
templateId: 'password-reset-email',
|
templateId: 'password-reset-email',
|
||||||
data: {
|
data: {
|
||||||
target_email_address: user.email,
|
target_email_address: user.email,
|
||||||
@@ -51,6 +86,6 @@ export const auth = betterAuth({
|
|||||||
window: 60,
|
window: 60,
|
||||||
max: 100
|
max: 100
|
||||||
},
|
},
|
||||||
trustedOrigins: ['http://localhost:5173', 'https://portal.rhemawavetech.com'],
|
trustedOrigins: ['http://localhost:5173', 'https://portal.radiantwavetech.com'],
|
||||||
plugins: [sveltekitCookies(getRequestEvent)]
|
plugins: [sveltekitCookies(getRequestEvent)]
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { Snippet } from 'svelte';
|
||||||
|
|
||||||
|
let {
|
||||||
|
title,
|
||||||
|
description = '',
|
||||||
|
children
|
||||||
|
}: {
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
children: Snippet;
|
||||||
|
} = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<section class="mt-8">
|
||||||
|
<div>
|
||||||
|
<h2 class="text-lg font-semibold text-slate-900 dark:text-slate-100">{title}</h2>
|
||||||
|
{#if description}
|
||||||
|
<p class="mt-1 text-sm text-slate-500 dark:text-slate-400">{description}</p>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="mt-4">
|
||||||
|
{@render children()}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export type DataTableColumn = {
|
||||||
|
label: string;
|
||||||
|
value: (record: Record<string, unknown>) => unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
let {
|
||||||
|
title = '',
|
||||||
|
columns = [] as DataTableColumn[],
|
||||||
|
records = [] as Record<string, unknown>[],
|
||||||
|
newLabel = 'New',
|
||||||
|
searchPlaceholder = 'Search...',
|
||||||
|
copyableFields = [] as string[],
|
||||||
|
draggable = false,
|
||||||
|
onRowClick,
|
||||||
|
onNew,
|
||||||
|
onReorder
|
||||||
|
}: {
|
||||||
|
title?: string;
|
||||||
|
columns?: DataTableColumn[];
|
||||||
|
records?: Record<string, unknown>[];
|
||||||
|
newLabel?: string;
|
||||||
|
searchPlaceholder?: string;
|
||||||
|
copyableFields?: string[];
|
||||||
|
draggable?: boolean;
|
||||||
|
onRowClick?: (record: Record<string, unknown>) => void;
|
||||||
|
onNew?: () => void;
|
||||||
|
onReorder?: (ids: (string | number)[]) => void;
|
||||||
|
} = $props();
|
||||||
|
|
||||||
|
let search = $state('');
|
||||||
|
|
||||||
|
const filteredRecords = $derived(
|
||||||
|
search
|
||||||
|
? records.filter((record) =>
|
||||||
|
columns.some((column) =>
|
||||||
|
String(column.value(record) ?? '')
|
||||||
|
.toLowerCase()
|
||||||
|
.includes(search.toLowerCase())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
: records
|
||||||
|
);
|
||||||
|
|
||||||
|
let dragOverride = $state<Record<string, unknown>[] | null>(null);
|
||||||
|
let dragIndex = $state<number | null>(null);
|
||||||
|
let hoverIndex = $state<number | null>(null);
|
||||||
|
|
||||||
|
const rows = $derived(dragOverride ?? filteredRecords);
|
||||||
|
|
||||||
|
function rowKey(record: Record<string, unknown>, index: number): string {
|
||||||
|
return String(record.id ?? record.key ?? index);
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyValue(value: unknown) {
|
||||||
|
navigator.clipboard.writeText(String(value ?? ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDragStart(e: DragEvent, i: number) {
|
||||||
|
dragOverride = filteredRecords;
|
||||||
|
dragIndex = i;
|
||||||
|
hoverIndex = i;
|
||||||
|
e.dataTransfer!.effectAllowed = 'move';
|
||||||
|
e.dataTransfer!.setData('text/plain', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDragOver(e: DragEvent, i: number) {
|
||||||
|
if (dragIndex === null) return;
|
||||||
|
e.preventDefault();
|
||||||
|
if (i === hoverIndex) return;
|
||||||
|
e.dataTransfer!.dropEffect = 'move';
|
||||||
|
|
||||||
|
const reordered = rows.slice();
|
||||||
|
const [moved] = reordered.splice(dragIndex, 1);
|
||||||
|
reordered.splice(i, 0, moved);
|
||||||
|
dragOverride = reordered;
|
||||||
|
dragIndex = i;
|
||||||
|
hoverIndex = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleDrop() {
|
||||||
|
if (dragIndex === null) return;
|
||||||
|
const ids = rows.map((r) => r.id as string | number);
|
||||||
|
await onReorder?.(ids);
|
||||||
|
dragIndex = null;
|
||||||
|
hoverIndex = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDragEnd() {
|
||||||
|
dragOverride = null;
|
||||||
|
dragIndex = null;
|
||||||
|
hoverIndex = null;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="mx-auto w-full">
|
||||||
|
<div class="mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||||
|
<h2 class="text-xl font-semibold text-slate-900 dark:text-slate-100">{title}</h2>
|
||||||
|
|
||||||
|
<div class="flex w-full flex-col gap-3 sm:w-auto sm:flex-row sm:items-center">
|
||||||
|
<input
|
||||||
|
type="search"
|
||||||
|
bind:value={search}
|
||||||
|
placeholder={searchPlaceholder}
|
||||||
|
class="w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 shadow-sm transition focus:border-indigo-500 focus:outline-none sm:w-64 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
|
||||||
|
/>
|
||||||
|
{#if onNew}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={onNew}
|
||||||
|
class="inline-flex h-10 items-center justify-center gap-1.5 rounded-md bg-indigo-600 px-4 text-sm font-medium text-white shadow-sm transition hover:bg-indigo-700"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
aria-hidden="true"><path d="M12 5v14" /><path d="M5 12h14" /></svg
|
||||||
|
>
|
||||||
|
{newLabel}
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="w-full overflow-x-auto rounded-lg border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900"
|
||||||
|
>
|
||||||
|
<table class="w-full min-w-max text-left text-sm text-slate-700 dark:text-slate-300">
|
||||||
|
<thead>
|
||||||
|
<tr
|
||||||
|
class="border-b border-slate-200 bg-slate-100 text-xs font-semibold tracking-wide text-slate-500 dark:border-slate-800 dark:bg-slate-800 dark:text-slate-400"
|
||||||
|
>
|
||||||
|
{#if draggable}
|
||||||
|
<th scope="col" class="w-10 px-4 py-3"></th>
|
||||||
|
{/if}
|
||||||
|
{#each columns as column (column.label)}
|
||||||
|
<th scope="col" class="px-4 py-3">{column.label}</th>
|
||||||
|
{/each}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each rows as record, i (rowKey(record, i))}
|
||||||
|
<tr
|
||||||
|
class="transition {i % 2 === 0
|
||||||
|
? 'bg-white dark:bg-slate-900'
|
||||||
|
: 'bg-slate-50 dark:bg-slate-950'} {onRowClick
|
||||||
|
? 'cursor-pointer hover:bg-indigo-50 dark:hover:bg-indigo-950'
|
||||||
|
: ''}"
|
||||||
|
{draggable}
|
||||||
|
ondragstart={(e) => handleDragStart(e, i)}
|
||||||
|
ondragover={(e) => handleDragOver(e, i)}
|
||||||
|
ondrop={handleDrop}
|
||||||
|
ondragend={handleDragEnd}
|
||||||
|
onclick={() => onRowClick?.(record)}
|
||||||
|
>
|
||||||
|
{#if draggable}
|
||||||
|
<td class="px-4 py-3" onclick={(e) => e.stopPropagation()} aria-hidden="true">
|
||||||
|
<span
|
||||||
|
class="cursor-grab text-slate-400 select-none active:cursor-grabbing dark:text-slate-500"
|
||||||
|
title="Drag to reorder">⠿</span
|
||||||
|
>
|
||||||
|
</td>
|
||||||
|
{/if}
|
||||||
|
{#each columns as column (column.label)}
|
||||||
|
<td class="px-4 py-3 whitespace-nowrap">
|
||||||
|
{#if copyableFields.includes(column.label) && column.value(record) != null}
|
||||||
|
<span>
|
||||||
|
{String(column.value(record))}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
copyValue(column.value(record));
|
||||||
|
}}
|
||||||
|
title="Copy to clipboard"
|
||||||
|
class="ml-1 inline-flex text-slate-400 transition hover:text-indigo-600 dark:text-slate-500 dark:hover:text-indigo-400"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
width="14"
|
||||||
|
height="14"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<rect x="3.5" y="2.5" width="11" height="11" rx="1" />
|
||||||
|
<path d="M5.5 2.5V1.5a1 1 0 011-1h3a1 1 0 011 1v1" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
{:else}
|
||||||
|
{String(column.value(record) ?? '')}
|
||||||
|
{/if}
|
||||||
|
</td>
|
||||||
|
{/each}
|
||||||
|
</tr>
|
||||||
|
{:else}
|
||||||
|
<tr>
|
||||||
|
<td
|
||||||
|
colspan={columns.length + (draggable ? 1 : 0)}
|
||||||
|
class="bg-white dark:bg-slate-900 px-4 py-10 text-center text-slate-500 dark:text-slate-400"
|
||||||
|
>
|
||||||
|
No results found.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
deleteAction?: string;
|
deleteAction?: string;
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
function handleBackdrop(e: MouseEvent) {
|
function handleBackdrop() {
|
||||||
if (e.target === e.currentTarget) onClose?.();
|
onClose?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKeydown(e: KeyboardEvent) {
|
function handleKeydown(e: KeyboardEvent) {
|
||||||
@@ -44,24 +44,27 @@
|
|||||||
|
|
||||||
{#if open}
|
{#if open}
|
||||||
<div
|
<div
|
||||||
role="presentation"
|
class="fixed inset-0 z-50 flex items-center justify-center bg-slate-900/50 p-4"
|
||||||
class="fixed inset-0 z-50 flex items-start justify-center overflow-y-auto bg-black/50 pt-12"
|
|
||||||
onclick={handleBackdrop}
|
|
||||||
onkeydown={handleKeydown}
|
onkeydown={handleKeydown}
|
||||||
|
role="presentation"
|
||||||
>
|
>
|
||||||
|
<div class="absolute inset-0" onclick={handleBackdrop} aria-hidden="true"></div>
|
||||||
<div
|
<div
|
||||||
class="my-8 w-full max-w-2xl border border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-900"
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
aria-label={title}
|
||||||
|
class="relative z-10 flex max-h-[90vh] w-full max-w-2xl flex-col overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-xl dark:border-slate-800 dark:bg-slate-900"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="flex items-center justify-between border-b border-gray-200 px-6 py-4 dark:border-gray-700"
|
class="flex shrink-0 items-center justify-between gap-3 border-b border-slate-200 px-6 py-4 dark:border-slate-800"
|
||||||
>
|
>
|
||||||
<h2 class="text-lg font-semibold text-gray-900 dark:text-gray-100">{title}</h2>
|
<h2 class="text-lg font-bold text-slate-900 dark:text-slate-100">{title}</h2>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
{#if canDelete && recordId}
|
{#if canDelete && recordId}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={confirmDelete}
|
onclick={confirmDelete}
|
||||||
class="px-3 py-1 text-sm text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-900/20"
|
class="rounded-md bg-red-50 px-3 py-1.5 text-sm font-medium text-red-600 ring-1 ring-red-200 transition ring-inset hover:bg-red-600 hover:text-white dark:bg-red-950/50 dark:text-red-400 dark:ring-red-900"
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
</button>
|
</button>
|
||||||
@@ -69,12 +72,14 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={onClose}
|
onclick={onClose}
|
||||||
class="px-2 py-1 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
aria-label="Close"
|
||||||
>×</button
|
class="rounded-md p-1.5 text-2xl leading-none text-slate-400 transition hover:bg-slate-100 hover:text-red-600 dark:text-slate-500 dark:hover:bg-slate-800"
|
||||||
>
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="px-6 py-4">
|
<div class="overflow-y-auto px-6 py-5">
|
||||||
{@render children()}
|
{@render children()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,248 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import type { PrismaScalarType } from '$lib/svelte-admin/types';
|
|
||||||
import { fieldNameToLabel, resolveFieldValue } from '$lib/svelte-admin/introspect';
|
|
||||||
|
|
||||||
let {
|
|
||||||
records = [],
|
|
||||||
listFields = [],
|
|
||||||
searchFields = [],
|
|
||||||
fieldTypes = {} as Record<string, PrismaScalarType>,
|
|
||||||
singular = 'record',
|
|
||||||
draggable = false,
|
|
||||||
copyableFields = [] as string[],
|
|
||||||
onRowClick,
|
|
||||||
onNew,
|
|
||||||
onReorder
|
|
||||||
}: {
|
|
||||||
records: Record<string, unknown>[];
|
|
||||||
listFields: string[];
|
|
||||||
searchFields?: string[];
|
|
||||||
fieldTypes?: Record<string, PrismaScalarType>;
|
|
||||||
singular?: string;
|
|
||||||
draggable?: boolean;
|
|
||||||
copyableFields?: string[];
|
|
||||||
onRowClick?: (record: Record<string, unknown>) => void;
|
|
||||||
onNew?: () => void;
|
|
||||||
onReorder?: (ids: (string | number)[]) => void;
|
|
||||||
} = $props();
|
|
||||||
|
|
||||||
let search = $state('');
|
|
||||||
let sortCol = $state<string | null>(null);
|
|
||||||
let sortAsc = $state(true);
|
|
||||||
|
|
||||||
let filtered = $derived(
|
|
||||||
search
|
|
||||||
? records.filter((r) =>
|
|
||||||
searchFields.some((f) => {
|
|
||||||
const v = resolveFieldValue(r, f);
|
|
||||||
return v != null && String(v).toLowerCase().includes(search.toLowerCase());
|
|
||||||
})
|
|
||||||
)
|
|
||||||
: records
|
|
||||||
);
|
|
||||||
|
|
||||||
let sorted = $derived(
|
|
||||||
sortCol
|
|
||||||
? [...filtered].sort((a, b) => {
|
|
||||||
const va = resolveFieldValue(a, sortCol!);
|
|
||||||
const vb = resolveFieldValue(b, sortCol!);
|
|
||||||
if (va == null) return 1;
|
|
||||||
if (vb == null) return -1;
|
|
||||||
const cmp = String(va).localeCompare(String(vb), undefined, { numeric: true });
|
|
||||||
return sortAsc ? cmp : -cmp;
|
|
||||||
})
|
|
||||||
: filtered
|
|
||||||
);
|
|
||||||
|
|
||||||
let isDragging = $state(false);
|
|
||||||
|
|
||||||
let dragOverride = $state<Record<string, unknown>[] | null>(null);
|
|
||||||
|
|
||||||
let rows = $derived(dragOverride ?? sorted);
|
|
||||||
|
|
||||||
let dragIndex = $state<number | null>(null);
|
|
||||||
let hoverIndex = $state<number | null>(null);
|
|
||||||
|
|
||||||
function toggleSort(field: string) {
|
|
||||||
if (sortCol === field) {
|
|
||||||
sortAsc = !sortAsc;
|
|
||||||
} else {
|
|
||||||
sortCol = field;
|
|
||||||
sortAsc = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDragStart(e: DragEvent, i: number) {
|
|
||||||
dragOverride = sorted;
|
|
||||||
isDragging = true;
|
|
||||||
dragIndex = i;
|
|
||||||
hoverIndex = i;
|
|
||||||
e.dataTransfer!.effectAllowed = 'move';
|
|
||||||
e.dataTransfer!.setData('text/plain', '');
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDragOver(e: DragEvent, i: number) {
|
|
||||||
if (dragIndex === null) return;
|
|
||||||
e.preventDefault();
|
|
||||||
if (i === hoverIndex) return;
|
|
||||||
e.dataTransfer!.dropEffect = 'move';
|
|
||||||
|
|
||||||
const reordered = rows.slice();
|
|
||||||
const [moved] = reordered.splice(dragIndex, 1);
|
|
||||||
reordered.splice(i, 0, moved);
|
|
||||||
dragOverride = reordered;
|
|
||||||
dragIndex = i;
|
|
||||||
hoverIndex = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleDrop() {
|
|
||||||
if (dragIndex === null) return;
|
|
||||||
const ids = rows.map((r) => r.id as string | number);
|
|
||||||
await onReorder?.(ids);
|
|
||||||
isDragging = false;
|
|
||||||
dragIndex = null;
|
|
||||||
hoverIndex = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDragEnd() {
|
|
||||||
dragOverride = null;
|
|
||||||
isDragging = false;
|
|
||||||
dragIndex = null;
|
|
||||||
hoverIndex = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function copyValue(v: unknown) {
|
|
||||||
navigator.clipboard.writeText(String(v ?? ''));
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatCellValue(v: unknown, field: string): string {
|
|
||||||
if (v == null) return '';
|
|
||||||
const ft = fieldTypes[field];
|
|
||||||
if (ft === 'DateTime') {
|
|
||||||
if (v instanceof Date) return v.toLocaleString();
|
|
||||||
return new Date(v as string).toLocaleString();
|
|
||||||
}
|
|
||||||
if (ft === 'Boolean') return v ? 'Active' : 'Inactive';
|
|
||||||
if (ft === 'Float') {
|
|
||||||
const n = Number(v);
|
|
||||||
return Number.isNaN(n) ? String(v) : n.toFixed(2);
|
|
||||||
}
|
|
||||||
return String(v);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="flex flex-col gap-4">
|
|
||||||
<div class="flex items-center justify-between gap-4">
|
|
||||||
{#if searchFields.length > 0}
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="Search {singular}s..."
|
|
||||||
bind:value={search}
|
|
||||||
class="max-w-xs border border-gray-300 px-3 py-1.5 text-sm dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100"
|
|
||||||
/>
|
|
||||||
{:else}
|
|
||||||
<div></div>
|
|
||||||
{/if}
|
|
||||||
{#if onNew}
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onclick={onNew}
|
|
||||||
class="border border-gray-300 px-3 py-1.5 text-sm font-medium dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800"
|
|
||||||
>
|
|
||||||
New {singular} +
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="overflow-x-auto border border-gray-200 dark:border-gray-700">
|
|
||||||
<table class="w-full">
|
|
||||||
<thead>
|
|
||||||
<tr class="border-b border-gray-200 bg-gray-50 dark:border-gray-700 dark:bg-gray-800">
|
|
||||||
{#if draggable}
|
|
||||||
<th scope="col" class="w-10 px-2 py-2"></th>
|
|
||||||
{/if}
|
|
||||||
{#each listFields as field (field)}
|
|
||||||
<th
|
|
||||||
scope="col"
|
|
||||||
onclick={() => toggleSort(field)}
|
|
||||||
class="cursor-pointer px-4 py-2 text-left text-xs font-medium tracking-wider text-gray-500 uppercase hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
|
||||||
>
|
|
||||||
{fieldNameToLabel(field)}
|
|
||||||
{#if sortCol === field}
|
|
||||||
<span class="ml-1">{sortAsc ? '\u25B2' : '\u25BC'}</span>
|
|
||||||
{/if}
|
|
||||||
</th>
|
|
||||||
{/each}
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{#each rows as record, i ((record['id'] as string) ?? (record['key'] as string) ?? i)}
|
|
||||||
<tr
|
|
||||||
{draggable}
|
|
||||||
ondragstart={(e) => handleDragStart(e, i)}
|
|
||||||
ondragover={(e) => handleDragOver(e, i)}
|
|
||||||
ondrop={handleDrop}
|
|
||||||
ondragend={handleDragEnd}
|
|
||||||
onclick={() => onRowClick?.(record)}
|
|
||||||
class="border-b border-gray-200 hover:bg-gray-100 dark:border-gray-700 dark:text-gray-100 dark:hover:bg-gray-800 {draggable &&
|
|
||||||
isDragging &&
|
|
||||||
dragIndex === i
|
|
||||||
? 'opacity-40'
|
|
||||||
: ''} {draggable && isDragging && hoverIndex === i
|
|
||||||
? 'border-t-2 border-t-blue-500'
|
|
||||||
: ''}"
|
|
||||||
>
|
|
||||||
{#if draggable}
|
|
||||||
<td
|
|
||||||
class="w-10 cursor-grab px-2 py-2 text-center"
|
|
||||||
onclick={(e) => e.stopPropagation()}
|
|
||||||
>
|
|
||||||
<span class="text-gray-400 select-none">⠿</span>
|
|
||||||
</td>
|
|
||||||
{/if}
|
|
||||||
{#each listFields as field (field)}
|
|
||||||
<td class="px-4 py-2 text-sm">
|
|
||||||
<span class="inline-flex items-center gap-1.5">
|
|
||||||
{formatCellValue(resolveFieldValue(record, field), field)}
|
|
||||||
{#if copyableFields.includes(field)}
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onclick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
copyValue(resolveFieldValue(record, field));
|
|
||||||
}}
|
|
||||||
title="Copy to clipboard"
|
|
||||||
class="shrink-0 leading-none text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
class="size-3.5"
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-width="1.5"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
>
|
|
||||||
<rect x="3.5" y="2.5" width="11" height="11" rx="1" />
|
|
||||||
<path d="M5.5 2.5V1.5a1 1 0 011-1h3a1 1 0 011 1v1" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
{/each}
|
|
||||||
</tr>
|
|
||||||
{:else}
|
|
||||||
<tr>
|
|
||||||
<td
|
|
||||||
colspan={listFields.length + (draggable ? 1 : 0)}
|
|
||||||
class="px-4 py-8 text-center text-sm text-gray-500 dark:text-gray-400"
|
|
||||||
>
|
|
||||||
No {singular}s found.
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/each}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,11 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
let {
|
let { message = '' }: { message?: string } = $props();
|
||||||
message = '',
|
|
||||||
type = 'success' as 'success' | 'error'
|
|
||||||
}: {
|
|
||||||
message?: string;
|
|
||||||
type?: 'success' | 'error';
|
|
||||||
} = $props();
|
|
||||||
|
|
||||||
let remaining = $state(5);
|
let remaining = $state(5);
|
||||||
let visible = $state(true);
|
let visible = $state(true);
|
||||||
@@ -32,15 +26,17 @@
|
|||||||
{#if visible}
|
{#if visible}
|
||||||
<div
|
<div
|
||||||
role="alert"
|
role="alert"
|
||||||
class="fixed bottom-4 left-4 z-50 flex min-w-64 items-center gap-3 border px-4 py-3 text-sm shadow-lg {type ===
|
class="fixed right-4 bottom-4 z-50 flex items-center gap-3 rounded-lg border border-slate-700 bg-slate-900 px-4 py-3 text-sm text-white shadow-lg dark:border-slate-300 dark:bg-slate-950"
|
||||||
'success'
|
|
||||||
? 'border-green-600 bg-green-50 text-green-800'
|
|
||||||
: 'border-red-600 bg-red-50 text-red-800'}"
|
|
||||||
>
|
>
|
||||||
<span class="flex-1">{message}</span>
|
<span>{message}</span>
|
||||||
<span class="text-xs tabular-nums opacity-70">{remaining}s</span>
|
<span class="text-xs text-slate-400 dark:text-slate-500">{remaining}s</span>
|
||||||
<button type="button" onclick={dismiss} class="ml-1 leading-none font-bold hover:opacity-70"
|
<button
|
||||||
>×</button
|
type="button"
|
||||||
|
onclick={dismiss}
|
||||||
|
aria-label="Dismiss"
|
||||||
|
class="rounded p-1 text-slate-400 transition hover:text-white dark:text-slate-500"
|
||||||
>
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
let { children } = $props();
|
let { children } = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex min-h-0 flex-1 items-center justify-center overflow-auto">
|
<div class="mx-auto w-full max-w-md px-4 py-8 sm:py-12">
|
||||||
{@render children()}
|
<div
|
||||||
|
class="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm sm:p-8 dark:border-slate-800 dark:bg-slate-900"
|
||||||
|
>
|
||||||
|
{@render children()}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { onMount } from 'svelte';
|
|
||||||
import { Editor } from '@tiptap/core';
|
|
||||||
import StarterKit from '@tiptap/starter-kit';
|
|
||||||
import Highlight from '@tiptap/extension-highlight';
|
|
||||||
import { TextStyle } from '@tiptap/extension-text-style';
|
|
||||||
import { Color } from '@tiptap/extension-color';
|
|
||||||
|
|
||||||
let { initialContent = '', name = 'content' }: { initialContent?: string; name?: string } =
|
|
||||||
$props();
|
|
||||||
|
|
||||||
let element: HTMLElement;
|
|
||||||
let editor: Editor;
|
|
||||||
let content = $state('');
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
content = initialContent;
|
|
||||||
|
|
||||||
editor = new Editor({
|
|
||||||
element,
|
|
||||||
extensions: [
|
|
||||||
StarterKit.configure({
|
|
||||||
bulletList: false,
|
|
||||||
orderedList: false
|
|
||||||
}),
|
|
||||||
TextStyle,
|
|
||||||
Color,
|
|
||||||
Highlight.configure({ multicolor: true })
|
|
||||||
],
|
|
||||||
content: initialContent,
|
|
||||||
editorProps: {
|
|
||||||
attributes: {
|
|
||||||
class:
|
|
||||||
'prose prose-sm max-w-none outline-none h-full min-h-40 p-3 dark:prose-invert dark:text-gray-100'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onUpdate: ({ editor }) => {
|
|
||||||
content = editor.getHTML();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
editor.setEditable(true);
|
|
||||||
|
|
||||||
return () => editor.destroy();
|
|
||||||
});
|
|
||||||
|
|
||||||
function toggleBold() {
|
|
||||||
editor?.chain().focus().toggleBold().run();
|
|
||||||
}
|
|
||||||
function toggleItalic() {
|
|
||||||
editor?.chain().focus().toggleItalic().run();
|
|
||||||
}
|
|
||||||
function toggleHighlight() {
|
|
||||||
editor?.chain().focus().toggleHighlight().run();
|
|
||||||
}
|
|
||||||
|
|
||||||
function setColor() {
|
|
||||||
const current = editor?.getAttributes('textStyle').color ?? '#000000';
|
|
||||||
const colors = ['#000000', '#FF0000', '#0000FF', '#008000', '#FFA500', '#800080', '#808080'];
|
|
||||||
const idx = colors.indexOf(current);
|
|
||||||
const next = colors[(idx + 1) % colors.length];
|
|
||||||
editor?.chain().focus().setColor(next).run();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="border border-gray-300 dark:border-gray-600">
|
|
||||||
<div
|
|
||||||
class="flex gap-1 border-b border-gray-300 bg-gray-100 p-2 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-300"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onclick={toggleBold}
|
|
||||||
class="min-w-8 px-2 py-1 font-bold hover:bg-gray-200 dark:hover:bg-gray-700"
|
|
||||||
title="Bold">B</button
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onclick={toggleItalic}
|
|
||||||
class="min-w-8 px-2 py-1 italic hover:bg-gray-200 dark:hover:bg-gray-700"
|
|
||||||
title="Italic">I</button
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onclick={toggleHighlight}
|
|
||||||
class="min-w-8 px-2 py-1 hover:bg-gray-200 dark:hover:bg-gray-700"
|
|
||||||
title="Highlight"><span class="bg-yellow-200 px-1 dark:bg-yellow-600">H</span></button
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onclick={setColor}
|
|
||||||
class="min-w-8 px-2 py-1 hover:bg-gray-200 dark:hover:bg-gray-700"
|
|
||||||
title="Cycle text color">A</button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
bind:this={element}
|
|
||||||
role="textbox"
|
|
||||||
tabindex="0"
|
|
||||||
contenteditable="true"
|
|
||||||
class="min-h-40 cursor-text bg-white dark:bg-gray-900"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
<input type="hidden" {name} value={content} />
|
|
||||||
+161
-7
@@ -2,18 +2,172 @@ import pg from 'pg';
|
|||||||
import { PrismaPg } from '@prisma/adapter-pg';
|
import { PrismaPg } from '@prisma/adapter-pg';
|
||||||
import { PrismaClient } from '$lib/generated/prisma/client';
|
import { PrismaClient } from '$lib/generated/prisma/client';
|
||||||
import { env } from '$env/dynamic/private';
|
import { env } from '$env/dynamic/private';
|
||||||
|
import { getAuditContext } from '$lib/server/audit-context';
|
||||||
|
import { describeAction, entityIdFor, sanitizeAuditValue, shouldAudit } from '$lib/server/audit';
|
||||||
|
|
||||||
let _prisma: PrismaClient | undefined;
|
let _prisma: PrismaClient | undefined;
|
||||||
|
let _base: PrismaClient | undefined;
|
||||||
|
let _pool: pg.Pool | undefined;
|
||||||
|
|
||||||
|
function getBase(): { base: PrismaClient; pool: pg.Pool } {
|
||||||
|
if (!_base || !_pool) {
|
||||||
|
_pool = new pg.Pool({ connectionString: env.DATABASE_URL });
|
||||||
|
const adapter = new PrismaPg(_pool);
|
||||||
|
_base = new PrismaClient({ adapter });
|
||||||
|
}
|
||||||
|
return { base: _base, pool: _pool };
|
||||||
|
}
|
||||||
|
|
||||||
|
const roleNameCache = new Map<number, string>();
|
||||||
|
|
||||||
|
async function resolveRoleName(base: PrismaClient, roleId: number | null): Promise<string | null> {
|
||||||
|
if (roleId === null) return null;
|
||||||
|
const cached = roleNameCache.get(roleId);
|
||||||
|
if (cached) return cached;
|
||||||
|
try {
|
||||||
|
const role = await base.userRole.findUnique({
|
||||||
|
where: { id: roleId },
|
||||||
|
select: { name: true }
|
||||||
|
});
|
||||||
|
if (role?.name) {
|
||||||
|
roleNameCache.set(roleId, role.name);
|
||||||
|
return role.name;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to resolve role name for audit log:', e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Delegate = {
|
||||||
|
findFirst?: (args: unknown) => Promise<unknown>;
|
||||||
|
findMany?: (args: unknown) => Promise<unknown>;
|
||||||
|
};
|
||||||
|
|
||||||
|
function delegateFor(base: PrismaClient, model: string): Delegate | undefined {
|
||||||
|
const key = model.charAt(0).toLowerCase() + model.slice(1);
|
||||||
|
return (base as unknown as Record<string, unknown>)[key] as Delegate | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readBefore(
|
||||||
|
base: PrismaClient,
|
||||||
|
model: string,
|
||||||
|
operation: string,
|
||||||
|
args: unknown
|
||||||
|
): Promise<unknown> {
|
||||||
|
if (operation.startsWith('create')) return null;
|
||||||
|
const delegate = delegateFor(base, model);
|
||||||
|
if (!delegate) return null;
|
||||||
|
const where = (args as { where?: unknown } | null)?.where;
|
||||||
|
try {
|
||||||
|
if (operation === 'updateMany' || operation === 'deleteMany') {
|
||||||
|
return delegate.findMany ? await delegate.findMany({ where, take: 50 }) : null;
|
||||||
|
}
|
||||||
|
return delegate.findFirst ? await delegate.findFirst({ where }) : null;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function writeAuditRow(
|
||||||
|
pool: pg.Pool,
|
||||||
|
base: PrismaClient,
|
||||||
|
entry: {
|
||||||
|
userId: string | null;
|
||||||
|
email: string | null;
|
||||||
|
roleId: number | null;
|
||||||
|
page: string;
|
||||||
|
action: string;
|
||||||
|
entity: string;
|
||||||
|
entityId: string | null;
|
||||||
|
before: unknown;
|
||||||
|
after: unknown;
|
||||||
|
ip: string | null;
|
||||||
|
}
|
||||||
|
): Promise<void> {
|
||||||
|
const roleName = await resolveRoleName(base, entry.roleId);
|
||||||
|
await pool.query(
|
||||||
|
`INSERT INTO "AuditLog"
|
||||||
|
("id", "createdAt", "userId", "email", "roleId", "roleName", "page", "action", "entity", "entityId", "before", "after", "ip")
|
||||||
|
VALUES ($1, now(), $2, $3, $4, $5, $6, $7, $8, $9, $10::jsonb, $11::jsonb, $12)`,
|
||||||
|
[
|
||||||
|
crypto.randomUUID(),
|
||||||
|
entry.userId,
|
||||||
|
entry.email,
|
||||||
|
entry.roleId,
|
||||||
|
roleName,
|
||||||
|
entry.page,
|
||||||
|
entry.action,
|
||||||
|
entry.entity,
|
||||||
|
entry.entityId,
|
||||||
|
entry.before === null ? null : JSON.stringify(entry.before),
|
||||||
|
entry.after === null ? null : JSON.stringify(entry.after),
|
||||||
|
entry.ip
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function getPrisma(): PrismaClient {
|
function getPrisma(): PrismaClient {
|
||||||
if (!_prisma) {
|
if (_prisma) return _prisma;
|
||||||
const pool = new pg.Pool({
|
|
||||||
connectionString: env.DATABASE_URL
|
|
||||||
});
|
|
||||||
|
|
||||||
const adapter = new PrismaPg(pool);
|
const { base, pool } = getBase();
|
||||||
_prisma = new PrismaClient({ adapter });
|
|
||||||
}
|
const extended = base.$extends({
|
||||||
|
query: {
|
||||||
|
$allModels: {
|
||||||
|
async $allOperations({ model, operation, args, query }) {
|
||||||
|
if (!shouldAudit(model, operation)) {
|
||||||
|
return query(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
const before = await readBefore(base, model, operation, args);
|
||||||
|
const result = await query(args);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const ctx = getAuditContext();
|
||||||
|
const res = result as { id?: unknown; email?: string; roleId?: number | null } | null;
|
||||||
|
|
||||||
|
let userId = ctx?.userId ?? null;
|
||||||
|
let email = ctx?.email ?? null;
|
||||||
|
let roleId = ctx?.roleId ?? null;
|
||||||
|
|
||||||
|
// Self-signup: attribute the new user to itself since no session exists yet.
|
||||||
|
if (!userId && model === 'User' && operation === 'create' && res) {
|
||||||
|
userId = (res.id as string | undefined) ?? null;
|
||||||
|
email = res.email ?? null;
|
||||||
|
roleId = res.roleId ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const after =
|
||||||
|
operation === 'updateMany' || operation === 'deleteMany'
|
||||||
|
? { count: (result as { count?: number } | null)?.count ?? null }
|
||||||
|
: operation.startsWith('delete')
|
||||||
|
? null
|
||||||
|
: result;
|
||||||
|
|
||||||
|
await writeAuditRow(pool, base, {
|
||||||
|
userId,
|
||||||
|
email,
|
||||||
|
roleId,
|
||||||
|
page: ctx?.page ?? '/system',
|
||||||
|
action: describeAction(operation),
|
||||||
|
entity: model,
|
||||||
|
entityId: entityIdFor(args, result, before),
|
||||||
|
before: sanitizeAuditValue(before),
|
||||||
|
after: sanitizeAuditValue(after),
|
||||||
|
ip: ctx?.ip ?? null
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to write audit log:', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_prisma = extended as unknown as PrismaClient;
|
||||||
return _prisma;
|
return _prisma;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,11 @@ export type Verification = Prisma.VerificationModel
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type Proclamation = Prisma.ProclamationModel
|
export type Proclamation = Prisma.ProclamationModel
|
||||||
|
/**
|
||||||
|
* Model ProclamationGroup
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type ProclamationGroup = Prisma.ProclamationGroupModel
|
||||||
/**
|
/**
|
||||||
* Model UserProclamations
|
* Model UserProclamations
|
||||||
*
|
*
|
||||||
@@ -72,11 +77,6 @@ export type SolfeggioFrequency = Prisma.SolfeggioFrequencyModel
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type ProclamationCategory = Prisma.ProclamationCategoryModel
|
export type ProclamationCategory = Prisma.ProclamationCategoryModel
|
||||||
/**
|
|
||||||
* Model ProclamationCategorySubscriptionLevel
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export type ProclamationCategorySubscriptionLevel = Prisma.ProclamationCategorySubscriptionLevelModel
|
|
||||||
/**
|
/**
|
||||||
* Model LicenseKey
|
* Model LicenseKey
|
||||||
*
|
*
|
||||||
@@ -87,13 +87,13 @@ export type LicenseKey = Prisma.LicenseKeyModel
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type ApiKey = Prisma.ApiKeyModel
|
export type ApiKey = Prisma.ApiKeyModel
|
||||||
/**
|
|
||||||
* Model SubscriptionLevel
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export type SubscriptionLevel = Prisma.SubscriptionLevelModel
|
|
||||||
/**
|
/**
|
||||||
* Model ColorPalette
|
* Model ColorPalette
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type ColorPalette = Prisma.ColorPaletteModel
|
export type ColorPalette = Prisma.ColorPaletteModel
|
||||||
|
/**
|
||||||
|
* Model AuditLog
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type AuditLog = Prisma.AuditLogModel
|
||||||
|
|||||||
@@ -81,6 +81,11 @@ export type Verification = Prisma.VerificationModel
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type Proclamation = Prisma.ProclamationModel
|
export type Proclamation = Prisma.ProclamationModel
|
||||||
|
/**
|
||||||
|
* Model ProclamationGroup
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type ProclamationGroup = Prisma.ProclamationGroupModel
|
||||||
/**
|
/**
|
||||||
* Model UserProclamations
|
* Model UserProclamations
|
||||||
*
|
*
|
||||||
@@ -96,11 +101,6 @@ export type SolfeggioFrequency = Prisma.SolfeggioFrequencyModel
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type ProclamationCategory = Prisma.ProclamationCategoryModel
|
export type ProclamationCategory = Prisma.ProclamationCategoryModel
|
||||||
/**
|
|
||||||
* Model ProclamationCategorySubscriptionLevel
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export type ProclamationCategorySubscriptionLevel = Prisma.ProclamationCategorySubscriptionLevelModel
|
|
||||||
/**
|
/**
|
||||||
* Model LicenseKey
|
* Model LicenseKey
|
||||||
*
|
*
|
||||||
@@ -111,13 +111,13 @@ export type LicenseKey = Prisma.LicenseKeyModel
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type ApiKey = Prisma.ApiKeyModel
|
export type ApiKey = Prisma.ApiKeyModel
|
||||||
/**
|
|
||||||
* Model SubscriptionLevel
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export type SubscriptionLevel = Prisma.SubscriptionLevelModel
|
|
||||||
/**
|
/**
|
||||||
* Model ColorPalette
|
* Model ColorPalette
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type ColorPalette = Prisma.ColorPaletteModel
|
export type ColorPalette = Prisma.ColorPaletteModel
|
||||||
|
/**
|
||||||
|
* Model AuditLog
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type AuditLog = Prisma.AuditLogModel
|
||||||
|
|||||||
@@ -253,6 +253,57 @@ export type JsonWithAggregatesFilterBase<$PrismaModel = never> = {
|
|||||||
_max?: Prisma.NestedJsonFilter<$PrismaModel>
|
_max?: Prisma.NestedJsonFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type JsonNullableFilter<$PrismaModel = never> =
|
||||||
|
| Prisma.PatchUndefined<
|
||||||
|
Prisma.Either<Required<JsonNullableFilterBase<$PrismaModel>>, Exclude<keyof Required<JsonNullableFilterBase<$PrismaModel>>, 'path'>>,
|
||||||
|
Required<JsonNullableFilterBase<$PrismaModel>>
|
||||||
|
>
|
||||||
|
| Prisma.OptionalFlat<Omit<Required<JsonNullableFilterBase<$PrismaModel>>, 'path'>>
|
||||||
|
|
||||||
|
export type JsonNullableFilterBase<$PrismaModel = never> = {
|
||||||
|
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||||
|
path?: string[]
|
||||||
|
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
|
||||||
|
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||||
|
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||||
|
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||||
|
lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||||
|
lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||||
|
gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||||
|
gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||||
|
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
export type JsonNullableWithAggregatesFilter<$PrismaModel = never> =
|
||||||
|
| Prisma.PatchUndefined<
|
||||||
|
Prisma.Either<Required<JsonNullableWithAggregatesFilterBase<$PrismaModel>>, Exclude<keyof Required<JsonNullableWithAggregatesFilterBase<$PrismaModel>>, 'path'>>,
|
||||||
|
Required<JsonNullableWithAggregatesFilterBase<$PrismaModel>>
|
||||||
|
>
|
||||||
|
| Prisma.OptionalFlat<Omit<Required<JsonNullableWithAggregatesFilterBase<$PrismaModel>>, 'path'>>
|
||||||
|
|
||||||
|
export type JsonNullableWithAggregatesFilterBase<$PrismaModel = never> = {
|
||||||
|
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||||
|
path?: string[]
|
||||||
|
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
|
||||||
|
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||||
|
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||||
|
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||||
|
lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||||
|
lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||||
|
gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||||
|
gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||||
|
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||||
|
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedJsonNullableFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedJsonNullableFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
export type NestedStringFilter<$PrismaModel = never> = {
|
export type NestedStringFilter<$PrismaModel = never> = {
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
@@ -478,4 +529,28 @@ export type NestedJsonFilterBase<$PrismaModel = never> = {
|
|||||||
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type NestedJsonNullableFilter<$PrismaModel = never> =
|
||||||
|
| Prisma.PatchUndefined<
|
||||||
|
Prisma.Either<Required<NestedJsonNullableFilterBase<$PrismaModel>>, Exclude<keyof Required<NestedJsonNullableFilterBase<$PrismaModel>>, 'path'>>,
|
||||||
|
Required<NestedJsonNullableFilterBase<$PrismaModel>>
|
||||||
|
>
|
||||||
|
| Prisma.OptionalFlat<Omit<Required<NestedJsonNullableFilterBase<$PrismaModel>>, 'path'>>
|
||||||
|
|
||||||
|
export type NestedJsonNullableFilterBase<$PrismaModel = never> = {
|
||||||
|
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||||
|
path?: string[]
|
||||||
|
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
|
||||||
|
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||||
|
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||||
|
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||||
|
lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||||
|
lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||||
|
gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||||
|
gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||||
|
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -392,14 +392,14 @@ export const ModelName = {
|
|||||||
UserRole: 'UserRole',
|
UserRole: 'UserRole',
|
||||||
Verification: 'Verification',
|
Verification: 'Verification',
|
||||||
Proclamation: 'Proclamation',
|
Proclamation: 'Proclamation',
|
||||||
|
ProclamationGroup: 'ProclamationGroup',
|
||||||
UserProclamations: 'UserProclamations',
|
UserProclamations: 'UserProclamations',
|
||||||
SolfeggioFrequency: 'SolfeggioFrequency',
|
SolfeggioFrequency: 'SolfeggioFrequency',
|
||||||
ProclamationCategory: 'ProclamationCategory',
|
ProclamationCategory: 'ProclamationCategory',
|
||||||
ProclamationCategorySubscriptionLevel: 'ProclamationCategorySubscriptionLevel',
|
|
||||||
LicenseKey: 'LicenseKey',
|
LicenseKey: 'LicenseKey',
|
||||||
ApiKey: 'ApiKey',
|
ApiKey: 'ApiKey',
|
||||||
SubscriptionLevel: 'SubscriptionLevel',
|
ColorPalette: 'ColorPalette',
|
||||||
ColorPalette: 'ColorPalette'
|
AuditLog: 'AuditLog'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type ModelName = (typeof ModelName)[keyof typeof ModelName]
|
export type ModelName = (typeof ModelName)[keyof typeof ModelName]
|
||||||
@@ -415,7 +415,7 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
|
|||||||
omit: GlobalOmitOptions
|
omit: GlobalOmitOptions
|
||||||
}
|
}
|
||||||
meta: {
|
meta: {
|
||||||
modelProps: "user" | "profile" | "address" | "session" | "account" | "userRole" | "verification" | "proclamation" | "userProclamations" | "solfeggioFrequency" | "proclamationCategory" | "proclamationCategorySubscriptionLevel" | "licenseKey" | "apiKey" | "subscriptionLevel" | "colorPalette"
|
modelProps: "user" | "profile" | "address" | "session" | "account" | "userRole" | "verification" | "proclamation" | "proclamationGroup" | "userProclamations" | "solfeggioFrequency" | "proclamationCategory" | "licenseKey" | "apiKey" | "colorPalette" | "auditLog"
|
||||||
txIsolationLevel: TransactionIsolationLevel
|
txIsolationLevel: TransactionIsolationLevel
|
||||||
}
|
}
|
||||||
model: {
|
model: {
|
||||||
@@ -1011,6 +1011,80 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ProclamationGroup: {
|
||||||
|
payload: Prisma.$ProclamationGroupPayload<ExtArgs>
|
||||||
|
fields: Prisma.ProclamationGroupFieldRefs
|
||||||
|
operations: {
|
||||||
|
findUnique: {
|
||||||
|
args: Prisma.ProclamationGroupFindUniqueArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProclamationGroupPayload> | null
|
||||||
|
}
|
||||||
|
findUniqueOrThrow: {
|
||||||
|
args: Prisma.ProclamationGroupFindUniqueOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProclamationGroupPayload>
|
||||||
|
}
|
||||||
|
findFirst: {
|
||||||
|
args: Prisma.ProclamationGroupFindFirstArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProclamationGroupPayload> | null
|
||||||
|
}
|
||||||
|
findFirstOrThrow: {
|
||||||
|
args: Prisma.ProclamationGroupFindFirstOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProclamationGroupPayload>
|
||||||
|
}
|
||||||
|
findMany: {
|
||||||
|
args: Prisma.ProclamationGroupFindManyArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProclamationGroupPayload>[]
|
||||||
|
}
|
||||||
|
create: {
|
||||||
|
args: Prisma.ProclamationGroupCreateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProclamationGroupPayload>
|
||||||
|
}
|
||||||
|
createMany: {
|
||||||
|
args: Prisma.ProclamationGroupCreateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
createManyAndReturn: {
|
||||||
|
args: Prisma.ProclamationGroupCreateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProclamationGroupPayload>[]
|
||||||
|
}
|
||||||
|
delete: {
|
||||||
|
args: Prisma.ProclamationGroupDeleteArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProclamationGroupPayload>
|
||||||
|
}
|
||||||
|
update: {
|
||||||
|
args: Prisma.ProclamationGroupUpdateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProclamationGroupPayload>
|
||||||
|
}
|
||||||
|
deleteMany: {
|
||||||
|
args: Prisma.ProclamationGroupDeleteManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateMany: {
|
||||||
|
args: Prisma.ProclamationGroupUpdateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateManyAndReturn: {
|
||||||
|
args: Prisma.ProclamationGroupUpdateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProclamationGroupPayload>[]
|
||||||
|
}
|
||||||
|
upsert: {
|
||||||
|
args: Prisma.ProclamationGroupUpsertArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProclamationGroupPayload>
|
||||||
|
}
|
||||||
|
aggregate: {
|
||||||
|
args: Prisma.ProclamationGroupAggregateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.AggregateProclamationGroup>
|
||||||
|
}
|
||||||
|
groupBy: {
|
||||||
|
args: Prisma.ProclamationGroupGroupByArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.ProclamationGroupGroupByOutputType>[]
|
||||||
|
}
|
||||||
|
count: {
|
||||||
|
args: Prisma.ProclamationGroupCountArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.ProclamationGroupCountAggregateOutputType> | number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
UserProclamations: {
|
UserProclamations: {
|
||||||
payload: Prisma.$UserProclamationsPayload<ExtArgs>
|
payload: Prisma.$UserProclamationsPayload<ExtArgs>
|
||||||
fields: Prisma.UserProclamationsFieldRefs
|
fields: Prisma.UserProclamationsFieldRefs
|
||||||
@@ -1233,80 +1307,6 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ProclamationCategorySubscriptionLevel: {
|
|
||||||
payload: Prisma.$ProclamationCategorySubscriptionLevelPayload<ExtArgs>
|
|
||||||
fields: Prisma.ProclamationCategorySubscriptionLevelFieldRefs
|
|
||||||
operations: {
|
|
||||||
findUnique: {
|
|
||||||
args: Prisma.ProclamationCategorySubscriptionLevelFindUniqueArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProclamationCategorySubscriptionLevelPayload> | null
|
|
||||||
}
|
|
||||||
findUniqueOrThrow: {
|
|
||||||
args: Prisma.ProclamationCategorySubscriptionLevelFindUniqueOrThrowArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProclamationCategorySubscriptionLevelPayload>
|
|
||||||
}
|
|
||||||
findFirst: {
|
|
||||||
args: Prisma.ProclamationCategorySubscriptionLevelFindFirstArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProclamationCategorySubscriptionLevelPayload> | null
|
|
||||||
}
|
|
||||||
findFirstOrThrow: {
|
|
||||||
args: Prisma.ProclamationCategorySubscriptionLevelFindFirstOrThrowArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProclamationCategorySubscriptionLevelPayload>
|
|
||||||
}
|
|
||||||
findMany: {
|
|
||||||
args: Prisma.ProclamationCategorySubscriptionLevelFindManyArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProclamationCategorySubscriptionLevelPayload>[]
|
|
||||||
}
|
|
||||||
create: {
|
|
||||||
args: Prisma.ProclamationCategorySubscriptionLevelCreateArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProclamationCategorySubscriptionLevelPayload>
|
|
||||||
}
|
|
||||||
createMany: {
|
|
||||||
args: Prisma.ProclamationCategorySubscriptionLevelCreateManyArgs<ExtArgs>
|
|
||||||
result: BatchPayload
|
|
||||||
}
|
|
||||||
createManyAndReturn: {
|
|
||||||
args: Prisma.ProclamationCategorySubscriptionLevelCreateManyAndReturnArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProclamationCategorySubscriptionLevelPayload>[]
|
|
||||||
}
|
|
||||||
delete: {
|
|
||||||
args: Prisma.ProclamationCategorySubscriptionLevelDeleteArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProclamationCategorySubscriptionLevelPayload>
|
|
||||||
}
|
|
||||||
update: {
|
|
||||||
args: Prisma.ProclamationCategorySubscriptionLevelUpdateArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProclamationCategorySubscriptionLevelPayload>
|
|
||||||
}
|
|
||||||
deleteMany: {
|
|
||||||
args: Prisma.ProclamationCategorySubscriptionLevelDeleteManyArgs<ExtArgs>
|
|
||||||
result: BatchPayload
|
|
||||||
}
|
|
||||||
updateMany: {
|
|
||||||
args: Prisma.ProclamationCategorySubscriptionLevelUpdateManyArgs<ExtArgs>
|
|
||||||
result: BatchPayload
|
|
||||||
}
|
|
||||||
updateManyAndReturn: {
|
|
||||||
args: Prisma.ProclamationCategorySubscriptionLevelUpdateManyAndReturnArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProclamationCategorySubscriptionLevelPayload>[]
|
|
||||||
}
|
|
||||||
upsert: {
|
|
||||||
args: Prisma.ProclamationCategorySubscriptionLevelUpsertArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProclamationCategorySubscriptionLevelPayload>
|
|
||||||
}
|
|
||||||
aggregate: {
|
|
||||||
args: Prisma.ProclamationCategorySubscriptionLevelAggregateArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.Optional<Prisma.AggregateProclamationCategorySubscriptionLevel>
|
|
||||||
}
|
|
||||||
groupBy: {
|
|
||||||
args: Prisma.ProclamationCategorySubscriptionLevelGroupByArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.Optional<Prisma.ProclamationCategorySubscriptionLevelGroupByOutputType>[]
|
|
||||||
}
|
|
||||||
count: {
|
|
||||||
args: Prisma.ProclamationCategorySubscriptionLevelCountArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.Optional<Prisma.ProclamationCategorySubscriptionLevelCountAggregateOutputType> | number
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LicenseKey: {
|
LicenseKey: {
|
||||||
payload: Prisma.$LicenseKeyPayload<ExtArgs>
|
payload: Prisma.$LicenseKeyPayload<ExtArgs>
|
||||||
fields: Prisma.LicenseKeyFieldRefs
|
fields: Prisma.LicenseKeyFieldRefs
|
||||||
@@ -1455,80 +1455,6 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SubscriptionLevel: {
|
|
||||||
payload: Prisma.$SubscriptionLevelPayload<ExtArgs>
|
|
||||||
fields: Prisma.SubscriptionLevelFieldRefs
|
|
||||||
operations: {
|
|
||||||
findUnique: {
|
|
||||||
args: Prisma.SubscriptionLevelFindUniqueArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$SubscriptionLevelPayload> | null
|
|
||||||
}
|
|
||||||
findUniqueOrThrow: {
|
|
||||||
args: Prisma.SubscriptionLevelFindUniqueOrThrowArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$SubscriptionLevelPayload>
|
|
||||||
}
|
|
||||||
findFirst: {
|
|
||||||
args: Prisma.SubscriptionLevelFindFirstArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$SubscriptionLevelPayload> | null
|
|
||||||
}
|
|
||||||
findFirstOrThrow: {
|
|
||||||
args: Prisma.SubscriptionLevelFindFirstOrThrowArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$SubscriptionLevelPayload>
|
|
||||||
}
|
|
||||||
findMany: {
|
|
||||||
args: Prisma.SubscriptionLevelFindManyArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$SubscriptionLevelPayload>[]
|
|
||||||
}
|
|
||||||
create: {
|
|
||||||
args: Prisma.SubscriptionLevelCreateArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$SubscriptionLevelPayload>
|
|
||||||
}
|
|
||||||
createMany: {
|
|
||||||
args: Prisma.SubscriptionLevelCreateManyArgs<ExtArgs>
|
|
||||||
result: BatchPayload
|
|
||||||
}
|
|
||||||
createManyAndReturn: {
|
|
||||||
args: Prisma.SubscriptionLevelCreateManyAndReturnArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$SubscriptionLevelPayload>[]
|
|
||||||
}
|
|
||||||
delete: {
|
|
||||||
args: Prisma.SubscriptionLevelDeleteArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$SubscriptionLevelPayload>
|
|
||||||
}
|
|
||||||
update: {
|
|
||||||
args: Prisma.SubscriptionLevelUpdateArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$SubscriptionLevelPayload>
|
|
||||||
}
|
|
||||||
deleteMany: {
|
|
||||||
args: Prisma.SubscriptionLevelDeleteManyArgs<ExtArgs>
|
|
||||||
result: BatchPayload
|
|
||||||
}
|
|
||||||
updateMany: {
|
|
||||||
args: Prisma.SubscriptionLevelUpdateManyArgs<ExtArgs>
|
|
||||||
result: BatchPayload
|
|
||||||
}
|
|
||||||
updateManyAndReturn: {
|
|
||||||
args: Prisma.SubscriptionLevelUpdateManyAndReturnArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$SubscriptionLevelPayload>[]
|
|
||||||
}
|
|
||||||
upsert: {
|
|
||||||
args: Prisma.SubscriptionLevelUpsertArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$SubscriptionLevelPayload>
|
|
||||||
}
|
|
||||||
aggregate: {
|
|
||||||
args: Prisma.SubscriptionLevelAggregateArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.Optional<Prisma.AggregateSubscriptionLevel>
|
|
||||||
}
|
|
||||||
groupBy: {
|
|
||||||
args: Prisma.SubscriptionLevelGroupByArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.Optional<Prisma.SubscriptionLevelGroupByOutputType>[]
|
|
||||||
}
|
|
||||||
count: {
|
|
||||||
args: Prisma.SubscriptionLevelCountArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.Optional<Prisma.SubscriptionLevelCountAggregateOutputType> | number
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ColorPalette: {
|
ColorPalette: {
|
||||||
payload: Prisma.$ColorPalettePayload<ExtArgs>
|
payload: Prisma.$ColorPalettePayload<ExtArgs>
|
||||||
fields: Prisma.ColorPaletteFieldRefs
|
fields: Prisma.ColorPaletteFieldRefs
|
||||||
@@ -1603,6 +1529,80 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
AuditLog: {
|
||||||
|
payload: Prisma.$AuditLogPayload<ExtArgs>
|
||||||
|
fields: Prisma.AuditLogFieldRefs
|
||||||
|
operations: {
|
||||||
|
findUnique: {
|
||||||
|
args: Prisma.AuditLogFindUniqueArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AuditLogPayload> | null
|
||||||
|
}
|
||||||
|
findUniqueOrThrow: {
|
||||||
|
args: Prisma.AuditLogFindUniqueOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AuditLogPayload>
|
||||||
|
}
|
||||||
|
findFirst: {
|
||||||
|
args: Prisma.AuditLogFindFirstArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AuditLogPayload> | null
|
||||||
|
}
|
||||||
|
findFirstOrThrow: {
|
||||||
|
args: Prisma.AuditLogFindFirstOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AuditLogPayload>
|
||||||
|
}
|
||||||
|
findMany: {
|
||||||
|
args: Prisma.AuditLogFindManyArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AuditLogPayload>[]
|
||||||
|
}
|
||||||
|
create: {
|
||||||
|
args: Prisma.AuditLogCreateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AuditLogPayload>
|
||||||
|
}
|
||||||
|
createMany: {
|
||||||
|
args: Prisma.AuditLogCreateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
createManyAndReturn: {
|
||||||
|
args: Prisma.AuditLogCreateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AuditLogPayload>[]
|
||||||
|
}
|
||||||
|
delete: {
|
||||||
|
args: Prisma.AuditLogDeleteArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AuditLogPayload>
|
||||||
|
}
|
||||||
|
update: {
|
||||||
|
args: Prisma.AuditLogUpdateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AuditLogPayload>
|
||||||
|
}
|
||||||
|
deleteMany: {
|
||||||
|
args: Prisma.AuditLogDeleteManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateMany: {
|
||||||
|
args: Prisma.AuditLogUpdateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateManyAndReturn: {
|
||||||
|
args: Prisma.AuditLogUpdateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AuditLogPayload>[]
|
||||||
|
}
|
||||||
|
upsert: {
|
||||||
|
args: Prisma.AuditLogUpsertArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AuditLogPayload>
|
||||||
|
}
|
||||||
|
aggregate: {
|
||||||
|
args: Prisma.AuditLogAggregateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.AggregateAuditLog>
|
||||||
|
}
|
||||||
|
groupBy: {
|
||||||
|
args: Prisma.AuditLogGroupByArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.AuditLogGroupByOutputType>[]
|
||||||
|
}
|
||||||
|
count: {
|
||||||
|
args: Prisma.AuditLogCountArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.AuditLogCountAggregateOutputType> | number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} & {
|
} & {
|
||||||
other: {
|
other: {
|
||||||
@@ -1659,6 +1659,7 @@ export type UserScalarFieldEnum = (typeof UserScalarFieldEnum)[keyof typeof User
|
|||||||
export const ProfileScalarFieldEnum = {
|
export const ProfileScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
userId: 'userId',
|
userId: 'userId',
|
||||||
|
email: 'email',
|
||||||
firstName: 'firstName',
|
firstName: 'firstName',
|
||||||
lastName: 'lastName',
|
lastName: 'lastName',
|
||||||
phoneNumber: 'phoneNumber',
|
phoneNumber: 'phoneNumber',
|
||||||
@@ -1744,12 +1745,21 @@ export const ProclamationScalarFieldEnum = {
|
|||||||
content: 'content',
|
content: 'content',
|
||||||
solfeggioFrequencyId: 'solfeggioFrequencyId',
|
solfeggioFrequencyId: 'solfeggioFrequencyId',
|
||||||
categoryId: 'categoryId',
|
categoryId: 'categoryId',
|
||||||
subscriptionLevelId: 'subscriptionLevelId'
|
groupId: 'groupId'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type ProclamationScalarFieldEnum = (typeof ProclamationScalarFieldEnum)[keyof typeof ProclamationScalarFieldEnum]
|
export type ProclamationScalarFieldEnum = (typeof ProclamationScalarFieldEnum)[keyof typeof ProclamationScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const ProclamationGroupScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
name: 'name',
|
||||||
|
sortOrder: 'sortOrder'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type ProclamationGroupScalarFieldEnum = (typeof ProclamationGroupScalarFieldEnum)[keyof typeof ProclamationGroupScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const UserProclamationsScalarFieldEnum = {
|
export const UserProclamationsScalarFieldEnum = {
|
||||||
userId: 'userId',
|
userId: 'userId',
|
||||||
proclamationId: 'proclamationId',
|
proclamationId: 'proclamationId',
|
||||||
@@ -1772,27 +1782,20 @@ export type SolfeggioFrequencyScalarFieldEnum = (typeof SolfeggioFrequencyScalar
|
|||||||
export const ProclamationCategoryScalarFieldEnum = {
|
export const ProclamationCategoryScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
name: 'name',
|
name: 'name',
|
||||||
sortOrder: 'sortOrder',
|
sortOrder: 'sortOrder'
|
||||||
parentId: 'parentId'
|
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type ProclamationCategoryScalarFieldEnum = (typeof ProclamationCategoryScalarFieldEnum)[keyof typeof ProclamationCategoryScalarFieldEnum]
|
export type ProclamationCategoryScalarFieldEnum = (typeof ProclamationCategoryScalarFieldEnum)[keyof typeof ProclamationCategoryScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const ProclamationCategorySubscriptionLevelScalarFieldEnum = {
|
|
||||||
categoryId: 'categoryId',
|
|
||||||
subscriptionLevelId: 'subscriptionLevelId'
|
|
||||||
} as const
|
|
||||||
|
|
||||||
export type ProclamationCategorySubscriptionLevelScalarFieldEnum = (typeof ProclamationCategorySubscriptionLevelScalarFieldEnum)[keyof typeof ProclamationCategorySubscriptionLevelScalarFieldEnum]
|
|
||||||
|
|
||||||
|
|
||||||
export const LicenseKeyScalarFieldEnum = {
|
export const LicenseKeyScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
key: 'key',
|
key: 'key',
|
||||||
active: 'active',
|
active: 'active',
|
||||||
level: 'level',
|
createdAt: 'createdAt',
|
||||||
userId: 'userId'
|
lastValidatedAt: 'lastValidatedAt',
|
||||||
|
userId: 'userId',
|
||||||
|
email: 'email'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type LicenseKeyScalarFieldEnum = (typeof LicenseKeyScalarFieldEnum)[keyof typeof LicenseKeyScalarFieldEnum]
|
export type LicenseKeyScalarFieldEnum = (typeof LicenseKeyScalarFieldEnum)[keyof typeof LicenseKeyScalarFieldEnum]
|
||||||
@@ -1810,15 +1813,6 @@ export const ApiKeyScalarFieldEnum = {
|
|||||||
export type ApiKeyScalarFieldEnum = (typeof ApiKeyScalarFieldEnum)[keyof typeof ApiKeyScalarFieldEnum]
|
export type ApiKeyScalarFieldEnum = (typeof ApiKeyScalarFieldEnum)[keyof typeof ApiKeyScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const SubscriptionLevelScalarFieldEnum = {
|
|
||||||
id: 'id',
|
|
||||||
name: 'name',
|
|
||||||
description: 'description'
|
|
||||||
} as const
|
|
||||||
|
|
||||||
export type SubscriptionLevelScalarFieldEnum = (typeof SubscriptionLevelScalarFieldEnum)[keyof typeof SubscriptionLevelScalarFieldEnum]
|
|
||||||
|
|
||||||
|
|
||||||
export const ColorPaletteScalarFieldEnum = {
|
export const ColorPaletteScalarFieldEnum = {
|
||||||
solfeggioFrequencyId: 'solfeggioFrequencyId',
|
solfeggioFrequencyId: 'solfeggioFrequencyId',
|
||||||
colors: 'colors'
|
colors: 'colors'
|
||||||
@@ -1827,6 +1821,25 @@ export const ColorPaletteScalarFieldEnum = {
|
|||||||
export type ColorPaletteScalarFieldEnum = (typeof ColorPaletteScalarFieldEnum)[keyof typeof ColorPaletteScalarFieldEnum]
|
export type ColorPaletteScalarFieldEnum = (typeof ColorPaletteScalarFieldEnum)[keyof typeof ColorPaletteScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const AuditLogScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
userId: 'userId',
|
||||||
|
email: 'email',
|
||||||
|
roleId: 'roleId',
|
||||||
|
roleName: 'roleName',
|
||||||
|
page: 'page',
|
||||||
|
action: 'action',
|
||||||
|
entity: 'entity',
|
||||||
|
entityId: 'entityId',
|
||||||
|
before: 'before',
|
||||||
|
after: 'after',
|
||||||
|
ip: 'ip'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type AuditLogScalarFieldEnum = (typeof AuditLogScalarFieldEnum)[keyof typeof AuditLogScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const SortOrder = {
|
export const SortOrder = {
|
||||||
asc: 'asc',
|
asc: 'asc',
|
||||||
desc: 'desc'
|
desc: 'desc'
|
||||||
@@ -1842,6 +1855,14 @@ export const JsonNullValueInput = {
|
|||||||
export type JsonNullValueInput = (typeof JsonNullValueInput)[keyof typeof JsonNullValueInput]
|
export type JsonNullValueInput = (typeof JsonNullValueInput)[keyof typeof JsonNullValueInput]
|
||||||
|
|
||||||
|
|
||||||
|
export const NullableJsonNullValueInput = {
|
||||||
|
DbNull: DbNull,
|
||||||
|
JsonNull: JsonNull
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type NullableJsonNullValueInput = (typeof NullableJsonNullValueInput)[keyof typeof NullableJsonNullValueInput]
|
||||||
|
|
||||||
|
|
||||||
export const QueryMode = {
|
export const QueryMode = {
|
||||||
default: 'default',
|
default: 'default',
|
||||||
insensitive: 'insensitive'
|
insensitive: 'insensitive'
|
||||||
@@ -2067,14 +2088,14 @@ export type GlobalOmitConfig = {
|
|||||||
userRole?: Prisma.UserRoleOmit
|
userRole?: Prisma.UserRoleOmit
|
||||||
verification?: Prisma.VerificationOmit
|
verification?: Prisma.VerificationOmit
|
||||||
proclamation?: Prisma.ProclamationOmit
|
proclamation?: Prisma.ProclamationOmit
|
||||||
|
proclamationGroup?: Prisma.ProclamationGroupOmit
|
||||||
userProclamations?: Prisma.UserProclamationsOmit
|
userProclamations?: Prisma.UserProclamationsOmit
|
||||||
solfeggioFrequency?: Prisma.SolfeggioFrequencyOmit
|
solfeggioFrequency?: Prisma.SolfeggioFrequencyOmit
|
||||||
proclamationCategory?: Prisma.ProclamationCategoryOmit
|
proclamationCategory?: Prisma.ProclamationCategoryOmit
|
||||||
proclamationCategorySubscriptionLevel?: Prisma.ProclamationCategorySubscriptionLevelOmit
|
|
||||||
licenseKey?: Prisma.LicenseKeyOmit
|
licenseKey?: Prisma.LicenseKeyOmit
|
||||||
apiKey?: Prisma.ApiKeyOmit
|
apiKey?: Prisma.ApiKeyOmit
|
||||||
subscriptionLevel?: Prisma.SubscriptionLevelOmit
|
|
||||||
colorPalette?: Prisma.ColorPaletteOmit
|
colorPalette?: Prisma.ColorPaletteOmit
|
||||||
|
auditLog?: Prisma.AuditLogOmit
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Types for Logging */
|
/* Types for Logging */
|
||||||
|
|||||||
@@ -59,14 +59,14 @@ export const ModelName = {
|
|||||||
UserRole: 'UserRole',
|
UserRole: 'UserRole',
|
||||||
Verification: 'Verification',
|
Verification: 'Verification',
|
||||||
Proclamation: 'Proclamation',
|
Proclamation: 'Proclamation',
|
||||||
|
ProclamationGroup: 'ProclamationGroup',
|
||||||
UserProclamations: 'UserProclamations',
|
UserProclamations: 'UserProclamations',
|
||||||
SolfeggioFrequency: 'SolfeggioFrequency',
|
SolfeggioFrequency: 'SolfeggioFrequency',
|
||||||
ProclamationCategory: 'ProclamationCategory',
|
ProclamationCategory: 'ProclamationCategory',
|
||||||
ProclamationCategorySubscriptionLevel: 'ProclamationCategorySubscriptionLevel',
|
|
||||||
LicenseKey: 'LicenseKey',
|
LicenseKey: 'LicenseKey',
|
||||||
ApiKey: 'ApiKey',
|
ApiKey: 'ApiKey',
|
||||||
SubscriptionLevel: 'SubscriptionLevel',
|
ColorPalette: 'ColorPalette',
|
||||||
ColorPalette: 'ColorPalette'
|
AuditLog: 'AuditLog'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type ModelName = (typeof ModelName)[keyof typeof ModelName]
|
export type ModelName = (typeof ModelName)[keyof typeof ModelName]
|
||||||
@@ -102,6 +102,7 @@ export type UserScalarFieldEnum = (typeof UserScalarFieldEnum)[keyof typeof User
|
|||||||
export const ProfileScalarFieldEnum = {
|
export const ProfileScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
userId: 'userId',
|
userId: 'userId',
|
||||||
|
email: 'email',
|
||||||
firstName: 'firstName',
|
firstName: 'firstName',
|
||||||
lastName: 'lastName',
|
lastName: 'lastName',
|
||||||
phoneNumber: 'phoneNumber',
|
phoneNumber: 'phoneNumber',
|
||||||
@@ -187,12 +188,21 @@ export const ProclamationScalarFieldEnum = {
|
|||||||
content: 'content',
|
content: 'content',
|
||||||
solfeggioFrequencyId: 'solfeggioFrequencyId',
|
solfeggioFrequencyId: 'solfeggioFrequencyId',
|
||||||
categoryId: 'categoryId',
|
categoryId: 'categoryId',
|
||||||
subscriptionLevelId: 'subscriptionLevelId'
|
groupId: 'groupId'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type ProclamationScalarFieldEnum = (typeof ProclamationScalarFieldEnum)[keyof typeof ProclamationScalarFieldEnum]
|
export type ProclamationScalarFieldEnum = (typeof ProclamationScalarFieldEnum)[keyof typeof ProclamationScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const ProclamationGroupScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
name: 'name',
|
||||||
|
sortOrder: 'sortOrder'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type ProclamationGroupScalarFieldEnum = (typeof ProclamationGroupScalarFieldEnum)[keyof typeof ProclamationGroupScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const UserProclamationsScalarFieldEnum = {
|
export const UserProclamationsScalarFieldEnum = {
|
||||||
userId: 'userId',
|
userId: 'userId',
|
||||||
proclamationId: 'proclamationId',
|
proclamationId: 'proclamationId',
|
||||||
@@ -215,27 +225,20 @@ export type SolfeggioFrequencyScalarFieldEnum = (typeof SolfeggioFrequencyScalar
|
|||||||
export const ProclamationCategoryScalarFieldEnum = {
|
export const ProclamationCategoryScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
name: 'name',
|
name: 'name',
|
||||||
sortOrder: 'sortOrder',
|
sortOrder: 'sortOrder'
|
||||||
parentId: 'parentId'
|
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type ProclamationCategoryScalarFieldEnum = (typeof ProclamationCategoryScalarFieldEnum)[keyof typeof ProclamationCategoryScalarFieldEnum]
|
export type ProclamationCategoryScalarFieldEnum = (typeof ProclamationCategoryScalarFieldEnum)[keyof typeof ProclamationCategoryScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const ProclamationCategorySubscriptionLevelScalarFieldEnum = {
|
|
||||||
categoryId: 'categoryId',
|
|
||||||
subscriptionLevelId: 'subscriptionLevelId'
|
|
||||||
} as const
|
|
||||||
|
|
||||||
export type ProclamationCategorySubscriptionLevelScalarFieldEnum = (typeof ProclamationCategorySubscriptionLevelScalarFieldEnum)[keyof typeof ProclamationCategorySubscriptionLevelScalarFieldEnum]
|
|
||||||
|
|
||||||
|
|
||||||
export const LicenseKeyScalarFieldEnum = {
|
export const LicenseKeyScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
key: 'key',
|
key: 'key',
|
||||||
active: 'active',
|
active: 'active',
|
||||||
level: 'level',
|
createdAt: 'createdAt',
|
||||||
userId: 'userId'
|
lastValidatedAt: 'lastValidatedAt',
|
||||||
|
userId: 'userId',
|
||||||
|
email: 'email'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type LicenseKeyScalarFieldEnum = (typeof LicenseKeyScalarFieldEnum)[keyof typeof LicenseKeyScalarFieldEnum]
|
export type LicenseKeyScalarFieldEnum = (typeof LicenseKeyScalarFieldEnum)[keyof typeof LicenseKeyScalarFieldEnum]
|
||||||
@@ -253,15 +256,6 @@ export const ApiKeyScalarFieldEnum = {
|
|||||||
export type ApiKeyScalarFieldEnum = (typeof ApiKeyScalarFieldEnum)[keyof typeof ApiKeyScalarFieldEnum]
|
export type ApiKeyScalarFieldEnum = (typeof ApiKeyScalarFieldEnum)[keyof typeof ApiKeyScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const SubscriptionLevelScalarFieldEnum = {
|
|
||||||
id: 'id',
|
|
||||||
name: 'name',
|
|
||||||
description: 'description'
|
|
||||||
} as const
|
|
||||||
|
|
||||||
export type SubscriptionLevelScalarFieldEnum = (typeof SubscriptionLevelScalarFieldEnum)[keyof typeof SubscriptionLevelScalarFieldEnum]
|
|
||||||
|
|
||||||
|
|
||||||
export const ColorPaletteScalarFieldEnum = {
|
export const ColorPaletteScalarFieldEnum = {
|
||||||
solfeggioFrequencyId: 'solfeggioFrequencyId',
|
solfeggioFrequencyId: 'solfeggioFrequencyId',
|
||||||
colors: 'colors'
|
colors: 'colors'
|
||||||
@@ -270,6 +264,25 @@ export const ColorPaletteScalarFieldEnum = {
|
|||||||
export type ColorPaletteScalarFieldEnum = (typeof ColorPaletteScalarFieldEnum)[keyof typeof ColorPaletteScalarFieldEnum]
|
export type ColorPaletteScalarFieldEnum = (typeof ColorPaletteScalarFieldEnum)[keyof typeof ColorPaletteScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const AuditLogScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
userId: 'userId',
|
||||||
|
email: 'email',
|
||||||
|
roleId: 'roleId',
|
||||||
|
roleName: 'roleName',
|
||||||
|
page: 'page',
|
||||||
|
action: 'action',
|
||||||
|
entity: 'entity',
|
||||||
|
entityId: 'entityId',
|
||||||
|
before: 'before',
|
||||||
|
after: 'after',
|
||||||
|
ip: 'ip'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type AuditLogScalarFieldEnum = (typeof AuditLogScalarFieldEnum)[keyof typeof AuditLogScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const SortOrder = {
|
export const SortOrder = {
|
||||||
asc: 'asc',
|
asc: 'asc',
|
||||||
desc: 'desc'
|
desc: 'desc'
|
||||||
@@ -285,6 +298,14 @@ export const JsonNullValueInput = {
|
|||||||
export type JsonNullValueInput = (typeof JsonNullValueInput)[keyof typeof JsonNullValueInput]
|
export type JsonNullValueInput = (typeof JsonNullValueInput)[keyof typeof JsonNullValueInput]
|
||||||
|
|
||||||
|
|
||||||
|
export const NullableJsonNullValueInput = {
|
||||||
|
DbNull: DbNull,
|
||||||
|
JsonNull: JsonNull
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type NullableJsonNullValueInput = (typeof NullableJsonNullValueInput)[keyof typeof NullableJsonNullValueInput]
|
||||||
|
|
||||||
|
|
||||||
export const QueryMode = {
|
export const QueryMode = {
|
||||||
default: 'default',
|
default: 'default',
|
||||||
insensitive: 'insensitive'
|
insensitive: 'insensitive'
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ export type * from './models/Account.ts'
|
|||||||
export type * from './models/UserRole.ts'
|
export type * from './models/UserRole.ts'
|
||||||
export type * from './models/Verification.ts'
|
export type * from './models/Verification.ts'
|
||||||
export type * from './models/Proclamation.ts'
|
export type * from './models/Proclamation.ts'
|
||||||
|
export type * from './models/ProclamationGroup.ts'
|
||||||
export type * from './models/UserProclamations.ts'
|
export type * from './models/UserProclamations.ts'
|
||||||
export type * from './models/SolfeggioFrequency.ts'
|
export type * from './models/SolfeggioFrequency.ts'
|
||||||
export type * from './models/ProclamationCategory.ts'
|
export type * from './models/ProclamationCategory.ts'
|
||||||
export type * from './models/ProclamationCategorySubscriptionLevel.ts'
|
|
||||||
export type * from './models/LicenseKey.ts'
|
export type * from './models/LicenseKey.ts'
|
||||||
export type * from './models/ApiKey.ts'
|
export type * from './models/ApiKey.ts'
|
||||||
export type * from './models/SubscriptionLevel.ts'
|
|
||||||
export type * from './models/ColorPalette.ts'
|
export type * from './models/ColorPalette.ts'
|
||||||
|
export type * from './models/AuditLog.ts'
|
||||||
export type * from './commonInputTypes.ts'
|
export type * from './commonInputTypes.ts'
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -28,24 +28,30 @@ export type LicenseKeyMinAggregateOutputType = {
|
|||||||
id: string | null
|
id: string | null
|
||||||
key: string | null
|
key: string | null
|
||||||
active: boolean | null
|
active: boolean | null
|
||||||
level: string | null
|
createdAt: Date | null
|
||||||
|
lastValidatedAt: Date | null
|
||||||
userId: string | null
|
userId: string | null
|
||||||
|
email: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LicenseKeyMaxAggregateOutputType = {
|
export type LicenseKeyMaxAggregateOutputType = {
|
||||||
id: string | null
|
id: string | null
|
||||||
key: string | null
|
key: string | null
|
||||||
active: boolean | null
|
active: boolean | null
|
||||||
level: string | null
|
createdAt: Date | null
|
||||||
|
lastValidatedAt: Date | null
|
||||||
userId: string | null
|
userId: string | null
|
||||||
|
email: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LicenseKeyCountAggregateOutputType = {
|
export type LicenseKeyCountAggregateOutputType = {
|
||||||
id: number
|
id: number
|
||||||
key: number
|
key: number
|
||||||
active: number
|
active: number
|
||||||
level: number
|
createdAt: number
|
||||||
|
lastValidatedAt: number
|
||||||
userId: number
|
userId: number
|
||||||
|
email: number
|
||||||
_all: number
|
_all: number
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,24 +60,30 @@ export type LicenseKeyMinAggregateInputType = {
|
|||||||
id?: true
|
id?: true
|
||||||
key?: true
|
key?: true
|
||||||
active?: true
|
active?: true
|
||||||
level?: true
|
createdAt?: true
|
||||||
|
lastValidatedAt?: true
|
||||||
userId?: true
|
userId?: true
|
||||||
|
email?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LicenseKeyMaxAggregateInputType = {
|
export type LicenseKeyMaxAggregateInputType = {
|
||||||
id?: true
|
id?: true
|
||||||
key?: true
|
key?: true
|
||||||
active?: true
|
active?: true
|
||||||
level?: true
|
createdAt?: true
|
||||||
|
lastValidatedAt?: true
|
||||||
userId?: true
|
userId?: true
|
||||||
|
email?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LicenseKeyCountAggregateInputType = {
|
export type LicenseKeyCountAggregateInputType = {
|
||||||
id?: true
|
id?: true
|
||||||
key?: true
|
key?: true
|
||||||
active?: true
|
active?: true
|
||||||
level?: true
|
createdAt?: true
|
||||||
|
lastValidatedAt?: true
|
||||||
userId?: true
|
userId?: true
|
||||||
|
email?: true
|
||||||
_all?: true
|
_all?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,8 +163,10 @@ export type LicenseKeyGroupByOutputType = {
|
|||||||
id: string
|
id: string
|
||||||
key: string
|
key: string
|
||||||
active: boolean
|
active: boolean
|
||||||
level: string
|
createdAt: Date
|
||||||
userId: string
|
lastValidatedAt: Date | null
|
||||||
|
userId: string | null
|
||||||
|
email: string | null
|
||||||
_count: LicenseKeyCountAggregateOutputType | null
|
_count: LicenseKeyCountAggregateOutputType | null
|
||||||
_min: LicenseKeyMinAggregateOutputType | null
|
_min: LicenseKeyMinAggregateOutputType | null
|
||||||
_max: LicenseKeyMaxAggregateOutputType | null
|
_max: LicenseKeyMaxAggregateOutputType | null
|
||||||
@@ -180,17 +194,21 @@ export type LicenseKeyWhereInput = {
|
|||||||
id?: Prisma.StringFilter<"LicenseKey"> | string
|
id?: Prisma.StringFilter<"LicenseKey"> | string
|
||||||
key?: Prisma.StringFilter<"LicenseKey"> | string
|
key?: Prisma.StringFilter<"LicenseKey"> | string
|
||||||
active?: Prisma.BoolFilter<"LicenseKey"> | boolean
|
active?: Prisma.BoolFilter<"LicenseKey"> | boolean
|
||||||
level?: Prisma.StringFilter<"LicenseKey"> | string
|
createdAt?: Prisma.DateTimeFilter<"LicenseKey"> | Date | string
|
||||||
userId?: Prisma.StringFilter<"LicenseKey"> | string
|
lastValidatedAt?: Prisma.DateTimeNullableFilter<"LicenseKey"> | Date | string | null
|
||||||
user?: Prisma.XOR<Prisma.UserScalarRelationFilter, Prisma.UserWhereInput>
|
userId?: Prisma.StringNullableFilter<"LicenseKey"> | string | null
|
||||||
|
email?: Prisma.StringNullableFilter<"LicenseKey"> | string | null
|
||||||
|
user?: Prisma.XOR<Prisma.UserNullableScalarRelationFilter, Prisma.UserWhereInput> | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LicenseKeyOrderByWithRelationInput = {
|
export type LicenseKeyOrderByWithRelationInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
key?: Prisma.SortOrder
|
key?: Prisma.SortOrder
|
||||||
active?: Prisma.SortOrder
|
active?: Prisma.SortOrder
|
||||||
level?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
userId?: Prisma.SortOrder
|
lastValidatedAt?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
|
userId?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
|
email?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
user?: Prisma.UserOrderByWithRelationInput
|
user?: Prisma.UserOrderByWithRelationInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,17 +219,21 @@ export type LicenseKeyWhereUniqueInput = Prisma.AtLeast<{
|
|||||||
OR?: Prisma.LicenseKeyWhereInput[]
|
OR?: Prisma.LicenseKeyWhereInput[]
|
||||||
NOT?: Prisma.LicenseKeyWhereInput | Prisma.LicenseKeyWhereInput[]
|
NOT?: Prisma.LicenseKeyWhereInput | Prisma.LicenseKeyWhereInput[]
|
||||||
active?: Prisma.BoolFilter<"LicenseKey"> | boolean
|
active?: Prisma.BoolFilter<"LicenseKey"> | boolean
|
||||||
level?: Prisma.StringFilter<"LicenseKey"> | string
|
createdAt?: Prisma.DateTimeFilter<"LicenseKey"> | Date | string
|
||||||
userId?: Prisma.StringFilter<"LicenseKey"> | string
|
lastValidatedAt?: Prisma.DateTimeNullableFilter<"LicenseKey"> | Date | string | null
|
||||||
user?: Prisma.XOR<Prisma.UserScalarRelationFilter, Prisma.UserWhereInput>
|
userId?: Prisma.StringNullableFilter<"LicenseKey"> | string | null
|
||||||
|
email?: Prisma.StringNullableFilter<"LicenseKey"> | string | null
|
||||||
|
user?: Prisma.XOR<Prisma.UserNullableScalarRelationFilter, Prisma.UserWhereInput> | null
|
||||||
}, "id" | "key">
|
}, "id" | "key">
|
||||||
|
|
||||||
export type LicenseKeyOrderByWithAggregationInput = {
|
export type LicenseKeyOrderByWithAggregationInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
key?: Prisma.SortOrder
|
key?: Prisma.SortOrder
|
||||||
active?: Prisma.SortOrder
|
active?: Prisma.SortOrder
|
||||||
level?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
userId?: Prisma.SortOrder
|
lastValidatedAt?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
|
userId?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
|
email?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
_count?: Prisma.LicenseKeyCountOrderByAggregateInput
|
_count?: Prisma.LicenseKeyCountOrderByAggregateInput
|
||||||
_max?: Prisma.LicenseKeyMaxOrderByAggregateInput
|
_max?: Prisma.LicenseKeyMaxOrderByAggregateInput
|
||||||
_min?: Prisma.LicenseKeyMinOrderByAggregateInput
|
_min?: Prisma.LicenseKeyMinOrderByAggregateInput
|
||||||
@@ -224,63 +246,79 @@ export type LicenseKeyScalarWhereWithAggregatesInput = {
|
|||||||
id?: Prisma.StringWithAggregatesFilter<"LicenseKey"> | string
|
id?: Prisma.StringWithAggregatesFilter<"LicenseKey"> | string
|
||||||
key?: Prisma.StringWithAggregatesFilter<"LicenseKey"> | string
|
key?: Prisma.StringWithAggregatesFilter<"LicenseKey"> | string
|
||||||
active?: Prisma.BoolWithAggregatesFilter<"LicenseKey"> | boolean
|
active?: Prisma.BoolWithAggregatesFilter<"LicenseKey"> | boolean
|
||||||
level?: Prisma.StringWithAggregatesFilter<"LicenseKey"> | string
|
createdAt?: Prisma.DateTimeWithAggregatesFilter<"LicenseKey"> | Date | string
|
||||||
userId?: Prisma.StringWithAggregatesFilter<"LicenseKey"> | string
|
lastValidatedAt?: Prisma.DateTimeNullableWithAggregatesFilter<"LicenseKey"> | Date | string | null
|
||||||
|
userId?: Prisma.StringNullableWithAggregatesFilter<"LicenseKey"> | string | null
|
||||||
|
email?: Prisma.StringNullableWithAggregatesFilter<"LicenseKey"> | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LicenseKeyCreateInput = {
|
export type LicenseKeyCreateInput = {
|
||||||
id?: string
|
id?: string
|
||||||
key: string
|
key: string
|
||||||
active: boolean
|
active: boolean
|
||||||
level: string
|
createdAt?: Date | string
|
||||||
user: Prisma.UserCreateNestedOneWithoutLicenseKeysInput
|
lastValidatedAt?: Date | string | null
|
||||||
|
email?: string | null
|
||||||
|
user?: Prisma.UserCreateNestedOneWithoutLicenseKeysInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LicenseKeyUncheckedCreateInput = {
|
export type LicenseKeyUncheckedCreateInput = {
|
||||||
id?: string
|
id?: string
|
||||||
key: string
|
key: string
|
||||||
active: boolean
|
active: boolean
|
||||||
level: string
|
createdAt?: Date | string
|
||||||
userId: string
|
lastValidatedAt?: Date | string | null
|
||||||
|
userId?: string | null
|
||||||
|
email?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LicenseKeyUpdateInput = {
|
export type LicenseKeyUpdateInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
key?: Prisma.StringFieldUpdateOperationsInput | string
|
key?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
active?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
active?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
level?: Prisma.StringFieldUpdateOperationsInput | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
user?: Prisma.UserUpdateOneRequiredWithoutLicenseKeysNestedInput
|
lastValidatedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||||
|
email?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
user?: Prisma.UserUpdateOneWithoutLicenseKeysNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LicenseKeyUncheckedUpdateInput = {
|
export type LicenseKeyUncheckedUpdateInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
key?: Prisma.StringFieldUpdateOperationsInput | string
|
key?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
active?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
active?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
level?: Prisma.StringFieldUpdateOperationsInput | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
userId?: Prisma.StringFieldUpdateOperationsInput | string
|
lastValidatedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||||
|
userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
email?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LicenseKeyCreateManyInput = {
|
export type LicenseKeyCreateManyInput = {
|
||||||
id?: string
|
id?: string
|
||||||
key: string
|
key: string
|
||||||
active: boolean
|
active: boolean
|
||||||
level: string
|
createdAt?: Date | string
|
||||||
userId: string
|
lastValidatedAt?: Date | string | null
|
||||||
|
userId?: string | null
|
||||||
|
email?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LicenseKeyUpdateManyMutationInput = {
|
export type LicenseKeyUpdateManyMutationInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
key?: Prisma.StringFieldUpdateOperationsInput | string
|
key?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
active?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
active?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
level?: Prisma.StringFieldUpdateOperationsInput | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
lastValidatedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||||
|
email?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LicenseKeyUncheckedUpdateManyInput = {
|
export type LicenseKeyUncheckedUpdateManyInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
key?: Prisma.StringFieldUpdateOperationsInput | string
|
key?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
active?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
active?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
level?: Prisma.StringFieldUpdateOperationsInput | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
userId?: Prisma.StringFieldUpdateOperationsInput | string
|
lastValidatedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||||
|
userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
email?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LicenseKeyListRelationFilter = {
|
export type LicenseKeyListRelationFilter = {
|
||||||
@@ -297,24 +335,30 @@ export type LicenseKeyCountOrderByAggregateInput = {
|
|||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
key?: Prisma.SortOrder
|
key?: Prisma.SortOrder
|
||||||
active?: Prisma.SortOrder
|
active?: Prisma.SortOrder
|
||||||
level?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
|
lastValidatedAt?: Prisma.SortOrder
|
||||||
userId?: Prisma.SortOrder
|
userId?: Prisma.SortOrder
|
||||||
|
email?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LicenseKeyMaxOrderByAggregateInput = {
|
export type LicenseKeyMaxOrderByAggregateInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
key?: Prisma.SortOrder
|
key?: Prisma.SortOrder
|
||||||
active?: Prisma.SortOrder
|
active?: Prisma.SortOrder
|
||||||
level?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
|
lastValidatedAt?: Prisma.SortOrder
|
||||||
userId?: Prisma.SortOrder
|
userId?: Prisma.SortOrder
|
||||||
|
email?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LicenseKeyMinOrderByAggregateInput = {
|
export type LicenseKeyMinOrderByAggregateInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
key?: Prisma.SortOrder
|
key?: Prisma.SortOrder
|
||||||
active?: Prisma.SortOrder
|
active?: Prisma.SortOrder
|
||||||
level?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
|
lastValidatedAt?: Prisma.SortOrder
|
||||||
userId?: Prisma.SortOrder
|
userId?: Prisma.SortOrder
|
||||||
|
email?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LicenseKeyCreateNestedManyWithoutUserInput = {
|
export type LicenseKeyCreateNestedManyWithoutUserInput = {
|
||||||
@@ -363,14 +407,18 @@ export type LicenseKeyCreateWithoutUserInput = {
|
|||||||
id?: string
|
id?: string
|
||||||
key: string
|
key: string
|
||||||
active: boolean
|
active: boolean
|
||||||
level: string
|
createdAt?: Date | string
|
||||||
|
lastValidatedAt?: Date | string | null
|
||||||
|
email?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LicenseKeyUncheckedCreateWithoutUserInput = {
|
export type LicenseKeyUncheckedCreateWithoutUserInput = {
|
||||||
id?: string
|
id?: string
|
||||||
key: string
|
key: string
|
||||||
active: boolean
|
active: boolean
|
||||||
level: string
|
createdAt?: Date | string
|
||||||
|
lastValidatedAt?: Date | string | null
|
||||||
|
email?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LicenseKeyCreateOrConnectWithoutUserInput = {
|
export type LicenseKeyCreateOrConnectWithoutUserInput = {
|
||||||
@@ -406,36 +454,46 @@ export type LicenseKeyScalarWhereInput = {
|
|||||||
id?: Prisma.StringFilter<"LicenseKey"> | string
|
id?: Prisma.StringFilter<"LicenseKey"> | string
|
||||||
key?: Prisma.StringFilter<"LicenseKey"> | string
|
key?: Prisma.StringFilter<"LicenseKey"> | string
|
||||||
active?: Prisma.BoolFilter<"LicenseKey"> | boolean
|
active?: Prisma.BoolFilter<"LicenseKey"> | boolean
|
||||||
level?: Prisma.StringFilter<"LicenseKey"> | string
|
createdAt?: Prisma.DateTimeFilter<"LicenseKey"> | Date | string
|
||||||
userId?: Prisma.StringFilter<"LicenseKey"> | string
|
lastValidatedAt?: Prisma.DateTimeNullableFilter<"LicenseKey"> | Date | string | null
|
||||||
|
userId?: Prisma.StringNullableFilter<"LicenseKey"> | string | null
|
||||||
|
email?: Prisma.StringNullableFilter<"LicenseKey"> | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LicenseKeyCreateManyUserInput = {
|
export type LicenseKeyCreateManyUserInput = {
|
||||||
id?: string
|
id?: string
|
||||||
key: string
|
key: string
|
||||||
active: boolean
|
active: boolean
|
||||||
level: string
|
createdAt?: Date | string
|
||||||
|
lastValidatedAt?: Date | string | null
|
||||||
|
email?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LicenseKeyUpdateWithoutUserInput = {
|
export type LicenseKeyUpdateWithoutUserInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
key?: Prisma.StringFieldUpdateOperationsInput | string
|
key?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
active?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
active?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
level?: Prisma.StringFieldUpdateOperationsInput | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
lastValidatedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||||
|
email?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LicenseKeyUncheckedUpdateWithoutUserInput = {
|
export type LicenseKeyUncheckedUpdateWithoutUserInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
key?: Prisma.StringFieldUpdateOperationsInput | string
|
key?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
active?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
active?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
level?: Prisma.StringFieldUpdateOperationsInput | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
lastValidatedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||||
|
email?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LicenseKeyUncheckedUpdateManyWithoutUserInput = {
|
export type LicenseKeyUncheckedUpdateManyWithoutUserInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
key?: Prisma.StringFieldUpdateOperationsInput | string
|
key?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
active?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
active?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
level?: Prisma.StringFieldUpdateOperationsInput | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
lastValidatedAt?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
|
||||||
|
email?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -444,59 +502,69 @@ export type LicenseKeySelect<ExtArgs extends runtime.Types.Extensions.InternalAr
|
|||||||
id?: boolean
|
id?: boolean
|
||||||
key?: boolean
|
key?: boolean
|
||||||
active?: boolean
|
active?: boolean
|
||||||
level?: boolean
|
createdAt?: boolean
|
||||||
|
lastValidatedAt?: boolean
|
||||||
userId?: boolean
|
userId?: boolean
|
||||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
email?: boolean
|
||||||
|
user?: boolean | Prisma.LicenseKey$userArgs<ExtArgs>
|
||||||
}, ExtArgs["result"]["licenseKey"]>
|
}, ExtArgs["result"]["licenseKey"]>
|
||||||
|
|
||||||
export type LicenseKeySelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type LicenseKeySelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
id?: boolean
|
id?: boolean
|
||||||
key?: boolean
|
key?: boolean
|
||||||
active?: boolean
|
active?: boolean
|
||||||
level?: boolean
|
createdAt?: boolean
|
||||||
|
lastValidatedAt?: boolean
|
||||||
userId?: boolean
|
userId?: boolean
|
||||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
email?: boolean
|
||||||
|
user?: boolean | Prisma.LicenseKey$userArgs<ExtArgs>
|
||||||
}, ExtArgs["result"]["licenseKey"]>
|
}, ExtArgs["result"]["licenseKey"]>
|
||||||
|
|
||||||
export type LicenseKeySelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type LicenseKeySelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
id?: boolean
|
id?: boolean
|
||||||
key?: boolean
|
key?: boolean
|
||||||
active?: boolean
|
active?: boolean
|
||||||
level?: boolean
|
createdAt?: boolean
|
||||||
|
lastValidatedAt?: boolean
|
||||||
userId?: boolean
|
userId?: boolean
|
||||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
email?: boolean
|
||||||
|
user?: boolean | Prisma.LicenseKey$userArgs<ExtArgs>
|
||||||
}, ExtArgs["result"]["licenseKey"]>
|
}, ExtArgs["result"]["licenseKey"]>
|
||||||
|
|
||||||
export type LicenseKeySelectScalar = {
|
export type LicenseKeySelectScalar = {
|
||||||
id?: boolean
|
id?: boolean
|
||||||
key?: boolean
|
key?: boolean
|
||||||
active?: boolean
|
active?: boolean
|
||||||
level?: boolean
|
createdAt?: boolean
|
||||||
|
lastValidatedAt?: boolean
|
||||||
userId?: boolean
|
userId?: boolean
|
||||||
|
email?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LicenseKeyOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "key" | "active" | "level" | "userId", ExtArgs["result"]["licenseKey"]>
|
export type LicenseKeyOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "key" | "active" | "createdAt" | "lastValidatedAt" | "userId" | "email", ExtArgs["result"]["licenseKey"]>
|
||||||
export type LicenseKeyInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type LicenseKeyInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
user?: boolean | Prisma.LicenseKey$userArgs<ExtArgs>
|
||||||
}
|
}
|
||||||
export type LicenseKeyIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type LicenseKeyIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
user?: boolean | Prisma.LicenseKey$userArgs<ExtArgs>
|
||||||
}
|
}
|
||||||
export type LicenseKeyIncludeUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type LicenseKeyIncludeUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
user?: boolean | Prisma.LicenseKey$userArgs<ExtArgs>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type $LicenseKeyPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type $LicenseKeyPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
name: "LicenseKey"
|
name: "LicenseKey"
|
||||||
objects: {
|
objects: {
|
||||||
user: Prisma.$UserPayload<ExtArgs>
|
user: Prisma.$UserPayload<ExtArgs> | null
|
||||||
}
|
}
|
||||||
scalars: runtime.Types.Extensions.GetPayloadResult<{
|
scalars: runtime.Types.Extensions.GetPayloadResult<{
|
||||||
id: string
|
id: string
|
||||||
key: string
|
key: string
|
||||||
active: boolean
|
active: boolean
|
||||||
level: string
|
createdAt: Date
|
||||||
userId: string
|
lastValidatedAt: Date | null
|
||||||
|
userId: string | null
|
||||||
|
email: string | null
|
||||||
}, ExtArgs["result"]["licenseKey"]>
|
}, ExtArgs["result"]["licenseKey"]>
|
||||||
composites: {}
|
composites: {}
|
||||||
}
|
}
|
||||||
@@ -891,7 +959,7 @@ readonly fields: LicenseKeyFieldRefs;
|
|||||||
*/
|
*/
|
||||||
export interface Prisma__LicenseKeyClient<T, Null = never, ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs, GlobalOmitOptions = {}> extends Prisma.PrismaPromise<T> {
|
export interface Prisma__LicenseKeyClient<T, Null = never, ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs, GlobalOmitOptions = {}> extends Prisma.PrismaPromise<T> {
|
||||||
readonly [Symbol.toStringTag]: "PrismaPromise"
|
readonly [Symbol.toStringTag]: "PrismaPromise"
|
||||||
user<T extends Prisma.UserDefaultArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.UserDefaultArgs<ExtArgs>>): Prisma.Prisma__UserClient<runtime.Types.Result.GetResult<Prisma.$UserPayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | Null, Null, ExtArgs, GlobalOmitOptions>
|
user<T extends Prisma.LicenseKey$userArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.LicenseKey$userArgs<ExtArgs>>): Prisma.Prisma__UserClient<runtime.Types.Result.GetResult<Prisma.$UserPayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions>
|
||||||
/**
|
/**
|
||||||
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
||||||
* @param onfulfilled The callback to execute when the Promise is resolved.
|
* @param onfulfilled The callback to execute when the Promise is resolved.
|
||||||
@@ -924,8 +992,10 @@ export interface LicenseKeyFieldRefs {
|
|||||||
readonly id: Prisma.FieldRef<"LicenseKey", 'String'>
|
readonly id: Prisma.FieldRef<"LicenseKey", 'String'>
|
||||||
readonly key: Prisma.FieldRef<"LicenseKey", 'String'>
|
readonly key: Prisma.FieldRef<"LicenseKey", 'String'>
|
||||||
readonly active: Prisma.FieldRef<"LicenseKey", 'Boolean'>
|
readonly active: Prisma.FieldRef<"LicenseKey", 'Boolean'>
|
||||||
readonly level: Prisma.FieldRef<"LicenseKey", 'String'>
|
readonly createdAt: Prisma.FieldRef<"LicenseKey", 'DateTime'>
|
||||||
|
readonly lastValidatedAt: Prisma.FieldRef<"LicenseKey", 'DateTime'>
|
||||||
readonly userId: Prisma.FieldRef<"LicenseKey", 'String'>
|
readonly userId: Prisma.FieldRef<"LicenseKey", 'String'>
|
||||||
|
readonly email: Prisma.FieldRef<"LicenseKey", 'String'>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1326,6 +1396,25 @@ export type LicenseKeyDeleteManyArgs<ExtArgs extends runtime.Types.Extensions.In
|
|||||||
limit?: number
|
limit?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LicenseKey.user
|
||||||
|
*/
|
||||||
|
export type LicenseKey$userArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
|
/**
|
||||||
|
* Select specific fields to fetch from the User
|
||||||
|
*/
|
||||||
|
select?: Prisma.UserSelect<ExtArgs> | null
|
||||||
|
/**
|
||||||
|
* Omit specific fields from the User
|
||||||
|
*/
|
||||||
|
omit?: Prisma.UserOmit<ExtArgs> | null
|
||||||
|
/**
|
||||||
|
* Choose, which related nodes to fetch as well
|
||||||
|
*/
|
||||||
|
include?: Prisma.UserInclude<ExtArgs> | null
|
||||||
|
where?: Prisma.UserWhereInput
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LicenseKey without action
|
* LicenseKey without action
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -29,13 +29,13 @@ export type AggregateProclamation = {
|
|||||||
export type ProclamationAvgAggregateOutputType = {
|
export type ProclamationAvgAggregateOutputType = {
|
||||||
solfeggioFrequencyId: number | null
|
solfeggioFrequencyId: number | null
|
||||||
categoryId: number | null
|
categoryId: number | null
|
||||||
subscriptionLevelId: number | null
|
groupId: number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationSumAggregateOutputType = {
|
export type ProclamationSumAggregateOutputType = {
|
||||||
solfeggioFrequencyId: number | null
|
solfeggioFrequencyId: number | null
|
||||||
categoryId: number | null
|
categoryId: number | null
|
||||||
subscriptionLevelId: number | null
|
groupId: number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationMinAggregateOutputType = {
|
export type ProclamationMinAggregateOutputType = {
|
||||||
@@ -44,7 +44,7 @@ export type ProclamationMinAggregateOutputType = {
|
|||||||
content: string | null
|
content: string | null
|
||||||
solfeggioFrequencyId: number | null
|
solfeggioFrequencyId: number | null
|
||||||
categoryId: number | null
|
categoryId: number | null
|
||||||
subscriptionLevelId: number | null
|
groupId: number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationMaxAggregateOutputType = {
|
export type ProclamationMaxAggregateOutputType = {
|
||||||
@@ -53,7 +53,7 @@ export type ProclamationMaxAggregateOutputType = {
|
|||||||
content: string | null
|
content: string | null
|
||||||
solfeggioFrequencyId: number | null
|
solfeggioFrequencyId: number | null
|
||||||
categoryId: number | null
|
categoryId: number | null
|
||||||
subscriptionLevelId: number | null
|
groupId: number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCountAggregateOutputType = {
|
export type ProclamationCountAggregateOutputType = {
|
||||||
@@ -62,7 +62,7 @@ export type ProclamationCountAggregateOutputType = {
|
|||||||
content: number
|
content: number
|
||||||
solfeggioFrequencyId: number
|
solfeggioFrequencyId: number
|
||||||
categoryId: number
|
categoryId: number
|
||||||
subscriptionLevelId: number
|
groupId: number
|
||||||
_all: number
|
_all: number
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,13 +70,13 @@ export type ProclamationCountAggregateOutputType = {
|
|||||||
export type ProclamationAvgAggregateInputType = {
|
export type ProclamationAvgAggregateInputType = {
|
||||||
solfeggioFrequencyId?: true
|
solfeggioFrequencyId?: true
|
||||||
categoryId?: true
|
categoryId?: true
|
||||||
subscriptionLevelId?: true
|
groupId?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationSumAggregateInputType = {
|
export type ProclamationSumAggregateInputType = {
|
||||||
solfeggioFrequencyId?: true
|
solfeggioFrequencyId?: true
|
||||||
categoryId?: true
|
categoryId?: true
|
||||||
subscriptionLevelId?: true
|
groupId?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationMinAggregateInputType = {
|
export type ProclamationMinAggregateInputType = {
|
||||||
@@ -85,7 +85,7 @@ export type ProclamationMinAggregateInputType = {
|
|||||||
content?: true
|
content?: true
|
||||||
solfeggioFrequencyId?: true
|
solfeggioFrequencyId?: true
|
||||||
categoryId?: true
|
categoryId?: true
|
||||||
subscriptionLevelId?: true
|
groupId?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationMaxAggregateInputType = {
|
export type ProclamationMaxAggregateInputType = {
|
||||||
@@ -94,7 +94,7 @@ export type ProclamationMaxAggregateInputType = {
|
|||||||
content?: true
|
content?: true
|
||||||
solfeggioFrequencyId?: true
|
solfeggioFrequencyId?: true
|
||||||
categoryId?: true
|
categoryId?: true
|
||||||
subscriptionLevelId?: true
|
groupId?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCountAggregateInputType = {
|
export type ProclamationCountAggregateInputType = {
|
||||||
@@ -103,7 +103,7 @@ export type ProclamationCountAggregateInputType = {
|
|||||||
content?: true
|
content?: true
|
||||||
solfeggioFrequencyId?: true
|
solfeggioFrequencyId?: true
|
||||||
categoryId?: true
|
categoryId?: true
|
||||||
subscriptionLevelId?: true
|
groupId?: true
|
||||||
_all?: true
|
_all?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +199,7 @@ export type ProclamationGroupByOutputType = {
|
|||||||
content: string
|
content: string
|
||||||
solfeggioFrequencyId: number
|
solfeggioFrequencyId: number
|
||||||
categoryId: number
|
categoryId: number
|
||||||
subscriptionLevelId: number
|
groupId: number
|
||||||
_count: ProclamationCountAggregateOutputType | null
|
_count: ProclamationCountAggregateOutputType | null
|
||||||
_avg: ProclamationAvgAggregateOutputType | null
|
_avg: ProclamationAvgAggregateOutputType | null
|
||||||
_sum: ProclamationSumAggregateOutputType | null
|
_sum: ProclamationSumAggregateOutputType | null
|
||||||
@@ -231,11 +231,11 @@ export type ProclamationWhereInput = {
|
|||||||
content?: Prisma.StringFilter<"Proclamation"> | string
|
content?: Prisma.StringFilter<"Proclamation"> | string
|
||||||
solfeggioFrequencyId?: Prisma.IntFilter<"Proclamation"> | number
|
solfeggioFrequencyId?: Prisma.IntFilter<"Proclamation"> | number
|
||||||
categoryId?: Prisma.IntFilter<"Proclamation"> | number
|
categoryId?: Prisma.IntFilter<"Proclamation"> | number
|
||||||
subscriptionLevelId?: Prisma.IntFilter<"Proclamation"> | number
|
groupId?: Prisma.IntFilter<"Proclamation"> | number
|
||||||
solfeggioFrequency?: Prisma.XOR<Prisma.SolfeggioFrequencyScalarRelationFilter, Prisma.SolfeggioFrequencyWhereInput>
|
solfeggioFrequency?: Prisma.XOR<Prisma.SolfeggioFrequencyScalarRelationFilter, Prisma.SolfeggioFrequencyWhereInput>
|
||||||
category?: Prisma.XOR<Prisma.ProclamationCategoryScalarRelationFilter, Prisma.ProclamationCategoryWhereInput>
|
category?: Prisma.XOR<Prisma.ProclamationCategoryScalarRelationFilter, Prisma.ProclamationCategoryWhereInput>
|
||||||
|
group?: Prisma.XOR<Prisma.ProclamationGroupScalarRelationFilter, Prisma.ProclamationGroupWhereInput>
|
||||||
userProclamations?: Prisma.UserProclamationsListRelationFilter
|
userProclamations?: Prisma.UserProclamationsListRelationFilter
|
||||||
subscriptionLevel?: Prisma.XOR<Prisma.SubscriptionLevelScalarRelationFilter, Prisma.SubscriptionLevelWhereInput>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationOrderByWithRelationInput = {
|
export type ProclamationOrderByWithRelationInput = {
|
||||||
@@ -244,11 +244,11 @@ export type ProclamationOrderByWithRelationInput = {
|
|||||||
content?: Prisma.SortOrder
|
content?: Prisma.SortOrder
|
||||||
solfeggioFrequencyId?: Prisma.SortOrder
|
solfeggioFrequencyId?: Prisma.SortOrder
|
||||||
categoryId?: Prisma.SortOrder
|
categoryId?: Prisma.SortOrder
|
||||||
subscriptionLevelId?: Prisma.SortOrder
|
groupId?: Prisma.SortOrder
|
||||||
solfeggioFrequency?: Prisma.SolfeggioFrequencyOrderByWithRelationInput
|
solfeggioFrequency?: Prisma.SolfeggioFrequencyOrderByWithRelationInput
|
||||||
category?: Prisma.ProclamationCategoryOrderByWithRelationInput
|
category?: Prisma.ProclamationCategoryOrderByWithRelationInput
|
||||||
|
group?: Prisma.ProclamationGroupOrderByWithRelationInput
|
||||||
userProclamations?: Prisma.UserProclamationsOrderByRelationAggregateInput
|
userProclamations?: Prisma.UserProclamationsOrderByRelationAggregateInput
|
||||||
subscriptionLevel?: Prisma.SubscriptionLevelOrderByWithRelationInput
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationWhereUniqueInput = Prisma.AtLeast<{
|
export type ProclamationWhereUniqueInput = Prisma.AtLeast<{
|
||||||
@@ -260,11 +260,11 @@ export type ProclamationWhereUniqueInput = Prisma.AtLeast<{
|
|||||||
content?: Prisma.StringFilter<"Proclamation"> | string
|
content?: Prisma.StringFilter<"Proclamation"> | string
|
||||||
solfeggioFrequencyId?: Prisma.IntFilter<"Proclamation"> | number
|
solfeggioFrequencyId?: Prisma.IntFilter<"Proclamation"> | number
|
||||||
categoryId?: Prisma.IntFilter<"Proclamation"> | number
|
categoryId?: Prisma.IntFilter<"Proclamation"> | number
|
||||||
subscriptionLevelId?: Prisma.IntFilter<"Proclamation"> | number
|
groupId?: Prisma.IntFilter<"Proclamation"> | number
|
||||||
solfeggioFrequency?: Prisma.XOR<Prisma.SolfeggioFrequencyScalarRelationFilter, Prisma.SolfeggioFrequencyWhereInput>
|
solfeggioFrequency?: Prisma.XOR<Prisma.SolfeggioFrequencyScalarRelationFilter, Prisma.SolfeggioFrequencyWhereInput>
|
||||||
category?: Prisma.XOR<Prisma.ProclamationCategoryScalarRelationFilter, Prisma.ProclamationCategoryWhereInput>
|
category?: Prisma.XOR<Prisma.ProclamationCategoryScalarRelationFilter, Prisma.ProclamationCategoryWhereInput>
|
||||||
|
group?: Prisma.XOR<Prisma.ProclamationGroupScalarRelationFilter, Prisma.ProclamationGroupWhereInput>
|
||||||
userProclamations?: Prisma.UserProclamationsListRelationFilter
|
userProclamations?: Prisma.UserProclamationsListRelationFilter
|
||||||
subscriptionLevel?: Prisma.XOR<Prisma.SubscriptionLevelScalarRelationFilter, Prisma.SubscriptionLevelWhereInput>
|
|
||||||
}, "id" | "name">
|
}, "id" | "name">
|
||||||
|
|
||||||
export type ProclamationOrderByWithAggregationInput = {
|
export type ProclamationOrderByWithAggregationInput = {
|
||||||
@@ -273,7 +273,7 @@ export type ProclamationOrderByWithAggregationInput = {
|
|||||||
content?: Prisma.SortOrder
|
content?: Prisma.SortOrder
|
||||||
solfeggioFrequencyId?: Prisma.SortOrder
|
solfeggioFrequencyId?: Prisma.SortOrder
|
||||||
categoryId?: Prisma.SortOrder
|
categoryId?: Prisma.SortOrder
|
||||||
subscriptionLevelId?: Prisma.SortOrder
|
groupId?: Prisma.SortOrder
|
||||||
_count?: Prisma.ProclamationCountOrderByAggregateInput
|
_count?: Prisma.ProclamationCountOrderByAggregateInput
|
||||||
_avg?: Prisma.ProclamationAvgOrderByAggregateInput
|
_avg?: Prisma.ProclamationAvgOrderByAggregateInput
|
||||||
_max?: Prisma.ProclamationMaxOrderByAggregateInput
|
_max?: Prisma.ProclamationMaxOrderByAggregateInput
|
||||||
@@ -290,7 +290,7 @@ export type ProclamationScalarWhereWithAggregatesInput = {
|
|||||||
content?: Prisma.StringWithAggregatesFilter<"Proclamation"> | string
|
content?: Prisma.StringWithAggregatesFilter<"Proclamation"> | string
|
||||||
solfeggioFrequencyId?: Prisma.IntWithAggregatesFilter<"Proclamation"> | number
|
solfeggioFrequencyId?: Prisma.IntWithAggregatesFilter<"Proclamation"> | number
|
||||||
categoryId?: Prisma.IntWithAggregatesFilter<"Proclamation"> | number
|
categoryId?: Prisma.IntWithAggregatesFilter<"Proclamation"> | number
|
||||||
subscriptionLevelId?: Prisma.IntWithAggregatesFilter<"Proclamation"> | number
|
groupId?: Prisma.IntWithAggregatesFilter<"Proclamation"> | number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCreateInput = {
|
export type ProclamationCreateInput = {
|
||||||
@@ -299,8 +299,8 @@ export type ProclamationCreateInput = {
|
|||||||
content: string
|
content: string
|
||||||
solfeggioFrequency: Prisma.SolfeggioFrequencyCreateNestedOneWithoutProclamationsInput
|
solfeggioFrequency: Prisma.SolfeggioFrequencyCreateNestedOneWithoutProclamationsInput
|
||||||
category: Prisma.ProclamationCategoryCreateNestedOneWithoutProclamationsInput
|
category: Prisma.ProclamationCategoryCreateNestedOneWithoutProclamationsInput
|
||||||
|
group: Prisma.ProclamationGroupCreateNestedOneWithoutProclamationsInput
|
||||||
userProclamations?: Prisma.UserProclamationsCreateNestedManyWithoutProclamationInput
|
userProclamations?: Prisma.UserProclamationsCreateNestedManyWithoutProclamationInput
|
||||||
subscriptionLevel: Prisma.SubscriptionLevelCreateNestedOneWithoutProclamationsInput
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationUncheckedCreateInput = {
|
export type ProclamationUncheckedCreateInput = {
|
||||||
@@ -309,7 +309,7 @@ export type ProclamationUncheckedCreateInput = {
|
|||||||
content: string
|
content: string
|
||||||
solfeggioFrequencyId: number
|
solfeggioFrequencyId: number
|
||||||
categoryId: number
|
categoryId: number
|
||||||
subscriptionLevelId: number
|
groupId: number
|
||||||
userProclamations?: Prisma.UserProclamationsUncheckedCreateNestedManyWithoutProclamationInput
|
userProclamations?: Prisma.UserProclamationsUncheckedCreateNestedManyWithoutProclamationInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,8 +319,8 @@ export type ProclamationUpdateInput = {
|
|||||||
content?: Prisma.StringFieldUpdateOperationsInput | string
|
content?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
solfeggioFrequency?: Prisma.SolfeggioFrequencyUpdateOneRequiredWithoutProclamationsNestedInput
|
solfeggioFrequency?: Prisma.SolfeggioFrequencyUpdateOneRequiredWithoutProclamationsNestedInput
|
||||||
category?: Prisma.ProclamationCategoryUpdateOneRequiredWithoutProclamationsNestedInput
|
category?: Prisma.ProclamationCategoryUpdateOneRequiredWithoutProclamationsNestedInput
|
||||||
|
group?: Prisma.ProclamationGroupUpdateOneRequiredWithoutProclamationsNestedInput
|
||||||
userProclamations?: Prisma.UserProclamationsUpdateManyWithoutProclamationNestedInput
|
userProclamations?: Prisma.UserProclamationsUpdateManyWithoutProclamationNestedInput
|
||||||
subscriptionLevel?: Prisma.SubscriptionLevelUpdateOneRequiredWithoutProclamationsNestedInput
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationUncheckedUpdateInput = {
|
export type ProclamationUncheckedUpdateInput = {
|
||||||
@@ -329,7 +329,7 @@ export type ProclamationUncheckedUpdateInput = {
|
|||||||
content?: Prisma.StringFieldUpdateOperationsInput | string
|
content?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
solfeggioFrequencyId?: Prisma.IntFieldUpdateOperationsInput | number
|
solfeggioFrequencyId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
categoryId?: Prisma.IntFieldUpdateOperationsInput | number
|
categoryId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
subscriptionLevelId?: Prisma.IntFieldUpdateOperationsInput | number
|
groupId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
userProclamations?: Prisma.UserProclamationsUncheckedUpdateManyWithoutProclamationNestedInput
|
userProclamations?: Prisma.UserProclamationsUncheckedUpdateManyWithoutProclamationNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,7 +339,7 @@ export type ProclamationCreateManyInput = {
|
|||||||
content: string
|
content: string
|
||||||
solfeggioFrequencyId: number
|
solfeggioFrequencyId: number
|
||||||
categoryId: number
|
categoryId: number
|
||||||
subscriptionLevelId: number
|
groupId: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationUpdateManyMutationInput = {
|
export type ProclamationUpdateManyMutationInput = {
|
||||||
@@ -354,7 +354,7 @@ export type ProclamationUncheckedUpdateManyInput = {
|
|||||||
content?: Prisma.StringFieldUpdateOperationsInput | string
|
content?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
solfeggioFrequencyId?: Prisma.IntFieldUpdateOperationsInput | number
|
solfeggioFrequencyId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
categoryId?: Prisma.IntFieldUpdateOperationsInput | number
|
categoryId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
subscriptionLevelId?: Prisma.IntFieldUpdateOperationsInput | number
|
groupId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCountOrderByAggregateInput = {
|
export type ProclamationCountOrderByAggregateInput = {
|
||||||
@@ -363,13 +363,13 @@ export type ProclamationCountOrderByAggregateInput = {
|
|||||||
content?: Prisma.SortOrder
|
content?: Prisma.SortOrder
|
||||||
solfeggioFrequencyId?: Prisma.SortOrder
|
solfeggioFrequencyId?: Prisma.SortOrder
|
||||||
categoryId?: Prisma.SortOrder
|
categoryId?: Prisma.SortOrder
|
||||||
subscriptionLevelId?: Prisma.SortOrder
|
groupId?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationAvgOrderByAggregateInput = {
|
export type ProclamationAvgOrderByAggregateInput = {
|
||||||
solfeggioFrequencyId?: Prisma.SortOrder
|
solfeggioFrequencyId?: Prisma.SortOrder
|
||||||
categoryId?: Prisma.SortOrder
|
categoryId?: Prisma.SortOrder
|
||||||
subscriptionLevelId?: Prisma.SortOrder
|
groupId?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationMaxOrderByAggregateInput = {
|
export type ProclamationMaxOrderByAggregateInput = {
|
||||||
@@ -378,7 +378,7 @@ export type ProclamationMaxOrderByAggregateInput = {
|
|||||||
content?: Prisma.SortOrder
|
content?: Prisma.SortOrder
|
||||||
solfeggioFrequencyId?: Prisma.SortOrder
|
solfeggioFrequencyId?: Prisma.SortOrder
|
||||||
categoryId?: Prisma.SortOrder
|
categoryId?: Prisma.SortOrder
|
||||||
subscriptionLevelId?: Prisma.SortOrder
|
groupId?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationMinOrderByAggregateInput = {
|
export type ProclamationMinOrderByAggregateInput = {
|
||||||
@@ -387,18 +387,13 @@ export type ProclamationMinOrderByAggregateInput = {
|
|||||||
content?: Prisma.SortOrder
|
content?: Prisma.SortOrder
|
||||||
solfeggioFrequencyId?: Prisma.SortOrder
|
solfeggioFrequencyId?: Prisma.SortOrder
|
||||||
categoryId?: Prisma.SortOrder
|
categoryId?: Prisma.SortOrder
|
||||||
subscriptionLevelId?: Prisma.SortOrder
|
groupId?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationSumOrderByAggregateInput = {
|
export type ProclamationSumOrderByAggregateInput = {
|
||||||
solfeggioFrequencyId?: Prisma.SortOrder
|
solfeggioFrequencyId?: Prisma.SortOrder
|
||||||
categoryId?: Prisma.SortOrder
|
categoryId?: Prisma.SortOrder
|
||||||
subscriptionLevelId?: Prisma.SortOrder
|
groupId?: Prisma.SortOrder
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationScalarRelationFilter = {
|
|
||||||
is?: Prisma.ProclamationWhereInput
|
|
||||||
isNot?: Prisma.ProclamationWhereInput
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationListRelationFilter = {
|
export type ProclamationListRelationFilter = {
|
||||||
@@ -411,6 +406,53 @@ export type ProclamationOrderByRelationAggregateInput = {
|
|||||||
_count?: Prisma.SortOrder
|
_count?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ProclamationScalarRelationFilter = {
|
||||||
|
is?: Prisma.ProclamationWhereInput
|
||||||
|
isNot?: Prisma.ProclamationWhereInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProclamationCreateNestedManyWithoutGroupInput = {
|
||||||
|
create?: Prisma.XOR<Prisma.ProclamationCreateWithoutGroupInput, Prisma.ProclamationUncheckedCreateWithoutGroupInput> | Prisma.ProclamationCreateWithoutGroupInput[] | Prisma.ProclamationUncheckedCreateWithoutGroupInput[]
|
||||||
|
connectOrCreate?: Prisma.ProclamationCreateOrConnectWithoutGroupInput | Prisma.ProclamationCreateOrConnectWithoutGroupInput[]
|
||||||
|
createMany?: Prisma.ProclamationCreateManyGroupInputEnvelope
|
||||||
|
connect?: Prisma.ProclamationWhereUniqueInput | Prisma.ProclamationWhereUniqueInput[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProclamationUncheckedCreateNestedManyWithoutGroupInput = {
|
||||||
|
create?: Prisma.XOR<Prisma.ProclamationCreateWithoutGroupInput, Prisma.ProclamationUncheckedCreateWithoutGroupInput> | Prisma.ProclamationCreateWithoutGroupInput[] | Prisma.ProclamationUncheckedCreateWithoutGroupInput[]
|
||||||
|
connectOrCreate?: Prisma.ProclamationCreateOrConnectWithoutGroupInput | Prisma.ProclamationCreateOrConnectWithoutGroupInput[]
|
||||||
|
createMany?: Prisma.ProclamationCreateManyGroupInputEnvelope
|
||||||
|
connect?: Prisma.ProclamationWhereUniqueInput | Prisma.ProclamationWhereUniqueInput[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProclamationUpdateManyWithoutGroupNestedInput = {
|
||||||
|
create?: Prisma.XOR<Prisma.ProclamationCreateWithoutGroupInput, Prisma.ProclamationUncheckedCreateWithoutGroupInput> | Prisma.ProclamationCreateWithoutGroupInput[] | Prisma.ProclamationUncheckedCreateWithoutGroupInput[]
|
||||||
|
connectOrCreate?: Prisma.ProclamationCreateOrConnectWithoutGroupInput | Prisma.ProclamationCreateOrConnectWithoutGroupInput[]
|
||||||
|
upsert?: Prisma.ProclamationUpsertWithWhereUniqueWithoutGroupInput | Prisma.ProclamationUpsertWithWhereUniqueWithoutGroupInput[]
|
||||||
|
createMany?: Prisma.ProclamationCreateManyGroupInputEnvelope
|
||||||
|
set?: Prisma.ProclamationWhereUniqueInput | Prisma.ProclamationWhereUniqueInput[]
|
||||||
|
disconnect?: Prisma.ProclamationWhereUniqueInput | Prisma.ProclamationWhereUniqueInput[]
|
||||||
|
delete?: Prisma.ProclamationWhereUniqueInput | Prisma.ProclamationWhereUniqueInput[]
|
||||||
|
connect?: Prisma.ProclamationWhereUniqueInput | Prisma.ProclamationWhereUniqueInput[]
|
||||||
|
update?: Prisma.ProclamationUpdateWithWhereUniqueWithoutGroupInput | Prisma.ProclamationUpdateWithWhereUniqueWithoutGroupInput[]
|
||||||
|
updateMany?: Prisma.ProclamationUpdateManyWithWhereWithoutGroupInput | Prisma.ProclamationUpdateManyWithWhereWithoutGroupInput[]
|
||||||
|
deleteMany?: Prisma.ProclamationScalarWhereInput | Prisma.ProclamationScalarWhereInput[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProclamationUncheckedUpdateManyWithoutGroupNestedInput = {
|
||||||
|
create?: Prisma.XOR<Prisma.ProclamationCreateWithoutGroupInput, Prisma.ProclamationUncheckedCreateWithoutGroupInput> | Prisma.ProclamationCreateWithoutGroupInput[] | Prisma.ProclamationUncheckedCreateWithoutGroupInput[]
|
||||||
|
connectOrCreate?: Prisma.ProclamationCreateOrConnectWithoutGroupInput | Prisma.ProclamationCreateOrConnectWithoutGroupInput[]
|
||||||
|
upsert?: Prisma.ProclamationUpsertWithWhereUniqueWithoutGroupInput | Prisma.ProclamationUpsertWithWhereUniqueWithoutGroupInput[]
|
||||||
|
createMany?: Prisma.ProclamationCreateManyGroupInputEnvelope
|
||||||
|
set?: Prisma.ProclamationWhereUniqueInput | Prisma.ProclamationWhereUniqueInput[]
|
||||||
|
disconnect?: Prisma.ProclamationWhereUniqueInput | Prisma.ProclamationWhereUniqueInput[]
|
||||||
|
delete?: Prisma.ProclamationWhereUniqueInput | Prisma.ProclamationWhereUniqueInput[]
|
||||||
|
connect?: Prisma.ProclamationWhereUniqueInput | Prisma.ProclamationWhereUniqueInput[]
|
||||||
|
update?: Prisma.ProclamationUpdateWithWhereUniqueWithoutGroupInput | Prisma.ProclamationUpdateWithWhereUniqueWithoutGroupInput[]
|
||||||
|
updateMany?: Prisma.ProclamationUpdateManyWithWhereWithoutGroupInput | Prisma.ProclamationUpdateManyWithWhereWithoutGroupInput[]
|
||||||
|
deleteMany?: Prisma.ProclamationScalarWhereInput | Prisma.ProclamationScalarWhereInput[]
|
||||||
|
}
|
||||||
|
|
||||||
export type ProclamationCreateNestedOneWithoutUserProclamationsInput = {
|
export type ProclamationCreateNestedOneWithoutUserProclamationsInput = {
|
||||||
create?: Prisma.XOR<Prisma.ProclamationCreateWithoutUserProclamationsInput, Prisma.ProclamationUncheckedCreateWithoutUserProclamationsInput>
|
create?: Prisma.XOR<Prisma.ProclamationCreateWithoutUserProclamationsInput, Prisma.ProclamationUncheckedCreateWithoutUserProclamationsInput>
|
||||||
connectOrCreate?: Prisma.ProclamationCreateOrConnectWithoutUserProclamationsInput
|
connectOrCreate?: Prisma.ProclamationCreateOrConnectWithoutUserProclamationsInput
|
||||||
@@ -509,46 +551,60 @@ export type ProclamationUncheckedUpdateManyWithoutCategoryNestedInput = {
|
|||||||
deleteMany?: Prisma.ProclamationScalarWhereInput | Prisma.ProclamationScalarWhereInput[]
|
deleteMany?: Prisma.ProclamationScalarWhereInput | Prisma.ProclamationScalarWhereInput[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCreateNestedManyWithoutSubscriptionLevelInput = {
|
export type ProclamationCreateWithoutGroupInput = {
|
||||||
create?: Prisma.XOR<Prisma.ProclamationCreateWithoutSubscriptionLevelInput, Prisma.ProclamationUncheckedCreateWithoutSubscriptionLevelInput> | Prisma.ProclamationCreateWithoutSubscriptionLevelInput[] | Prisma.ProclamationUncheckedCreateWithoutSubscriptionLevelInput[]
|
id?: string
|
||||||
connectOrCreate?: Prisma.ProclamationCreateOrConnectWithoutSubscriptionLevelInput | Prisma.ProclamationCreateOrConnectWithoutSubscriptionLevelInput[]
|
name: string
|
||||||
createMany?: Prisma.ProclamationCreateManySubscriptionLevelInputEnvelope
|
content: string
|
||||||
connect?: Prisma.ProclamationWhereUniqueInput | Prisma.ProclamationWhereUniqueInput[]
|
solfeggioFrequency: Prisma.SolfeggioFrequencyCreateNestedOneWithoutProclamationsInput
|
||||||
|
category: Prisma.ProclamationCategoryCreateNestedOneWithoutProclamationsInput
|
||||||
|
userProclamations?: Prisma.UserProclamationsCreateNestedManyWithoutProclamationInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationUncheckedCreateNestedManyWithoutSubscriptionLevelInput = {
|
export type ProclamationUncheckedCreateWithoutGroupInput = {
|
||||||
create?: Prisma.XOR<Prisma.ProclamationCreateWithoutSubscriptionLevelInput, Prisma.ProclamationUncheckedCreateWithoutSubscriptionLevelInput> | Prisma.ProclamationCreateWithoutSubscriptionLevelInput[] | Prisma.ProclamationUncheckedCreateWithoutSubscriptionLevelInput[]
|
id?: string
|
||||||
connectOrCreate?: Prisma.ProclamationCreateOrConnectWithoutSubscriptionLevelInput | Prisma.ProclamationCreateOrConnectWithoutSubscriptionLevelInput[]
|
name: string
|
||||||
createMany?: Prisma.ProclamationCreateManySubscriptionLevelInputEnvelope
|
content: string
|
||||||
connect?: Prisma.ProclamationWhereUniqueInput | Prisma.ProclamationWhereUniqueInput[]
|
solfeggioFrequencyId: number
|
||||||
|
categoryId: number
|
||||||
|
userProclamations?: Prisma.UserProclamationsUncheckedCreateNestedManyWithoutProclamationInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationUpdateManyWithoutSubscriptionLevelNestedInput = {
|
export type ProclamationCreateOrConnectWithoutGroupInput = {
|
||||||
create?: Prisma.XOR<Prisma.ProclamationCreateWithoutSubscriptionLevelInput, Prisma.ProclamationUncheckedCreateWithoutSubscriptionLevelInput> | Prisma.ProclamationCreateWithoutSubscriptionLevelInput[] | Prisma.ProclamationUncheckedCreateWithoutSubscriptionLevelInput[]
|
where: Prisma.ProclamationWhereUniqueInput
|
||||||
connectOrCreate?: Prisma.ProclamationCreateOrConnectWithoutSubscriptionLevelInput | Prisma.ProclamationCreateOrConnectWithoutSubscriptionLevelInput[]
|
create: Prisma.XOR<Prisma.ProclamationCreateWithoutGroupInput, Prisma.ProclamationUncheckedCreateWithoutGroupInput>
|
||||||
upsert?: Prisma.ProclamationUpsertWithWhereUniqueWithoutSubscriptionLevelInput | Prisma.ProclamationUpsertWithWhereUniqueWithoutSubscriptionLevelInput[]
|
|
||||||
createMany?: Prisma.ProclamationCreateManySubscriptionLevelInputEnvelope
|
|
||||||
set?: Prisma.ProclamationWhereUniqueInput | Prisma.ProclamationWhereUniqueInput[]
|
|
||||||
disconnect?: Prisma.ProclamationWhereUniqueInput | Prisma.ProclamationWhereUniqueInput[]
|
|
||||||
delete?: Prisma.ProclamationWhereUniqueInput | Prisma.ProclamationWhereUniqueInput[]
|
|
||||||
connect?: Prisma.ProclamationWhereUniqueInput | Prisma.ProclamationWhereUniqueInput[]
|
|
||||||
update?: Prisma.ProclamationUpdateWithWhereUniqueWithoutSubscriptionLevelInput | Prisma.ProclamationUpdateWithWhereUniqueWithoutSubscriptionLevelInput[]
|
|
||||||
updateMany?: Prisma.ProclamationUpdateManyWithWhereWithoutSubscriptionLevelInput | Prisma.ProclamationUpdateManyWithWhereWithoutSubscriptionLevelInput[]
|
|
||||||
deleteMany?: Prisma.ProclamationScalarWhereInput | Prisma.ProclamationScalarWhereInput[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationUncheckedUpdateManyWithoutSubscriptionLevelNestedInput = {
|
export type ProclamationCreateManyGroupInputEnvelope = {
|
||||||
create?: Prisma.XOR<Prisma.ProclamationCreateWithoutSubscriptionLevelInput, Prisma.ProclamationUncheckedCreateWithoutSubscriptionLevelInput> | Prisma.ProclamationCreateWithoutSubscriptionLevelInput[] | Prisma.ProclamationUncheckedCreateWithoutSubscriptionLevelInput[]
|
data: Prisma.ProclamationCreateManyGroupInput | Prisma.ProclamationCreateManyGroupInput[]
|
||||||
connectOrCreate?: Prisma.ProclamationCreateOrConnectWithoutSubscriptionLevelInput | Prisma.ProclamationCreateOrConnectWithoutSubscriptionLevelInput[]
|
skipDuplicates?: boolean
|
||||||
upsert?: Prisma.ProclamationUpsertWithWhereUniqueWithoutSubscriptionLevelInput | Prisma.ProclamationUpsertWithWhereUniqueWithoutSubscriptionLevelInput[]
|
}
|
||||||
createMany?: Prisma.ProclamationCreateManySubscriptionLevelInputEnvelope
|
|
||||||
set?: Prisma.ProclamationWhereUniqueInput | Prisma.ProclamationWhereUniqueInput[]
|
export type ProclamationUpsertWithWhereUniqueWithoutGroupInput = {
|
||||||
disconnect?: Prisma.ProclamationWhereUniqueInput | Prisma.ProclamationWhereUniqueInput[]
|
where: Prisma.ProclamationWhereUniqueInput
|
||||||
delete?: Prisma.ProclamationWhereUniqueInput | Prisma.ProclamationWhereUniqueInput[]
|
update: Prisma.XOR<Prisma.ProclamationUpdateWithoutGroupInput, Prisma.ProclamationUncheckedUpdateWithoutGroupInput>
|
||||||
connect?: Prisma.ProclamationWhereUniqueInput | Prisma.ProclamationWhereUniqueInput[]
|
create: Prisma.XOR<Prisma.ProclamationCreateWithoutGroupInput, Prisma.ProclamationUncheckedCreateWithoutGroupInput>
|
||||||
update?: Prisma.ProclamationUpdateWithWhereUniqueWithoutSubscriptionLevelInput | Prisma.ProclamationUpdateWithWhereUniqueWithoutSubscriptionLevelInput[]
|
}
|
||||||
updateMany?: Prisma.ProclamationUpdateManyWithWhereWithoutSubscriptionLevelInput | Prisma.ProclamationUpdateManyWithWhereWithoutSubscriptionLevelInput[]
|
|
||||||
deleteMany?: Prisma.ProclamationScalarWhereInput | Prisma.ProclamationScalarWhereInput[]
|
export type ProclamationUpdateWithWhereUniqueWithoutGroupInput = {
|
||||||
|
where: Prisma.ProclamationWhereUniqueInput
|
||||||
|
data: Prisma.XOR<Prisma.ProclamationUpdateWithoutGroupInput, Prisma.ProclamationUncheckedUpdateWithoutGroupInput>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProclamationUpdateManyWithWhereWithoutGroupInput = {
|
||||||
|
where: Prisma.ProclamationScalarWhereInput
|
||||||
|
data: Prisma.XOR<Prisma.ProclamationUpdateManyMutationInput, Prisma.ProclamationUncheckedUpdateManyWithoutGroupInput>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProclamationScalarWhereInput = {
|
||||||
|
AND?: Prisma.ProclamationScalarWhereInput | Prisma.ProclamationScalarWhereInput[]
|
||||||
|
OR?: Prisma.ProclamationScalarWhereInput[]
|
||||||
|
NOT?: Prisma.ProclamationScalarWhereInput | Prisma.ProclamationScalarWhereInput[]
|
||||||
|
id?: Prisma.StringFilter<"Proclamation"> | string
|
||||||
|
name?: Prisma.StringFilter<"Proclamation"> | string
|
||||||
|
content?: Prisma.StringFilter<"Proclamation"> | string
|
||||||
|
solfeggioFrequencyId?: Prisma.IntFilter<"Proclamation"> | number
|
||||||
|
categoryId?: Prisma.IntFilter<"Proclamation"> | number
|
||||||
|
groupId?: Prisma.IntFilter<"Proclamation"> | number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCreateWithoutUserProclamationsInput = {
|
export type ProclamationCreateWithoutUserProclamationsInput = {
|
||||||
@@ -557,7 +613,7 @@ export type ProclamationCreateWithoutUserProclamationsInput = {
|
|||||||
content: string
|
content: string
|
||||||
solfeggioFrequency: Prisma.SolfeggioFrequencyCreateNestedOneWithoutProclamationsInput
|
solfeggioFrequency: Prisma.SolfeggioFrequencyCreateNestedOneWithoutProclamationsInput
|
||||||
category: Prisma.ProclamationCategoryCreateNestedOneWithoutProclamationsInput
|
category: Prisma.ProclamationCategoryCreateNestedOneWithoutProclamationsInput
|
||||||
subscriptionLevel: Prisma.SubscriptionLevelCreateNestedOneWithoutProclamationsInput
|
group: Prisma.ProclamationGroupCreateNestedOneWithoutProclamationsInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationUncheckedCreateWithoutUserProclamationsInput = {
|
export type ProclamationUncheckedCreateWithoutUserProclamationsInput = {
|
||||||
@@ -566,7 +622,7 @@ export type ProclamationUncheckedCreateWithoutUserProclamationsInput = {
|
|||||||
content: string
|
content: string
|
||||||
solfeggioFrequencyId: number
|
solfeggioFrequencyId: number
|
||||||
categoryId: number
|
categoryId: number
|
||||||
subscriptionLevelId: number
|
groupId: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCreateOrConnectWithoutUserProclamationsInput = {
|
export type ProclamationCreateOrConnectWithoutUserProclamationsInput = {
|
||||||
@@ -591,7 +647,7 @@ export type ProclamationUpdateWithoutUserProclamationsInput = {
|
|||||||
content?: Prisma.StringFieldUpdateOperationsInput | string
|
content?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
solfeggioFrequency?: Prisma.SolfeggioFrequencyUpdateOneRequiredWithoutProclamationsNestedInput
|
solfeggioFrequency?: Prisma.SolfeggioFrequencyUpdateOneRequiredWithoutProclamationsNestedInput
|
||||||
category?: Prisma.ProclamationCategoryUpdateOneRequiredWithoutProclamationsNestedInput
|
category?: Prisma.ProclamationCategoryUpdateOneRequiredWithoutProclamationsNestedInput
|
||||||
subscriptionLevel?: Prisma.SubscriptionLevelUpdateOneRequiredWithoutProclamationsNestedInput
|
group?: Prisma.ProclamationGroupUpdateOneRequiredWithoutProclamationsNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationUncheckedUpdateWithoutUserProclamationsInput = {
|
export type ProclamationUncheckedUpdateWithoutUserProclamationsInput = {
|
||||||
@@ -600,7 +656,7 @@ export type ProclamationUncheckedUpdateWithoutUserProclamationsInput = {
|
|||||||
content?: Prisma.StringFieldUpdateOperationsInput | string
|
content?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
solfeggioFrequencyId?: Prisma.IntFieldUpdateOperationsInput | number
|
solfeggioFrequencyId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
categoryId?: Prisma.IntFieldUpdateOperationsInput | number
|
categoryId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
subscriptionLevelId?: Prisma.IntFieldUpdateOperationsInput | number
|
groupId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCreateWithoutSolfeggioFrequencyInput = {
|
export type ProclamationCreateWithoutSolfeggioFrequencyInput = {
|
||||||
@@ -608,8 +664,8 @@ export type ProclamationCreateWithoutSolfeggioFrequencyInput = {
|
|||||||
name: string
|
name: string
|
||||||
content: string
|
content: string
|
||||||
category: Prisma.ProclamationCategoryCreateNestedOneWithoutProclamationsInput
|
category: Prisma.ProclamationCategoryCreateNestedOneWithoutProclamationsInput
|
||||||
|
group: Prisma.ProclamationGroupCreateNestedOneWithoutProclamationsInput
|
||||||
userProclamations?: Prisma.UserProclamationsCreateNestedManyWithoutProclamationInput
|
userProclamations?: Prisma.UserProclamationsCreateNestedManyWithoutProclamationInput
|
||||||
subscriptionLevel: Prisma.SubscriptionLevelCreateNestedOneWithoutProclamationsInput
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationUncheckedCreateWithoutSolfeggioFrequencyInput = {
|
export type ProclamationUncheckedCreateWithoutSolfeggioFrequencyInput = {
|
||||||
@@ -617,7 +673,7 @@ export type ProclamationUncheckedCreateWithoutSolfeggioFrequencyInput = {
|
|||||||
name: string
|
name: string
|
||||||
content: string
|
content: string
|
||||||
categoryId: number
|
categoryId: number
|
||||||
subscriptionLevelId: number
|
groupId: number
|
||||||
userProclamations?: Prisma.UserProclamationsUncheckedCreateNestedManyWithoutProclamationInput
|
userProclamations?: Prisma.UserProclamationsUncheckedCreateNestedManyWithoutProclamationInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -647,25 +703,13 @@ export type ProclamationUpdateManyWithWhereWithoutSolfeggioFrequencyInput = {
|
|||||||
data: Prisma.XOR<Prisma.ProclamationUpdateManyMutationInput, Prisma.ProclamationUncheckedUpdateManyWithoutSolfeggioFrequencyInput>
|
data: Prisma.XOR<Prisma.ProclamationUpdateManyMutationInput, Prisma.ProclamationUncheckedUpdateManyWithoutSolfeggioFrequencyInput>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationScalarWhereInput = {
|
|
||||||
AND?: Prisma.ProclamationScalarWhereInput | Prisma.ProclamationScalarWhereInput[]
|
|
||||||
OR?: Prisma.ProclamationScalarWhereInput[]
|
|
||||||
NOT?: Prisma.ProclamationScalarWhereInput | Prisma.ProclamationScalarWhereInput[]
|
|
||||||
id?: Prisma.StringFilter<"Proclamation"> | string
|
|
||||||
name?: Prisma.StringFilter<"Proclamation"> | string
|
|
||||||
content?: Prisma.StringFilter<"Proclamation"> | string
|
|
||||||
solfeggioFrequencyId?: Prisma.IntFilter<"Proclamation"> | number
|
|
||||||
categoryId?: Prisma.IntFilter<"Proclamation"> | number
|
|
||||||
subscriptionLevelId?: Prisma.IntFilter<"Proclamation"> | number
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCreateWithoutCategoryInput = {
|
export type ProclamationCreateWithoutCategoryInput = {
|
||||||
id?: string
|
id?: string
|
||||||
name: string
|
name: string
|
||||||
content: string
|
content: string
|
||||||
solfeggioFrequency: Prisma.SolfeggioFrequencyCreateNestedOneWithoutProclamationsInput
|
solfeggioFrequency: Prisma.SolfeggioFrequencyCreateNestedOneWithoutProclamationsInput
|
||||||
|
group: Prisma.ProclamationGroupCreateNestedOneWithoutProclamationsInput
|
||||||
userProclamations?: Prisma.UserProclamationsCreateNestedManyWithoutProclamationInput
|
userProclamations?: Prisma.UserProclamationsCreateNestedManyWithoutProclamationInput
|
||||||
subscriptionLevel: Prisma.SubscriptionLevelCreateNestedOneWithoutProclamationsInput
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationUncheckedCreateWithoutCategoryInput = {
|
export type ProclamationUncheckedCreateWithoutCategoryInput = {
|
||||||
@@ -673,7 +717,7 @@ export type ProclamationUncheckedCreateWithoutCategoryInput = {
|
|||||||
name: string
|
name: string
|
||||||
content: string
|
content: string
|
||||||
solfeggioFrequencyId: number
|
solfeggioFrequencyId: number
|
||||||
subscriptionLevelId: number
|
groupId: number
|
||||||
userProclamations?: Prisma.UserProclamationsUncheckedCreateNestedManyWithoutProclamationInput
|
userProclamations?: Prisma.UserProclamationsUncheckedCreateNestedManyWithoutProclamationInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -703,48 +747,38 @@ export type ProclamationUpdateManyWithWhereWithoutCategoryInput = {
|
|||||||
data: Prisma.XOR<Prisma.ProclamationUpdateManyMutationInput, Prisma.ProclamationUncheckedUpdateManyWithoutCategoryInput>
|
data: Prisma.XOR<Prisma.ProclamationUpdateManyMutationInput, Prisma.ProclamationUncheckedUpdateManyWithoutCategoryInput>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCreateWithoutSubscriptionLevelInput = {
|
export type ProclamationCreateManyGroupInput = {
|
||||||
id?: string
|
|
||||||
name: string
|
|
||||||
content: string
|
|
||||||
solfeggioFrequency: Prisma.SolfeggioFrequencyCreateNestedOneWithoutProclamationsInput
|
|
||||||
category: Prisma.ProclamationCategoryCreateNestedOneWithoutProclamationsInput
|
|
||||||
userProclamations?: Prisma.UserProclamationsCreateNestedManyWithoutProclamationInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationUncheckedCreateWithoutSubscriptionLevelInput = {
|
|
||||||
id?: string
|
id?: string
|
||||||
name: string
|
name: string
|
||||||
content: string
|
content: string
|
||||||
solfeggioFrequencyId: number
|
solfeggioFrequencyId: number
|
||||||
categoryId: number
|
categoryId: number
|
||||||
userProclamations?: Prisma.UserProclamationsUncheckedCreateNestedManyWithoutProclamationInput
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCreateOrConnectWithoutSubscriptionLevelInput = {
|
export type ProclamationUpdateWithoutGroupInput = {
|
||||||
where: Prisma.ProclamationWhereUniqueInput
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
create: Prisma.XOR<Prisma.ProclamationCreateWithoutSubscriptionLevelInput, Prisma.ProclamationUncheckedCreateWithoutSubscriptionLevelInput>
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
content?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
solfeggioFrequency?: Prisma.SolfeggioFrequencyUpdateOneRequiredWithoutProclamationsNestedInput
|
||||||
|
category?: Prisma.ProclamationCategoryUpdateOneRequiredWithoutProclamationsNestedInput
|
||||||
|
userProclamations?: Prisma.UserProclamationsUpdateManyWithoutProclamationNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCreateManySubscriptionLevelInputEnvelope = {
|
export type ProclamationUncheckedUpdateWithoutGroupInput = {
|
||||||
data: Prisma.ProclamationCreateManySubscriptionLevelInput | Prisma.ProclamationCreateManySubscriptionLevelInput[]
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
skipDuplicates?: boolean
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
content?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
solfeggioFrequencyId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
|
categoryId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
|
userProclamations?: Prisma.UserProclamationsUncheckedUpdateManyWithoutProclamationNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationUpsertWithWhereUniqueWithoutSubscriptionLevelInput = {
|
export type ProclamationUncheckedUpdateManyWithoutGroupInput = {
|
||||||
where: Prisma.ProclamationWhereUniqueInput
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
update: Prisma.XOR<Prisma.ProclamationUpdateWithoutSubscriptionLevelInput, Prisma.ProclamationUncheckedUpdateWithoutSubscriptionLevelInput>
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
create: Prisma.XOR<Prisma.ProclamationCreateWithoutSubscriptionLevelInput, Prisma.ProclamationUncheckedCreateWithoutSubscriptionLevelInput>
|
content?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
}
|
solfeggioFrequencyId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
|
categoryId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
export type ProclamationUpdateWithWhereUniqueWithoutSubscriptionLevelInput = {
|
|
||||||
where: Prisma.ProclamationWhereUniqueInput
|
|
||||||
data: Prisma.XOR<Prisma.ProclamationUpdateWithoutSubscriptionLevelInput, Prisma.ProclamationUncheckedUpdateWithoutSubscriptionLevelInput>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationUpdateManyWithWhereWithoutSubscriptionLevelInput = {
|
|
||||||
where: Prisma.ProclamationScalarWhereInput
|
|
||||||
data: Prisma.XOR<Prisma.ProclamationUpdateManyMutationInput, Prisma.ProclamationUncheckedUpdateManyWithoutSubscriptionLevelInput>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCreateManySolfeggioFrequencyInput = {
|
export type ProclamationCreateManySolfeggioFrequencyInput = {
|
||||||
@@ -752,7 +786,7 @@ export type ProclamationCreateManySolfeggioFrequencyInput = {
|
|||||||
name: string
|
name: string
|
||||||
content: string
|
content: string
|
||||||
categoryId: number
|
categoryId: number
|
||||||
subscriptionLevelId: number
|
groupId: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationUpdateWithoutSolfeggioFrequencyInput = {
|
export type ProclamationUpdateWithoutSolfeggioFrequencyInput = {
|
||||||
@@ -760,8 +794,8 @@ export type ProclamationUpdateWithoutSolfeggioFrequencyInput = {
|
|||||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
content?: Prisma.StringFieldUpdateOperationsInput | string
|
content?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
category?: Prisma.ProclamationCategoryUpdateOneRequiredWithoutProclamationsNestedInput
|
category?: Prisma.ProclamationCategoryUpdateOneRequiredWithoutProclamationsNestedInput
|
||||||
|
group?: Prisma.ProclamationGroupUpdateOneRequiredWithoutProclamationsNestedInput
|
||||||
userProclamations?: Prisma.UserProclamationsUpdateManyWithoutProclamationNestedInput
|
userProclamations?: Prisma.UserProclamationsUpdateManyWithoutProclamationNestedInput
|
||||||
subscriptionLevel?: Prisma.SubscriptionLevelUpdateOneRequiredWithoutProclamationsNestedInput
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationUncheckedUpdateWithoutSolfeggioFrequencyInput = {
|
export type ProclamationUncheckedUpdateWithoutSolfeggioFrequencyInput = {
|
||||||
@@ -769,7 +803,7 @@ export type ProclamationUncheckedUpdateWithoutSolfeggioFrequencyInput = {
|
|||||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
content?: Prisma.StringFieldUpdateOperationsInput | string
|
content?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
categoryId?: Prisma.IntFieldUpdateOperationsInput | number
|
categoryId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
subscriptionLevelId?: Prisma.IntFieldUpdateOperationsInput | number
|
groupId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
userProclamations?: Prisma.UserProclamationsUncheckedUpdateManyWithoutProclamationNestedInput
|
userProclamations?: Prisma.UserProclamationsUncheckedUpdateManyWithoutProclamationNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -778,7 +812,7 @@ export type ProclamationUncheckedUpdateManyWithoutSolfeggioFrequencyInput = {
|
|||||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
content?: Prisma.StringFieldUpdateOperationsInput | string
|
content?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
categoryId?: Prisma.IntFieldUpdateOperationsInput | number
|
categoryId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
subscriptionLevelId?: Prisma.IntFieldUpdateOperationsInput | number
|
groupId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCreateManyCategoryInput = {
|
export type ProclamationCreateManyCategoryInput = {
|
||||||
@@ -786,7 +820,7 @@ export type ProclamationCreateManyCategoryInput = {
|
|||||||
name: string
|
name: string
|
||||||
content: string
|
content: string
|
||||||
solfeggioFrequencyId: number
|
solfeggioFrequencyId: number
|
||||||
subscriptionLevelId: number
|
groupId: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationUpdateWithoutCategoryInput = {
|
export type ProclamationUpdateWithoutCategoryInput = {
|
||||||
@@ -794,8 +828,8 @@ export type ProclamationUpdateWithoutCategoryInput = {
|
|||||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
content?: Prisma.StringFieldUpdateOperationsInput | string
|
content?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
solfeggioFrequency?: Prisma.SolfeggioFrequencyUpdateOneRequiredWithoutProclamationsNestedInput
|
solfeggioFrequency?: Prisma.SolfeggioFrequencyUpdateOneRequiredWithoutProclamationsNestedInput
|
||||||
|
group?: Prisma.ProclamationGroupUpdateOneRequiredWithoutProclamationsNestedInput
|
||||||
userProclamations?: Prisma.UserProclamationsUpdateManyWithoutProclamationNestedInput
|
userProclamations?: Prisma.UserProclamationsUpdateManyWithoutProclamationNestedInput
|
||||||
subscriptionLevel?: Prisma.SubscriptionLevelUpdateOneRequiredWithoutProclamationsNestedInput
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationUncheckedUpdateWithoutCategoryInput = {
|
export type ProclamationUncheckedUpdateWithoutCategoryInput = {
|
||||||
@@ -803,7 +837,7 @@ export type ProclamationUncheckedUpdateWithoutCategoryInput = {
|
|||||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
content?: Prisma.StringFieldUpdateOperationsInput | string
|
content?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
solfeggioFrequencyId?: Prisma.IntFieldUpdateOperationsInput | number
|
solfeggioFrequencyId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
subscriptionLevelId?: Prisma.IntFieldUpdateOperationsInput | number
|
groupId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
userProclamations?: Prisma.UserProclamationsUncheckedUpdateManyWithoutProclamationNestedInput
|
userProclamations?: Prisma.UserProclamationsUncheckedUpdateManyWithoutProclamationNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -812,41 +846,7 @@ export type ProclamationUncheckedUpdateManyWithoutCategoryInput = {
|
|||||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
content?: Prisma.StringFieldUpdateOperationsInput | string
|
content?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
solfeggioFrequencyId?: Prisma.IntFieldUpdateOperationsInput | number
|
solfeggioFrequencyId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
subscriptionLevelId?: Prisma.IntFieldUpdateOperationsInput | number
|
groupId?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCreateManySubscriptionLevelInput = {
|
|
||||||
id?: string
|
|
||||||
name: string
|
|
||||||
content: string
|
|
||||||
solfeggioFrequencyId: number
|
|
||||||
categoryId: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationUpdateWithoutSubscriptionLevelInput = {
|
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
|
||||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
|
||||||
content?: Prisma.StringFieldUpdateOperationsInput | string
|
|
||||||
solfeggioFrequency?: Prisma.SolfeggioFrequencyUpdateOneRequiredWithoutProclamationsNestedInput
|
|
||||||
category?: Prisma.ProclamationCategoryUpdateOneRequiredWithoutProclamationsNestedInput
|
|
||||||
userProclamations?: Prisma.UserProclamationsUpdateManyWithoutProclamationNestedInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationUncheckedUpdateWithoutSubscriptionLevelInput = {
|
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
|
||||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
|
||||||
content?: Prisma.StringFieldUpdateOperationsInput | string
|
|
||||||
solfeggioFrequencyId?: Prisma.IntFieldUpdateOperationsInput | number
|
|
||||||
categoryId?: Prisma.IntFieldUpdateOperationsInput | number
|
|
||||||
userProclamations?: Prisma.UserProclamationsUncheckedUpdateManyWithoutProclamationNestedInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationUncheckedUpdateManyWithoutSubscriptionLevelInput = {
|
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
|
||||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
|
||||||
content?: Prisma.StringFieldUpdateOperationsInput | string
|
|
||||||
solfeggioFrequencyId?: Prisma.IntFieldUpdateOperationsInput | number
|
|
||||||
categoryId?: Prisma.IntFieldUpdateOperationsInput | number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -886,11 +886,11 @@ export type ProclamationSelect<ExtArgs extends runtime.Types.Extensions.Internal
|
|||||||
content?: boolean
|
content?: boolean
|
||||||
solfeggioFrequencyId?: boolean
|
solfeggioFrequencyId?: boolean
|
||||||
categoryId?: boolean
|
categoryId?: boolean
|
||||||
subscriptionLevelId?: boolean
|
groupId?: boolean
|
||||||
solfeggioFrequency?: boolean | Prisma.SolfeggioFrequencyDefaultArgs<ExtArgs>
|
solfeggioFrequency?: boolean | Prisma.SolfeggioFrequencyDefaultArgs<ExtArgs>
|
||||||
category?: boolean | Prisma.ProclamationCategoryDefaultArgs<ExtArgs>
|
category?: boolean | Prisma.ProclamationCategoryDefaultArgs<ExtArgs>
|
||||||
|
group?: boolean | Prisma.ProclamationGroupDefaultArgs<ExtArgs>
|
||||||
userProclamations?: boolean | Prisma.Proclamation$userProclamationsArgs<ExtArgs>
|
userProclamations?: boolean | Prisma.Proclamation$userProclamationsArgs<ExtArgs>
|
||||||
subscriptionLevel?: boolean | Prisma.SubscriptionLevelDefaultArgs<ExtArgs>
|
|
||||||
_count?: boolean | Prisma.ProclamationCountOutputTypeDefaultArgs<ExtArgs>
|
_count?: boolean | Prisma.ProclamationCountOutputTypeDefaultArgs<ExtArgs>
|
||||||
}, ExtArgs["result"]["proclamation"]>
|
}, ExtArgs["result"]["proclamation"]>
|
||||||
|
|
||||||
@@ -900,10 +900,10 @@ export type ProclamationSelectCreateManyAndReturn<ExtArgs extends runtime.Types.
|
|||||||
content?: boolean
|
content?: boolean
|
||||||
solfeggioFrequencyId?: boolean
|
solfeggioFrequencyId?: boolean
|
||||||
categoryId?: boolean
|
categoryId?: boolean
|
||||||
subscriptionLevelId?: boolean
|
groupId?: boolean
|
||||||
solfeggioFrequency?: boolean | Prisma.SolfeggioFrequencyDefaultArgs<ExtArgs>
|
solfeggioFrequency?: boolean | Prisma.SolfeggioFrequencyDefaultArgs<ExtArgs>
|
||||||
category?: boolean | Prisma.ProclamationCategoryDefaultArgs<ExtArgs>
|
category?: boolean | Prisma.ProclamationCategoryDefaultArgs<ExtArgs>
|
||||||
subscriptionLevel?: boolean | Prisma.SubscriptionLevelDefaultArgs<ExtArgs>
|
group?: boolean | Prisma.ProclamationGroupDefaultArgs<ExtArgs>
|
||||||
}, ExtArgs["result"]["proclamation"]>
|
}, ExtArgs["result"]["proclamation"]>
|
||||||
|
|
||||||
export type ProclamationSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type ProclamationSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
@@ -912,10 +912,10 @@ export type ProclamationSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.
|
|||||||
content?: boolean
|
content?: boolean
|
||||||
solfeggioFrequencyId?: boolean
|
solfeggioFrequencyId?: boolean
|
||||||
categoryId?: boolean
|
categoryId?: boolean
|
||||||
subscriptionLevelId?: boolean
|
groupId?: boolean
|
||||||
solfeggioFrequency?: boolean | Prisma.SolfeggioFrequencyDefaultArgs<ExtArgs>
|
solfeggioFrequency?: boolean | Prisma.SolfeggioFrequencyDefaultArgs<ExtArgs>
|
||||||
category?: boolean | Prisma.ProclamationCategoryDefaultArgs<ExtArgs>
|
category?: boolean | Prisma.ProclamationCategoryDefaultArgs<ExtArgs>
|
||||||
subscriptionLevel?: boolean | Prisma.SubscriptionLevelDefaultArgs<ExtArgs>
|
group?: boolean | Prisma.ProclamationGroupDefaultArgs<ExtArgs>
|
||||||
}, ExtArgs["result"]["proclamation"]>
|
}, ExtArgs["result"]["proclamation"]>
|
||||||
|
|
||||||
export type ProclamationSelectScalar = {
|
export type ProclamationSelectScalar = {
|
||||||
@@ -924,26 +924,26 @@ export type ProclamationSelectScalar = {
|
|||||||
content?: boolean
|
content?: boolean
|
||||||
solfeggioFrequencyId?: boolean
|
solfeggioFrequencyId?: boolean
|
||||||
categoryId?: boolean
|
categoryId?: boolean
|
||||||
subscriptionLevelId?: boolean
|
groupId?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "name" | "content" | "solfeggioFrequencyId" | "categoryId" | "subscriptionLevelId", ExtArgs["result"]["proclamation"]>
|
export type ProclamationOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "name" | "content" | "solfeggioFrequencyId" | "categoryId" | "groupId", ExtArgs["result"]["proclamation"]>
|
||||||
export type ProclamationInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type ProclamationInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
solfeggioFrequency?: boolean | Prisma.SolfeggioFrequencyDefaultArgs<ExtArgs>
|
solfeggioFrequency?: boolean | Prisma.SolfeggioFrequencyDefaultArgs<ExtArgs>
|
||||||
category?: boolean | Prisma.ProclamationCategoryDefaultArgs<ExtArgs>
|
category?: boolean | Prisma.ProclamationCategoryDefaultArgs<ExtArgs>
|
||||||
|
group?: boolean | Prisma.ProclamationGroupDefaultArgs<ExtArgs>
|
||||||
userProclamations?: boolean | Prisma.Proclamation$userProclamationsArgs<ExtArgs>
|
userProclamations?: boolean | Prisma.Proclamation$userProclamationsArgs<ExtArgs>
|
||||||
subscriptionLevel?: boolean | Prisma.SubscriptionLevelDefaultArgs<ExtArgs>
|
|
||||||
_count?: boolean | Prisma.ProclamationCountOutputTypeDefaultArgs<ExtArgs>
|
_count?: boolean | Prisma.ProclamationCountOutputTypeDefaultArgs<ExtArgs>
|
||||||
}
|
}
|
||||||
export type ProclamationIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type ProclamationIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
solfeggioFrequency?: boolean | Prisma.SolfeggioFrequencyDefaultArgs<ExtArgs>
|
solfeggioFrequency?: boolean | Prisma.SolfeggioFrequencyDefaultArgs<ExtArgs>
|
||||||
category?: boolean | Prisma.ProclamationCategoryDefaultArgs<ExtArgs>
|
category?: boolean | Prisma.ProclamationCategoryDefaultArgs<ExtArgs>
|
||||||
subscriptionLevel?: boolean | Prisma.SubscriptionLevelDefaultArgs<ExtArgs>
|
group?: boolean | Prisma.ProclamationGroupDefaultArgs<ExtArgs>
|
||||||
}
|
}
|
||||||
export type ProclamationIncludeUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type ProclamationIncludeUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
solfeggioFrequency?: boolean | Prisma.SolfeggioFrequencyDefaultArgs<ExtArgs>
|
solfeggioFrequency?: boolean | Prisma.SolfeggioFrequencyDefaultArgs<ExtArgs>
|
||||||
category?: boolean | Prisma.ProclamationCategoryDefaultArgs<ExtArgs>
|
category?: boolean | Prisma.ProclamationCategoryDefaultArgs<ExtArgs>
|
||||||
subscriptionLevel?: boolean | Prisma.SubscriptionLevelDefaultArgs<ExtArgs>
|
group?: boolean | Prisma.ProclamationGroupDefaultArgs<ExtArgs>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type $ProclamationPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type $ProclamationPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
@@ -951,8 +951,8 @@ export type $ProclamationPayload<ExtArgs extends runtime.Types.Extensions.Intern
|
|||||||
objects: {
|
objects: {
|
||||||
solfeggioFrequency: Prisma.$SolfeggioFrequencyPayload<ExtArgs>
|
solfeggioFrequency: Prisma.$SolfeggioFrequencyPayload<ExtArgs>
|
||||||
category: Prisma.$ProclamationCategoryPayload<ExtArgs>
|
category: Prisma.$ProclamationCategoryPayload<ExtArgs>
|
||||||
|
group: Prisma.$ProclamationGroupPayload<ExtArgs>
|
||||||
userProclamations: Prisma.$UserProclamationsPayload<ExtArgs>[]
|
userProclamations: Prisma.$UserProclamationsPayload<ExtArgs>[]
|
||||||
subscriptionLevel: Prisma.$SubscriptionLevelPayload<ExtArgs>
|
|
||||||
}
|
}
|
||||||
scalars: runtime.Types.Extensions.GetPayloadResult<{
|
scalars: runtime.Types.Extensions.GetPayloadResult<{
|
||||||
id: string
|
id: string
|
||||||
@@ -960,7 +960,7 @@ export type $ProclamationPayload<ExtArgs extends runtime.Types.Extensions.Intern
|
|||||||
content: string
|
content: string
|
||||||
solfeggioFrequencyId: number
|
solfeggioFrequencyId: number
|
||||||
categoryId: number
|
categoryId: number
|
||||||
subscriptionLevelId: number
|
groupId: number
|
||||||
}, ExtArgs["result"]["proclamation"]>
|
}, ExtArgs["result"]["proclamation"]>
|
||||||
composites: {}
|
composites: {}
|
||||||
}
|
}
|
||||||
@@ -1357,8 +1357,8 @@ export interface Prisma__ProclamationClient<T, Null = never, ExtArgs extends run
|
|||||||
readonly [Symbol.toStringTag]: "PrismaPromise"
|
readonly [Symbol.toStringTag]: "PrismaPromise"
|
||||||
solfeggioFrequency<T extends Prisma.SolfeggioFrequencyDefaultArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.SolfeggioFrequencyDefaultArgs<ExtArgs>>): Prisma.Prisma__SolfeggioFrequencyClient<runtime.Types.Result.GetResult<Prisma.$SolfeggioFrequencyPayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | Null, Null, ExtArgs, GlobalOmitOptions>
|
solfeggioFrequency<T extends Prisma.SolfeggioFrequencyDefaultArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.SolfeggioFrequencyDefaultArgs<ExtArgs>>): Prisma.Prisma__SolfeggioFrequencyClient<runtime.Types.Result.GetResult<Prisma.$SolfeggioFrequencyPayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | Null, Null, ExtArgs, GlobalOmitOptions>
|
||||||
category<T extends Prisma.ProclamationCategoryDefaultArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.ProclamationCategoryDefaultArgs<ExtArgs>>): Prisma.Prisma__ProclamationCategoryClient<runtime.Types.Result.GetResult<Prisma.$ProclamationCategoryPayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | Null, Null, ExtArgs, GlobalOmitOptions>
|
category<T extends Prisma.ProclamationCategoryDefaultArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.ProclamationCategoryDefaultArgs<ExtArgs>>): Prisma.Prisma__ProclamationCategoryClient<runtime.Types.Result.GetResult<Prisma.$ProclamationCategoryPayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | Null, Null, ExtArgs, GlobalOmitOptions>
|
||||||
|
group<T extends Prisma.ProclamationGroupDefaultArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.ProclamationGroupDefaultArgs<ExtArgs>>): Prisma.Prisma__ProclamationGroupClient<runtime.Types.Result.GetResult<Prisma.$ProclamationGroupPayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | Null, Null, ExtArgs, GlobalOmitOptions>
|
||||||
userProclamations<T extends Prisma.Proclamation$userProclamationsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Proclamation$userProclamationsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$UserProclamationsPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
userProclamations<T extends Prisma.Proclamation$userProclamationsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Proclamation$userProclamationsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$UserProclamationsPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||||
subscriptionLevel<T extends Prisma.SubscriptionLevelDefaultArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.SubscriptionLevelDefaultArgs<ExtArgs>>): Prisma.Prisma__SubscriptionLevelClient<runtime.Types.Result.GetResult<Prisma.$SubscriptionLevelPayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | Null, Null, ExtArgs, GlobalOmitOptions>
|
|
||||||
/**
|
/**
|
||||||
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
||||||
* @param onfulfilled The callback to execute when the Promise is resolved.
|
* @param onfulfilled The callback to execute when the Promise is resolved.
|
||||||
@@ -1393,7 +1393,7 @@ export interface ProclamationFieldRefs {
|
|||||||
readonly content: Prisma.FieldRef<"Proclamation", 'String'>
|
readonly content: Prisma.FieldRef<"Proclamation", 'String'>
|
||||||
readonly solfeggioFrequencyId: Prisma.FieldRef<"Proclamation", 'Int'>
|
readonly solfeggioFrequencyId: Prisma.FieldRef<"Proclamation", 'Int'>
|
||||||
readonly categoryId: Prisma.FieldRef<"Proclamation", 'Int'>
|
readonly categoryId: Prisma.FieldRef<"Proclamation", 'Int'>
|
||||||
readonly subscriptionLevelId: Prisma.FieldRef<"Proclamation", 'Int'>
|
readonly groupId: Prisma.FieldRef<"Proclamation", 'Int'>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -29,34 +29,29 @@ export type AggregateProclamationCategory = {
|
|||||||
export type ProclamationCategoryAvgAggregateOutputType = {
|
export type ProclamationCategoryAvgAggregateOutputType = {
|
||||||
id: number | null
|
id: number | null
|
||||||
sortOrder: number | null
|
sortOrder: number | null
|
||||||
parentId: number | null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategorySumAggregateOutputType = {
|
export type ProclamationCategorySumAggregateOutputType = {
|
||||||
id: number | null
|
id: number | null
|
||||||
sortOrder: number | null
|
sortOrder: number | null
|
||||||
parentId: number | null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryMinAggregateOutputType = {
|
export type ProclamationCategoryMinAggregateOutputType = {
|
||||||
id: number | null
|
id: number | null
|
||||||
name: string | null
|
name: string | null
|
||||||
sortOrder: number | null
|
sortOrder: number | null
|
||||||
parentId: number | null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryMaxAggregateOutputType = {
|
export type ProclamationCategoryMaxAggregateOutputType = {
|
||||||
id: number | null
|
id: number | null
|
||||||
name: string | null
|
name: string | null
|
||||||
sortOrder: number | null
|
sortOrder: number | null
|
||||||
parentId: number | null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryCountAggregateOutputType = {
|
export type ProclamationCategoryCountAggregateOutputType = {
|
||||||
id: number
|
id: number
|
||||||
name: number
|
name: number
|
||||||
sortOrder: number
|
sortOrder: number
|
||||||
parentId: number
|
|
||||||
_all: number
|
_all: number
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,34 +59,29 @@ export type ProclamationCategoryCountAggregateOutputType = {
|
|||||||
export type ProclamationCategoryAvgAggregateInputType = {
|
export type ProclamationCategoryAvgAggregateInputType = {
|
||||||
id?: true
|
id?: true
|
||||||
sortOrder?: true
|
sortOrder?: true
|
||||||
parentId?: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategorySumAggregateInputType = {
|
export type ProclamationCategorySumAggregateInputType = {
|
||||||
id?: true
|
id?: true
|
||||||
sortOrder?: true
|
sortOrder?: true
|
||||||
parentId?: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryMinAggregateInputType = {
|
export type ProclamationCategoryMinAggregateInputType = {
|
||||||
id?: true
|
id?: true
|
||||||
name?: true
|
name?: true
|
||||||
sortOrder?: true
|
sortOrder?: true
|
||||||
parentId?: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryMaxAggregateInputType = {
|
export type ProclamationCategoryMaxAggregateInputType = {
|
||||||
id?: true
|
id?: true
|
||||||
name?: true
|
name?: true
|
||||||
sortOrder?: true
|
sortOrder?: true
|
||||||
parentId?: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryCountAggregateInputType = {
|
export type ProclamationCategoryCountAggregateInputType = {
|
||||||
id?: true
|
id?: true
|
||||||
name?: true
|
name?: true
|
||||||
sortOrder?: true
|
sortOrder?: true
|
||||||
parentId?: true
|
|
||||||
_all?: true
|
_all?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,7 +175,6 @@ export type ProclamationCategoryGroupByOutputType = {
|
|||||||
id: number
|
id: number
|
||||||
name: string
|
name: string
|
||||||
sortOrder: number
|
sortOrder: number
|
||||||
parentId: number | null
|
|
||||||
_count: ProclamationCategoryCountAggregateOutputType | null
|
_count: ProclamationCategoryCountAggregateOutputType | null
|
||||||
_avg: ProclamationCategoryAvgAggregateOutputType | null
|
_avg: ProclamationCategoryAvgAggregateOutputType | null
|
||||||
_sum: ProclamationCategorySumAggregateOutputType | null
|
_sum: ProclamationCategorySumAggregateOutputType | null
|
||||||
@@ -215,44 +204,30 @@ export type ProclamationCategoryWhereInput = {
|
|||||||
id?: Prisma.IntFilter<"ProclamationCategory"> | number
|
id?: Prisma.IntFilter<"ProclamationCategory"> | number
|
||||||
name?: Prisma.StringFilter<"ProclamationCategory"> | string
|
name?: Prisma.StringFilter<"ProclamationCategory"> | string
|
||||||
sortOrder?: Prisma.IntFilter<"ProclamationCategory"> | number
|
sortOrder?: Prisma.IntFilter<"ProclamationCategory"> | number
|
||||||
parentId?: Prisma.IntNullableFilter<"ProclamationCategory"> | number | null
|
|
||||||
parent?: Prisma.XOR<Prisma.ProclamationCategoryNullableScalarRelationFilter, Prisma.ProclamationCategoryWhereInput> | null
|
|
||||||
children?: Prisma.ProclamationCategoryListRelationFilter
|
|
||||||
proclamations?: Prisma.ProclamationListRelationFilter
|
proclamations?: Prisma.ProclamationListRelationFilter
|
||||||
subscriptionLevels?: Prisma.ProclamationCategorySubscriptionLevelListRelationFilter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryOrderByWithRelationInput = {
|
export type ProclamationCategoryOrderByWithRelationInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
name?: Prisma.SortOrder
|
name?: Prisma.SortOrder
|
||||||
sortOrder?: Prisma.SortOrder
|
sortOrder?: Prisma.SortOrder
|
||||||
parentId?: Prisma.SortOrderInput | Prisma.SortOrder
|
|
||||||
parent?: Prisma.ProclamationCategoryOrderByWithRelationInput
|
|
||||||
children?: Prisma.ProclamationCategoryOrderByRelationAggregateInput
|
|
||||||
proclamations?: Prisma.ProclamationOrderByRelationAggregateInput
|
proclamations?: Prisma.ProclamationOrderByRelationAggregateInput
|
||||||
subscriptionLevels?: Prisma.ProclamationCategorySubscriptionLevelOrderByRelationAggregateInput
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryWhereUniqueInput = Prisma.AtLeast<{
|
export type ProclamationCategoryWhereUniqueInput = Prisma.AtLeast<{
|
||||||
id?: number
|
id?: number
|
||||||
parentId_name?: Prisma.ProclamationCategoryParentIdNameCompoundUniqueInput
|
name?: string
|
||||||
AND?: Prisma.ProclamationCategoryWhereInput | Prisma.ProclamationCategoryWhereInput[]
|
AND?: Prisma.ProclamationCategoryWhereInput | Prisma.ProclamationCategoryWhereInput[]
|
||||||
OR?: Prisma.ProclamationCategoryWhereInput[]
|
OR?: Prisma.ProclamationCategoryWhereInput[]
|
||||||
NOT?: Prisma.ProclamationCategoryWhereInput | Prisma.ProclamationCategoryWhereInput[]
|
NOT?: Prisma.ProclamationCategoryWhereInput | Prisma.ProclamationCategoryWhereInput[]
|
||||||
name?: Prisma.StringFilter<"ProclamationCategory"> | string
|
|
||||||
sortOrder?: Prisma.IntFilter<"ProclamationCategory"> | number
|
sortOrder?: Prisma.IntFilter<"ProclamationCategory"> | number
|
||||||
parentId?: Prisma.IntNullableFilter<"ProclamationCategory"> | number | null
|
|
||||||
parent?: Prisma.XOR<Prisma.ProclamationCategoryNullableScalarRelationFilter, Prisma.ProclamationCategoryWhereInput> | null
|
|
||||||
children?: Prisma.ProclamationCategoryListRelationFilter
|
|
||||||
proclamations?: Prisma.ProclamationListRelationFilter
|
proclamations?: Prisma.ProclamationListRelationFilter
|
||||||
subscriptionLevels?: Prisma.ProclamationCategorySubscriptionLevelListRelationFilter
|
}, "id" | "name">
|
||||||
}, "id" | "parentId_name">
|
|
||||||
|
|
||||||
export type ProclamationCategoryOrderByWithAggregationInput = {
|
export type ProclamationCategoryOrderByWithAggregationInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
name?: Prisma.SortOrder
|
name?: Prisma.SortOrder
|
||||||
sortOrder?: Prisma.SortOrder
|
sortOrder?: Prisma.SortOrder
|
||||||
parentId?: Prisma.SortOrderInput | Prisma.SortOrder
|
|
||||||
_count?: Prisma.ProclamationCategoryCountOrderByAggregateInput
|
_count?: Prisma.ProclamationCategoryCountOrderByAggregateInput
|
||||||
_avg?: Prisma.ProclamationCategoryAvgOrderByAggregateInput
|
_avg?: Prisma.ProclamationCategoryAvgOrderByAggregateInput
|
||||||
_max?: Prisma.ProclamationCategoryMaxOrderByAggregateInput
|
_max?: Prisma.ProclamationCategoryMaxOrderByAggregateInput
|
||||||
@@ -267,52 +242,38 @@ export type ProclamationCategoryScalarWhereWithAggregatesInput = {
|
|||||||
id?: Prisma.IntWithAggregatesFilter<"ProclamationCategory"> | number
|
id?: Prisma.IntWithAggregatesFilter<"ProclamationCategory"> | number
|
||||||
name?: Prisma.StringWithAggregatesFilter<"ProclamationCategory"> | string
|
name?: Prisma.StringWithAggregatesFilter<"ProclamationCategory"> | string
|
||||||
sortOrder?: Prisma.IntWithAggregatesFilter<"ProclamationCategory"> | number
|
sortOrder?: Prisma.IntWithAggregatesFilter<"ProclamationCategory"> | number
|
||||||
parentId?: Prisma.IntNullableWithAggregatesFilter<"ProclamationCategory"> | number | null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryCreateInput = {
|
export type ProclamationCategoryCreateInput = {
|
||||||
name: string
|
name: string
|
||||||
sortOrder?: number
|
sortOrder?: number
|
||||||
parent?: Prisma.ProclamationCategoryCreateNestedOneWithoutChildrenInput
|
|
||||||
children?: Prisma.ProclamationCategoryCreateNestedManyWithoutParentInput
|
|
||||||
proclamations?: Prisma.ProclamationCreateNestedManyWithoutCategoryInput
|
proclamations?: Prisma.ProclamationCreateNestedManyWithoutCategoryInput
|
||||||
subscriptionLevels?: Prisma.ProclamationCategorySubscriptionLevelCreateNestedManyWithoutCategoryInput
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryUncheckedCreateInput = {
|
export type ProclamationCategoryUncheckedCreateInput = {
|
||||||
id?: number
|
id?: number
|
||||||
name: string
|
name: string
|
||||||
sortOrder?: number
|
sortOrder?: number
|
||||||
parentId?: number | null
|
|
||||||
children?: Prisma.ProclamationCategoryUncheckedCreateNestedManyWithoutParentInput
|
|
||||||
proclamations?: Prisma.ProclamationUncheckedCreateNestedManyWithoutCategoryInput
|
proclamations?: Prisma.ProclamationUncheckedCreateNestedManyWithoutCategoryInput
|
||||||
subscriptionLevels?: Prisma.ProclamationCategorySubscriptionLevelUncheckedCreateNestedManyWithoutCategoryInput
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryUpdateInput = {
|
export type ProclamationCategoryUpdateInput = {
|
||||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
sortOrder?: Prisma.IntFieldUpdateOperationsInput | number
|
sortOrder?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
parent?: Prisma.ProclamationCategoryUpdateOneWithoutChildrenNestedInput
|
|
||||||
children?: Prisma.ProclamationCategoryUpdateManyWithoutParentNestedInput
|
|
||||||
proclamations?: Prisma.ProclamationUpdateManyWithoutCategoryNestedInput
|
proclamations?: Prisma.ProclamationUpdateManyWithoutCategoryNestedInput
|
||||||
subscriptionLevels?: Prisma.ProclamationCategorySubscriptionLevelUpdateManyWithoutCategoryNestedInput
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryUncheckedUpdateInput = {
|
export type ProclamationCategoryUncheckedUpdateInput = {
|
||||||
id?: Prisma.IntFieldUpdateOperationsInput | number
|
id?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
sortOrder?: Prisma.IntFieldUpdateOperationsInput | number
|
sortOrder?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
parentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
|
||||||
children?: Prisma.ProclamationCategoryUncheckedUpdateManyWithoutParentNestedInput
|
|
||||||
proclamations?: Prisma.ProclamationUncheckedUpdateManyWithoutCategoryNestedInput
|
proclamations?: Prisma.ProclamationUncheckedUpdateManyWithoutCategoryNestedInput
|
||||||
subscriptionLevels?: Prisma.ProclamationCategorySubscriptionLevelUncheckedUpdateManyWithoutCategoryNestedInput
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryCreateManyInput = {
|
export type ProclamationCategoryCreateManyInput = {
|
||||||
id?: number
|
id?: number
|
||||||
name: string
|
name: string
|
||||||
sortOrder?: number
|
sortOrder?: number
|
||||||
parentId?: number | null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryUpdateManyMutationInput = {
|
export type ProclamationCategoryUpdateManyMutationInput = {
|
||||||
@@ -324,7 +285,6 @@ export type ProclamationCategoryUncheckedUpdateManyInput = {
|
|||||||
id?: Prisma.IntFieldUpdateOperationsInput | number
|
id?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
sortOrder?: Prisma.IntFieldUpdateOperationsInput | number
|
sortOrder?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
parentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryScalarRelationFilter = {
|
export type ProclamationCategoryScalarRelationFilter = {
|
||||||
@@ -332,57 +292,32 @@ export type ProclamationCategoryScalarRelationFilter = {
|
|||||||
isNot?: Prisma.ProclamationCategoryWhereInput
|
isNot?: Prisma.ProclamationCategoryWhereInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryNullableScalarRelationFilter = {
|
|
||||||
is?: Prisma.ProclamationCategoryWhereInput | null
|
|
||||||
isNot?: Prisma.ProclamationCategoryWhereInput | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryListRelationFilter = {
|
|
||||||
every?: Prisma.ProclamationCategoryWhereInput
|
|
||||||
some?: Prisma.ProclamationCategoryWhereInput
|
|
||||||
none?: Prisma.ProclamationCategoryWhereInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryOrderByRelationAggregateInput = {
|
|
||||||
_count?: Prisma.SortOrder
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryParentIdNameCompoundUniqueInput = {
|
|
||||||
parentId: number
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryCountOrderByAggregateInput = {
|
export type ProclamationCategoryCountOrderByAggregateInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
name?: Prisma.SortOrder
|
name?: Prisma.SortOrder
|
||||||
sortOrder?: Prisma.SortOrder
|
sortOrder?: Prisma.SortOrder
|
||||||
parentId?: Prisma.SortOrder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryAvgOrderByAggregateInput = {
|
export type ProclamationCategoryAvgOrderByAggregateInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
sortOrder?: Prisma.SortOrder
|
sortOrder?: Prisma.SortOrder
|
||||||
parentId?: Prisma.SortOrder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryMaxOrderByAggregateInput = {
|
export type ProclamationCategoryMaxOrderByAggregateInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
name?: Prisma.SortOrder
|
name?: Prisma.SortOrder
|
||||||
sortOrder?: Prisma.SortOrder
|
sortOrder?: Prisma.SortOrder
|
||||||
parentId?: Prisma.SortOrder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryMinOrderByAggregateInput = {
|
export type ProclamationCategoryMinOrderByAggregateInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
name?: Prisma.SortOrder
|
name?: Prisma.SortOrder
|
||||||
sortOrder?: Prisma.SortOrder
|
sortOrder?: Prisma.SortOrder
|
||||||
parentId?: Prisma.SortOrder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategorySumOrderByAggregateInput = {
|
export type ProclamationCategorySumOrderByAggregateInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
sortOrder?: Prisma.SortOrder
|
sortOrder?: Prisma.SortOrder
|
||||||
parentId?: Prisma.SortOrder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryCreateNestedOneWithoutProclamationsInput = {
|
export type ProclamationCategoryCreateNestedOneWithoutProclamationsInput = {
|
||||||
@@ -399,93 +334,15 @@ export type ProclamationCategoryUpdateOneRequiredWithoutProclamationsNestedInput
|
|||||||
update?: Prisma.XOR<Prisma.XOR<Prisma.ProclamationCategoryUpdateToOneWithWhereWithoutProclamationsInput, Prisma.ProclamationCategoryUpdateWithoutProclamationsInput>, Prisma.ProclamationCategoryUncheckedUpdateWithoutProclamationsInput>
|
update?: Prisma.XOR<Prisma.XOR<Prisma.ProclamationCategoryUpdateToOneWithWhereWithoutProclamationsInput, Prisma.ProclamationCategoryUpdateWithoutProclamationsInput>, Prisma.ProclamationCategoryUncheckedUpdateWithoutProclamationsInput>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryCreateNestedOneWithoutChildrenInput = {
|
|
||||||
create?: Prisma.XOR<Prisma.ProclamationCategoryCreateWithoutChildrenInput, Prisma.ProclamationCategoryUncheckedCreateWithoutChildrenInput>
|
|
||||||
connectOrCreate?: Prisma.ProclamationCategoryCreateOrConnectWithoutChildrenInput
|
|
||||||
connect?: Prisma.ProclamationCategoryWhereUniqueInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryCreateNestedManyWithoutParentInput = {
|
|
||||||
create?: Prisma.XOR<Prisma.ProclamationCategoryCreateWithoutParentInput, Prisma.ProclamationCategoryUncheckedCreateWithoutParentInput> | Prisma.ProclamationCategoryCreateWithoutParentInput[] | Prisma.ProclamationCategoryUncheckedCreateWithoutParentInput[]
|
|
||||||
connectOrCreate?: Prisma.ProclamationCategoryCreateOrConnectWithoutParentInput | Prisma.ProclamationCategoryCreateOrConnectWithoutParentInput[]
|
|
||||||
createMany?: Prisma.ProclamationCategoryCreateManyParentInputEnvelope
|
|
||||||
connect?: Prisma.ProclamationCategoryWhereUniqueInput | Prisma.ProclamationCategoryWhereUniqueInput[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryUncheckedCreateNestedManyWithoutParentInput = {
|
|
||||||
create?: Prisma.XOR<Prisma.ProclamationCategoryCreateWithoutParentInput, Prisma.ProclamationCategoryUncheckedCreateWithoutParentInput> | Prisma.ProclamationCategoryCreateWithoutParentInput[] | Prisma.ProclamationCategoryUncheckedCreateWithoutParentInput[]
|
|
||||||
connectOrCreate?: Prisma.ProclamationCategoryCreateOrConnectWithoutParentInput | Prisma.ProclamationCategoryCreateOrConnectWithoutParentInput[]
|
|
||||||
createMany?: Prisma.ProclamationCategoryCreateManyParentInputEnvelope
|
|
||||||
connect?: Prisma.ProclamationCategoryWhereUniqueInput | Prisma.ProclamationCategoryWhereUniqueInput[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryUpdateOneWithoutChildrenNestedInput = {
|
|
||||||
create?: Prisma.XOR<Prisma.ProclamationCategoryCreateWithoutChildrenInput, Prisma.ProclamationCategoryUncheckedCreateWithoutChildrenInput>
|
|
||||||
connectOrCreate?: Prisma.ProclamationCategoryCreateOrConnectWithoutChildrenInput
|
|
||||||
upsert?: Prisma.ProclamationCategoryUpsertWithoutChildrenInput
|
|
||||||
disconnect?: Prisma.ProclamationCategoryWhereInput | boolean
|
|
||||||
delete?: Prisma.ProclamationCategoryWhereInput | boolean
|
|
||||||
connect?: Prisma.ProclamationCategoryWhereUniqueInput
|
|
||||||
update?: Prisma.XOR<Prisma.XOR<Prisma.ProclamationCategoryUpdateToOneWithWhereWithoutChildrenInput, Prisma.ProclamationCategoryUpdateWithoutChildrenInput>, Prisma.ProclamationCategoryUncheckedUpdateWithoutChildrenInput>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryUpdateManyWithoutParentNestedInput = {
|
|
||||||
create?: Prisma.XOR<Prisma.ProclamationCategoryCreateWithoutParentInput, Prisma.ProclamationCategoryUncheckedCreateWithoutParentInput> | Prisma.ProclamationCategoryCreateWithoutParentInput[] | Prisma.ProclamationCategoryUncheckedCreateWithoutParentInput[]
|
|
||||||
connectOrCreate?: Prisma.ProclamationCategoryCreateOrConnectWithoutParentInput | Prisma.ProclamationCategoryCreateOrConnectWithoutParentInput[]
|
|
||||||
upsert?: Prisma.ProclamationCategoryUpsertWithWhereUniqueWithoutParentInput | Prisma.ProclamationCategoryUpsertWithWhereUniqueWithoutParentInput[]
|
|
||||||
createMany?: Prisma.ProclamationCategoryCreateManyParentInputEnvelope
|
|
||||||
set?: Prisma.ProclamationCategoryWhereUniqueInput | Prisma.ProclamationCategoryWhereUniqueInput[]
|
|
||||||
disconnect?: Prisma.ProclamationCategoryWhereUniqueInput | Prisma.ProclamationCategoryWhereUniqueInput[]
|
|
||||||
delete?: Prisma.ProclamationCategoryWhereUniqueInput | Prisma.ProclamationCategoryWhereUniqueInput[]
|
|
||||||
connect?: Prisma.ProclamationCategoryWhereUniqueInput | Prisma.ProclamationCategoryWhereUniqueInput[]
|
|
||||||
update?: Prisma.ProclamationCategoryUpdateWithWhereUniqueWithoutParentInput | Prisma.ProclamationCategoryUpdateWithWhereUniqueWithoutParentInput[]
|
|
||||||
updateMany?: Prisma.ProclamationCategoryUpdateManyWithWhereWithoutParentInput | Prisma.ProclamationCategoryUpdateManyWithWhereWithoutParentInput[]
|
|
||||||
deleteMany?: Prisma.ProclamationCategoryScalarWhereInput | Prisma.ProclamationCategoryScalarWhereInput[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryUncheckedUpdateManyWithoutParentNestedInput = {
|
|
||||||
create?: Prisma.XOR<Prisma.ProclamationCategoryCreateWithoutParentInput, Prisma.ProclamationCategoryUncheckedCreateWithoutParentInput> | Prisma.ProclamationCategoryCreateWithoutParentInput[] | Prisma.ProclamationCategoryUncheckedCreateWithoutParentInput[]
|
|
||||||
connectOrCreate?: Prisma.ProclamationCategoryCreateOrConnectWithoutParentInput | Prisma.ProclamationCategoryCreateOrConnectWithoutParentInput[]
|
|
||||||
upsert?: Prisma.ProclamationCategoryUpsertWithWhereUniqueWithoutParentInput | Prisma.ProclamationCategoryUpsertWithWhereUniqueWithoutParentInput[]
|
|
||||||
createMany?: Prisma.ProclamationCategoryCreateManyParentInputEnvelope
|
|
||||||
set?: Prisma.ProclamationCategoryWhereUniqueInput | Prisma.ProclamationCategoryWhereUniqueInput[]
|
|
||||||
disconnect?: Prisma.ProclamationCategoryWhereUniqueInput | Prisma.ProclamationCategoryWhereUniqueInput[]
|
|
||||||
delete?: Prisma.ProclamationCategoryWhereUniqueInput | Prisma.ProclamationCategoryWhereUniqueInput[]
|
|
||||||
connect?: Prisma.ProclamationCategoryWhereUniqueInput | Prisma.ProclamationCategoryWhereUniqueInput[]
|
|
||||||
update?: Prisma.ProclamationCategoryUpdateWithWhereUniqueWithoutParentInput | Prisma.ProclamationCategoryUpdateWithWhereUniqueWithoutParentInput[]
|
|
||||||
updateMany?: Prisma.ProclamationCategoryUpdateManyWithWhereWithoutParentInput | Prisma.ProclamationCategoryUpdateManyWithWhereWithoutParentInput[]
|
|
||||||
deleteMany?: Prisma.ProclamationCategoryScalarWhereInput | Prisma.ProclamationCategoryScalarWhereInput[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryCreateNestedOneWithoutSubscriptionLevelsInput = {
|
|
||||||
create?: Prisma.XOR<Prisma.ProclamationCategoryCreateWithoutSubscriptionLevelsInput, Prisma.ProclamationCategoryUncheckedCreateWithoutSubscriptionLevelsInput>
|
|
||||||
connectOrCreate?: Prisma.ProclamationCategoryCreateOrConnectWithoutSubscriptionLevelsInput
|
|
||||||
connect?: Prisma.ProclamationCategoryWhereUniqueInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryUpdateOneRequiredWithoutSubscriptionLevelsNestedInput = {
|
|
||||||
create?: Prisma.XOR<Prisma.ProclamationCategoryCreateWithoutSubscriptionLevelsInput, Prisma.ProclamationCategoryUncheckedCreateWithoutSubscriptionLevelsInput>
|
|
||||||
connectOrCreate?: Prisma.ProclamationCategoryCreateOrConnectWithoutSubscriptionLevelsInput
|
|
||||||
upsert?: Prisma.ProclamationCategoryUpsertWithoutSubscriptionLevelsInput
|
|
||||||
connect?: Prisma.ProclamationCategoryWhereUniqueInput
|
|
||||||
update?: Prisma.XOR<Prisma.XOR<Prisma.ProclamationCategoryUpdateToOneWithWhereWithoutSubscriptionLevelsInput, Prisma.ProclamationCategoryUpdateWithoutSubscriptionLevelsInput>, Prisma.ProclamationCategoryUncheckedUpdateWithoutSubscriptionLevelsInput>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryCreateWithoutProclamationsInput = {
|
export type ProclamationCategoryCreateWithoutProclamationsInput = {
|
||||||
name: string
|
name: string
|
||||||
sortOrder?: number
|
sortOrder?: number
|
||||||
parent?: Prisma.ProclamationCategoryCreateNestedOneWithoutChildrenInput
|
|
||||||
children?: Prisma.ProclamationCategoryCreateNestedManyWithoutParentInput
|
|
||||||
subscriptionLevels?: Prisma.ProclamationCategorySubscriptionLevelCreateNestedManyWithoutCategoryInput
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryUncheckedCreateWithoutProclamationsInput = {
|
export type ProclamationCategoryUncheckedCreateWithoutProclamationsInput = {
|
||||||
id?: number
|
id?: number
|
||||||
name: string
|
name: string
|
||||||
sortOrder?: number
|
sortOrder?: number
|
||||||
parentId?: number | null
|
|
||||||
children?: Prisma.ProclamationCategoryUncheckedCreateNestedManyWithoutParentInput
|
|
||||||
subscriptionLevels?: Prisma.ProclamationCategorySubscriptionLevelUncheckedCreateNestedManyWithoutCategoryInput
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryCreateOrConnectWithoutProclamationsInput = {
|
export type ProclamationCategoryCreateOrConnectWithoutProclamationsInput = {
|
||||||
@@ -507,200 +364,12 @@ export type ProclamationCategoryUpdateToOneWithWhereWithoutProclamationsInput =
|
|||||||
export type ProclamationCategoryUpdateWithoutProclamationsInput = {
|
export type ProclamationCategoryUpdateWithoutProclamationsInput = {
|
||||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
sortOrder?: Prisma.IntFieldUpdateOperationsInput | number
|
sortOrder?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
parent?: Prisma.ProclamationCategoryUpdateOneWithoutChildrenNestedInput
|
|
||||||
children?: Prisma.ProclamationCategoryUpdateManyWithoutParentNestedInput
|
|
||||||
subscriptionLevels?: Prisma.ProclamationCategorySubscriptionLevelUpdateManyWithoutCategoryNestedInput
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryUncheckedUpdateWithoutProclamationsInput = {
|
export type ProclamationCategoryUncheckedUpdateWithoutProclamationsInput = {
|
||||||
id?: Prisma.IntFieldUpdateOperationsInput | number
|
id?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
sortOrder?: Prisma.IntFieldUpdateOperationsInput | number
|
sortOrder?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
parentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
|
||||||
children?: Prisma.ProclamationCategoryUncheckedUpdateManyWithoutParentNestedInput
|
|
||||||
subscriptionLevels?: Prisma.ProclamationCategorySubscriptionLevelUncheckedUpdateManyWithoutCategoryNestedInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryCreateWithoutChildrenInput = {
|
|
||||||
name: string
|
|
||||||
sortOrder?: number
|
|
||||||
parent?: Prisma.ProclamationCategoryCreateNestedOneWithoutChildrenInput
|
|
||||||
proclamations?: Prisma.ProclamationCreateNestedManyWithoutCategoryInput
|
|
||||||
subscriptionLevels?: Prisma.ProclamationCategorySubscriptionLevelCreateNestedManyWithoutCategoryInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryUncheckedCreateWithoutChildrenInput = {
|
|
||||||
id?: number
|
|
||||||
name: string
|
|
||||||
sortOrder?: number
|
|
||||||
parentId?: number | null
|
|
||||||
proclamations?: Prisma.ProclamationUncheckedCreateNestedManyWithoutCategoryInput
|
|
||||||
subscriptionLevels?: Prisma.ProclamationCategorySubscriptionLevelUncheckedCreateNestedManyWithoutCategoryInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryCreateOrConnectWithoutChildrenInput = {
|
|
||||||
where: Prisma.ProclamationCategoryWhereUniqueInput
|
|
||||||
create: Prisma.XOR<Prisma.ProclamationCategoryCreateWithoutChildrenInput, Prisma.ProclamationCategoryUncheckedCreateWithoutChildrenInput>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryCreateWithoutParentInput = {
|
|
||||||
name: string
|
|
||||||
sortOrder?: number
|
|
||||||
children?: Prisma.ProclamationCategoryCreateNestedManyWithoutParentInput
|
|
||||||
proclamations?: Prisma.ProclamationCreateNestedManyWithoutCategoryInput
|
|
||||||
subscriptionLevels?: Prisma.ProclamationCategorySubscriptionLevelCreateNestedManyWithoutCategoryInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryUncheckedCreateWithoutParentInput = {
|
|
||||||
id?: number
|
|
||||||
name: string
|
|
||||||
sortOrder?: number
|
|
||||||
children?: Prisma.ProclamationCategoryUncheckedCreateNestedManyWithoutParentInput
|
|
||||||
proclamations?: Prisma.ProclamationUncheckedCreateNestedManyWithoutCategoryInput
|
|
||||||
subscriptionLevels?: Prisma.ProclamationCategorySubscriptionLevelUncheckedCreateNestedManyWithoutCategoryInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryCreateOrConnectWithoutParentInput = {
|
|
||||||
where: Prisma.ProclamationCategoryWhereUniqueInput
|
|
||||||
create: Prisma.XOR<Prisma.ProclamationCategoryCreateWithoutParentInput, Prisma.ProclamationCategoryUncheckedCreateWithoutParentInput>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryCreateManyParentInputEnvelope = {
|
|
||||||
data: Prisma.ProclamationCategoryCreateManyParentInput | Prisma.ProclamationCategoryCreateManyParentInput[]
|
|
||||||
skipDuplicates?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryUpsertWithoutChildrenInput = {
|
|
||||||
update: Prisma.XOR<Prisma.ProclamationCategoryUpdateWithoutChildrenInput, Prisma.ProclamationCategoryUncheckedUpdateWithoutChildrenInput>
|
|
||||||
create: Prisma.XOR<Prisma.ProclamationCategoryCreateWithoutChildrenInput, Prisma.ProclamationCategoryUncheckedCreateWithoutChildrenInput>
|
|
||||||
where?: Prisma.ProclamationCategoryWhereInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryUpdateToOneWithWhereWithoutChildrenInput = {
|
|
||||||
where?: Prisma.ProclamationCategoryWhereInput
|
|
||||||
data: Prisma.XOR<Prisma.ProclamationCategoryUpdateWithoutChildrenInput, Prisma.ProclamationCategoryUncheckedUpdateWithoutChildrenInput>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryUpdateWithoutChildrenInput = {
|
|
||||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
|
||||||
sortOrder?: Prisma.IntFieldUpdateOperationsInput | number
|
|
||||||
parent?: Prisma.ProclamationCategoryUpdateOneWithoutChildrenNestedInput
|
|
||||||
proclamations?: Prisma.ProclamationUpdateManyWithoutCategoryNestedInput
|
|
||||||
subscriptionLevels?: Prisma.ProclamationCategorySubscriptionLevelUpdateManyWithoutCategoryNestedInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryUncheckedUpdateWithoutChildrenInput = {
|
|
||||||
id?: Prisma.IntFieldUpdateOperationsInput | number
|
|
||||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
|
||||||
sortOrder?: Prisma.IntFieldUpdateOperationsInput | number
|
|
||||||
parentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
|
||||||
proclamations?: Prisma.ProclamationUncheckedUpdateManyWithoutCategoryNestedInput
|
|
||||||
subscriptionLevels?: Prisma.ProclamationCategorySubscriptionLevelUncheckedUpdateManyWithoutCategoryNestedInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryUpsertWithWhereUniqueWithoutParentInput = {
|
|
||||||
where: Prisma.ProclamationCategoryWhereUniqueInput
|
|
||||||
update: Prisma.XOR<Prisma.ProclamationCategoryUpdateWithoutParentInput, Prisma.ProclamationCategoryUncheckedUpdateWithoutParentInput>
|
|
||||||
create: Prisma.XOR<Prisma.ProclamationCategoryCreateWithoutParentInput, Prisma.ProclamationCategoryUncheckedCreateWithoutParentInput>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryUpdateWithWhereUniqueWithoutParentInput = {
|
|
||||||
where: Prisma.ProclamationCategoryWhereUniqueInput
|
|
||||||
data: Prisma.XOR<Prisma.ProclamationCategoryUpdateWithoutParentInput, Prisma.ProclamationCategoryUncheckedUpdateWithoutParentInput>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryUpdateManyWithWhereWithoutParentInput = {
|
|
||||||
where: Prisma.ProclamationCategoryScalarWhereInput
|
|
||||||
data: Prisma.XOR<Prisma.ProclamationCategoryUpdateManyMutationInput, Prisma.ProclamationCategoryUncheckedUpdateManyWithoutParentInput>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryScalarWhereInput = {
|
|
||||||
AND?: Prisma.ProclamationCategoryScalarWhereInput | Prisma.ProclamationCategoryScalarWhereInput[]
|
|
||||||
OR?: Prisma.ProclamationCategoryScalarWhereInput[]
|
|
||||||
NOT?: Prisma.ProclamationCategoryScalarWhereInput | Prisma.ProclamationCategoryScalarWhereInput[]
|
|
||||||
id?: Prisma.IntFilter<"ProclamationCategory"> | number
|
|
||||||
name?: Prisma.StringFilter<"ProclamationCategory"> | string
|
|
||||||
sortOrder?: Prisma.IntFilter<"ProclamationCategory"> | number
|
|
||||||
parentId?: Prisma.IntNullableFilter<"ProclamationCategory"> | number | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryCreateWithoutSubscriptionLevelsInput = {
|
|
||||||
name: string
|
|
||||||
sortOrder?: number
|
|
||||||
parent?: Prisma.ProclamationCategoryCreateNestedOneWithoutChildrenInput
|
|
||||||
children?: Prisma.ProclamationCategoryCreateNestedManyWithoutParentInput
|
|
||||||
proclamations?: Prisma.ProclamationCreateNestedManyWithoutCategoryInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryUncheckedCreateWithoutSubscriptionLevelsInput = {
|
|
||||||
id?: number
|
|
||||||
name: string
|
|
||||||
sortOrder?: number
|
|
||||||
parentId?: number | null
|
|
||||||
children?: Prisma.ProclamationCategoryUncheckedCreateNestedManyWithoutParentInput
|
|
||||||
proclamations?: Prisma.ProclamationUncheckedCreateNestedManyWithoutCategoryInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryCreateOrConnectWithoutSubscriptionLevelsInput = {
|
|
||||||
where: Prisma.ProclamationCategoryWhereUniqueInput
|
|
||||||
create: Prisma.XOR<Prisma.ProclamationCategoryCreateWithoutSubscriptionLevelsInput, Prisma.ProclamationCategoryUncheckedCreateWithoutSubscriptionLevelsInput>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryUpsertWithoutSubscriptionLevelsInput = {
|
|
||||||
update: Prisma.XOR<Prisma.ProclamationCategoryUpdateWithoutSubscriptionLevelsInput, Prisma.ProclamationCategoryUncheckedUpdateWithoutSubscriptionLevelsInput>
|
|
||||||
create: Prisma.XOR<Prisma.ProclamationCategoryCreateWithoutSubscriptionLevelsInput, Prisma.ProclamationCategoryUncheckedCreateWithoutSubscriptionLevelsInput>
|
|
||||||
where?: Prisma.ProclamationCategoryWhereInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryUpdateToOneWithWhereWithoutSubscriptionLevelsInput = {
|
|
||||||
where?: Prisma.ProclamationCategoryWhereInput
|
|
||||||
data: Prisma.XOR<Prisma.ProclamationCategoryUpdateWithoutSubscriptionLevelsInput, Prisma.ProclamationCategoryUncheckedUpdateWithoutSubscriptionLevelsInput>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryUpdateWithoutSubscriptionLevelsInput = {
|
|
||||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
|
||||||
sortOrder?: Prisma.IntFieldUpdateOperationsInput | number
|
|
||||||
parent?: Prisma.ProclamationCategoryUpdateOneWithoutChildrenNestedInput
|
|
||||||
children?: Prisma.ProclamationCategoryUpdateManyWithoutParentNestedInput
|
|
||||||
proclamations?: Prisma.ProclamationUpdateManyWithoutCategoryNestedInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryUncheckedUpdateWithoutSubscriptionLevelsInput = {
|
|
||||||
id?: Prisma.IntFieldUpdateOperationsInput | number
|
|
||||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
|
||||||
sortOrder?: Prisma.IntFieldUpdateOperationsInput | number
|
|
||||||
parentId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
|
||||||
children?: Prisma.ProclamationCategoryUncheckedUpdateManyWithoutParentNestedInput
|
|
||||||
proclamations?: Prisma.ProclamationUncheckedUpdateManyWithoutCategoryNestedInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryCreateManyParentInput = {
|
|
||||||
id?: number
|
|
||||||
name: string
|
|
||||||
sortOrder?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryUpdateWithoutParentInput = {
|
|
||||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
|
||||||
sortOrder?: Prisma.IntFieldUpdateOperationsInput | number
|
|
||||||
children?: Prisma.ProclamationCategoryUpdateManyWithoutParentNestedInput
|
|
||||||
proclamations?: Prisma.ProclamationUpdateManyWithoutCategoryNestedInput
|
|
||||||
subscriptionLevels?: Prisma.ProclamationCategorySubscriptionLevelUpdateManyWithoutCategoryNestedInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryUncheckedUpdateWithoutParentInput = {
|
|
||||||
id?: Prisma.IntFieldUpdateOperationsInput | number
|
|
||||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
|
||||||
sortOrder?: Prisma.IntFieldUpdateOperationsInput | number
|
|
||||||
children?: Prisma.ProclamationCategoryUncheckedUpdateManyWithoutParentNestedInput
|
|
||||||
proclamations?: Prisma.ProclamationUncheckedUpdateManyWithoutCategoryNestedInput
|
|
||||||
subscriptionLevels?: Prisma.ProclamationCategorySubscriptionLevelUncheckedUpdateManyWithoutCategoryNestedInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProclamationCategoryUncheckedUpdateManyWithoutParentInput = {
|
|
||||||
id?: Prisma.IntFieldUpdateOperationsInput | number
|
|
||||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
|
||||||
sortOrder?: Prisma.IntFieldUpdateOperationsInput | number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -709,15 +378,11 @@ export type ProclamationCategoryUncheckedUpdateManyWithoutParentInput = {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export type ProclamationCategoryCountOutputType = {
|
export type ProclamationCategoryCountOutputType = {
|
||||||
children: number
|
|
||||||
proclamations: number
|
proclamations: number
|
||||||
subscriptionLevels: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryCountOutputTypeSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type ProclamationCategoryCountOutputTypeSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
children?: boolean | ProclamationCategoryCountOutputTypeCountChildrenArgs
|
|
||||||
proclamations?: boolean | ProclamationCategoryCountOutputTypeCountProclamationsArgs
|
proclamations?: boolean | ProclamationCategoryCountOutputTypeCountProclamationsArgs
|
||||||
subscriptionLevels?: boolean | ProclamationCategoryCountOutputTypeCountSubscriptionLevelsArgs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -730,13 +395,6 @@ export type ProclamationCategoryCountOutputTypeDefaultArgs<ExtArgs extends runti
|
|||||||
select?: Prisma.ProclamationCategoryCountOutputTypeSelect<ExtArgs> | null
|
select?: Prisma.ProclamationCategoryCountOutputTypeSelect<ExtArgs> | null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* ProclamationCategoryCountOutputType without action
|
|
||||||
*/
|
|
||||||
export type ProclamationCategoryCountOutputTypeCountChildrenArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
|
||||||
where?: Prisma.ProclamationCategoryWhereInput
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ProclamationCategoryCountOutputType without action
|
* ProclamationCategoryCountOutputType without action
|
||||||
*/
|
*/
|
||||||
@@ -744,23 +402,12 @@ export type ProclamationCategoryCountOutputTypeCountProclamationsArgs<ExtArgs ex
|
|||||||
where?: Prisma.ProclamationWhereInput
|
where?: Prisma.ProclamationWhereInput
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* ProclamationCategoryCountOutputType without action
|
|
||||||
*/
|
|
||||||
export type ProclamationCategoryCountOutputTypeCountSubscriptionLevelsArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
|
||||||
where?: Prisma.ProclamationCategorySubscriptionLevelWhereInput
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export type ProclamationCategorySelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type ProclamationCategorySelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
id?: boolean
|
id?: boolean
|
||||||
name?: boolean
|
name?: boolean
|
||||||
sortOrder?: boolean
|
sortOrder?: boolean
|
||||||
parentId?: boolean
|
|
||||||
parent?: boolean | Prisma.ProclamationCategory$parentArgs<ExtArgs>
|
|
||||||
children?: boolean | Prisma.ProclamationCategory$childrenArgs<ExtArgs>
|
|
||||||
proclamations?: boolean | Prisma.ProclamationCategory$proclamationsArgs<ExtArgs>
|
proclamations?: boolean | Prisma.ProclamationCategory$proclamationsArgs<ExtArgs>
|
||||||
subscriptionLevels?: boolean | Prisma.ProclamationCategory$subscriptionLevelsArgs<ExtArgs>
|
|
||||||
_count?: boolean | Prisma.ProclamationCategoryCountOutputTypeDefaultArgs<ExtArgs>
|
_count?: boolean | Prisma.ProclamationCategoryCountOutputTypeDefaultArgs<ExtArgs>
|
||||||
}, ExtArgs["result"]["proclamationCategory"]>
|
}, ExtArgs["result"]["proclamationCategory"]>
|
||||||
|
|
||||||
@@ -768,53 +415,37 @@ export type ProclamationCategorySelectCreateManyAndReturn<ExtArgs extends runtim
|
|||||||
id?: boolean
|
id?: boolean
|
||||||
name?: boolean
|
name?: boolean
|
||||||
sortOrder?: boolean
|
sortOrder?: boolean
|
||||||
parentId?: boolean
|
|
||||||
parent?: boolean | Prisma.ProclamationCategory$parentArgs<ExtArgs>
|
|
||||||
}, ExtArgs["result"]["proclamationCategory"]>
|
}, ExtArgs["result"]["proclamationCategory"]>
|
||||||
|
|
||||||
export type ProclamationCategorySelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type ProclamationCategorySelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
id?: boolean
|
id?: boolean
|
||||||
name?: boolean
|
name?: boolean
|
||||||
sortOrder?: boolean
|
sortOrder?: boolean
|
||||||
parentId?: boolean
|
|
||||||
parent?: boolean | Prisma.ProclamationCategory$parentArgs<ExtArgs>
|
|
||||||
}, ExtArgs["result"]["proclamationCategory"]>
|
}, ExtArgs["result"]["proclamationCategory"]>
|
||||||
|
|
||||||
export type ProclamationCategorySelectScalar = {
|
export type ProclamationCategorySelectScalar = {
|
||||||
id?: boolean
|
id?: boolean
|
||||||
name?: boolean
|
name?: boolean
|
||||||
sortOrder?: boolean
|
sortOrder?: boolean
|
||||||
parentId?: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProclamationCategoryOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "name" | "sortOrder" | "parentId", ExtArgs["result"]["proclamationCategory"]>
|
export type ProclamationCategoryOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "name" | "sortOrder", ExtArgs["result"]["proclamationCategory"]>
|
||||||
export type ProclamationCategoryInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type ProclamationCategoryInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
parent?: boolean | Prisma.ProclamationCategory$parentArgs<ExtArgs>
|
|
||||||
children?: boolean | Prisma.ProclamationCategory$childrenArgs<ExtArgs>
|
|
||||||
proclamations?: boolean | Prisma.ProclamationCategory$proclamationsArgs<ExtArgs>
|
proclamations?: boolean | Prisma.ProclamationCategory$proclamationsArgs<ExtArgs>
|
||||||
subscriptionLevels?: boolean | Prisma.ProclamationCategory$subscriptionLevelsArgs<ExtArgs>
|
|
||||||
_count?: boolean | Prisma.ProclamationCategoryCountOutputTypeDefaultArgs<ExtArgs>
|
_count?: boolean | Prisma.ProclamationCategoryCountOutputTypeDefaultArgs<ExtArgs>
|
||||||
}
|
}
|
||||||
export type ProclamationCategoryIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type ProclamationCategoryIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {}
|
||||||
parent?: boolean | Prisma.ProclamationCategory$parentArgs<ExtArgs>
|
export type ProclamationCategoryIncludeUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {}
|
||||||
}
|
|
||||||
export type ProclamationCategoryIncludeUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
|
||||||
parent?: boolean | Prisma.ProclamationCategory$parentArgs<ExtArgs>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type $ProclamationCategoryPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type $ProclamationCategoryPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
name: "ProclamationCategory"
|
name: "ProclamationCategory"
|
||||||
objects: {
|
objects: {
|
||||||
parent: Prisma.$ProclamationCategoryPayload<ExtArgs> | null
|
|
||||||
children: Prisma.$ProclamationCategoryPayload<ExtArgs>[]
|
|
||||||
proclamations: Prisma.$ProclamationPayload<ExtArgs>[]
|
proclamations: Prisma.$ProclamationPayload<ExtArgs>[]
|
||||||
subscriptionLevels: Prisma.$ProclamationCategorySubscriptionLevelPayload<ExtArgs>[]
|
|
||||||
}
|
}
|
||||||
scalars: runtime.Types.Extensions.GetPayloadResult<{
|
scalars: runtime.Types.Extensions.GetPayloadResult<{
|
||||||
id: number
|
id: number
|
||||||
name: string
|
name: string
|
||||||
sortOrder: number
|
sortOrder: number
|
||||||
parentId: number | null
|
|
||||||
}, ExtArgs["result"]["proclamationCategory"]>
|
}, ExtArgs["result"]["proclamationCategory"]>
|
||||||
composites: {}
|
composites: {}
|
||||||
}
|
}
|
||||||
@@ -1209,10 +840,7 @@ readonly fields: ProclamationCategoryFieldRefs;
|
|||||||
*/
|
*/
|
||||||
export interface Prisma__ProclamationCategoryClient<T, Null = never, ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs, GlobalOmitOptions = {}> extends Prisma.PrismaPromise<T> {
|
export interface Prisma__ProclamationCategoryClient<T, Null = never, ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs, GlobalOmitOptions = {}> extends Prisma.PrismaPromise<T> {
|
||||||
readonly [Symbol.toStringTag]: "PrismaPromise"
|
readonly [Symbol.toStringTag]: "PrismaPromise"
|
||||||
parent<T extends Prisma.ProclamationCategory$parentArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.ProclamationCategory$parentArgs<ExtArgs>>): Prisma.Prisma__ProclamationCategoryClient<runtime.Types.Result.GetResult<Prisma.$ProclamationCategoryPayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions>
|
|
||||||
children<T extends Prisma.ProclamationCategory$childrenArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.ProclamationCategory$childrenArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ProclamationCategoryPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
|
||||||
proclamations<T extends Prisma.ProclamationCategory$proclamationsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.ProclamationCategory$proclamationsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ProclamationPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
proclamations<T extends Prisma.ProclamationCategory$proclamationsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.ProclamationCategory$proclamationsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ProclamationPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||||
subscriptionLevels<T extends Prisma.ProclamationCategory$subscriptionLevelsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.ProclamationCategory$subscriptionLevelsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ProclamationCategorySubscriptionLevelPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
|
||||||
/**
|
/**
|
||||||
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
||||||
* @param onfulfilled The callback to execute when the Promise is resolved.
|
* @param onfulfilled The callback to execute when the Promise is resolved.
|
||||||
@@ -1245,7 +873,6 @@ export interface ProclamationCategoryFieldRefs {
|
|||||||
readonly id: Prisma.FieldRef<"ProclamationCategory", 'Int'>
|
readonly id: Prisma.FieldRef<"ProclamationCategory", 'Int'>
|
||||||
readonly name: Prisma.FieldRef<"ProclamationCategory", 'String'>
|
readonly name: Prisma.FieldRef<"ProclamationCategory", 'String'>
|
||||||
readonly sortOrder: Prisma.FieldRef<"ProclamationCategory", 'Int'>
|
readonly sortOrder: Prisma.FieldRef<"ProclamationCategory", 'Int'>
|
||||||
readonly parentId: Prisma.FieldRef<"ProclamationCategory", 'Int'>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1500,10 +1127,6 @@ export type ProclamationCategoryCreateManyAndReturnArgs<ExtArgs extends runtime.
|
|||||||
*/
|
*/
|
||||||
data: Prisma.ProclamationCategoryCreateManyInput | Prisma.ProclamationCategoryCreateManyInput[]
|
data: Prisma.ProclamationCategoryCreateManyInput | Prisma.ProclamationCategoryCreateManyInput[]
|
||||||
skipDuplicates?: boolean
|
skipDuplicates?: boolean
|
||||||
/**
|
|
||||||
* Choose, which related nodes to fetch as well
|
|
||||||
*/
|
|
||||||
include?: Prisma.ProclamationCategoryIncludeCreateManyAndReturn<ExtArgs> | null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1574,10 +1197,6 @@ export type ProclamationCategoryUpdateManyAndReturnArgs<ExtArgs extends runtime.
|
|||||||
* Limit how many ProclamationCategories to update.
|
* Limit how many ProclamationCategories to update.
|
||||||
*/
|
*/
|
||||||
limit?: number
|
limit?: number
|
||||||
/**
|
|
||||||
* Choose, which related nodes to fetch as well
|
|
||||||
*/
|
|
||||||
include?: Prisma.ProclamationCategoryIncludeUpdateManyAndReturn<ExtArgs> | null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1646,49 +1265,6 @@ export type ProclamationCategoryDeleteManyArgs<ExtArgs extends runtime.Types.Ext
|
|||||||
limit?: number
|
limit?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* ProclamationCategory.parent
|
|
||||||
*/
|
|
||||||
export type ProclamationCategory$parentArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
|
||||||
/**
|
|
||||||
* Select specific fields to fetch from the ProclamationCategory
|
|
||||||
*/
|
|
||||||
select?: Prisma.ProclamationCategorySelect<ExtArgs> | null
|
|
||||||
/**
|
|
||||||
* Omit specific fields from the ProclamationCategory
|
|
||||||
*/
|
|
||||||
omit?: Prisma.ProclamationCategoryOmit<ExtArgs> | null
|
|
||||||
/**
|
|
||||||
* Choose, which related nodes to fetch as well
|
|
||||||
*/
|
|
||||||
include?: Prisma.ProclamationCategoryInclude<ExtArgs> | null
|
|
||||||
where?: Prisma.ProclamationCategoryWhereInput
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ProclamationCategory.children
|
|
||||||
*/
|
|
||||||
export type ProclamationCategory$childrenArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
|
||||||
/**
|
|
||||||
* Select specific fields to fetch from the ProclamationCategory
|
|
||||||
*/
|
|
||||||
select?: Prisma.ProclamationCategorySelect<ExtArgs> | null
|
|
||||||
/**
|
|
||||||
* Omit specific fields from the ProclamationCategory
|
|
||||||
*/
|
|
||||||
omit?: Prisma.ProclamationCategoryOmit<ExtArgs> | null
|
|
||||||
/**
|
|
||||||
* Choose, which related nodes to fetch as well
|
|
||||||
*/
|
|
||||||
include?: Prisma.ProclamationCategoryInclude<ExtArgs> | null
|
|
||||||
where?: Prisma.ProclamationCategoryWhereInput
|
|
||||||
orderBy?: Prisma.ProclamationCategoryOrderByWithRelationInput | Prisma.ProclamationCategoryOrderByWithRelationInput[]
|
|
||||||
cursor?: Prisma.ProclamationCategoryWhereUniqueInput
|
|
||||||
take?: number
|
|
||||||
skip?: number
|
|
||||||
distinct?: Prisma.ProclamationCategoryScalarFieldEnum | Prisma.ProclamationCategoryScalarFieldEnum[]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ProclamationCategory.proclamations
|
* ProclamationCategory.proclamations
|
||||||
*/
|
*/
|
||||||
@@ -1713,30 +1289,6 @@ export type ProclamationCategory$proclamationsArgs<ExtArgs extends runtime.Types
|
|||||||
distinct?: Prisma.ProclamationScalarFieldEnum | Prisma.ProclamationScalarFieldEnum[]
|
distinct?: Prisma.ProclamationScalarFieldEnum | Prisma.ProclamationScalarFieldEnum[]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* ProclamationCategory.subscriptionLevels
|
|
||||||
*/
|
|
||||||
export type ProclamationCategory$subscriptionLevelsArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
|
||||||
/**
|
|
||||||
* Select specific fields to fetch from the ProclamationCategorySubscriptionLevel
|
|
||||||
*/
|
|
||||||
select?: Prisma.ProclamationCategorySubscriptionLevelSelect<ExtArgs> | null
|
|
||||||
/**
|
|
||||||
* Omit specific fields from the ProclamationCategorySubscriptionLevel
|
|
||||||
*/
|
|
||||||
omit?: Prisma.ProclamationCategorySubscriptionLevelOmit<ExtArgs> | null
|
|
||||||
/**
|
|
||||||
* Choose, which related nodes to fetch as well
|
|
||||||
*/
|
|
||||||
include?: Prisma.ProclamationCategorySubscriptionLevelInclude<ExtArgs> | null
|
|
||||||
where?: Prisma.ProclamationCategorySubscriptionLevelWhereInput
|
|
||||||
orderBy?: Prisma.ProclamationCategorySubscriptionLevelOrderByWithRelationInput | Prisma.ProclamationCategorySubscriptionLevelOrderByWithRelationInput[]
|
|
||||||
cursor?: Prisma.ProclamationCategorySubscriptionLevelWhereUniqueInput
|
|
||||||
take?: number
|
|
||||||
skip?: number
|
|
||||||
distinct?: Prisma.ProclamationCategorySubscriptionLevelScalarFieldEnum | Prisma.ProclamationCategorySubscriptionLevelScalarFieldEnum[]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ProclamationCategory without action
|
* ProclamationCategory without action
|
||||||
*/
|
*/
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -27,6 +27,7 @@ export type AggregateProfile = {
|
|||||||
export type ProfileMinAggregateOutputType = {
|
export type ProfileMinAggregateOutputType = {
|
||||||
id: string | null
|
id: string | null
|
||||||
userId: string | null
|
userId: string | null
|
||||||
|
email: string | null
|
||||||
firstName: string | null
|
firstName: string | null
|
||||||
lastName: string | null
|
lastName: string | null
|
||||||
phoneNumber: string | null
|
phoneNumber: string | null
|
||||||
@@ -36,6 +37,7 @@ export type ProfileMinAggregateOutputType = {
|
|||||||
export type ProfileMaxAggregateOutputType = {
|
export type ProfileMaxAggregateOutputType = {
|
||||||
id: string | null
|
id: string | null
|
||||||
userId: string | null
|
userId: string | null
|
||||||
|
email: string | null
|
||||||
firstName: string | null
|
firstName: string | null
|
||||||
lastName: string | null
|
lastName: string | null
|
||||||
phoneNumber: string | null
|
phoneNumber: string | null
|
||||||
@@ -45,6 +47,7 @@ export type ProfileMaxAggregateOutputType = {
|
|||||||
export type ProfileCountAggregateOutputType = {
|
export type ProfileCountAggregateOutputType = {
|
||||||
id: number
|
id: number
|
||||||
userId: number
|
userId: number
|
||||||
|
email: number
|
||||||
firstName: number
|
firstName: number
|
||||||
lastName: number
|
lastName: number
|
||||||
phoneNumber: number
|
phoneNumber: number
|
||||||
@@ -56,6 +59,7 @@ export type ProfileCountAggregateOutputType = {
|
|||||||
export type ProfileMinAggregateInputType = {
|
export type ProfileMinAggregateInputType = {
|
||||||
id?: true
|
id?: true
|
||||||
userId?: true
|
userId?: true
|
||||||
|
email?: true
|
||||||
firstName?: true
|
firstName?: true
|
||||||
lastName?: true
|
lastName?: true
|
||||||
phoneNumber?: true
|
phoneNumber?: true
|
||||||
@@ -65,6 +69,7 @@ export type ProfileMinAggregateInputType = {
|
|||||||
export type ProfileMaxAggregateInputType = {
|
export type ProfileMaxAggregateInputType = {
|
||||||
id?: true
|
id?: true
|
||||||
userId?: true
|
userId?: true
|
||||||
|
email?: true
|
||||||
firstName?: true
|
firstName?: true
|
||||||
lastName?: true
|
lastName?: true
|
||||||
phoneNumber?: true
|
phoneNumber?: true
|
||||||
@@ -74,6 +79,7 @@ export type ProfileMaxAggregateInputType = {
|
|||||||
export type ProfileCountAggregateInputType = {
|
export type ProfileCountAggregateInputType = {
|
||||||
id?: true
|
id?: true
|
||||||
userId?: true
|
userId?: true
|
||||||
|
email?: true
|
||||||
firstName?: true
|
firstName?: true
|
||||||
lastName?: true
|
lastName?: true
|
||||||
phoneNumber?: true
|
phoneNumber?: true
|
||||||
@@ -155,7 +161,8 @@ export type ProfileGroupByArgs<ExtArgs extends runtime.Types.Extensions.Internal
|
|||||||
|
|
||||||
export type ProfileGroupByOutputType = {
|
export type ProfileGroupByOutputType = {
|
||||||
id: string
|
id: string
|
||||||
userId: string
|
userId: string | null
|
||||||
|
email: string | null
|
||||||
firstName: string
|
firstName: string
|
||||||
lastName: string
|
lastName: string
|
||||||
phoneNumber: string
|
phoneNumber: string
|
||||||
@@ -185,18 +192,20 @@ export type ProfileWhereInput = {
|
|||||||
OR?: Prisma.ProfileWhereInput[]
|
OR?: Prisma.ProfileWhereInput[]
|
||||||
NOT?: Prisma.ProfileWhereInput | Prisma.ProfileWhereInput[]
|
NOT?: Prisma.ProfileWhereInput | Prisma.ProfileWhereInput[]
|
||||||
id?: Prisma.StringFilter<"Profile"> | string
|
id?: Prisma.StringFilter<"Profile"> | string
|
||||||
userId?: Prisma.StringFilter<"Profile"> | string
|
userId?: Prisma.StringNullableFilter<"Profile"> | string | null
|
||||||
|
email?: Prisma.StringNullableFilter<"Profile"> | string | null
|
||||||
firstName?: Prisma.StringFilter<"Profile"> | string
|
firstName?: Prisma.StringFilter<"Profile"> | string
|
||||||
lastName?: Prisma.StringFilter<"Profile"> | string
|
lastName?: Prisma.StringFilter<"Profile"> | string
|
||||||
phoneNumber?: Prisma.StringFilter<"Profile"> | string
|
phoneNumber?: Prisma.StringFilter<"Profile"> | string
|
||||||
ghlContactId?: Prisma.StringNullableFilter<"Profile"> | string | null
|
ghlContactId?: Prisma.StringNullableFilter<"Profile"> | string | null
|
||||||
user?: Prisma.XOR<Prisma.UserScalarRelationFilter, Prisma.UserWhereInput>
|
user?: Prisma.XOR<Prisma.UserNullableScalarRelationFilter, Prisma.UserWhereInput> | null
|
||||||
addresses?: Prisma.AddressListRelationFilter
|
addresses?: Prisma.AddressListRelationFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProfileOrderByWithRelationInput = {
|
export type ProfileOrderByWithRelationInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
userId?: Prisma.SortOrder
|
userId?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
|
email?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
firstName?: Prisma.SortOrder
|
firstName?: Prisma.SortOrder
|
||||||
lastName?: Prisma.SortOrder
|
lastName?: Prisma.SortOrder
|
||||||
phoneNumber?: Prisma.SortOrder
|
phoneNumber?: Prisma.SortOrder
|
||||||
@@ -207,21 +216,23 @@ export type ProfileOrderByWithRelationInput = {
|
|||||||
|
|
||||||
export type ProfileWhereUniqueInput = Prisma.AtLeast<{
|
export type ProfileWhereUniqueInput = Prisma.AtLeast<{
|
||||||
id?: string
|
id?: string
|
||||||
userId?: string
|
|
||||||
AND?: Prisma.ProfileWhereInput | Prisma.ProfileWhereInput[]
|
AND?: Prisma.ProfileWhereInput | Prisma.ProfileWhereInput[]
|
||||||
OR?: Prisma.ProfileWhereInput[]
|
OR?: Prisma.ProfileWhereInput[]
|
||||||
NOT?: Prisma.ProfileWhereInput | Prisma.ProfileWhereInput[]
|
NOT?: Prisma.ProfileWhereInput | Prisma.ProfileWhereInput[]
|
||||||
|
userId?: Prisma.StringNullableFilter<"Profile"> | string | null
|
||||||
|
email?: Prisma.StringNullableFilter<"Profile"> | string | null
|
||||||
firstName?: Prisma.StringFilter<"Profile"> | string
|
firstName?: Prisma.StringFilter<"Profile"> | string
|
||||||
lastName?: Prisma.StringFilter<"Profile"> | string
|
lastName?: Prisma.StringFilter<"Profile"> | string
|
||||||
phoneNumber?: Prisma.StringFilter<"Profile"> | string
|
phoneNumber?: Prisma.StringFilter<"Profile"> | string
|
||||||
ghlContactId?: Prisma.StringNullableFilter<"Profile"> | string | null
|
ghlContactId?: Prisma.StringNullableFilter<"Profile"> | string | null
|
||||||
user?: Prisma.XOR<Prisma.UserScalarRelationFilter, Prisma.UserWhereInput>
|
user?: Prisma.XOR<Prisma.UserNullableScalarRelationFilter, Prisma.UserWhereInput> | null
|
||||||
addresses?: Prisma.AddressListRelationFilter
|
addresses?: Prisma.AddressListRelationFilter
|
||||||
}, "id" | "userId">
|
}, "id">
|
||||||
|
|
||||||
export type ProfileOrderByWithAggregationInput = {
|
export type ProfileOrderByWithAggregationInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
userId?: Prisma.SortOrder
|
userId?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
|
email?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
firstName?: Prisma.SortOrder
|
firstName?: Prisma.SortOrder
|
||||||
lastName?: Prisma.SortOrder
|
lastName?: Prisma.SortOrder
|
||||||
phoneNumber?: Prisma.SortOrder
|
phoneNumber?: Prisma.SortOrder
|
||||||
@@ -236,7 +247,8 @@ export type ProfileScalarWhereWithAggregatesInput = {
|
|||||||
OR?: Prisma.ProfileScalarWhereWithAggregatesInput[]
|
OR?: Prisma.ProfileScalarWhereWithAggregatesInput[]
|
||||||
NOT?: Prisma.ProfileScalarWhereWithAggregatesInput | Prisma.ProfileScalarWhereWithAggregatesInput[]
|
NOT?: Prisma.ProfileScalarWhereWithAggregatesInput | Prisma.ProfileScalarWhereWithAggregatesInput[]
|
||||||
id?: Prisma.StringWithAggregatesFilter<"Profile"> | string
|
id?: Prisma.StringWithAggregatesFilter<"Profile"> | string
|
||||||
userId?: Prisma.StringWithAggregatesFilter<"Profile"> | string
|
userId?: Prisma.StringNullableWithAggregatesFilter<"Profile"> | string | null
|
||||||
|
email?: Prisma.StringNullableWithAggregatesFilter<"Profile"> | string | null
|
||||||
firstName?: Prisma.StringWithAggregatesFilter<"Profile"> | string
|
firstName?: Prisma.StringWithAggregatesFilter<"Profile"> | string
|
||||||
lastName?: Prisma.StringWithAggregatesFilter<"Profile"> | string
|
lastName?: Prisma.StringWithAggregatesFilter<"Profile"> | string
|
||||||
phoneNumber?: Prisma.StringWithAggregatesFilter<"Profile"> | string
|
phoneNumber?: Prisma.StringWithAggregatesFilter<"Profile"> | string
|
||||||
@@ -245,17 +257,19 @@ export type ProfileScalarWhereWithAggregatesInput = {
|
|||||||
|
|
||||||
export type ProfileCreateInput = {
|
export type ProfileCreateInput = {
|
||||||
id?: string
|
id?: string
|
||||||
|
email?: string | null
|
||||||
firstName: string
|
firstName: string
|
||||||
lastName: string
|
lastName: string
|
||||||
phoneNumber: string
|
phoneNumber: string
|
||||||
ghlContactId?: string | null
|
ghlContactId?: string | null
|
||||||
user: Prisma.UserCreateNestedOneWithoutProfileInput
|
user?: Prisma.UserCreateNestedOneWithoutProfilesInput
|
||||||
addresses?: Prisma.AddressCreateNestedManyWithoutProfileInput
|
addresses?: Prisma.AddressCreateNestedManyWithoutProfileInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProfileUncheckedCreateInput = {
|
export type ProfileUncheckedCreateInput = {
|
||||||
id?: string
|
id?: string
|
||||||
userId: string
|
userId?: string | null
|
||||||
|
email?: string | null
|
||||||
firstName: string
|
firstName: string
|
||||||
lastName: string
|
lastName: string
|
||||||
phoneNumber: string
|
phoneNumber: string
|
||||||
@@ -265,17 +279,19 @@ export type ProfileUncheckedCreateInput = {
|
|||||||
|
|
||||||
export type ProfileUpdateInput = {
|
export type ProfileUpdateInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
email?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
firstName?: Prisma.StringFieldUpdateOperationsInput | string
|
firstName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
lastName?: Prisma.StringFieldUpdateOperationsInput | string
|
lastName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
phoneNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
phoneNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
ghlContactId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
ghlContactId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
user?: Prisma.UserUpdateOneRequiredWithoutProfileNestedInput
|
user?: Prisma.UserUpdateOneWithoutProfilesNestedInput
|
||||||
addresses?: Prisma.AddressUpdateManyWithoutProfileNestedInput
|
addresses?: Prisma.AddressUpdateManyWithoutProfileNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProfileUncheckedUpdateInput = {
|
export type ProfileUncheckedUpdateInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
userId?: Prisma.StringFieldUpdateOperationsInput | string
|
userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
email?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
firstName?: Prisma.StringFieldUpdateOperationsInput | string
|
firstName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
lastName?: Prisma.StringFieldUpdateOperationsInput | string
|
lastName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
phoneNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
phoneNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
@@ -285,7 +301,8 @@ export type ProfileUncheckedUpdateInput = {
|
|||||||
|
|
||||||
export type ProfileCreateManyInput = {
|
export type ProfileCreateManyInput = {
|
||||||
id?: string
|
id?: string
|
||||||
userId: string
|
userId?: string | null
|
||||||
|
email?: string | null
|
||||||
firstName: string
|
firstName: string
|
||||||
lastName: string
|
lastName: string
|
||||||
phoneNumber: string
|
phoneNumber: string
|
||||||
@@ -294,6 +311,7 @@ export type ProfileCreateManyInput = {
|
|||||||
|
|
||||||
export type ProfileUpdateManyMutationInput = {
|
export type ProfileUpdateManyMutationInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
email?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
firstName?: Prisma.StringFieldUpdateOperationsInput | string
|
firstName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
lastName?: Prisma.StringFieldUpdateOperationsInput | string
|
lastName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
phoneNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
phoneNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
@@ -302,21 +320,28 @@ export type ProfileUpdateManyMutationInput = {
|
|||||||
|
|
||||||
export type ProfileUncheckedUpdateManyInput = {
|
export type ProfileUncheckedUpdateManyInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
userId?: Prisma.StringFieldUpdateOperationsInput | string
|
userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
email?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
firstName?: Prisma.StringFieldUpdateOperationsInput | string
|
firstName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
lastName?: Prisma.StringFieldUpdateOperationsInput | string
|
lastName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
phoneNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
phoneNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
ghlContactId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
ghlContactId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProfileNullableScalarRelationFilter = {
|
export type ProfileListRelationFilter = {
|
||||||
is?: Prisma.ProfileWhereInput | null
|
every?: Prisma.ProfileWhereInput
|
||||||
isNot?: Prisma.ProfileWhereInput | null
|
some?: Prisma.ProfileWhereInput
|
||||||
|
none?: Prisma.ProfileWhereInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProfileOrderByRelationAggregateInput = {
|
||||||
|
_count?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProfileCountOrderByAggregateInput = {
|
export type ProfileCountOrderByAggregateInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
userId?: Prisma.SortOrder
|
userId?: Prisma.SortOrder
|
||||||
|
email?: Prisma.SortOrder
|
||||||
firstName?: Prisma.SortOrder
|
firstName?: Prisma.SortOrder
|
||||||
lastName?: Prisma.SortOrder
|
lastName?: Prisma.SortOrder
|
||||||
phoneNumber?: Prisma.SortOrder
|
phoneNumber?: Prisma.SortOrder
|
||||||
@@ -326,6 +351,7 @@ export type ProfileCountOrderByAggregateInput = {
|
|||||||
export type ProfileMaxOrderByAggregateInput = {
|
export type ProfileMaxOrderByAggregateInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
userId?: Prisma.SortOrder
|
userId?: Prisma.SortOrder
|
||||||
|
email?: Prisma.SortOrder
|
||||||
firstName?: Prisma.SortOrder
|
firstName?: Prisma.SortOrder
|
||||||
lastName?: Prisma.SortOrder
|
lastName?: Prisma.SortOrder
|
||||||
phoneNumber?: Prisma.SortOrder
|
phoneNumber?: Prisma.SortOrder
|
||||||
@@ -335,6 +361,7 @@ export type ProfileMaxOrderByAggregateInput = {
|
|||||||
export type ProfileMinOrderByAggregateInput = {
|
export type ProfileMinOrderByAggregateInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
userId?: Prisma.SortOrder
|
userId?: Prisma.SortOrder
|
||||||
|
email?: Prisma.SortOrder
|
||||||
firstName?: Prisma.SortOrder
|
firstName?: Prisma.SortOrder
|
||||||
lastName?: Prisma.SortOrder
|
lastName?: Prisma.SortOrder
|
||||||
phoneNumber?: Prisma.SortOrder
|
phoneNumber?: Prisma.SortOrder
|
||||||
@@ -346,36 +373,46 @@ export type ProfileScalarRelationFilter = {
|
|||||||
isNot?: Prisma.ProfileWhereInput
|
isNot?: Prisma.ProfileWhereInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProfileCreateNestedOneWithoutUserInput = {
|
export type ProfileCreateNestedManyWithoutUserInput = {
|
||||||
create?: Prisma.XOR<Prisma.ProfileCreateWithoutUserInput, Prisma.ProfileUncheckedCreateWithoutUserInput>
|
create?: Prisma.XOR<Prisma.ProfileCreateWithoutUserInput, Prisma.ProfileUncheckedCreateWithoutUserInput> | Prisma.ProfileCreateWithoutUserInput[] | Prisma.ProfileUncheckedCreateWithoutUserInput[]
|
||||||
connectOrCreate?: Prisma.ProfileCreateOrConnectWithoutUserInput
|
connectOrCreate?: Prisma.ProfileCreateOrConnectWithoutUserInput | Prisma.ProfileCreateOrConnectWithoutUserInput[]
|
||||||
connect?: Prisma.ProfileWhereUniqueInput
|
createMany?: Prisma.ProfileCreateManyUserInputEnvelope
|
||||||
|
connect?: Prisma.ProfileWhereUniqueInput | Prisma.ProfileWhereUniqueInput[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProfileUncheckedCreateNestedOneWithoutUserInput = {
|
export type ProfileUncheckedCreateNestedManyWithoutUserInput = {
|
||||||
create?: Prisma.XOR<Prisma.ProfileCreateWithoutUserInput, Prisma.ProfileUncheckedCreateWithoutUserInput>
|
create?: Prisma.XOR<Prisma.ProfileCreateWithoutUserInput, Prisma.ProfileUncheckedCreateWithoutUserInput> | Prisma.ProfileCreateWithoutUserInput[] | Prisma.ProfileUncheckedCreateWithoutUserInput[]
|
||||||
connectOrCreate?: Prisma.ProfileCreateOrConnectWithoutUserInput
|
connectOrCreate?: Prisma.ProfileCreateOrConnectWithoutUserInput | Prisma.ProfileCreateOrConnectWithoutUserInput[]
|
||||||
connect?: Prisma.ProfileWhereUniqueInput
|
createMany?: Prisma.ProfileCreateManyUserInputEnvelope
|
||||||
|
connect?: Prisma.ProfileWhereUniqueInput | Prisma.ProfileWhereUniqueInput[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProfileUpdateOneWithoutUserNestedInput = {
|
export type ProfileUpdateManyWithoutUserNestedInput = {
|
||||||
create?: Prisma.XOR<Prisma.ProfileCreateWithoutUserInput, Prisma.ProfileUncheckedCreateWithoutUserInput>
|
create?: Prisma.XOR<Prisma.ProfileCreateWithoutUserInput, Prisma.ProfileUncheckedCreateWithoutUserInput> | Prisma.ProfileCreateWithoutUserInput[] | Prisma.ProfileUncheckedCreateWithoutUserInput[]
|
||||||
connectOrCreate?: Prisma.ProfileCreateOrConnectWithoutUserInput
|
connectOrCreate?: Prisma.ProfileCreateOrConnectWithoutUserInput | Prisma.ProfileCreateOrConnectWithoutUserInput[]
|
||||||
upsert?: Prisma.ProfileUpsertWithoutUserInput
|
upsert?: Prisma.ProfileUpsertWithWhereUniqueWithoutUserInput | Prisma.ProfileUpsertWithWhereUniqueWithoutUserInput[]
|
||||||
disconnect?: Prisma.ProfileWhereInput | boolean
|
createMany?: Prisma.ProfileCreateManyUserInputEnvelope
|
||||||
delete?: Prisma.ProfileWhereInput | boolean
|
set?: Prisma.ProfileWhereUniqueInput | Prisma.ProfileWhereUniqueInput[]
|
||||||
connect?: Prisma.ProfileWhereUniqueInput
|
disconnect?: Prisma.ProfileWhereUniqueInput | Prisma.ProfileWhereUniqueInput[]
|
||||||
update?: Prisma.XOR<Prisma.XOR<Prisma.ProfileUpdateToOneWithWhereWithoutUserInput, Prisma.ProfileUpdateWithoutUserInput>, Prisma.ProfileUncheckedUpdateWithoutUserInput>
|
delete?: Prisma.ProfileWhereUniqueInput | Prisma.ProfileWhereUniqueInput[]
|
||||||
|
connect?: Prisma.ProfileWhereUniqueInput | Prisma.ProfileWhereUniqueInput[]
|
||||||
|
update?: Prisma.ProfileUpdateWithWhereUniqueWithoutUserInput | Prisma.ProfileUpdateWithWhereUniqueWithoutUserInput[]
|
||||||
|
updateMany?: Prisma.ProfileUpdateManyWithWhereWithoutUserInput | Prisma.ProfileUpdateManyWithWhereWithoutUserInput[]
|
||||||
|
deleteMany?: Prisma.ProfileScalarWhereInput | Prisma.ProfileScalarWhereInput[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProfileUncheckedUpdateOneWithoutUserNestedInput = {
|
export type ProfileUncheckedUpdateManyWithoutUserNestedInput = {
|
||||||
create?: Prisma.XOR<Prisma.ProfileCreateWithoutUserInput, Prisma.ProfileUncheckedCreateWithoutUserInput>
|
create?: Prisma.XOR<Prisma.ProfileCreateWithoutUserInput, Prisma.ProfileUncheckedCreateWithoutUserInput> | Prisma.ProfileCreateWithoutUserInput[] | Prisma.ProfileUncheckedCreateWithoutUserInput[]
|
||||||
connectOrCreate?: Prisma.ProfileCreateOrConnectWithoutUserInput
|
connectOrCreate?: Prisma.ProfileCreateOrConnectWithoutUserInput | Prisma.ProfileCreateOrConnectWithoutUserInput[]
|
||||||
upsert?: Prisma.ProfileUpsertWithoutUserInput
|
upsert?: Prisma.ProfileUpsertWithWhereUniqueWithoutUserInput | Prisma.ProfileUpsertWithWhereUniqueWithoutUserInput[]
|
||||||
disconnect?: Prisma.ProfileWhereInput | boolean
|
createMany?: Prisma.ProfileCreateManyUserInputEnvelope
|
||||||
delete?: Prisma.ProfileWhereInput | boolean
|
set?: Prisma.ProfileWhereUniqueInput | Prisma.ProfileWhereUniqueInput[]
|
||||||
connect?: Prisma.ProfileWhereUniqueInput
|
disconnect?: Prisma.ProfileWhereUniqueInput | Prisma.ProfileWhereUniqueInput[]
|
||||||
update?: Prisma.XOR<Prisma.XOR<Prisma.ProfileUpdateToOneWithWhereWithoutUserInput, Prisma.ProfileUpdateWithoutUserInput>, Prisma.ProfileUncheckedUpdateWithoutUserInput>
|
delete?: Prisma.ProfileWhereUniqueInput | Prisma.ProfileWhereUniqueInput[]
|
||||||
|
connect?: Prisma.ProfileWhereUniqueInput | Prisma.ProfileWhereUniqueInput[]
|
||||||
|
update?: Prisma.ProfileUpdateWithWhereUniqueWithoutUserInput | Prisma.ProfileUpdateWithWhereUniqueWithoutUserInput[]
|
||||||
|
updateMany?: Prisma.ProfileUpdateManyWithWhereWithoutUserInput | Prisma.ProfileUpdateManyWithWhereWithoutUserInput[]
|
||||||
|
deleteMany?: Prisma.ProfileScalarWhereInput | Prisma.ProfileScalarWhereInput[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProfileCreateNestedOneWithoutAddressesInput = {
|
export type ProfileCreateNestedOneWithoutAddressesInput = {
|
||||||
@@ -394,6 +431,7 @@ export type ProfileUpdateOneRequiredWithoutAddressesNestedInput = {
|
|||||||
|
|
||||||
export type ProfileCreateWithoutUserInput = {
|
export type ProfileCreateWithoutUserInput = {
|
||||||
id?: string
|
id?: string
|
||||||
|
email?: string | null
|
||||||
firstName: string
|
firstName: string
|
||||||
lastName: string
|
lastName: string
|
||||||
phoneNumber: string
|
phoneNumber: string
|
||||||
@@ -403,6 +441,7 @@ export type ProfileCreateWithoutUserInput = {
|
|||||||
|
|
||||||
export type ProfileUncheckedCreateWithoutUserInput = {
|
export type ProfileUncheckedCreateWithoutUserInput = {
|
||||||
id?: string
|
id?: string
|
||||||
|
email?: string | null
|
||||||
firstName: string
|
firstName: string
|
||||||
lastName: string
|
lastName: string
|
||||||
phoneNumber: string
|
phoneNumber: string
|
||||||
@@ -415,47 +454,54 @@ export type ProfileCreateOrConnectWithoutUserInput = {
|
|||||||
create: Prisma.XOR<Prisma.ProfileCreateWithoutUserInput, Prisma.ProfileUncheckedCreateWithoutUserInput>
|
create: Prisma.XOR<Prisma.ProfileCreateWithoutUserInput, Prisma.ProfileUncheckedCreateWithoutUserInput>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProfileUpsertWithoutUserInput = {
|
export type ProfileCreateManyUserInputEnvelope = {
|
||||||
update: Prisma.XOR<Prisma.ProfileUpdateWithoutUserInput, Prisma.ProfileUncheckedUpdateWithoutUserInput>
|
data: Prisma.ProfileCreateManyUserInput | Prisma.ProfileCreateManyUserInput[]
|
||||||
create: Prisma.XOR<Prisma.ProfileCreateWithoutUserInput, Prisma.ProfileUncheckedCreateWithoutUserInput>
|
skipDuplicates?: boolean
|
||||||
where?: Prisma.ProfileWhereInput
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProfileUpdateToOneWithWhereWithoutUserInput = {
|
export type ProfileUpsertWithWhereUniqueWithoutUserInput = {
|
||||||
where?: Prisma.ProfileWhereInput
|
where: Prisma.ProfileWhereUniqueInput
|
||||||
|
update: Prisma.XOR<Prisma.ProfileUpdateWithoutUserInput, Prisma.ProfileUncheckedUpdateWithoutUserInput>
|
||||||
|
create: Prisma.XOR<Prisma.ProfileCreateWithoutUserInput, Prisma.ProfileUncheckedCreateWithoutUserInput>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProfileUpdateWithWhereUniqueWithoutUserInput = {
|
||||||
|
where: Prisma.ProfileWhereUniqueInput
|
||||||
data: Prisma.XOR<Prisma.ProfileUpdateWithoutUserInput, Prisma.ProfileUncheckedUpdateWithoutUserInput>
|
data: Prisma.XOR<Prisma.ProfileUpdateWithoutUserInput, Prisma.ProfileUncheckedUpdateWithoutUserInput>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProfileUpdateWithoutUserInput = {
|
export type ProfileUpdateManyWithWhereWithoutUserInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
where: Prisma.ProfileScalarWhereInput
|
||||||
firstName?: Prisma.StringFieldUpdateOperationsInput | string
|
data: Prisma.XOR<Prisma.ProfileUpdateManyMutationInput, Prisma.ProfileUncheckedUpdateManyWithoutUserInput>
|
||||||
lastName?: Prisma.StringFieldUpdateOperationsInput | string
|
|
||||||
phoneNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
|
||||||
ghlContactId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
|
||||||
addresses?: Prisma.AddressUpdateManyWithoutProfileNestedInput
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProfileUncheckedUpdateWithoutUserInput = {
|
export type ProfileScalarWhereInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
AND?: Prisma.ProfileScalarWhereInput | Prisma.ProfileScalarWhereInput[]
|
||||||
firstName?: Prisma.StringFieldUpdateOperationsInput | string
|
OR?: Prisma.ProfileScalarWhereInput[]
|
||||||
lastName?: Prisma.StringFieldUpdateOperationsInput | string
|
NOT?: Prisma.ProfileScalarWhereInput | Prisma.ProfileScalarWhereInput[]
|
||||||
phoneNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFilter<"Profile"> | string
|
||||||
ghlContactId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
userId?: Prisma.StringNullableFilter<"Profile"> | string | null
|
||||||
addresses?: Prisma.AddressUncheckedUpdateManyWithoutProfileNestedInput
|
email?: Prisma.StringNullableFilter<"Profile"> | string | null
|
||||||
|
firstName?: Prisma.StringFilter<"Profile"> | string
|
||||||
|
lastName?: Prisma.StringFilter<"Profile"> | string
|
||||||
|
phoneNumber?: Prisma.StringFilter<"Profile"> | string
|
||||||
|
ghlContactId?: Prisma.StringNullableFilter<"Profile"> | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProfileCreateWithoutAddressesInput = {
|
export type ProfileCreateWithoutAddressesInput = {
|
||||||
id?: string
|
id?: string
|
||||||
|
email?: string | null
|
||||||
firstName: string
|
firstName: string
|
||||||
lastName: string
|
lastName: string
|
||||||
phoneNumber: string
|
phoneNumber: string
|
||||||
ghlContactId?: string | null
|
ghlContactId?: string | null
|
||||||
user: Prisma.UserCreateNestedOneWithoutProfileInput
|
user?: Prisma.UserCreateNestedOneWithoutProfilesInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProfileUncheckedCreateWithoutAddressesInput = {
|
export type ProfileUncheckedCreateWithoutAddressesInput = {
|
||||||
id?: string
|
id?: string
|
||||||
userId: string
|
userId?: string | null
|
||||||
|
email?: string | null
|
||||||
firstName: string
|
firstName: string
|
||||||
lastName: string
|
lastName: string
|
||||||
phoneNumber: string
|
phoneNumber: string
|
||||||
@@ -480,16 +526,56 @@ export type ProfileUpdateToOneWithWhereWithoutAddressesInput = {
|
|||||||
|
|
||||||
export type ProfileUpdateWithoutAddressesInput = {
|
export type ProfileUpdateWithoutAddressesInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
email?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
firstName?: Prisma.StringFieldUpdateOperationsInput | string
|
firstName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
lastName?: Prisma.StringFieldUpdateOperationsInput | string
|
lastName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
phoneNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
phoneNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
ghlContactId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
ghlContactId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
user?: Prisma.UserUpdateOneRequiredWithoutProfileNestedInput
|
user?: Prisma.UserUpdateOneWithoutProfilesNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProfileUncheckedUpdateWithoutAddressesInput = {
|
export type ProfileUncheckedUpdateWithoutAddressesInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
userId?: Prisma.StringFieldUpdateOperationsInput | string
|
userId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
email?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
firstName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
lastName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
phoneNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
ghlContactId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProfileCreateManyUserInput = {
|
||||||
|
id?: string
|
||||||
|
email?: string | null
|
||||||
|
firstName: string
|
||||||
|
lastName: string
|
||||||
|
phoneNumber: string
|
||||||
|
ghlContactId?: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProfileUpdateWithoutUserInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
email?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
firstName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
lastName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
phoneNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
ghlContactId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
addresses?: Prisma.AddressUpdateManyWithoutProfileNestedInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProfileUncheckedUpdateWithoutUserInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
email?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
firstName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
lastName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
phoneNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
ghlContactId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
addresses?: Prisma.AddressUncheckedUpdateManyWithoutProfileNestedInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProfileUncheckedUpdateManyWithoutUserInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
email?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
firstName?: Prisma.StringFieldUpdateOperationsInput | string
|
firstName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
lastName?: Prisma.StringFieldUpdateOperationsInput | string
|
lastName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
phoneNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
phoneNumber?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
@@ -530,11 +616,12 @@ export type ProfileCountOutputTypeCountAddressesArgs<ExtArgs extends runtime.Typ
|
|||||||
export type ProfileSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type ProfileSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
id?: boolean
|
id?: boolean
|
||||||
userId?: boolean
|
userId?: boolean
|
||||||
|
email?: boolean
|
||||||
firstName?: boolean
|
firstName?: boolean
|
||||||
lastName?: boolean
|
lastName?: boolean
|
||||||
phoneNumber?: boolean
|
phoneNumber?: boolean
|
||||||
ghlContactId?: boolean
|
ghlContactId?: boolean
|
||||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
user?: boolean | Prisma.Profile$userArgs<ExtArgs>
|
||||||
addresses?: boolean | Prisma.Profile$addressesArgs<ExtArgs>
|
addresses?: boolean | Prisma.Profile$addressesArgs<ExtArgs>
|
||||||
_count?: boolean | Prisma.ProfileCountOutputTypeDefaultArgs<ExtArgs>
|
_count?: boolean | Prisma.ProfileCountOutputTypeDefaultArgs<ExtArgs>
|
||||||
}, ExtArgs["result"]["profile"]>
|
}, ExtArgs["result"]["profile"]>
|
||||||
@@ -542,54 +629,58 @@ export type ProfileSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs
|
|||||||
export type ProfileSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type ProfileSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
id?: boolean
|
id?: boolean
|
||||||
userId?: boolean
|
userId?: boolean
|
||||||
|
email?: boolean
|
||||||
firstName?: boolean
|
firstName?: boolean
|
||||||
lastName?: boolean
|
lastName?: boolean
|
||||||
phoneNumber?: boolean
|
phoneNumber?: boolean
|
||||||
ghlContactId?: boolean
|
ghlContactId?: boolean
|
||||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
user?: boolean | Prisma.Profile$userArgs<ExtArgs>
|
||||||
}, ExtArgs["result"]["profile"]>
|
}, ExtArgs["result"]["profile"]>
|
||||||
|
|
||||||
export type ProfileSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type ProfileSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
id?: boolean
|
id?: boolean
|
||||||
userId?: boolean
|
userId?: boolean
|
||||||
|
email?: boolean
|
||||||
firstName?: boolean
|
firstName?: boolean
|
||||||
lastName?: boolean
|
lastName?: boolean
|
||||||
phoneNumber?: boolean
|
phoneNumber?: boolean
|
||||||
ghlContactId?: boolean
|
ghlContactId?: boolean
|
||||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
user?: boolean | Prisma.Profile$userArgs<ExtArgs>
|
||||||
}, ExtArgs["result"]["profile"]>
|
}, ExtArgs["result"]["profile"]>
|
||||||
|
|
||||||
export type ProfileSelectScalar = {
|
export type ProfileSelectScalar = {
|
||||||
id?: boolean
|
id?: boolean
|
||||||
userId?: boolean
|
userId?: boolean
|
||||||
|
email?: boolean
|
||||||
firstName?: boolean
|
firstName?: boolean
|
||||||
lastName?: boolean
|
lastName?: boolean
|
||||||
phoneNumber?: boolean
|
phoneNumber?: boolean
|
||||||
ghlContactId?: boolean
|
ghlContactId?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProfileOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "userId" | "firstName" | "lastName" | "phoneNumber" | "ghlContactId", ExtArgs["result"]["profile"]>
|
export type ProfileOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "userId" | "email" | "firstName" | "lastName" | "phoneNumber" | "ghlContactId", ExtArgs["result"]["profile"]>
|
||||||
export type ProfileInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type ProfileInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
user?: boolean | Prisma.Profile$userArgs<ExtArgs>
|
||||||
addresses?: boolean | Prisma.Profile$addressesArgs<ExtArgs>
|
addresses?: boolean | Prisma.Profile$addressesArgs<ExtArgs>
|
||||||
_count?: boolean | Prisma.ProfileCountOutputTypeDefaultArgs<ExtArgs>
|
_count?: boolean | Prisma.ProfileCountOutputTypeDefaultArgs<ExtArgs>
|
||||||
}
|
}
|
||||||
export type ProfileIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type ProfileIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
user?: boolean | Prisma.Profile$userArgs<ExtArgs>
|
||||||
}
|
}
|
||||||
export type ProfileIncludeUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type ProfileIncludeUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
user?: boolean | Prisma.Profile$userArgs<ExtArgs>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type $ProfilePayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type $ProfilePayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
name: "Profile"
|
name: "Profile"
|
||||||
objects: {
|
objects: {
|
||||||
user: Prisma.$UserPayload<ExtArgs>
|
user: Prisma.$UserPayload<ExtArgs> | null
|
||||||
addresses: Prisma.$AddressPayload<ExtArgs>[]
|
addresses: Prisma.$AddressPayload<ExtArgs>[]
|
||||||
}
|
}
|
||||||
scalars: runtime.Types.Extensions.GetPayloadResult<{
|
scalars: runtime.Types.Extensions.GetPayloadResult<{
|
||||||
id: string
|
id: string
|
||||||
userId: string
|
userId: string | null
|
||||||
|
email: string | null
|
||||||
firstName: string
|
firstName: string
|
||||||
lastName: string
|
lastName: string
|
||||||
phoneNumber: string
|
phoneNumber: string
|
||||||
@@ -988,7 +1079,7 @@ readonly fields: ProfileFieldRefs;
|
|||||||
*/
|
*/
|
||||||
export interface Prisma__ProfileClient<T, Null = never, ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs, GlobalOmitOptions = {}> extends Prisma.PrismaPromise<T> {
|
export interface Prisma__ProfileClient<T, Null = never, ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs, GlobalOmitOptions = {}> extends Prisma.PrismaPromise<T> {
|
||||||
readonly [Symbol.toStringTag]: "PrismaPromise"
|
readonly [Symbol.toStringTag]: "PrismaPromise"
|
||||||
user<T extends Prisma.UserDefaultArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.UserDefaultArgs<ExtArgs>>): Prisma.Prisma__UserClient<runtime.Types.Result.GetResult<Prisma.$UserPayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | Null, Null, ExtArgs, GlobalOmitOptions>
|
user<T extends Prisma.Profile$userArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Profile$userArgs<ExtArgs>>): Prisma.Prisma__UserClient<runtime.Types.Result.GetResult<Prisma.$UserPayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions>
|
||||||
addresses<T extends Prisma.Profile$addressesArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Profile$addressesArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$AddressPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
addresses<T extends Prisma.Profile$addressesArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Profile$addressesArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$AddressPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||||
/**
|
/**
|
||||||
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
||||||
@@ -1021,6 +1112,7 @@ export interface Prisma__ProfileClient<T, Null = never, ExtArgs extends runtime.
|
|||||||
export interface ProfileFieldRefs {
|
export interface ProfileFieldRefs {
|
||||||
readonly id: Prisma.FieldRef<"Profile", 'String'>
|
readonly id: Prisma.FieldRef<"Profile", 'String'>
|
||||||
readonly userId: Prisma.FieldRef<"Profile", 'String'>
|
readonly userId: Prisma.FieldRef<"Profile", 'String'>
|
||||||
|
readonly email: Prisma.FieldRef<"Profile", 'String'>
|
||||||
readonly firstName: Prisma.FieldRef<"Profile", 'String'>
|
readonly firstName: Prisma.FieldRef<"Profile", 'String'>
|
||||||
readonly lastName: Prisma.FieldRef<"Profile", 'String'>
|
readonly lastName: Prisma.FieldRef<"Profile", 'String'>
|
||||||
readonly phoneNumber: Prisma.FieldRef<"Profile", 'String'>
|
readonly phoneNumber: Prisma.FieldRef<"Profile", 'String'>
|
||||||
@@ -1425,6 +1517,25 @@ export type ProfileDeleteManyArgs<ExtArgs extends runtime.Types.Extensions.Inter
|
|||||||
limit?: number
|
limit?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Profile.user
|
||||||
|
*/
|
||||||
|
export type Profile$userArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
|
/**
|
||||||
|
* Select specific fields to fetch from the User
|
||||||
|
*/
|
||||||
|
select?: Prisma.UserSelect<ExtArgs> | null
|
||||||
|
/**
|
||||||
|
* Omit specific fields from the User
|
||||||
|
*/
|
||||||
|
omit?: Prisma.UserOmit<ExtArgs> | null
|
||||||
|
/**
|
||||||
|
* Choose, which related nodes to fetch as well
|
||||||
|
*/
|
||||||
|
include?: Prisma.UserInclude<ExtArgs> | null
|
||||||
|
where?: Prisma.UserWhereInput
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Profile.addresses
|
* Profile.addresses
|
||||||
*/
|
*/
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -243,7 +243,7 @@ export type UserWhereInput = {
|
|||||||
role?: Prisma.XOR<Prisma.UserRoleNullableScalarRelationFilter, Prisma.UserRoleWhereInput> | null
|
role?: Prisma.XOR<Prisma.UserRoleNullableScalarRelationFilter, Prisma.UserRoleWhereInput> | null
|
||||||
sessions?: Prisma.SessionListRelationFilter
|
sessions?: Prisma.SessionListRelationFilter
|
||||||
accounts?: Prisma.AccountListRelationFilter
|
accounts?: Prisma.AccountListRelationFilter
|
||||||
profile?: Prisma.XOR<Prisma.ProfileNullableScalarRelationFilter, Prisma.ProfileWhereInput> | null
|
profiles?: Prisma.ProfileListRelationFilter
|
||||||
licenseKeys?: Prisma.LicenseKeyListRelationFilter
|
licenseKeys?: Prisma.LicenseKeyListRelationFilter
|
||||||
userProclamations?: Prisma.UserProclamationsListRelationFilter
|
userProclamations?: Prisma.UserProclamationsListRelationFilter
|
||||||
}
|
}
|
||||||
@@ -260,7 +260,7 @@ export type UserOrderByWithRelationInput = {
|
|||||||
role?: Prisma.UserRoleOrderByWithRelationInput
|
role?: Prisma.UserRoleOrderByWithRelationInput
|
||||||
sessions?: Prisma.SessionOrderByRelationAggregateInput
|
sessions?: Prisma.SessionOrderByRelationAggregateInput
|
||||||
accounts?: Prisma.AccountOrderByRelationAggregateInput
|
accounts?: Prisma.AccountOrderByRelationAggregateInput
|
||||||
profile?: Prisma.ProfileOrderByWithRelationInput
|
profiles?: Prisma.ProfileOrderByRelationAggregateInput
|
||||||
licenseKeys?: Prisma.LicenseKeyOrderByRelationAggregateInput
|
licenseKeys?: Prisma.LicenseKeyOrderByRelationAggregateInput
|
||||||
userProclamations?: Prisma.UserProclamationsOrderByRelationAggregateInput
|
userProclamations?: Prisma.UserProclamationsOrderByRelationAggregateInput
|
||||||
}
|
}
|
||||||
@@ -280,7 +280,7 @@ export type UserWhereUniqueInput = Prisma.AtLeast<{
|
|||||||
role?: Prisma.XOR<Prisma.UserRoleNullableScalarRelationFilter, Prisma.UserRoleWhereInput> | null
|
role?: Prisma.XOR<Prisma.UserRoleNullableScalarRelationFilter, Prisma.UserRoleWhereInput> | null
|
||||||
sessions?: Prisma.SessionListRelationFilter
|
sessions?: Prisma.SessionListRelationFilter
|
||||||
accounts?: Prisma.AccountListRelationFilter
|
accounts?: Prisma.AccountListRelationFilter
|
||||||
profile?: Prisma.XOR<Prisma.ProfileNullableScalarRelationFilter, Prisma.ProfileWhereInput> | null
|
profiles?: Prisma.ProfileListRelationFilter
|
||||||
licenseKeys?: Prisma.LicenseKeyListRelationFilter
|
licenseKeys?: Prisma.LicenseKeyListRelationFilter
|
||||||
userProclamations?: Prisma.UserProclamationsListRelationFilter
|
userProclamations?: Prisma.UserProclamationsListRelationFilter
|
||||||
}, "id" | "email">
|
}, "id" | "email">
|
||||||
@@ -326,7 +326,7 @@ export type UserCreateInput = {
|
|||||||
role?: Prisma.UserRoleCreateNestedOneWithoutUsersInput
|
role?: Prisma.UserRoleCreateNestedOneWithoutUsersInput
|
||||||
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
|
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
|
||||||
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
||||||
profile?: Prisma.ProfileCreateNestedOneWithoutUserInput
|
profiles?: Prisma.ProfileCreateNestedManyWithoutUserInput
|
||||||
licenseKeys?: Prisma.LicenseKeyCreateNestedManyWithoutUserInput
|
licenseKeys?: Prisma.LicenseKeyCreateNestedManyWithoutUserInput
|
||||||
userProclamations?: Prisma.UserProclamationsCreateNestedManyWithoutUserInput
|
userProclamations?: Prisma.UserProclamationsCreateNestedManyWithoutUserInput
|
||||||
}
|
}
|
||||||
@@ -342,7 +342,7 @@ export type UserUncheckedCreateInput = {
|
|||||||
roleId?: number | null
|
roleId?: number | null
|
||||||
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
|
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
|
||||||
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
||||||
profile?: Prisma.ProfileUncheckedCreateNestedOneWithoutUserInput
|
profiles?: Prisma.ProfileUncheckedCreateNestedManyWithoutUserInput
|
||||||
licenseKeys?: Prisma.LicenseKeyUncheckedCreateNestedManyWithoutUserInput
|
licenseKeys?: Prisma.LicenseKeyUncheckedCreateNestedManyWithoutUserInput
|
||||||
userProclamations?: Prisma.UserProclamationsUncheckedCreateNestedManyWithoutUserInput
|
userProclamations?: Prisma.UserProclamationsUncheckedCreateNestedManyWithoutUserInput
|
||||||
}
|
}
|
||||||
@@ -358,7 +358,7 @@ export type UserUpdateInput = {
|
|||||||
role?: Prisma.UserRoleUpdateOneWithoutUsersNestedInput
|
role?: Prisma.UserRoleUpdateOneWithoutUsersNestedInput
|
||||||
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
|
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
|
||||||
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
||||||
profile?: Prisma.ProfileUpdateOneWithoutUserNestedInput
|
profiles?: Prisma.ProfileUpdateManyWithoutUserNestedInput
|
||||||
licenseKeys?: Prisma.LicenseKeyUpdateManyWithoutUserNestedInput
|
licenseKeys?: Prisma.LicenseKeyUpdateManyWithoutUserNestedInput
|
||||||
userProclamations?: Prisma.UserProclamationsUpdateManyWithoutUserNestedInput
|
userProclamations?: Prisma.UserProclamationsUpdateManyWithoutUserNestedInput
|
||||||
}
|
}
|
||||||
@@ -374,7 +374,7 @@ export type UserUncheckedUpdateInput = {
|
|||||||
roleId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
roleId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||||
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
|
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
|
||||||
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
||||||
profile?: Prisma.ProfileUncheckedUpdateOneWithoutUserNestedInput
|
profiles?: Prisma.ProfileUncheckedUpdateManyWithoutUserNestedInput
|
||||||
licenseKeys?: Prisma.LicenseKeyUncheckedUpdateManyWithoutUserNestedInput
|
licenseKeys?: Prisma.LicenseKeyUncheckedUpdateManyWithoutUserNestedInput
|
||||||
userProclamations?: Prisma.UserProclamationsUncheckedUpdateManyWithoutUserNestedInput
|
userProclamations?: Prisma.UserProclamationsUncheckedUpdateManyWithoutUserNestedInput
|
||||||
}
|
}
|
||||||
@@ -452,6 +452,11 @@ export type UserSumOrderByAggregateInput = {
|
|||||||
roleId?: Prisma.SortOrder
|
roleId?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type UserNullableScalarRelationFilter = {
|
||||||
|
is?: Prisma.UserWhereInput | null
|
||||||
|
isNot?: Prisma.UserWhereInput | null
|
||||||
|
}
|
||||||
|
|
||||||
export type UserScalarRelationFilter = {
|
export type UserScalarRelationFilter = {
|
||||||
is?: Prisma.UserWhereInput
|
is?: Prisma.UserWhereInput
|
||||||
isNot?: Prisma.UserWhereInput
|
isNot?: Prisma.UserWhereInput
|
||||||
@@ -491,18 +496,20 @@ export type NullableIntFieldUpdateOperationsInput = {
|
|||||||
divide?: number
|
divide?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserCreateNestedOneWithoutProfileInput = {
|
export type UserCreateNestedOneWithoutProfilesInput = {
|
||||||
create?: Prisma.XOR<Prisma.UserCreateWithoutProfileInput, Prisma.UserUncheckedCreateWithoutProfileInput>
|
create?: Prisma.XOR<Prisma.UserCreateWithoutProfilesInput, Prisma.UserUncheckedCreateWithoutProfilesInput>
|
||||||
connectOrCreate?: Prisma.UserCreateOrConnectWithoutProfileInput
|
connectOrCreate?: Prisma.UserCreateOrConnectWithoutProfilesInput
|
||||||
connect?: Prisma.UserWhereUniqueInput
|
connect?: Prisma.UserWhereUniqueInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUpdateOneRequiredWithoutProfileNestedInput = {
|
export type UserUpdateOneWithoutProfilesNestedInput = {
|
||||||
create?: Prisma.XOR<Prisma.UserCreateWithoutProfileInput, Prisma.UserUncheckedCreateWithoutProfileInput>
|
create?: Prisma.XOR<Prisma.UserCreateWithoutProfilesInput, Prisma.UserUncheckedCreateWithoutProfilesInput>
|
||||||
connectOrCreate?: Prisma.UserCreateOrConnectWithoutProfileInput
|
connectOrCreate?: Prisma.UserCreateOrConnectWithoutProfilesInput
|
||||||
upsert?: Prisma.UserUpsertWithoutProfileInput
|
upsert?: Prisma.UserUpsertWithoutProfilesInput
|
||||||
|
disconnect?: Prisma.UserWhereInput | boolean
|
||||||
|
delete?: Prisma.UserWhereInput | boolean
|
||||||
connect?: Prisma.UserWhereUniqueInput
|
connect?: Prisma.UserWhereUniqueInput
|
||||||
update?: Prisma.XOR<Prisma.XOR<Prisma.UserUpdateToOneWithWhereWithoutProfileInput, Prisma.UserUpdateWithoutProfileInput>, Prisma.UserUncheckedUpdateWithoutProfileInput>
|
update?: Prisma.XOR<Prisma.XOR<Prisma.UserUpdateToOneWithWhereWithoutProfilesInput, Prisma.UserUpdateWithoutProfilesInput>, Prisma.UserUncheckedUpdateWithoutProfilesInput>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserCreateNestedOneWithoutSessionsInput = {
|
export type UserCreateNestedOneWithoutSessionsInput = {
|
||||||
@@ -595,15 +602,17 @@ export type UserCreateNestedOneWithoutLicenseKeysInput = {
|
|||||||
connect?: Prisma.UserWhereUniqueInput
|
connect?: Prisma.UserWhereUniqueInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUpdateOneRequiredWithoutLicenseKeysNestedInput = {
|
export type UserUpdateOneWithoutLicenseKeysNestedInput = {
|
||||||
create?: Prisma.XOR<Prisma.UserCreateWithoutLicenseKeysInput, Prisma.UserUncheckedCreateWithoutLicenseKeysInput>
|
create?: Prisma.XOR<Prisma.UserCreateWithoutLicenseKeysInput, Prisma.UserUncheckedCreateWithoutLicenseKeysInput>
|
||||||
connectOrCreate?: Prisma.UserCreateOrConnectWithoutLicenseKeysInput
|
connectOrCreate?: Prisma.UserCreateOrConnectWithoutLicenseKeysInput
|
||||||
upsert?: Prisma.UserUpsertWithoutLicenseKeysInput
|
upsert?: Prisma.UserUpsertWithoutLicenseKeysInput
|
||||||
|
disconnect?: Prisma.UserWhereInput | boolean
|
||||||
|
delete?: Prisma.UserWhereInput | boolean
|
||||||
connect?: Prisma.UserWhereUniqueInput
|
connect?: Prisma.UserWhereUniqueInput
|
||||||
update?: Prisma.XOR<Prisma.XOR<Prisma.UserUpdateToOneWithWhereWithoutLicenseKeysInput, Prisma.UserUpdateWithoutLicenseKeysInput>, Prisma.UserUncheckedUpdateWithoutLicenseKeysInput>
|
update?: Prisma.XOR<Prisma.XOR<Prisma.UserUpdateToOneWithWhereWithoutLicenseKeysInput, Prisma.UserUpdateWithoutLicenseKeysInput>, Prisma.UserUncheckedUpdateWithoutLicenseKeysInput>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserCreateWithoutProfileInput = {
|
export type UserCreateWithoutProfilesInput = {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
email: string
|
email: string
|
||||||
@@ -618,7 +627,7 @@ export type UserCreateWithoutProfileInput = {
|
|||||||
userProclamations?: Prisma.UserProclamationsCreateNestedManyWithoutUserInput
|
userProclamations?: Prisma.UserProclamationsCreateNestedManyWithoutUserInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUncheckedCreateWithoutProfileInput = {
|
export type UserUncheckedCreateWithoutProfilesInput = {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
email: string
|
email: string
|
||||||
@@ -633,23 +642,23 @@ export type UserUncheckedCreateWithoutProfileInput = {
|
|||||||
userProclamations?: Prisma.UserProclamationsUncheckedCreateNestedManyWithoutUserInput
|
userProclamations?: Prisma.UserProclamationsUncheckedCreateNestedManyWithoutUserInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserCreateOrConnectWithoutProfileInput = {
|
export type UserCreateOrConnectWithoutProfilesInput = {
|
||||||
where: Prisma.UserWhereUniqueInput
|
where: Prisma.UserWhereUniqueInput
|
||||||
create: Prisma.XOR<Prisma.UserCreateWithoutProfileInput, Prisma.UserUncheckedCreateWithoutProfileInput>
|
create: Prisma.XOR<Prisma.UserCreateWithoutProfilesInput, Prisma.UserUncheckedCreateWithoutProfilesInput>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUpsertWithoutProfileInput = {
|
export type UserUpsertWithoutProfilesInput = {
|
||||||
update: Prisma.XOR<Prisma.UserUpdateWithoutProfileInput, Prisma.UserUncheckedUpdateWithoutProfileInput>
|
update: Prisma.XOR<Prisma.UserUpdateWithoutProfilesInput, Prisma.UserUncheckedUpdateWithoutProfilesInput>
|
||||||
create: Prisma.XOR<Prisma.UserCreateWithoutProfileInput, Prisma.UserUncheckedCreateWithoutProfileInput>
|
create: Prisma.XOR<Prisma.UserCreateWithoutProfilesInput, Prisma.UserUncheckedCreateWithoutProfilesInput>
|
||||||
where?: Prisma.UserWhereInput
|
where?: Prisma.UserWhereInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUpdateToOneWithWhereWithoutProfileInput = {
|
export type UserUpdateToOneWithWhereWithoutProfilesInput = {
|
||||||
where?: Prisma.UserWhereInput
|
where?: Prisma.UserWhereInput
|
||||||
data: Prisma.XOR<Prisma.UserUpdateWithoutProfileInput, Prisma.UserUncheckedUpdateWithoutProfileInput>
|
data: Prisma.XOR<Prisma.UserUpdateWithoutProfilesInput, Prisma.UserUncheckedUpdateWithoutProfilesInput>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUpdateWithoutProfileInput = {
|
export type UserUpdateWithoutProfilesInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
@@ -664,7 +673,7 @@ export type UserUpdateWithoutProfileInput = {
|
|||||||
userProclamations?: Prisma.UserProclamationsUpdateManyWithoutUserNestedInput
|
userProclamations?: Prisma.UserProclamationsUpdateManyWithoutUserNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUncheckedUpdateWithoutProfileInput = {
|
export type UserUncheckedUpdateWithoutProfilesInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
name?: Prisma.StringFieldUpdateOperationsInput | string
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
@@ -689,7 +698,7 @@ export type UserCreateWithoutSessionsInput = {
|
|||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
role?: Prisma.UserRoleCreateNestedOneWithoutUsersInput
|
role?: Prisma.UserRoleCreateNestedOneWithoutUsersInput
|
||||||
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
||||||
profile?: Prisma.ProfileCreateNestedOneWithoutUserInput
|
profiles?: Prisma.ProfileCreateNestedManyWithoutUserInput
|
||||||
licenseKeys?: Prisma.LicenseKeyCreateNestedManyWithoutUserInput
|
licenseKeys?: Prisma.LicenseKeyCreateNestedManyWithoutUserInput
|
||||||
userProclamations?: Prisma.UserProclamationsCreateNestedManyWithoutUserInput
|
userProclamations?: Prisma.UserProclamationsCreateNestedManyWithoutUserInput
|
||||||
}
|
}
|
||||||
@@ -704,7 +713,7 @@ export type UserUncheckedCreateWithoutSessionsInput = {
|
|||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
roleId?: number | null
|
roleId?: number | null
|
||||||
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
||||||
profile?: Prisma.ProfileUncheckedCreateNestedOneWithoutUserInput
|
profiles?: Prisma.ProfileUncheckedCreateNestedManyWithoutUserInput
|
||||||
licenseKeys?: Prisma.LicenseKeyUncheckedCreateNestedManyWithoutUserInput
|
licenseKeys?: Prisma.LicenseKeyUncheckedCreateNestedManyWithoutUserInput
|
||||||
userProclamations?: Prisma.UserProclamationsUncheckedCreateNestedManyWithoutUserInput
|
userProclamations?: Prisma.UserProclamationsUncheckedCreateNestedManyWithoutUserInput
|
||||||
}
|
}
|
||||||
@@ -735,7 +744,7 @@ export type UserUpdateWithoutSessionsInput = {
|
|||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
role?: Prisma.UserRoleUpdateOneWithoutUsersNestedInput
|
role?: Prisma.UserRoleUpdateOneWithoutUsersNestedInput
|
||||||
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
||||||
profile?: Prisma.ProfileUpdateOneWithoutUserNestedInput
|
profiles?: Prisma.ProfileUpdateManyWithoutUserNestedInput
|
||||||
licenseKeys?: Prisma.LicenseKeyUpdateManyWithoutUserNestedInput
|
licenseKeys?: Prisma.LicenseKeyUpdateManyWithoutUserNestedInput
|
||||||
userProclamations?: Prisma.UserProclamationsUpdateManyWithoutUserNestedInput
|
userProclamations?: Prisma.UserProclamationsUpdateManyWithoutUserNestedInput
|
||||||
}
|
}
|
||||||
@@ -750,7 +759,7 @@ export type UserUncheckedUpdateWithoutSessionsInput = {
|
|||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
roleId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
roleId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||||
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
||||||
profile?: Prisma.ProfileUncheckedUpdateOneWithoutUserNestedInput
|
profiles?: Prisma.ProfileUncheckedUpdateManyWithoutUserNestedInput
|
||||||
licenseKeys?: Prisma.LicenseKeyUncheckedUpdateManyWithoutUserNestedInput
|
licenseKeys?: Prisma.LicenseKeyUncheckedUpdateManyWithoutUserNestedInput
|
||||||
userProclamations?: Prisma.UserProclamationsUncheckedUpdateManyWithoutUserNestedInput
|
userProclamations?: Prisma.UserProclamationsUncheckedUpdateManyWithoutUserNestedInput
|
||||||
}
|
}
|
||||||
@@ -765,7 +774,7 @@ export type UserCreateWithoutAccountsInput = {
|
|||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
role?: Prisma.UserRoleCreateNestedOneWithoutUsersInput
|
role?: Prisma.UserRoleCreateNestedOneWithoutUsersInput
|
||||||
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
|
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
|
||||||
profile?: Prisma.ProfileCreateNestedOneWithoutUserInput
|
profiles?: Prisma.ProfileCreateNestedManyWithoutUserInput
|
||||||
licenseKeys?: Prisma.LicenseKeyCreateNestedManyWithoutUserInput
|
licenseKeys?: Prisma.LicenseKeyCreateNestedManyWithoutUserInput
|
||||||
userProclamations?: Prisma.UserProclamationsCreateNestedManyWithoutUserInput
|
userProclamations?: Prisma.UserProclamationsCreateNestedManyWithoutUserInput
|
||||||
}
|
}
|
||||||
@@ -780,7 +789,7 @@ export type UserUncheckedCreateWithoutAccountsInput = {
|
|||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
roleId?: number | null
|
roleId?: number | null
|
||||||
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
|
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
|
||||||
profile?: Prisma.ProfileUncheckedCreateNestedOneWithoutUserInput
|
profiles?: Prisma.ProfileUncheckedCreateNestedManyWithoutUserInput
|
||||||
licenseKeys?: Prisma.LicenseKeyUncheckedCreateNestedManyWithoutUserInput
|
licenseKeys?: Prisma.LicenseKeyUncheckedCreateNestedManyWithoutUserInput
|
||||||
userProclamations?: Prisma.UserProclamationsUncheckedCreateNestedManyWithoutUserInput
|
userProclamations?: Prisma.UserProclamationsUncheckedCreateNestedManyWithoutUserInput
|
||||||
}
|
}
|
||||||
@@ -811,7 +820,7 @@ export type UserUpdateWithoutAccountsInput = {
|
|||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
role?: Prisma.UserRoleUpdateOneWithoutUsersNestedInput
|
role?: Prisma.UserRoleUpdateOneWithoutUsersNestedInput
|
||||||
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
|
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
|
||||||
profile?: Prisma.ProfileUpdateOneWithoutUserNestedInput
|
profiles?: Prisma.ProfileUpdateManyWithoutUserNestedInput
|
||||||
licenseKeys?: Prisma.LicenseKeyUpdateManyWithoutUserNestedInput
|
licenseKeys?: Prisma.LicenseKeyUpdateManyWithoutUserNestedInput
|
||||||
userProclamations?: Prisma.UserProclamationsUpdateManyWithoutUserNestedInput
|
userProclamations?: Prisma.UserProclamationsUpdateManyWithoutUserNestedInput
|
||||||
}
|
}
|
||||||
@@ -826,7 +835,7 @@ export type UserUncheckedUpdateWithoutAccountsInput = {
|
|||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
roleId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
roleId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||||
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
|
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
|
||||||
profile?: Prisma.ProfileUncheckedUpdateOneWithoutUserNestedInput
|
profiles?: Prisma.ProfileUncheckedUpdateManyWithoutUserNestedInput
|
||||||
licenseKeys?: Prisma.LicenseKeyUncheckedUpdateManyWithoutUserNestedInput
|
licenseKeys?: Prisma.LicenseKeyUncheckedUpdateManyWithoutUserNestedInput
|
||||||
userProclamations?: Prisma.UserProclamationsUncheckedUpdateManyWithoutUserNestedInput
|
userProclamations?: Prisma.UserProclamationsUncheckedUpdateManyWithoutUserNestedInput
|
||||||
}
|
}
|
||||||
@@ -841,7 +850,7 @@ export type UserCreateWithoutRoleInput = {
|
|||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
|
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
|
||||||
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
||||||
profile?: Prisma.ProfileCreateNestedOneWithoutUserInput
|
profiles?: Prisma.ProfileCreateNestedManyWithoutUserInput
|
||||||
licenseKeys?: Prisma.LicenseKeyCreateNestedManyWithoutUserInput
|
licenseKeys?: Prisma.LicenseKeyCreateNestedManyWithoutUserInput
|
||||||
userProclamations?: Prisma.UserProclamationsCreateNestedManyWithoutUserInput
|
userProclamations?: Prisma.UserProclamationsCreateNestedManyWithoutUserInput
|
||||||
}
|
}
|
||||||
@@ -856,7 +865,7 @@ export type UserUncheckedCreateWithoutRoleInput = {
|
|||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
|
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
|
||||||
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
||||||
profile?: Prisma.ProfileUncheckedCreateNestedOneWithoutUserInput
|
profiles?: Prisma.ProfileUncheckedCreateNestedManyWithoutUserInput
|
||||||
licenseKeys?: Prisma.LicenseKeyUncheckedCreateNestedManyWithoutUserInput
|
licenseKeys?: Prisma.LicenseKeyUncheckedCreateNestedManyWithoutUserInput
|
||||||
userProclamations?: Prisma.UserProclamationsUncheckedCreateNestedManyWithoutUserInput
|
userProclamations?: Prisma.UserProclamationsUncheckedCreateNestedManyWithoutUserInput
|
||||||
}
|
}
|
||||||
@@ -912,7 +921,7 @@ export type UserCreateWithoutUserProclamationsInput = {
|
|||||||
role?: Prisma.UserRoleCreateNestedOneWithoutUsersInput
|
role?: Prisma.UserRoleCreateNestedOneWithoutUsersInput
|
||||||
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
|
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
|
||||||
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
||||||
profile?: Prisma.ProfileCreateNestedOneWithoutUserInput
|
profiles?: Prisma.ProfileCreateNestedManyWithoutUserInput
|
||||||
licenseKeys?: Prisma.LicenseKeyCreateNestedManyWithoutUserInput
|
licenseKeys?: Prisma.LicenseKeyCreateNestedManyWithoutUserInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -927,7 +936,7 @@ export type UserUncheckedCreateWithoutUserProclamationsInput = {
|
|||||||
roleId?: number | null
|
roleId?: number | null
|
||||||
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
|
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
|
||||||
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
||||||
profile?: Prisma.ProfileUncheckedCreateNestedOneWithoutUserInput
|
profiles?: Prisma.ProfileUncheckedCreateNestedManyWithoutUserInput
|
||||||
licenseKeys?: Prisma.LicenseKeyUncheckedCreateNestedManyWithoutUserInput
|
licenseKeys?: Prisma.LicenseKeyUncheckedCreateNestedManyWithoutUserInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -958,7 +967,7 @@ export type UserUpdateWithoutUserProclamationsInput = {
|
|||||||
role?: Prisma.UserRoleUpdateOneWithoutUsersNestedInput
|
role?: Prisma.UserRoleUpdateOneWithoutUsersNestedInput
|
||||||
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
|
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
|
||||||
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
||||||
profile?: Prisma.ProfileUpdateOneWithoutUserNestedInput
|
profiles?: Prisma.ProfileUpdateManyWithoutUserNestedInput
|
||||||
licenseKeys?: Prisma.LicenseKeyUpdateManyWithoutUserNestedInput
|
licenseKeys?: Prisma.LicenseKeyUpdateManyWithoutUserNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -973,7 +982,7 @@ export type UserUncheckedUpdateWithoutUserProclamationsInput = {
|
|||||||
roleId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
roleId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||||
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
|
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
|
||||||
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
||||||
profile?: Prisma.ProfileUncheckedUpdateOneWithoutUserNestedInput
|
profiles?: Prisma.ProfileUncheckedUpdateManyWithoutUserNestedInput
|
||||||
licenseKeys?: Prisma.LicenseKeyUncheckedUpdateManyWithoutUserNestedInput
|
licenseKeys?: Prisma.LicenseKeyUncheckedUpdateManyWithoutUserNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -988,7 +997,7 @@ export type UserCreateWithoutLicenseKeysInput = {
|
|||||||
role?: Prisma.UserRoleCreateNestedOneWithoutUsersInput
|
role?: Prisma.UserRoleCreateNestedOneWithoutUsersInput
|
||||||
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
|
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
|
||||||
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
||||||
profile?: Prisma.ProfileCreateNestedOneWithoutUserInput
|
profiles?: Prisma.ProfileCreateNestedManyWithoutUserInput
|
||||||
userProclamations?: Prisma.UserProclamationsCreateNestedManyWithoutUserInput
|
userProclamations?: Prisma.UserProclamationsCreateNestedManyWithoutUserInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1003,7 +1012,7 @@ export type UserUncheckedCreateWithoutLicenseKeysInput = {
|
|||||||
roleId?: number | null
|
roleId?: number | null
|
||||||
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
|
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
|
||||||
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
||||||
profile?: Prisma.ProfileUncheckedCreateNestedOneWithoutUserInput
|
profiles?: Prisma.ProfileUncheckedCreateNestedManyWithoutUserInput
|
||||||
userProclamations?: Prisma.UserProclamationsUncheckedCreateNestedManyWithoutUserInput
|
userProclamations?: Prisma.UserProclamationsUncheckedCreateNestedManyWithoutUserInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1034,7 +1043,7 @@ export type UserUpdateWithoutLicenseKeysInput = {
|
|||||||
role?: Prisma.UserRoleUpdateOneWithoutUsersNestedInput
|
role?: Prisma.UserRoleUpdateOneWithoutUsersNestedInput
|
||||||
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
|
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
|
||||||
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
||||||
profile?: Prisma.ProfileUpdateOneWithoutUserNestedInput
|
profiles?: Prisma.ProfileUpdateManyWithoutUserNestedInput
|
||||||
userProclamations?: Prisma.UserProclamationsUpdateManyWithoutUserNestedInput
|
userProclamations?: Prisma.UserProclamationsUpdateManyWithoutUserNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1049,7 +1058,7 @@ export type UserUncheckedUpdateWithoutLicenseKeysInput = {
|
|||||||
roleId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
roleId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
|
||||||
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
|
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
|
||||||
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
||||||
profile?: Prisma.ProfileUncheckedUpdateOneWithoutUserNestedInput
|
profiles?: Prisma.ProfileUncheckedUpdateManyWithoutUserNestedInput
|
||||||
userProclamations?: Prisma.UserProclamationsUncheckedUpdateManyWithoutUserNestedInput
|
userProclamations?: Prisma.UserProclamationsUncheckedUpdateManyWithoutUserNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1073,7 +1082,7 @@ export type UserUpdateWithoutRoleInput = {
|
|||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
|
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
|
||||||
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
||||||
profile?: Prisma.ProfileUpdateOneWithoutUserNestedInput
|
profiles?: Prisma.ProfileUpdateManyWithoutUserNestedInput
|
||||||
licenseKeys?: Prisma.LicenseKeyUpdateManyWithoutUserNestedInput
|
licenseKeys?: Prisma.LicenseKeyUpdateManyWithoutUserNestedInput
|
||||||
userProclamations?: Prisma.UserProclamationsUpdateManyWithoutUserNestedInput
|
userProclamations?: Prisma.UserProclamationsUpdateManyWithoutUserNestedInput
|
||||||
}
|
}
|
||||||
@@ -1088,7 +1097,7 @@ export type UserUncheckedUpdateWithoutRoleInput = {
|
|||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
|
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
|
||||||
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
||||||
profile?: Prisma.ProfileUncheckedUpdateOneWithoutUserNestedInput
|
profiles?: Prisma.ProfileUncheckedUpdateManyWithoutUserNestedInput
|
||||||
licenseKeys?: Prisma.LicenseKeyUncheckedUpdateManyWithoutUserNestedInput
|
licenseKeys?: Prisma.LicenseKeyUncheckedUpdateManyWithoutUserNestedInput
|
||||||
userProclamations?: Prisma.UserProclamationsUncheckedUpdateManyWithoutUserNestedInput
|
userProclamations?: Prisma.UserProclamationsUncheckedUpdateManyWithoutUserNestedInput
|
||||||
}
|
}
|
||||||
@@ -1111,6 +1120,7 @@ export type UserUncheckedUpdateManyWithoutRoleInput = {
|
|||||||
export type UserCountOutputType = {
|
export type UserCountOutputType = {
|
||||||
sessions: number
|
sessions: number
|
||||||
accounts: number
|
accounts: number
|
||||||
|
profiles: number
|
||||||
licenseKeys: number
|
licenseKeys: number
|
||||||
userProclamations: number
|
userProclamations: number
|
||||||
}
|
}
|
||||||
@@ -1118,6 +1128,7 @@ export type UserCountOutputType = {
|
|||||||
export type UserCountOutputTypeSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type UserCountOutputTypeSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
sessions?: boolean | UserCountOutputTypeCountSessionsArgs
|
sessions?: boolean | UserCountOutputTypeCountSessionsArgs
|
||||||
accounts?: boolean | UserCountOutputTypeCountAccountsArgs
|
accounts?: boolean | UserCountOutputTypeCountAccountsArgs
|
||||||
|
profiles?: boolean | UserCountOutputTypeCountProfilesArgs
|
||||||
licenseKeys?: boolean | UserCountOutputTypeCountLicenseKeysArgs
|
licenseKeys?: boolean | UserCountOutputTypeCountLicenseKeysArgs
|
||||||
userProclamations?: boolean | UserCountOutputTypeCountUserProclamationsArgs
|
userProclamations?: boolean | UserCountOutputTypeCountUserProclamationsArgs
|
||||||
}
|
}
|
||||||
@@ -1146,6 +1157,13 @@ export type UserCountOutputTypeCountAccountsArgs<ExtArgs extends runtime.Types.E
|
|||||||
where?: Prisma.AccountWhereInput
|
where?: Prisma.AccountWhereInput
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UserCountOutputType without action
|
||||||
|
*/
|
||||||
|
export type UserCountOutputTypeCountProfilesArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
|
where?: Prisma.ProfileWhereInput
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UserCountOutputType without action
|
* UserCountOutputType without action
|
||||||
*/
|
*/
|
||||||
@@ -1173,7 +1191,7 @@ export type UserSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = r
|
|||||||
role?: boolean | Prisma.User$roleArgs<ExtArgs>
|
role?: boolean | Prisma.User$roleArgs<ExtArgs>
|
||||||
sessions?: boolean | Prisma.User$sessionsArgs<ExtArgs>
|
sessions?: boolean | Prisma.User$sessionsArgs<ExtArgs>
|
||||||
accounts?: boolean | Prisma.User$accountsArgs<ExtArgs>
|
accounts?: boolean | Prisma.User$accountsArgs<ExtArgs>
|
||||||
profile?: boolean | Prisma.User$profileArgs<ExtArgs>
|
profiles?: boolean | Prisma.User$profilesArgs<ExtArgs>
|
||||||
licenseKeys?: boolean | Prisma.User$licenseKeysArgs<ExtArgs>
|
licenseKeys?: boolean | Prisma.User$licenseKeysArgs<ExtArgs>
|
||||||
userProclamations?: boolean | Prisma.User$userProclamationsArgs<ExtArgs>
|
userProclamations?: boolean | Prisma.User$userProclamationsArgs<ExtArgs>
|
||||||
_count?: boolean | Prisma.UserCountOutputTypeDefaultArgs<ExtArgs>
|
_count?: boolean | Prisma.UserCountOutputTypeDefaultArgs<ExtArgs>
|
||||||
@@ -1219,7 +1237,7 @@ export type UserInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs =
|
|||||||
role?: boolean | Prisma.User$roleArgs<ExtArgs>
|
role?: boolean | Prisma.User$roleArgs<ExtArgs>
|
||||||
sessions?: boolean | Prisma.User$sessionsArgs<ExtArgs>
|
sessions?: boolean | Prisma.User$sessionsArgs<ExtArgs>
|
||||||
accounts?: boolean | Prisma.User$accountsArgs<ExtArgs>
|
accounts?: boolean | Prisma.User$accountsArgs<ExtArgs>
|
||||||
profile?: boolean | Prisma.User$profileArgs<ExtArgs>
|
profiles?: boolean | Prisma.User$profilesArgs<ExtArgs>
|
||||||
licenseKeys?: boolean | Prisma.User$licenseKeysArgs<ExtArgs>
|
licenseKeys?: boolean | Prisma.User$licenseKeysArgs<ExtArgs>
|
||||||
userProclamations?: boolean | Prisma.User$userProclamationsArgs<ExtArgs>
|
userProclamations?: boolean | Prisma.User$userProclamationsArgs<ExtArgs>
|
||||||
_count?: boolean | Prisma.UserCountOutputTypeDefaultArgs<ExtArgs>
|
_count?: boolean | Prisma.UserCountOutputTypeDefaultArgs<ExtArgs>
|
||||||
@@ -1237,7 +1255,7 @@ export type $UserPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs =
|
|||||||
role: Prisma.$UserRolePayload<ExtArgs> | null
|
role: Prisma.$UserRolePayload<ExtArgs> | null
|
||||||
sessions: Prisma.$SessionPayload<ExtArgs>[]
|
sessions: Prisma.$SessionPayload<ExtArgs>[]
|
||||||
accounts: Prisma.$AccountPayload<ExtArgs>[]
|
accounts: Prisma.$AccountPayload<ExtArgs>[]
|
||||||
profile: Prisma.$ProfilePayload<ExtArgs> | null
|
profiles: Prisma.$ProfilePayload<ExtArgs>[]
|
||||||
licenseKeys: Prisma.$LicenseKeyPayload<ExtArgs>[]
|
licenseKeys: Prisma.$LicenseKeyPayload<ExtArgs>[]
|
||||||
userProclamations: Prisma.$UserProclamationsPayload<ExtArgs>[]
|
userProclamations: Prisma.$UserProclamationsPayload<ExtArgs>[]
|
||||||
}
|
}
|
||||||
@@ -1647,7 +1665,7 @@ export interface Prisma__UserClient<T, Null = never, ExtArgs extends runtime.Typ
|
|||||||
role<T extends Prisma.User$roleArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$roleArgs<ExtArgs>>): Prisma.Prisma__UserRoleClient<runtime.Types.Result.GetResult<Prisma.$UserRolePayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions>
|
role<T extends Prisma.User$roleArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$roleArgs<ExtArgs>>): Prisma.Prisma__UserRoleClient<runtime.Types.Result.GetResult<Prisma.$UserRolePayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions>
|
||||||
sessions<T extends Prisma.User$sessionsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$sessionsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$SessionPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
sessions<T extends Prisma.User$sessionsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$sessionsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$SessionPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||||
accounts<T extends Prisma.User$accountsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$accountsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$AccountPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
accounts<T extends Prisma.User$accountsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$accountsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$AccountPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||||
profile<T extends Prisma.User$profileArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$profileArgs<ExtArgs>>): Prisma.Prisma__ProfileClient<runtime.Types.Result.GetResult<Prisma.$ProfilePayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions>
|
profiles<T extends Prisma.User$profilesArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$profilesArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ProfilePayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||||
licenseKeys<T extends Prisma.User$licenseKeysArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$licenseKeysArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$LicenseKeyPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
licenseKeys<T extends Prisma.User$licenseKeysArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$licenseKeysArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$LicenseKeyPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||||
userProclamations<T extends Prisma.User$userProclamationsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$userProclamationsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$UserProclamationsPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
userProclamations<T extends Prisma.User$userProclamationsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$userProclamationsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$UserProclamationsPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||||
/**
|
/**
|
||||||
@@ -2155,9 +2173,9 @@ export type User$accountsArgs<ExtArgs extends runtime.Types.Extensions.InternalA
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User.profile
|
* User.profiles
|
||||||
*/
|
*/
|
||||||
export type User$profileArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type User$profilesArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
/**
|
/**
|
||||||
* Select specific fields to fetch from the Profile
|
* Select specific fields to fetch from the Profile
|
||||||
*/
|
*/
|
||||||
@@ -2171,6 +2189,11 @@ export type User$profileArgs<ExtArgs extends runtime.Types.Extensions.InternalAr
|
|||||||
*/
|
*/
|
||||||
include?: Prisma.ProfileInclude<ExtArgs> | null
|
include?: Prisma.ProfileInclude<ExtArgs> | null
|
||||||
where?: Prisma.ProfileWhereInput
|
where?: Prisma.ProfileWhereInput
|
||||||
|
orderBy?: Prisma.ProfileOrderByWithRelationInput | Prisma.ProfileOrderByWithRelationInput[]
|
||||||
|
cursor?: Prisma.ProfileWhereUniqueInput
|
||||||
|
take?: number
|
||||||
|
skip?: number
|
||||||
|
distinct?: Prisma.ProfileScalarFieldEnum | Prisma.ProfileScalarFieldEnum[]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The actor for the current request, used by the Prisma audit extension to
|
||||||
|
* attribute database mutations. Populated in `hooks.server.ts`.
|
||||||
|
*/
|
||||||
|
export type AuditContext = {
|
||||||
|
userId: string | null;
|
||||||
|
email: string | null;
|
||||||
|
roleId: number | null;
|
||||||
|
page: string;
|
||||||
|
ip: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const storage = new AsyncLocalStorage<AuditContext>();
|
||||||
|
|
||||||
|
export function runWithAuditContext<T>(context: AuditContext, fn: () => T): T {
|
||||||
|
return storage.run(context, fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAuditContext(): AuditContext | undefined {
|
||||||
|
return storage.getStore();
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { describeAction, entityIdFor, sanitizeAuditValue, shouldAudit } from './audit';
|
||||||
|
|
||||||
|
describe('shouldAudit', () => {
|
||||||
|
it('audits mutations', () => {
|
||||||
|
expect(shouldAudit('User', 'create')).toBe(true);
|
||||||
|
expect(shouldAudit('Profile', 'update')).toBe(true);
|
||||||
|
expect(shouldAudit('Proclamation', 'delete')).toBe(true);
|
||||||
|
expect(shouldAudit('LicenseKey', 'updateMany')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ignores reads', () => {
|
||||||
|
expect(shouldAudit('User', 'findMany')).toBe(false);
|
||||||
|
expect(shouldAudit('User', 'findUnique')).toBe(false);
|
||||||
|
expect(shouldAudit('User', 'count')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ignores the audit log and auth internals', () => {
|
||||||
|
for (const model of ['AuditLog', 'Session', 'Account', 'Verification']) {
|
||||||
|
expect(shouldAudit(model, 'create')).toBe(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('sanitizeAuditValue', () => {
|
||||||
|
it('redacts sensitive keys', () => {
|
||||||
|
const out = sanitizeAuditValue({
|
||||||
|
password: 'hunter2',
|
||||||
|
token: 'abc',
|
||||||
|
keyHash: 'deadbeef',
|
||||||
|
email: 'jane@example.com'
|
||||||
|
}) as Record<string, unknown>;
|
||||||
|
|
||||||
|
expect(out.password).toBe('[redacted]');
|
||||||
|
expect(out.token).toBe('[redacted]');
|
||||||
|
expect(out.keyHash).toBe('[redacted]');
|
||||||
|
expect(out.email).toBe('jane@example.com');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('converts dates to ISO strings', () => {
|
||||||
|
const date = new Date('2026-01-01T00:00:00.000Z');
|
||||||
|
expect(sanitizeAuditValue({ at: date })).toEqual({ at: '2026-01-01T00:00:00.000Z' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles arrays and nullish values', () => {
|
||||||
|
expect(sanitizeAuditValue([1, null, 'a'])).toEqual([1, null, 'a']);
|
||||||
|
expect(sanitizeAuditValue(null)).toBeNull();
|
||||||
|
expect(sanitizeAuditValue(undefined)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('truncates very long strings', () => {
|
||||||
|
const long = 'a'.repeat(21000);
|
||||||
|
const out = sanitizeAuditValue(long) as string;
|
||||||
|
expect(out.length).toBe(20001);
|
||||||
|
expect(out.endsWith('…')).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('entityIdFor', () => {
|
||||||
|
it('prefers the result id', () => {
|
||||||
|
expect(entityIdFor({ where: { id: 'w' } }, { id: 'r' }, { id: 'b' })).toBe('r');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('falls back to where, data, then before', () => {
|
||||||
|
expect(entityIdFor({ where: { id: 5 } }, null, null)).toBe('5');
|
||||||
|
expect(entityIdFor({ data: { id: 'd' } }, null, null)).toBe('d');
|
||||||
|
expect(entityIdFor({}, null, { id: 'b' })).toBe('b');
|
||||||
|
expect(entityIdFor({}, null, null)).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('describeAction', () => {
|
||||||
|
it('labels bulk operations', () => {
|
||||||
|
expect(describeAction('updateMany')).toBe('bulk update');
|
||||||
|
expect(describeAction('deleteMany')).toBe('bulk delete');
|
||||||
|
expect(describeAction('create')).toBe('create');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
/**
|
||||||
|
* Pure helpers for the audit extension. Kept free of database imports so the
|
||||||
|
* extension in `$lib/db.ts` can use them without a cycle.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Prisma operations that change data. Reads are never audited. */
|
||||||
|
const MUTATION_OPERATIONS = new Set([
|
||||||
|
'create',
|
||||||
|
'createMany',
|
||||||
|
'createManyAndReturn',
|
||||||
|
'update',
|
||||||
|
'updateMany',
|
||||||
|
'updateManyAndReturn',
|
||||||
|
'upsert',
|
||||||
|
'delete',
|
||||||
|
'deleteMany'
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Models we never audit:
|
||||||
|
* - `AuditLog` itself (would recurse and is not user data)
|
||||||
|
* - `Session`/`Account`/`Verification` are high-churn auth internals that hold
|
||||||
|
* tokens, password hashes and other secrets. Business mutations are audited.
|
||||||
|
*/
|
||||||
|
const EXCLUDED_MODELS = new Set(['AuditLog', 'Session', 'Account', 'Verification']);
|
||||||
|
|
||||||
|
export function shouldAudit(model: string, operation: string): boolean {
|
||||||
|
return !EXCLUDED_MODELS.has(model) && MUTATION_OPERATIONS.has(operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
const SENSITIVE_KEY = /pass(word)?|secret|token|hash/i;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a value safe to persist as JSON: dates become ISO strings, sensitive
|
||||||
|
* keys are redacted, very long strings are truncated, and cycles/depth are
|
||||||
|
* bounded.
|
||||||
|
*/
|
||||||
|
export function sanitizeAuditValue(value: unknown, depth = 0): unknown {
|
||||||
|
if (value === undefined || value === null) return null;
|
||||||
|
if (depth > 8) return '[truncated]';
|
||||||
|
if (value instanceof Date) return value.toISOString();
|
||||||
|
if (Array.isArray(value)) return value.map((item) => sanitizeAuditValue(item, depth + 1));
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
const out: Record<string, unknown> = {};
|
||||||
|
for (const [key, nested] of Object.entries(value as Record<string, unknown>)) {
|
||||||
|
out[key] = SENSITIVE_KEY.test(key) ? '[redacted]' : sanitizeAuditValue(nested, depth + 1);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
if (typeof value === 'string' && value.length > 20000) return `${value.slice(0, 20000)}…`;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Best-effort row id for the changed record. */
|
||||||
|
export function entityIdFor(args: unknown, result: unknown, before: unknown): string | null {
|
||||||
|
const where = (args as { where?: { id?: unknown } } | null)?.where;
|
||||||
|
const data = (args as { data?: { id?: unknown } } | null)?.data;
|
||||||
|
const resultId = (result as { id?: unknown } | null)?.id;
|
||||||
|
const first = Array.isArray(before) ? before[0] : before;
|
||||||
|
const beforeId = (first as { id?: unknown } | undefined)?.id;
|
||||||
|
const id = resultId ?? where?.id ?? data?.id ?? beforeId;
|
||||||
|
return id === undefined || id === null ? null : String(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Human label for an operation, e.g. `updateMany` → `bulk update`. */
|
||||||
|
export function describeAction(operation: string): string {
|
||||||
|
switch (operation) {
|
||||||
|
case 'create':
|
||||||
|
return 'create';
|
||||||
|
case 'update':
|
||||||
|
return 'update';
|
||||||
|
case 'delete':
|
||||||
|
return 'delete';
|
||||||
|
case 'upsert':
|
||||||
|
return 'upsert';
|
||||||
|
case 'createMany':
|
||||||
|
case 'createManyAndReturn':
|
||||||
|
return 'bulk create';
|
||||||
|
case 'updateMany':
|
||||||
|
case 'updateManyAndReturn':
|
||||||
|
return 'bulk update';
|
||||||
|
case 'deleteMany':
|
||||||
|
return 'bulk delete';
|
||||||
|
default:
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { env } from '$env/dynamic/private';
|
||||||
|
import { sendMail } from './resend';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the "finish your registration" email to a user created by an admin.
|
||||||
|
*
|
||||||
|
* The link points at Better Auth's reset-password callback, which redirects to
|
||||||
|
* `/reset-password?invite=1&token=...`. There the user sets a password, then
|
||||||
|
* logs in and is sent to `/profile/setup` to complete their profile.
|
||||||
|
*
|
||||||
|
* Uses a Resend-hosted template (`registration-email` by default, or
|
||||||
|
* `RESEND_REGISTRATION_TEMPLATE_ID`) with variables:
|
||||||
|
* - `target_email_address`
|
||||||
|
* - `registration_link`
|
||||||
|
*/
|
||||||
|
export async function sendRegistrationEmail(options: { email: string; url: string }) {
|
||||||
|
await sendMail({
|
||||||
|
to: options.email,
|
||||||
|
subject: 'Welcome to Radiant Wave — set your password',
|
||||||
|
templateId: env.RESEND_REGISTRATION_TEMPLATE_ID || 'registration-email',
|
||||||
|
data: {
|
||||||
|
target_email_address: options.email,
|
||||||
|
registration_link: options.url
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { auth } from '$lib/auth';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Email an invited user a link to set their password and finish their profile.
|
||||||
|
*
|
||||||
|
* Reuses Better Auth's password-reset token flow; the `invite=1` marker makes
|
||||||
|
* the reset-password hook send the `registration-email` template instead of the
|
||||||
|
* usual "you requested a reset" email. The link lands on /reset-password, and
|
||||||
|
* after setting a password the user logs in and is sent to /profile/setup.
|
||||||
|
*/
|
||||||
|
export async function sendRegistrationInvite(email: string): Promise<void> {
|
||||||
|
await auth.api.requestPasswordReset({
|
||||||
|
body: {
|
||||||
|
email,
|
||||||
|
redirectTo: '/reset-password?invite=1'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -26,7 +26,7 @@ function getResend(): Resend {
|
|||||||
* @example
|
* @example
|
||||||
* await sendMail({
|
* await sendMail({
|
||||||
* to: 'jane@example.com',
|
* to: 'jane@example.com',
|
||||||
* templateId: 'tmpl_abc123', // optional — defaults to RESEND_TEMPLATE_ID
|
* templateId: 'tmpl_abc123', // required — Resend template id or alias
|
||||||
* subject: 'Your license key is ready', // optional — defaults to 'Template Email'
|
* subject: 'Your license key is ready', // optional — defaults to 'Template Email'
|
||||||
* data: { // optional — template variables
|
* data: { // optional — template variables
|
||||||
* firstName: 'Jane',
|
* firstName: 'Jane',
|
||||||
@@ -40,7 +40,7 @@ function getResend(): Resend {
|
|||||||
export async function sendMail(options: {
|
export async function sendMail(options: {
|
||||||
to: string;
|
to: string;
|
||||||
subject?: string;
|
subject?: string;
|
||||||
templateId?: string;
|
templateId: string;
|
||||||
data?: Record<string, string | number>;
|
data?: Record<string, string | number>;
|
||||||
}) {
|
}) {
|
||||||
const { to, subject, templateId, data } = options;
|
const { to, subject, templateId, data } = options;
|
||||||
@@ -50,7 +50,7 @@ export async function sendMail(options: {
|
|||||||
to: [to],
|
to: [to],
|
||||||
subject: subject ?? 'Template Email',
|
subject: subject ?? 'Template Email',
|
||||||
template: {
|
template: {
|
||||||
id: templateId ?? env.RESEND_TEMPLATE_ID,
|
id: templateId,
|
||||||
variables: data ?? {}
|
variables: data ?? {}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* Role hierarchy. Lower ids are more privileged:
|
||||||
|
* SUPER = 1, ADMIN = 2, STAFF = 3, USER = 4
|
||||||
|
*
|
||||||
|
* `roleId` is exposed on the Better Auth session user (see `auth.ts`
|
||||||
|
* `user.additionalFields`), so these helpers never need a database call.
|
||||||
|
*/
|
||||||
|
export const SUPER = 1;
|
||||||
|
export const ADMIN = 2;
|
||||||
|
export const STAFF = 3;
|
||||||
|
export const USER = 4;
|
||||||
|
|
||||||
|
/** The current user's role id, defaulting to the least privileged role. */
|
||||||
|
export function roleId(locals: App.Locals): number {
|
||||||
|
return locals.user?.roleId ?? USER;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isSuper(locals: App.Locals): boolean {
|
||||||
|
return roleId(locals) <= SUPER;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isAdmin(locals: App.Locals): boolean {
|
||||||
|
return roleId(locals) <= ADMIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isStaff(locals: App.Locals): boolean {
|
||||||
|
return roleId(locals) <= STAFF;
|
||||||
|
}
|
||||||
@@ -5,9 +5,9 @@ import {
|
|||||||
canonicalIdForFrequency,
|
canonicalIdForFrequency,
|
||||||
serializeCategory,
|
serializeCategory,
|
||||||
serializeColorPalette,
|
serializeColorPalette,
|
||||||
|
serializeGroup,
|
||||||
serializeLicense,
|
serializeLicense,
|
||||||
serializeProclamation,
|
serializeProclamation
|
||||||
serializeSubscriptionLevel
|
|
||||||
} from './v2-serializers';
|
} from './v2-serializers';
|
||||||
|
|
||||||
describe('canonicalIdForFrequency', () => {
|
describe('canonicalIdForFrequency', () => {
|
||||||
@@ -61,7 +61,7 @@ describe('serializeProclamation', () => {
|
|||||||
name: 'Peace & Relaxation',
|
name: 'Peace & Relaxation',
|
||||||
content: '<p>The text content, HTML formatted</p>',
|
content: '<p>The text content, HTML formatted</p>',
|
||||||
categoryId: 5,
|
categoryId: 5,
|
||||||
subscriptionLevelId: 1
|
groupId: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
it('derives solfeggioFrequencyId from the Hz value, not the DB id', () => {
|
it('derives solfeggioFrequencyId from the Hz value, not the DB id', () => {
|
||||||
@@ -84,13 +84,13 @@ describe('serializeProclamation', () => {
|
|||||||
expect(record).toBeNull();
|
expect(record).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('passes through a null subscriptionLevelId (kiosk treats as level 1)', () => {
|
it('passes through categoryId and groupId unchanged', () => {
|
||||||
const record = serializeProclamation({
|
const record = serializeProclamation({
|
||||||
...base,
|
...base,
|
||||||
subscriptionLevelId: null,
|
|
||||||
solfeggioFrequency: solarPlexus528
|
solfeggioFrequency: solarPlexus528
|
||||||
});
|
});
|
||||||
expect(record?.subscriptionLevelId).toBeNull();
|
expect(record?.categoryId).toBe(5);
|
||||||
|
expect(record?.groupId).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('embeds a 7-color palette when provided', () => {
|
it('embeds a 7-color palette when provided', () => {
|
||||||
@@ -116,64 +116,39 @@ describe('serializeProclamation', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('serializeCategory', () => {
|
describe('serializeCategory', () => {
|
||||||
it('keeps parentId null for root categories', () => {
|
it('maps rows to a flat {id, name, sortOrder} with no parentId', () => {
|
||||||
expect(serializeCategory({ id: 4, name: 'Spiritual', sortOrder: 0, parentId: null })).toEqual({
|
expect(serializeCategory({ id: 4, name: 'Spiritual', sortOrder: 0 })).toEqual({
|
||||||
id: 4,
|
id: 4,
|
||||||
name: 'Spiritual',
|
name: 'Spiritual',
|
||||||
sortOrder: 0,
|
sortOrder: 0
|
||||||
parentId: null
|
|
||||||
});
|
});
|
||||||
|
expect(
|
||||||
|
Object.keys(serializeCategory({ id: 4, name: 'Spiritual', sortOrder: 0 }))
|
||||||
|
).not.toContain('parentId');
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('keeps parentId int for children', () => {
|
describe('serializeGroup', () => {
|
||||||
expect(serializeCategory({ id: 5, name: 'General', sortOrder: 0, parentId: 4 })).toEqual({
|
it('maps rows to {id, name, sortOrder}', () => {
|
||||||
id: 5,
|
expect(serializeGroup({ id: 1, name: 'General', sortOrder: 0 })).toEqual({
|
||||||
|
id: 1,
|
||||||
name: 'General',
|
name: 'General',
|
||||||
sortOrder: 0,
|
sortOrder: 0
|
||||||
parentId: 4
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('serializeLicense', () => {
|
describe('serializeLicense', () => {
|
||||||
it('returns subscription_level as an object when the level row exists', () => {
|
it('returns only key and email_address (levels removed)', () => {
|
||||||
const result = serializeLicense({
|
const result = serializeLicense({
|
||||||
key: 'XXXX-XXXX-XXXX-XXXX',
|
key: 'XXXX-XXXX-XXXX-XXXX',
|
||||||
emailAddress: 'user@example.com',
|
emailAddress: 'user@example.com'
|
||||||
levelName: 'Advanced',
|
|
||||||
level: { id: 2, name: 'Advanced', description: 'General + Specific' }
|
|
||||||
});
|
});
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
key: 'XXXX-XXXX-XXXX-XXXX',
|
key: 'XXXX-XXXX-XXXX-XXXX',
|
||||||
email_address: 'user@example.com',
|
email_address: 'user@example.com'
|
||||||
subscription_level: { id: 2, name: 'Advanced', description: 'General + Specific' }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('falls back to a structurally consistent object when no level row matches', () => {
|
|
||||||
const result = serializeLicense({
|
|
||||||
key: 'XXXX-XXXX-XXXX-XXXX',
|
|
||||||
emailAddress: 'user@example.com',
|
|
||||||
levelName: 'SomeCustomLevel',
|
|
||||||
level: null
|
|
||||||
});
|
|
||||||
expect(result).toEqual({
|
|
||||||
key: 'XXXX-XXXX-XXXX-XXXX',
|
|
||||||
email_address: 'user@example.com',
|
|
||||||
subscription_level: { id: null, name: 'SomeCustomLevel', description: null }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('serializeSubscriptionLevel', () => {
|
|
||||||
it('maps rows to {id, name, description}', () => {
|
|
||||||
expect(
|
|
||||||
serializeSubscriptionLevel({ id: 1, name: 'Basic', description: 'General only' })
|
|
||||||
).toEqual({
|
|
||||||
id: 1,
|
|
||||||
name: 'Basic',
|
|
||||||
description: 'General only'
|
|
||||||
});
|
});
|
||||||
|
expect(Object.keys(result)).not.toContain('subscription_level');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ export type V2Proclamation = {
|
|||||||
name: string;
|
name: string;
|
||||||
content: string;
|
content: string;
|
||||||
categoryId: number;
|
categoryId: number;
|
||||||
|
groupId: number;
|
||||||
solfeggioFrequencyId: number;
|
solfeggioFrequencyId: number;
|
||||||
subscriptionLevelId: number | null;
|
|
||||||
colorPalette?: string[];
|
colorPalette?: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ export type ProclamationWithFrequency = {
|
|||||||
name: string;
|
name: string;
|
||||||
content: string;
|
content: string;
|
||||||
categoryId: number;
|
categoryId: number;
|
||||||
subscriptionLevelId: number | null;
|
groupId: number;
|
||||||
solfeggioFrequency: { value: number };
|
solfeggioFrequency: { value: number };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -83,8 +83,8 @@ export function serializeProclamation(
|
|||||||
name: p.name,
|
name: p.name,
|
||||||
content: p.content,
|
content: p.content,
|
||||||
categoryId: p.categoryId,
|
categoryId: p.categoryId,
|
||||||
solfeggioFrequencyId,
|
groupId: p.groupId,
|
||||||
subscriptionLevelId: p.subscriptionLevelId
|
solfeggioFrequencyId
|
||||||
};
|
};
|
||||||
|
|
||||||
if (palette && palette.length > 0) {
|
if (palette && palette.length > 0) {
|
||||||
@@ -98,53 +98,32 @@ export type V2Category = {
|
|||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
sortOrder: number;
|
sortOrder: number;
|
||||||
parentId: number | null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function serializeCategory(c: {
|
export function serializeCategory(c: { id: number; name: string; sortOrder: number }): V2Category {
|
||||||
|
return { id: c.id, name: c.name, sortOrder: c.sortOrder };
|
||||||
|
}
|
||||||
|
|
||||||
|
export type V2Group = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
sortOrder: number;
|
sortOrder: number;
|
||||||
parentId: number | null;
|
|
||||||
}): V2Category {
|
|
||||||
return { id: c.id, name: c.name, sortOrder: c.sortOrder, parentId: c.parentId };
|
|
||||||
}
|
|
||||||
|
|
||||||
export type V2SubscriptionLevel = {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
description: string | null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function serializeSubscriptionLevel(level: {
|
export function serializeGroup(g: { id: number; name: string; sortOrder: number }): V2Group {
|
||||||
id: number;
|
return { id: g.id, name: g.name, sortOrder: g.sortOrder };
|
||||||
name: string;
|
|
||||||
description: string | null;
|
|
||||||
}): V2SubscriptionLevel {
|
|
||||||
return { id: level.id, name: level.name, description: level.description };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type V2License = {
|
export type V2License = {
|
||||||
key: string;
|
key: string;
|
||||||
email_address: string;
|
email_address: string;
|
||||||
subscription_level: { id: number | null; name: string; description: string | null };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Build the POST /license response. `subscription_level` is always an object;
|
/** Build the POST /license response. */
|
||||||
* when no SubscriptionLevel row matches the license's level name, fall back to
|
export function serializeLicense(opts: { key: string; emailAddress: string }): V2License {
|
||||||
* a structurally consistent object with a null id/description. */
|
|
||||||
export function serializeLicense(opts: {
|
|
||||||
key: string;
|
|
||||||
emailAddress: string;
|
|
||||||
levelName: string;
|
|
||||||
level: { id: number; name: string; description: string | null } | null;
|
|
||||||
}): V2License {
|
|
||||||
return {
|
return {
|
||||||
key: opts.key,
|
key: opts.key,
|
||||||
email_address: opts.emailAddress,
|
email_address: opts.emailAddress
|
||||||
subscription_level: opts.level
|
|
||||||
? { id: opts.level.id, name: opts.level.name, description: opts.level.description }
|
|
||||||
: { id: null, name: opts.levelName, description: null }
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ function signedRequest(opts: {
|
|||||||
const path = '/api/v2/automations/create/account';
|
const path = '/api/v2/automations/create/account';
|
||||||
const timestamp = String(Math.floor((opts.now ?? new Date()).getTime() / 1000));
|
const timestamp = String(Math.floor((opts.now ?? new Date()).getTime() / 1000));
|
||||||
const nonce = 'a48c0f2e-5f34-4b0b-9a12-3c7d6b8e1f00';
|
const nonce = 'a48c0f2e-5f34-4b0b-9a12-3c7d6b8e1f00';
|
||||||
const body = '{"email":"user@example.com","subscription_level":"Basic"}';
|
const body = '{"email":"user@example.com"}';
|
||||||
|
|
||||||
let ts = timestamp;
|
let ts = timestamp;
|
||||||
let nonceValue = nonce;
|
let nonceValue = nonce;
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
export function superFormErrorsToFieldErrors(superErrors: Record<string, unknown>): {
|
||||||
|
error: string;
|
||||||
|
fieldErrors: Record<string, string>;
|
||||||
|
} {
|
||||||
|
const fieldErrors: Record<string, string> = {};
|
||||||
|
let formError = '';
|
||||||
|
for (const [key, value] of Object.entries(superErrors)) {
|
||||||
|
if (key === '_errors' && Array.isArray(value)) {
|
||||||
|
formError = (value[0] as string) ?? '';
|
||||||
|
} else if (
|
||||||
|
value &&
|
||||||
|
typeof value === 'object' &&
|
||||||
|
'_errors' in value &&
|
||||||
|
Array.isArray((value as Record<string, unknown>)._errors)
|
||||||
|
) {
|
||||||
|
const errMsg = ((value as Record<string, unknown>)._errors as string[])[0];
|
||||||
|
fieldErrors[key] = errMsg;
|
||||||
|
} else if (Array.isArray(value) && typeof value[0] === 'string') {
|
||||||
|
fieldErrors[key] = value[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { error: formError, fieldErrors };
|
||||||
|
}
|
||||||
@@ -1,391 +0,0 @@
|
|||||||
# svelte-admin
|
|
||||||
|
|
||||||
A config-driven admin panel framework for SvelteKit + Prisma. Define your models once in a
|
|
||||||
registry and get full CRUD UI with per-model static pages, role-based access control, searchable
|
|
||||||
tables, FK dropdowns, TipTap editor for rich text, and related-record panels.
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
```
|
|
||||||
src/lib/svelte-admin/ # Framework core
|
|
||||||
├── types.ts # AdminModelConfig and related types
|
|
||||||
├── registry.ts # Model configurations (the single source of truth)
|
|
||||||
├── roles.ts # Role hierarchy and access helpers
|
|
||||||
├── prisma-delegate.ts # Dynamic Prisma delegate lookup
|
|
||||||
├── introspect.ts # Field label generation and dot-notation resolution
|
|
||||||
├── form-data.ts # FormData -> typed object parser
|
|
||||||
├── crud.ts # CRUD action factory (load, upsert, delete)
|
|
||||||
└── index.ts # Barrel exports
|
|
||||||
|
|
||||||
src/routes/(protected)/admin/ # Per-model admin pages (no dynamic [model] routing)
|
|
||||||
├── +layout.server.ts # Role-based access guard
|
|
||||||
├── +layout.svelte # Layout shell
|
|
||||||
├── +page.server.ts # Admin home (model grid)
|
|
||||||
├── +page.svelte # Admin home UI
|
|
||||||
├── users/ # Each model gets its own page directory
|
|
||||||
├── profiles/
|
|
||||||
├── addresses/
|
|
||||||
├── user-roles/
|
|
||||||
├── proclamations/
|
|
||||||
├── solfeggio-frequencies/
|
|
||||||
├── proclamation-categories/
|
|
||||||
├── subscription-levels/
|
|
||||||
└── license-keys/ # listOnly — no create/edit modal
|
|
||||||
|
|
||||||
src/lib/components/admin/ # Reusable admin UI components
|
|
||||||
├── AdminTable.svelte # Sortable, searchable data table
|
|
||||||
├── AdminForm.svelte # Dynamic form with type-aware inputs
|
|
||||||
├── AdminModal.svelte # Modal overlay for CRUD forms
|
|
||||||
├── TipTap.svelte # Rich-text editor (in components/tiptap/)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Adding a New Model
|
|
||||||
|
|
||||||
1. **Define the Prisma model** in `prisma/schema.prisma` and create a migration:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run migrate -- --name add_my_model
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Add a config object** to the `models` array in `registry.ts`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
{
|
|
||||||
slug: 'widgets', // URL segment: /admin/widgets
|
|
||||||
modelName: 'Widget', // Prisma model name (PascalCase, singular)
|
|
||||||
displayName: 'Widgets', // Plural display name
|
|
||||||
displayNameSingular: 'Widget', // Singular display name
|
|
||||||
idType: 'String', // 'String' or 'Int' — matches Prisma @id type
|
|
||||||
include: { category: true }, // Optional: Prisma include for relations
|
|
||||||
listFields: ['name', 'category.name', 'price'],
|
|
||||||
searchFields: ['name'],
|
|
||||||
formFields: ['name', 'categoryId', 'price'],
|
|
||||||
readonlyFields: ['id', 'createdAt'],
|
|
||||||
fieldTypes: {
|
|
||||||
name: 'String',
|
|
||||||
categoryId: 'Int',
|
|
||||||
price: 'Float',
|
|
||||||
createdAt: 'DateTime'
|
|
||||||
},
|
|
||||||
relationOptions: {
|
|
||||||
categoryId: {
|
|
||||||
model: 'Category',
|
|
||||||
labelField: 'name',
|
|
||||||
valueField: 'id'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Create the page directory** `src/routes/(protected)/admin/widgets/` with:
|
|
||||||
- `+page.server.ts` — load records + form actions using `crud.ts` helpers
|
|
||||||
- `+page.svelte` — render AdminTable + AdminModal with AdminForm
|
|
||||||
|
|
||||||
4. **Done.** Visit `/admin/widgets`. The page handles list, create, edit via modal.
|
|
||||||
|
|
||||||
## Configuration Reference
|
|
||||||
|
|
||||||
### AdminModelConfig
|
|
||||||
|
|
||||||
| Field | Type | Required | Description |
|
|
||||||
| --------------------- | ---------------------------------------------- | -------- | ------------------------------------------------------------------------------------------ |
|
|
||||||
| `slug` | `string` | Yes | URL segment (kebab-case). Used in `/admin/{slug}`. |
|
|
||||||
| `modelName` | `string` | Yes | Prisma model name (PascalCase, **singular**). Maps to `prisma[lcfirst(modelName)]`. |
|
|
||||||
| `displayName` | `string` | Yes | Plural display name for headings and navigation. |
|
|
||||||
| `displayNameSingular` | `string` | Yes | Singular name for edit page headings. |
|
|
||||||
| `idType` | `'String' \| 'Int'` | Yes | Primary key type. Controls how the `id` URL param is parsed. |
|
|
||||||
| `include` | `Record<string, boolean \| { include: ... }>` | No | Prisma `include` clause for eager-loading relations. |
|
|
||||||
| `listFields` | `string[]` | Yes | Table columns. Supports dot-notation for relations (e.g., `'role.name'`). |
|
|
||||||
| `searchFields` | `string[]` | Yes | Fields searched by the client-side search filter. |
|
|
||||||
| `formFields` | `string[]` | Yes | Fields rendered in create/edit forms, in display order. |
|
|
||||||
| `readonlyFields` | `string[]` | No | Fields shown as read-only on the edit page. |
|
|
||||||
| `listOnly` | `boolean` | No | If `true`, suppresses edit links in the table and no modal. |
|
|
||||||
| `fieldTypes` | `Record<string, PrismaScalarType>` | Yes | Maps field names to scalar types. Controls input rendering and FormData parsing. |
|
|
||||||
| `relationOptions` | `Record<string, RelationOptionConfig>` | No | FK fields that render as `<select>` dropdowns. |
|
|
||||||
| `relatedRecords` | `RelatedRecordConfig[]` | No | Has-one/has-many panels shown on edit pages (display only via related records table). |
|
|
||||||
| `accessFilter` | `(viewerRoleId: number) => where \| undefined` | No | Returns a Prisma `where` clause to filter records by viewer role. `undefined` = no filter. |
|
|
||||||
|
|
||||||
### PrismaScalarType
|
|
||||||
|
|
||||||
`'String' | 'Int' | 'Float' | 'Boolean' | 'DateTime' | 'Tiptap'`
|
|
||||||
|
|
||||||
Controls both form input rendering and FormData parsing:
|
|
||||||
|
|
||||||
| Type | HTML Input | Parse Logic |
|
|
||||||
| ---------- | -------------------------------------- | ------------------------------- |
|
|
||||||
| `String` | `<input type="text">` | `raw.toString()` |
|
|
||||||
| `Int` | `<input type="number" step="1">` | `parseInt()`, empty -> `null` |
|
|
||||||
| `Float` | `<input type="number" step="0.01">` | `parseFloat()`, empty -> `null` |
|
|
||||||
| `Boolean` | `<input type="checkbox">` | `'on'` or `'true'` -> `true` |
|
|
||||||
| `DateTime` | `<input type="datetime-local">` | `new Date()`, empty -> `null` |
|
|
||||||
| `Tiptap` | TipTap rich-text editor (hidden input) | `raw.toString()` |
|
|
||||||
|
|
||||||
### RelationOptionConfig
|
|
||||||
|
|
||||||
```ts
|
|
||||||
{
|
|
||||||
model: 'UserRole', // Prisma model to query for options
|
|
||||||
labelField: 'name', // Field displayed in the dropdown
|
|
||||||
valueField: 'id' // Field value submitted with the form
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### RelatedRecordConfig
|
|
||||||
|
|
||||||
```ts
|
|
||||||
{
|
|
||||||
relationKey: 'profile', // Key on parent record (must match a key in `include`)
|
|
||||||
label: 'Profile', // Section heading on edit page
|
|
||||||
type: 'has-one', // 'has-one' or 'has-many'
|
|
||||||
displayFields: ['firstName', 'lastName'], // Fields shown in the panel/table
|
|
||||||
editSlug: 'profiles' // Optional: admin slug to link for editing
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Field Types & Form Rendering
|
|
||||||
|
|
||||||
The `fieldTypes` mapping is the central mechanism connecting Prisma scalar types to HTML inputs
|
|
||||||
and form data parsing. Every field in `formFields` must have a corresponding entry in `fieldTypes`.
|
|
||||||
|
|
||||||
When a field also has a `relationOptions` entry, the framework renders a `<select>` dropdown
|
|
||||||
instead of the default input for that type. The options are fetched server-side by `crud.ts`
|
|
||||||
and passed to the client as `relationOptionsData`.
|
|
||||||
|
|
||||||
For `Tiptap` fields, the TipTap rich-text editor is rendered. The editor content is stored in a
|
|
||||||
hidden input and submitted with the form.
|
|
||||||
|
|
||||||
## Relations
|
|
||||||
|
|
||||||
### Foreign Key Dropdowns (relationOptions)
|
|
||||||
|
|
||||||
When a form field is a foreign key, add a `relationOptions` entry to render a `<select>` dropdown:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
{
|
|
||||||
formFields: ['name', 'categoryId'],
|
|
||||||
fieldTypes: { name: 'String', categoryId: 'Int' },
|
|
||||||
relationOptions: {
|
|
||||||
categoryId: {
|
|
||||||
model: 'ProclamationCategory',
|
|
||||||
labelField: 'name',
|
|
||||||
valueField: 'id'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The `crud.ts` `loadRecords` function automatically queries the related model and passes options
|
|
||||||
to the page as `relationOptionsData`.
|
|
||||||
|
|
||||||
### Eager Loading (include)
|
|
||||||
|
|
||||||
Use `include` to load related data for display in list tables or edit pages:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
{
|
|
||||||
include: { category: { include: { parent: true } } },
|
|
||||||
listFields: ['name', 'category.name'] // dot-notation resolves nested values
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Related Records
|
|
||||||
|
|
||||||
Display has-one or has-many related records as panels on the edit page using `relatedRecords`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
relatedRecords: [
|
|
||||||
{
|
|
||||||
relationKey: 'profile',
|
|
||||||
label: 'Profile',
|
|
||||||
type: 'has-one',
|
|
||||||
displayFields: ['firstName', 'lastName'],
|
|
||||||
editSlug: 'profiles' // links to /admin/profiles/{id}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
relationKey: 'licenseKeys',
|
|
||||||
label: 'License Keys',
|
|
||||||
type: 'has-many',
|
|
||||||
displayFields: ['key', 'active', 'level']
|
|
||||||
}
|
|
||||||
];
|
|
||||||
```
|
|
||||||
|
|
||||||
The `relationKey` must match a key in `include` so the data is actually loaded.
|
|
||||||
|
|
||||||
## Access Control
|
|
||||||
|
|
||||||
### Role Hierarchy
|
|
||||||
|
|
||||||
Defined in `roles.ts`. Lower numbers = higher privilege:
|
|
||||||
|
|
||||||
| Constant | Value | Access Level |
|
|
||||||
| ------------- | ----- | -------------------------------------- |
|
|
||||||
| `ROLES.SUPER` | 1 | Full access, sees all users |
|
|
||||||
| `ROLES.ADMIN` | 2 | Admin access, sees staff and below |
|
|
||||||
| `ROLES.STAFF` | 3 | Staff access (minimum for admin panel) |
|
|
||||||
| `ROLES.USER` | 4 | Regular user (no admin access) |
|
|
||||||
|
|
||||||
### Layout Guard
|
|
||||||
|
|
||||||
`+layout.server.ts` calls `canAccessAdmin(user.roleId)` which returns `true` when
|
|
||||||
`roleId <= ROLES.STAFF`. Users with `ROLES.USER` (4) or no role get a 403.
|
|
||||||
|
|
||||||
### Per-Model Access Filtering
|
|
||||||
|
|
||||||
Use `accessFilter` to restrict which records a viewer can see:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
accessFilter: (viewerRoleId: number) => {
|
|
||||||
if (viewerRoleId <= ROLES.SUPER) return undefined;
|
|
||||||
if (viewerRoleId <= ROLES.ADMIN) {
|
|
||||||
return { roleId: { gte: ROLES.STAFF } };
|
|
||||||
}
|
|
||||||
return { roleId: { gt: ROLES.STAFF } };
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## How Static Pages Work
|
|
||||||
|
|
||||||
1. User visits `/admin/proclamations`
|
|
||||||
2. SvelteKit matches the static route `/admin/proclamations/+page.server.ts`
|
|
||||||
3. `+page.server.ts` calls `getModelConfig('proclamations')` to look up the config
|
|
||||||
4. `loadRecords(config)` calls `prisma.proclamation.findMany()` with the config's `include` and `accessFilter`
|
|
||||||
5. For each FK field with a `relationOptions`, it also queries the related model for dropdown options
|
|
||||||
6. Data + config are passed to `+page.svelte`
|
|
||||||
7. The page renders `AdminTable` with records and `AdminModal` + `AdminForm` for create/edit
|
|
||||||
|
|
||||||
## Component API
|
|
||||||
|
|
||||||
### AdminTable
|
|
||||||
|
|
||||||
| Prop | Type | Description |
|
|
||||||
| -------------- | ---------------------------------- | ------------------------------------ |
|
|
||||||
| `records` | `Record<string, unknown>[]` | Data rows |
|
|
||||||
| `listFields` | `string[]` | Column keys (dot-notation supported) |
|
|
||||||
| `searchFields` | `string[]` | Fields for client-side search |
|
|
||||||
| `fieldTypes` | `Record<string, PrismaScalarType>` | Type mapping for cell formatting |
|
|
||||||
| `singular` | `string` | Singular label for UI text |
|
|
||||||
| `onRowClick` | `(record) => void?` | Click handler for edit modal |
|
|
||||||
| `onNew` | `() => void?` | Click handler for create modal |
|
|
||||||
|
|
||||||
Features: client-side search, click-to-sort columns, auto-formatted cells (dates, booleans,
|
|
||||||
decimals), empty state.
|
|
||||||
|
|
||||||
### AdminForm
|
|
||||||
|
|
||||||
| Prop | Type | Description |
|
|
||||||
| --------------------- | --------------------------------------- | -------------------------------- |
|
|
||||||
| `fields` | `string[]` | Field names to render |
|
|
||||||
| `fieldTypes` | `Record<string, PrismaScalarType>` | Type mapping for inputs |
|
|
||||||
| `relationOptions` | `Record<string, RelationOptionConfig>?` | FK dropdown configs |
|
|
||||||
| `relationOptionsData` | `Record<string, { label; value }[]>?` | Loaded dropdown options |
|
|
||||||
| `record` | `Record<string, unknown>?` | Pre-fill values (edit mode) |
|
|
||||||
| `readonlyFields` | `string[]?` | Read-only field names |
|
|
||||||
| `action` | `string` | Form action (e.g., `'?/upsert'`) |
|
|
||||||
| `submitLabel` | `string?` | Button text |
|
|
||||||
|
|
||||||
### AdminModal
|
|
||||||
|
|
||||||
| Prop | Type | Description |
|
|
||||||
| ---------- | ------------ | ------------------ |
|
|
||||||
| `open` | `boolean` | Show/hide modal |
|
|
||||||
| `title` | `string` | Modal heading |
|
|
||||||
| `onClose` | `() => void` | Close handler |
|
|
||||||
| `children` | `Snippet` | Modal body content |
|
|
||||||
|
|
||||||
### TipTap
|
|
||||||
|
|
||||||
| Prop | Type | Description |
|
|
||||||
| ---------------- | -------- | --------------------------------- |
|
|
||||||
| `initialContent` | `string` | Initial HTML content |
|
|
||||||
| `name` | `string` | Hidden input name for form submit |
|
|
||||||
|
|
||||||
## Utility Functions
|
|
||||||
|
|
||||||
| Function | Module | Description |
|
|
||||||
| ------------------------------------------ | -------------------- | ------------------------------------------------------------------------------ |
|
|
||||||
| `fieldNameToLabel(name)` | `introspect.ts` | `'solfeggioFrequencyId'` -> `'Solfeggio Frequency'` (strips `Id`, adds spaces) |
|
|
||||||
| `resolveFieldValue(record, path)` | `introspect.ts` | Resolves dot-notation paths like `'category.name'` on nested objects |
|
|
||||||
| `extractFormData(formData, fields, types)` | `form-data.ts` | Parses `FormData` with type-aware conversion per `PrismaScalarType` |
|
|
||||||
| `getDelegate(config)` | `prisma-delegate.ts` | Returns `prisma[lcfirst(modelName)]` delegate |
|
|
||||||
| `parseId(config, raw)` | `prisma-delegate.ts` | Parses string to `number` when `idType` is `'Int'` |
|
|
||||||
| `getModelConfig(slug)` | `registry.ts` | Looks up config by URL slug |
|
|
||||||
| `getAllModelConfigs()` | `registry.ts` | Returns all registered model configs |
|
|
||||||
| `hasRole(roleId, required)` | `roles.ts` | `true` if `roleId <= required` |
|
|
||||||
| `canAccessAdmin(roleId)` | `roles.ts` | `true` if `roleId <= ROLES.STAFF` |
|
|
||||||
| `loadRecords(locals, config)` | `crud.ts` | Fetches records + FK dropdown options for a model |
|
|
||||||
| `upsertRecord(formData, config, locals)` | `crud.ts` | Creates or updates a record based on presence of `id` in form data |
|
|
||||||
| `deleteRecord(formData, config, locals)` | `crud.ts` | Deletes a record by ID |
|
|
||||||
|
|
||||||
## Models in Admin
|
|
||||||
|
|
||||||
| Registry Slug | Prisma Model | Notes |
|
|
||||||
| ------------------------- | -------------------- | ----------------------------------- |
|
|
||||||
| `users` | User | Role assignment, view-only fields |
|
|
||||||
| `profiles` | Profile | Linked to user FK |
|
|
||||||
| `addresses` | Address | Linked to profile FK |
|
|
||||||
| `user-roles` | UserRole | |
|
|
||||||
| `proclamations` | Proclamation | TipTap rich-text editor for content |
|
|
||||||
| `solfeggio-frequencies` | SolfeggioFrequency | |
|
|
||||||
| `proclamation-categories` | ProclamationCategory | Parent category hierarchy |
|
|
||||||
| `license-keys` | LicenseKey | listOnly — read-only table, no CRUD |
|
|
||||||
| `subscription-levels` | SubscriptionLevel | |
|
|
||||||
|
|
||||||
### Excluded from Admin
|
|
||||||
|
|
||||||
| Model | Reason |
|
|
||||||
| ----------------- | --------------------------------------------- |
|
|
||||||
| Session | Better Auth internal — not for manual editing |
|
|
||||||
| Account | Better Auth internal |
|
|
||||||
| Verification | Better Auth internal |
|
|
||||||
| UserProclamations | Composite key — managed via other models |
|
|
||||||
|
|
||||||
## Complete Example
|
|
||||||
|
|
||||||
A hypothetical `Product` model showing all configuration features:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
{
|
|
||||||
slug: 'products',
|
|
||||||
modelName: 'Product',
|
|
||||||
displayName: 'Products',
|
|
||||||
displayNameSingular: 'Product',
|
|
||||||
idType: 'Int',
|
|
||||||
include: { category: true, variants: true, createdBy: true },
|
|
||||||
listFields: ['name', 'category.name', 'price', 'active', 'createdAt'],
|
|
||||||
searchFields: ['name', 'sku'],
|
|
||||||
formFields: ['name', 'sku', 'price', 'active', 'categoryId'],
|
|
||||||
readonlyFields: ['id', 'createdAt', 'updatedAt'],
|
|
||||||
fieldTypes: {
|
|
||||||
name: 'String',
|
|
||||||
sku: 'String',
|
|
||||||
price: 'Float',
|
|
||||||
active: 'Boolean',
|
|
||||||
categoryId: 'Int',
|
|
||||||
createdAt: 'DateTime',
|
|
||||||
updatedAt: 'DateTime'
|
|
||||||
},
|
|
||||||
relationOptions: {
|
|
||||||
categoryId: { model: 'Category', labelField: 'name', valueField: 'id' }
|
|
||||||
},
|
|
||||||
relatedRecords: [
|
|
||||||
{
|
|
||||||
relationKey: 'variants',
|
|
||||||
label: 'Variants',
|
|
||||||
type: 'has-many',
|
|
||||||
displayFields: ['size', 'color', 'stock'],
|
|
||||||
editSlug: 'variants'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
relationKey: 'createdBy',
|
|
||||||
label: 'Created By',
|
|
||||||
type: 'has-one',
|
|
||||||
displayFields: ['name', 'email']
|
|
||||||
}
|
|
||||||
],
|
|
||||||
accessFilter: (viewerRoleId: number) => {
|
|
||||||
if (viewerRoleId <= 2) return undefined;
|
|
||||||
return { active: true };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@@ -1,227 +0,0 @@
|
|||||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
||||||
import { createLocals, mockUser } from '$lib/test-utils';
|
|
||||||
import { getModelConfig } from './registry';
|
|
||||||
|
|
||||||
// Ensure superValidate can be imported from server path in Node.js/SSR context
|
|
||||||
import { superValidate } from 'sveltekit-superforms/server';
|
|
||||||
import { zod4 } from 'sveltekit-superforms/adapters';
|
|
||||||
|
|
||||||
vi.mock('$lib/db', () => {
|
|
||||||
const mkdel = () => ({
|
|
||||||
findMany: vi.fn(),
|
|
||||||
create: vi.fn(),
|
|
||||||
update: vi.fn(),
|
|
||||||
delete: vi.fn(),
|
|
||||||
findUnique: vi.fn(),
|
|
||||||
findFirst: vi.fn()
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
prisma: {
|
|
||||||
userRole: mkdel(),
|
|
||||||
proclamation: mkdel(),
|
|
||||||
proclamationCategory: mkdel(),
|
|
||||||
solfeggioFrequency: mkdel(),
|
|
||||||
subscriptionLevel: mkdel()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
import { prisma } from '$lib/db';
|
|
||||||
import { loadRecords, upsertRecord, deleteRecord } from './crud';
|
|
||||||
|
|
||||||
const userRoleFindMany = prisma.userRole.findMany as ReturnType<typeof vi.fn>;
|
|
||||||
const create = prisma.userRole.create as ReturnType<typeof vi.fn>;
|
|
||||||
const update = prisma.userRole.update as ReturnType<typeof vi.fn>;
|
|
||||||
const deletes = prisma.userRole.delete as ReturnType<typeof vi.fn>;
|
|
||||||
const findUnique = prisma.userRole.findUnique as ReturnType<typeof vi.fn>;
|
|
||||||
const findFirst = prisma.userRole.findFirst as ReturnType<typeof vi.fn>;
|
|
||||||
const config = getModelConfig('user-roles')!;
|
|
||||||
|
|
||||||
describe('crud loadRecords', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
userRoleFindMany.mockReset();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns records', async () => {
|
|
||||||
userRoleFindMany.mockResolvedValue([{ id: 1, name: 'Admin' }]);
|
|
||||||
|
|
||||||
const locals = createLocals(mockUser() as any);
|
|
||||||
const result = await loadRecords(locals, config);
|
|
||||||
|
|
||||||
expect(result.records).toEqual([{ id: 1, name: 'Admin' }]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns empty records array', async () => {
|
|
||||||
userRoleFindMany.mockResolvedValue([]);
|
|
||||||
|
|
||||||
const locals = createLocals(mockUser() as any);
|
|
||||||
const result = await loadRecords(locals, config);
|
|
||||||
|
|
||||||
expect(result.records).toEqual([]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('crud upsertRecord', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
create.mockReset();
|
|
||||||
update.mockReset();
|
|
||||||
findUnique.mockReset();
|
|
||||||
findFirst.mockReset();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('creates a new record when no id', async () => {
|
|
||||||
create.mockResolvedValue({ id: 2, name: 'Editor' });
|
|
||||||
|
|
||||||
const fd = new FormData();
|
|
||||||
fd.set('name', 'Editor');
|
|
||||||
const locals = createLocals(mockUser() as any);
|
|
||||||
const result = await upsertRecord(fd, config, locals);
|
|
||||||
|
|
||||||
expect(create).toHaveBeenCalledWith({ data: { name: 'Editor' } });
|
|
||||||
expect(result).toEqual({ success: true });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('updates an existing record when id is present', async () => {
|
|
||||||
findUnique.mockResolvedValue({ id: 1, name: 'Old Name' });
|
|
||||||
|
|
||||||
const fd = new FormData();
|
|
||||||
fd.set('id', '1');
|
|
||||||
fd.set('name', 'New Name');
|
|
||||||
const locals = createLocals(mockUser() as any);
|
|
||||||
const result = await upsertRecord(fd, config, locals);
|
|
||||||
|
|
||||||
expect(update).toHaveBeenCalledWith({ where: { id: 1 }, data: { name: 'New Name' } });
|
|
||||||
expect(result).toEqual({ success: true });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns error when updating non-existent record', async () => {
|
|
||||||
findUnique.mockResolvedValue(null);
|
|
||||||
|
|
||||||
const fd = new FormData();
|
|
||||||
fd.set('id', '999');
|
|
||||||
fd.set('name', 'Ghost');
|
|
||||||
const locals = createLocals(mockUser() as any);
|
|
||||||
const result = await upsertRecord(fd, config, locals);
|
|
||||||
|
|
||||||
expect(result).toEqual({ success: false, error: 'Record not found' });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('crud deleteRecord', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
deletes.mockReset();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('deletes a record by id', async () => {
|
|
||||||
deletes.mockResolvedValue({ id: 1, name: 'Admin' });
|
|
||||||
|
|
||||||
const fd = new FormData();
|
|
||||||
fd.set('id', '1');
|
|
||||||
const locals = createLocals(mockUser() as any);
|
|
||||||
const result = await deleteRecord(fd, config, locals);
|
|
||||||
|
|
||||||
expect(deletes).toHaveBeenCalledWith({ where: { id: 1 } });
|
|
||||||
expect(result).toEqual({ success: true });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns error when id is missing', async () => {
|
|
||||||
const fd = new FormData();
|
|
||||||
const locals = createLocals(mockUser() as any);
|
|
||||||
const result = await deleteRecord(fd, config, locals);
|
|
||||||
|
|
||||||
expect(result).toEqual({ success: false, error: 'Missing id' });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('P2002 unique constraint handling', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
create.mockReset();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns error with field names when meta.target is populated', async () => {
|
|
||||||
const p2002 = new Error('Unique constraint failed');
|
|
||||||
(p2002 as any).code = 'P2002';
|
|
||||||
(p2002 as any).meta = { target: ['name'], modelName: 'UserRole' };
|
|
||||||
create.mockRejectedValue(p2002);
|
|
||||||
|
|
||||||
const fd = new FormData();
|
|
||||||
fd.set('name', 'Admin');
|
|
||||||
const locals = createLocals(mockUser() as any);
|
|
||||||
const result = await upsertRecord(fd, config, locals);
|
|
||||||
|
|
||||||
expect(result).toEqual({
|
|
||||||
success: false,
|
|
||||||
error: 'A record with this name already exists.',
|
|
||||||
fieldErrors: { name: 'Already taken' }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns generic message when meta.target is empty', async () => {
|
|
||||||
const p2002 = new Error('Unique constraint failed');
|
|
||||||
(p2002 as any).code = 'P2002';
|
|
||||||
(p2002 as any).meta = { target: [], modelName: 'UserRole' };
|
|
||||||
create.mockRejectedValue(p2002);
|
|
||||||
|
|
||||||
const fd = new FormData();
|
|
||||||
fd.set('name', 'Admin');
|
|
||||||
const locals = createLocals(mockUser() as any);
|
|
||||||
const result = await upsertRecord(fd, config, locals);
|
|
||||||
|
|
||||||
expect(result).toEqual({
|
|
||||||
success: false,
|
|
||||||
error: 'A database constraint violation occurred. The record could not be created.'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns generic message when meta.target is missing', async () => {
|
|
||||||
const p2002 = new Error('Unique constraint failed');
|
|
||||||
(p2002 as any).code = 'P2002';
|
|
||||||
(p2002 as any).meta = { modelName: 'UserRole' };
|
|
||||||
create.mockRejectedValue(p2002);
|
|
||||||
|
|
||||||
const fd = new FormData();
|
|
||||||
fd.set('name', 'Admin');
|
|
||||||
const locals = createLocals(mockUser() as any);
|
|
||||||
const result = await upsertRecord(fd, config, locals);
|
|
||||||
|
|
||||||
expect(result).toEqual({
|
|
||||||
success: false,
|
|
||||||
error: 'A database constraint violation occurred. The record could not be created.'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('sveltekit-superforms import compatibility', () => {
|
|
||||||
it('imports superValidate from server path without pulling in SuperDebug.svelte', async () => {
|
|
||||||
expect(superValidate).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('calls superValidate with a Request and validates against a Zod schema', async () => {
|
|
||||||
const { z } = await import('zod');
|
|
||||||
const schema = z.object({ name: z.string().min(1) });
|
|
||||||
const body = new URLSearchParams({ name: 'test' });
|
|
||||||
const request = new Request('http://localhost', {
|
|
||||||
method: 'POST',
|
|
||||||
body,
|
|
||||||
headers: { 'content-type': 'application/x-www-form-urlencoded' }
|
|
||||||
});
|
|
||||||
const form = await superValidate(request, zod4(schema));
|
|
||||||
expect(form.valid).toBe(true);
|
|
||||||
expect(form.data).toEqual({ name: 'test' });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('prisma client version consistency', () => {
|
|
||||||
it('generated client references existing query compiler files', async () => {
|
|
||||||
const fs = await import('fs');
|
|
||||||
const path = await import('path');
|
|
||||||
const runtimeDir = path.resolve('node_modules/@prisma/client/runtime');
|
|
||||||
|
|
||||||
const generatedFile = path.resolve('src/lib/generated/prisma/internal/class.ts');
|
|
||||||
const content = fs.readFileSync(generatedFile, 'utf-8');
|
|
||||||
const compilerMatch = content.match(/import\(["']@prisma\/client\/runtime\/([^"']+)["']\)/);
|
|
||||||
expect(compilerMatch).not.toBeNull();
|
|
||||||
const compilerPath = path.join(runtimeDir, compilerMatch![1]);
|
|
||||||
expect(fs.existsSync(compilerPath)).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,190 +0,0 @@
|
|||||||
import { getDelegate, parseId } from './prisma-delegate';
|
|
||||||
import { extractFormData } from './form-data';
|
|
||||||
import type { AdminModelConfig } from './types';
|
|
||||||
import { superValidate } from 'sveltekit-superforms/server';
|
|
||||||
import { zod4 } from 'sveltekit-superforms/adapters';
|
|
||||||
import * as Schemas from './schemas';
|
|
||||||
import type { z } from 'zod';
|
|
||||||
|
|
||||||
const modelSchemas: Record<string, z.ZodObject<any>> = {
|
|
||||||
User: Schemas.User,
|
|
||||||
Profile: Schemas.Profile,
|
|
||||||
UserRole: Schemas.UserRole,
|
|
||||||
Proclamation: Schemas.Proclamation,
|
|
||||||
ProclamationCategory: Schemas.ProclamationCategory,
|
|
||||||
SolfeggioFrequency: Schemas.SolfeggioFrequency,
|
|
||||||
SubscriptionLevel: Schemas.SubscriptionLevel,
|
|
||||||
LicenseKey: Schemas.LicenseKey
|
|
||||||
};
|
|
||||||
|
|
||||||
export function superFormErrorsToFieldErrors(superErrors: Record<string, unknown>): {
|
|
||||||
error: string;
|
|
||||||
fieldErrors: Record<string, string>;
|
|
||||||
} {
|
|
||||||
const fieldErrors: Record<string, string> = {};
|
|
||||||
let formError = '';
|
|
||||||
for (const [key, value] of Object.entries(superErrors)) {
|
|
||||||
if (key === '_errors' && Array.isArray(value)) {
|
|
||||||
formError = (value[0] as string) ?? '';
|
|
||||||
} else if (
|
|
||||||
value &&
|
|
||||||
typeof value === 'object' &&
|
|
||||||
'_errors' in value &&
|
|
||||||
Array.isArray((value as Record<string, unknown>)._errors)
|
|
||||||
) {
|
|
||||||
const errMsg = ((value as Record<string, unknown>)._errors as string[])[0];
|
|
||||||
fieldErrors[key] = errMsg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { error: formError, fieldErrors };
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function loadRecords(locals: App.Locals, config: AdminModelConfig) {
|
|
||||||
const delegate = getDelegate(config);
|
|
||||||
|
|
||||||
const viewerRoleId = (locals.user as { roleId?: number } | null)?.roleId ?? 4;
|
|
||||||
const where = config.accessFilter?.(viewerRoleId);
|
|
||||||
|
|
||||||
const records = await delegate.findMany({
|
|
||||||
where,
|
|
||||||
include: config.include,
|
|
||||||
orderBy: { id: 'asc' }
|
|
||||||
});
|
|
||||||
|
|
||||||
const relationOptionsData: Record<string, { label: string; value: string | number }[]> = {};
|
|
||||||
|
|
||||||
for (const [field, relConfig] of Object.entries(config.relationOptions ?? {})) {
|
|
||||||
const relDelegate = getDelegate({ ...config, modelName: relConfig.model });
|
|
||||||
const selectFields: Record<string, boolean> = {
|
|
||||||
[relConfig.valueField]: true,
|
|
||||||
[relConfig.labelField]: true
|
|
||||||
};
|
|
||||||
const options = await relDelegate.findMany({
|
|
||||||
select: selectFields,
|
|
||||||
orderBy: { [relConfig.labelField]: 'asc' }
|
|
||||||
});
|
|
||||||
const mapped = (options as Record<string, unknown>[]).map((opt: Record<string, unknown>) => ({
|
|
||||||
label: String(opt[relConfig.labelField]),
|
|
||||||
value: opt[relConfig.valueField] as string | number
|
|
||||||
}));
|
|
||||||
relationOptionsData[field] = mapped;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
records: records as Record<string, unknown>[],
|
|
||||||
relationOptionsData
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function upsertRecord(
|
|
||||||
requestOrFormData: Request | FormData,
|
|
||||||
config: AdminModelConfig,
|
|
||||||
locals: App.Locals
|
|
||||||
) {
|
|
||||||
const formData =
|
|
||||||
requestOrFormData instanceof FormData ? requestOrFormData : await requestOrFormData.formData();
|
|
||||||
|
|
||||||
const schema = modelSchemas[config.modelName];
|
|
||||||
const form = schema ? await superValidate(formData, zod4(schema)) : null;
|
|
||||||
|
|
||||||
if (form && !form.valid) {
|
|
||||||
const { error, fieldErrors } = superFormErrorsToFieldErrors(
|
|
||||||
form.errors as Record<string, unknown>
|
|
||||||
);
|
|
||||||
return { ...form, success: false, error, fieldErrors };
|
|
||||||
}
|
|
||||||
|
|
||||||
const rawId = formData.get('id')?.toString();
|
|
||||||
const data =
|
|
||||||
form?.data ??
|
|
||||||
(extractFormData(formData, config.formFields, config.fieldTypes) as Record<string, unknown>);
|
|
||||||
|
|
||||||
// Strip virtual/UI-only fields from Prisma payload
|
|
||||||
for (const f of config.dataOnlyFields ?? []) {
|
|
||||||
delete data[f];
|
|
||||||
}
|
|
||||||
|
|
||||||
const delegate = getDelegate(config);
|
|
||||||
const viewerRoleId = (locals.user as { roleId?: number } | null)?.roleId ?? 4;
|
|
||||||
const where = config.accessFilter?.(viewerRoleId);
|
|
||||||
|
|
||||||
if (rawId) {
|
|
||||||
const id = parseId(config, rawId);
|
|
||||||
|
|
||||||
const existing = await delegate.findUnique({
|
|
||||||
where: { id },
|
|
||||||
...((config.include ? { include: config.include } : {}) as object)
|
|
||||||
});
|
|
||||||
if (!existing) {
|
|
||||||
return { success: false, error: 'Record not found' };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (where) {
|
|
||||||
const match = await delegate.findFirst({
|
|
||||||
where: { id, ...where }
|
|
||||||
});
|
|
||||||
if (!match) {
|
|
||||||
return { success: false, error: 'Access denied' };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await delegate.update({
|
|
||||||
where: { id },
|
|
||||||
data
|
|
||||||
});
|
|
||||||
|
|
||||||
return { success: true };
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
await delegate.create({ data });
|
|
||||||
} catch (e: unknown) {
|
|
||||||
if (e && typeof e === 'object' && 'code' in e && (e as { code: string }).code === 'P2002') {
|
|
||||||
const meta = (e as { meta?: { target?: string[]; modelName?: string } }).meta;
|
|
||||||
const fields = meta?.target ?? [];
|
|
||||||
console.error(
|
|
||||||
`[crud] P2002 on ${meta?.modelName ?? 'unknown'} create. data=`,
|
|
||||||
data,
|
|
||||||
`meta.target=`,
|
|
||||||
meta?.target
|
|
||||||
);
|
|
||||||
if (fields.length === 0) {
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: 'A database constraint violation occurred. The record could not be created.'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: `A record with this ${fields.join(', ')} already exists.`,
|
|
||||||
fieldErrors: Object.fromEntries(fields.map((f) => [f, 'Already taken']))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
return { success: true };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function deleteRecord(
|
|
||||||
requestOrFormData: Request | FormData,
|
|
||||||
config: AdminModelConfig,
|
|
||||||
locals: App.Locals
|
|
||||||
) {
|
|
||||||
const delegate = getDelegate(config);
|
|
||||||
const formData =
|
|
||||||
requestOrFormData instanceof FormData ? requestOrFormData : await requestOrFormData.formData();
|
|
||||||
const rawId = formData.get('id')?.toString();
|
|
||||||
if (!rawId) return { success: false, error: 'Missing id' };
|
|
||||||
|
|
||||||
const id = parseId(config, rawId);
|
|
||||||
|
|
||||||
const viewerRoleId = (locals.user as { roleId?: number } | null)?.roleId ?? 4;
|
|
||||||
const where = config.accessFilter?.(viewerRoleId);
|
|
||||||
if (where) {
|
|
||||||
const match = await delegate.findFirst({ where: { id, ...where } });
|
|
||||||
if (!match) return { success: false, error: 'Access denied' };
|
|
||||||
}
|
|
||||||
|
|
||||||
await delegate.delete({ where: { id } });
|
|
||||||
return { success: true };
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
import type { PrismaScalarType } from './types';
|
|
||||||
|
|
||||||
export function extractFormData(
|
|
||||||
formData: FormData,
|
|
||||||
formFields: string[],
|
|
||||||
fieldTypes: Record<string, PrismaScalarType>
|
|
||||||
): Record<string, unknown> {
|
|
||||||
const data: Record<string, unknown> = {};
|
|
||||||
|
|
||||||
for (const field of formFields) {
|
|
||||||
const type = fieldTypes[field] ?? 'String';
|
|
||||||
const raw = formData.get(field);
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case 'Int': {
|
|
||||||
const val = raw?.toString() ?? '';
|
|
||||||
data[field] = val === '' ? null : parseInt(val, 10);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'Float': {
|
|
||||||
const val = raw?.toString() ?? '';
|
|
||||||
data[field] = val === '' ? null : parseFloat(val);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'Boolean': {
|
|
||||||
data[field] = raw === 'on' || raw === 'true';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'DateTime': {
|
|
||||||
const val = raw?.toString() ?? '';
|
|
||||||
data[field] = val === '' ? null : new Date(val);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'Tiptap': {
|
|
||||||
data[field] = raw?.toString() ?? '';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
data[field] = raw?.toString() ?? '';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
export type { AdminModelConfig, RelationOptionConfig, AdminField, PrismaScalarType } from './types';
|
|
||||||
export { ROLES, hasRole, canAccessAdmin } from './roles';
|
|
||||||
export { fieldNameToLabel, resolveFieldValue } from './introspect';
|
|
||||||
export { getDelegate, parseId } from './prisma-delegate';
|
|
||||||
export { extractFormData } from './form-data';
|
|
||||||
export { getModelConfig, getAllModelConfigs, getSerializableModelConfigs } from './registry';
|
|
||||||
export type { SerializableModelConfig } from './registry';
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
export function fieldNameToLabel(fieldName: string): string {
|
|
||||||
const withoutId = fieldName.replace(/Id$/, '');
|
|
||||||
const spaced = withoutId.replace(/([a-z])([A-Z])/g, '$1 $2');
|
|
||||||
return spaced.charAt(0).toUpperCase() + spaced.slice(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resolveFieldValue(record: Record<string, unknown>, path: string): unknown {
|
|
||||||
const parts = path.split('.');
|
|
||||||
let current: unknown = record;
|
|
||||||
for (const part of parts) {
|
|
||||||
if (current == null || typeof current !== 'object') return undefined;
|
|
||||||
current = (current as Record<string, unknown>)[part];
|
|
||||||
}
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import { prisma } from '$lib/db';
|
|
||||||
import type { AdminModelConfig } from './types';
|
|
||||||
|
|
||||||
function lcfirst(str: string): string {
|
|
||||||
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
export function getDelegate(config: AdminModelConfig): any {
|
|
||||||
const key = lcfirst(config.modelName);
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const delegate = (prisma as any)[key];
|
|
||||||
if (!delegate) {
|
|
||||||
throw new Error(`Prisma delegate not found for model: ${config.modelName} (key: ${key})`);
|
|
||||||
}
|
|
||||||
return delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function parseId(config: AdminModelConfig, raw: string): string | number {
|
|
||||||
if (config.idType === 'Int') {
|
|
||||||
const parsed = parseInt(raw, 10);
|
|
||||||
if (isNaN(parsed)) throw new Error(`Invalid integer ID: ${raw}`);
|
|
||||||
return parsed;
|
|
||||||
}
|
|
||||||
return raw;
|
|
||||||
}
|
|
||||||
@@ -1,290 +0,0 @@
|
|||||||
import type { AdminModelConfig } from './types';
|
|
||||||
import { ROLES } from './roles';
|
|
||||||
|
|
||||||
const models: AdminModelConfig[] = [
|
|
||||||
{
|
|
||||||
slug: 'users',
|
|
||||||
modelName: 'User',
|
|
||||||
displayName: 'Users',
|
|
||||||
displayNameSingular: 'User',
|
|
||||||
listOnly: true,
|
|
||||||
idType: 'String',
|
|
||||||
include: { role: true, profile: true, licenseKeys: true },
|
|
||||||
listFields: ['name', 'email', 'emailVerified', 'role.name', 'createdAt'],
|
|
||||||
searchFields: ['name', 'email'],
|
|
||||||
formFields: ['roleId'],
|
|
||||||
readonlyFields: ['id', 'name', 'email', 'emailVerified', 'createdAt', 'updatedAt'],
|
|
||||||
fieldTypes: {
|
|
||||||
name: 'String',
|
|
||||||
email: 'String',
|
|
||||||
emailVerified: 'Boolean',
|
|
||||||
roleId: 'Int',
|
|
||||||
createdAt: 'DateTime',
|
|
||||||
updatedAt: 'DateTime'
|
|
||||||
},
|
|
||||||
relationOptions: {
|
|
||||||
roleId: { model: 'UserRole', labelField: 'name', valueField: 'id' }
|
|
||||||
},
|
|
||||||
relatedRecords: [
|
|
||||||
{
|
|
||||||
relationKey: 'profile',
|
|
||||||
label: 'Profile',
|
|
||||||
type: 'has-one',
|
|
||||||
displayFields: ['firstName', 'lastName', 'phoneNumber'],
|
|
||||||
editSlug: 'profiles'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
relationKey: 'licenseKeys',
|
|
||||||
label: 'License Keys',
|
|
||||||
type: 'has-many',
|
|
||||||
displayFields: ['key', 'active', 'level']
|
|
||||||
}
|
|
||||||
],
|
|
||||||
accessFilter: (viewerRoleId: number) => {
|
|
||||||
if (viewerRoleId <= ROLES.SUPER) return undefined;
|
|
||||||
if (viewerRoleId <= ROLES.ADMIN) {
|
|
||||||
return { roleId: { gte: ROLES.STAFF } };
|
|
||||||
}
|
|
||||||
return { roleId: { gt: ROLES.STAFF } };
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
slug: 'profiles',
|
|
||||||
modelName: 'Profile',
|
|
||||||
displayName: 'Profiles',
|
|
||||||
displayNameSingular: 'Profile',
|
|
||||||
idType: 'String',
|
|
||||||
include: { user: true, addresses: true },
|
|
||||||
listFields: ['firstName', 'lastName', 'phoneNumber', 'user.name'],
|
|
||||||
searchFields: ['firstName', 'lastName', 'phoneNumber'],
|
|
||||||
formFields: ['userId', 'firstName', 'lastName', 'phoneNumber'],
|
|
||||||
readonlyFields: ['id'],
|
|
||||||
fieldTypes: {
|
|
||||||
userId: 'String',
|
|
||||||
firstName: 'String',
|
|
||||||
lastName: 'String',
|
|
||||||
phoneNumber: 'String'
|
|
||||||
},
|
|
||||||
relationOptions: {
|
|
||||||
userId: { model: 'User', labelField: 'name', valueField: 'id' }
|
|
||||||
},
|
|
||||||
relatedRecords: [
|
|
||||||
{
|
|
||||||
relationKey: 'addresses',
|
|
||||||
label: 'Addresses',
|
|
||||||
type: 'has-many',
|
|
||||||
displayFields: ['type', 'street', 'city', 'state', 'postalCode', 'country', 'isDefault']
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
slug: 'user-roles',
|
|
||||||
modelName: 'UserRole',
|
|
||||||
displayName: 'User Roles',
|
|
||||||
displayNameSingular: 'User Role',
|
|
||||||
idType: 'Int',
|
|
||||||
listFields: ['id', 'name'],
|
|
||||||
searchFields: ['name'],
|
|
||||||
formFields: ['name'],
|
|
||||||
readonlyFields: ['id'],
|
|
||||||
fieldTypes: {
|
|
||||||
name: 'String'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
slug: 'proclamations',
|
|
||||||
modelName: 'Proclamation',
|
|
||||||
displayName: 'Proclamations',
|
|
||||||
displayNameSingular: 'Proclamation',
|
|
||||||
idType: 'String',
|
|
||||||
include: {
|
|
||||||
solfeggioFrequency: true,
|
|
||||||
category: { include: { parent: true } },
|
|
||||||
subscriptionLevel: true
|
|
||||||
},
|
|
||||||
listFields: ['name', 'category.name', 'solfeggioFrequency.name', 'subscriptionLevel.name'],
|
|
||||||
searchFields: ['name', 'content'],
|
|
||||||
formFields: [
|
|
||||||
'name',
|
|
||||||
'solfeggioFrequencyId',
|
|
||||||
'parentCategoryId',
|
|
||||||
'categoryId',
|
|
||||||
'subscriptionLevelId',
|
|
||||||
'content'
|
|
||||||
],
|
|
||||||
readonlyFields: ['id'],
|
|
||||||
dataOnlyFields: ['parentCategoryId'],
|
|
||||||
cascadeFields: { categoryId: { parentField: 'parentCategoryId' } },
|
|
||||||
fieldTypes: {
|
|
||||||
name: 'String',
|
|
||||||
content: 'Tiptap',
|
|
||||||
solfeggioFrequencyId: 'Int',
|
|
||||||
parentCategoryId: 'Int',
|
|
||||||
categoryId: 'Int',
|
|
||||||
subscriptionLevelId: 'Int'
|
|
||||||
},
|
|
||||||
relationOptions: {
|
|
||||||
solfeggioFrequencyId: {
|
|
||||||
model: 'SolfeggioFrequency',
|
|
||||||
labelField: 'name',
|
|
||||||
valueField: 'id'
|
|
||||||
},
|
|
||||||
parentCategoryId: {
|
|
||||||
model: 'ProclamationCategory',
|
|
||||||
labelField: 'name',
|
|
||||||
valueField: 'id'
|
|
||||||
},
|
|
||||||
categoryId: {
|
|
||||||
model: 'ProclamationCategory',
|
|
||||||
labelField: 'name',
|
|
||||||
valueField: 'id'
|
|
||||||
},
|
|
||||||
subscriptionLevelId: {
|
|
||||||
model: 'SubscriptionLevel',
|
|
||||||
labelField: 'name',
|
|
||||||
valueField: 'id'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
slug: 'solfeggio-frequencies',
|
|
||||||
modelName: 'SolfeggioFrequency',
|
|
||||||
displayName: 'Solfeggio Frequencies',
|
|
||||||
displayNameSingular: 'Solfeggio Frequency',
|
|
||||||
idType: 'Int',
|
|
||||||
include: { proclamations: true },
|
|
||||||
listFields: ['name', 'value'],
|
|
||||||
searchFields: ['name'],
|
|
||||||
formFields: ['name', 'value'],
|
|
||||||
readonlyFields: ['id'],
|
|
||||||
fieldTypes: {
|
|
||||||
name: 'String',
|
|
||||||
value: 'Int'
|
|
||||||
},
|
|
||||||
relatedRecords: [
|
|
||||||
{
|
|
||||||
relationKey: 'proclamations',
|
|
||||||
label: 'Proclamations',
|
|
||||||
type: 'has-many',
|
|
||||||
displayFields: ['name'],
|
|
||||||
editSlug: 'proclamations'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
slug: 'subscription-levels',
|
|
||||||
modelName: 'SubscriptionLevel',
|
|
||||||
displayName: 'Subscription Levels',
|
|
||||||
displayNameSingular: 'Subscription Level',
|
|
||||||
idType: 'Int',
|
|
||||||
include: { proclamations: true },
|
|
||||||
listFields: ['id', 'name', 'description'],
|
|
||||||
searchFields: ['name', 'description'],
|
|
||||||
formFields: ['name', 'description'],
|
|
||||||
readonlyFields: ['id'],
|
|
||||||
fieldTypes: {
|
|
||||||
name: 'String',
|
|
||||||
description: 'String'
|
|
||||||
},
|
|
||||||
relatedRecords: [
|
|
||||||
{
|
|
||||||
relationKey: 'proclamations',
|
|
||||||
label: 'Proclamations',
|
|
||||||
type: 'has-many',
|
|
||||||
displayFields: ['name'],
|
|
||||||
editSlug: 'proclamations'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
slug: 'proclamation-categories',
|
|
||||||
modelName: 'ProclamationCategory',
|
|
||||||
displayName: 'Proclamation Categories',
|
|
||||||
displayNameSingular: 'Proclamation Category',
|
|
||||||
idType: 'Int',
|
|
||||||
include: { proclamations: true, children: true },
|
|
||||||
listFields: ['name'],
|
|
||||||
searchFields: ['name'],
|
|
||||||
formFields: ['name', 'parentId'],
|
|
||||||
readonlyFields: ['id'],
|
|
||||||
fieldTypes: {
|
|
||||||
name: 'String',
|
|
||||||
parentId: 'Int',
|
|
||||||
sortOrder: 'Int'
|
|
||||||
},
|
|
||||||
relationOptions: {
|
|
||||||
parentId: { model: 'ProclamationCategory', labelField: 'name', valueField: 'id' }
|
|
||||||
},
|
|
||||||
relatedRecords: [
|
|
||||||
{
|
|
||||||
relationKey: 'proclamations',
|
|
||||||
label: 'Proclamations',
|
|
||||||
type: 'has-many',
|
|
||||||
displayFields: ['name'],
|
|
||||||
editSlug: 'proclamations'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
relationKey: 'children',
|
|
||||||
label: 'Sub-categories',
|
|
||||||
type: 'has-many',
|
|
||||||
displayFields: ['name'],
|
|
||||||
editSlug: 'proclamation-categories'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
slug: 'license-keys',
|
|
||||||
modelName: 'LicenseKey',
|
|
||||||
displayName: 'License Keys',
|
|
||||||
displayNameSingular: 'License Key',
|
|
||||||
idType: 'String',
|
|
||||||
include: { user: true },
|
|
||||||
listFields: ['key', 'active', 'level', 'user.name'],
|
|
||||||
searchFields: ['key', 'level'],
|
|
||||||
formFields: ['active', 'level', 'userId'],
|
|
||||||
readonlyFields: ['id'],
|
|
||||||
fieldTypes: {
|
|
||||||
key: 'String',
|
|
||||||
active: 'Boolean',
|
|
||||||
level: 'String',
|
|
||||||
userId: 'String'
|
|
||||||
},
|
|
||||||
relationOptions: {
|
|
||||||
userId: { model: 'User', labelField: 'name', valueField: 'id' }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const modelsBySlug = new Map<string, AdminModelConfig>();
|
|
||||||
for (const model of models) {
|
|
||||||
modelsBySlug.set(model.slug, model);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getModelConfig(slug: string): AdminModelConfig | undefined {
|
|
||||||
return modelsBySlug.get(slug);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getAllModelConfigs(): AdminModelConfig[] {
|
|
||||||
return models;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SerializableModelConfig = {
|
|
||||||
slug: string;
|
|
||||||
displayName: string;
|
|
||||||
listFields: string[];
|
|
||||||
accessFilterResult: Record<string, unknown> | undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function getSerializableModelConfigs(
|
|
||||||
viewerRoleId: number,
|
|
||||||
excludeSlugs?: string[]
|
|
||||||
): SerializableModelConfig[] {
|
|
||||||
return models
|
|
||||||
.filter((m) => !excludeSlugs?.includes(m.slug))
|
|
||||||
.map((m) => ({
|
|
||||||
slug: m.slug,
|
|
||||||
displayName: m.displayName,
|
|
||||||
listFields: m.listFields,
|
|
||||||
accessFilterResult: m.accessFilter?.(viewerRoleId)
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
export const ROLES = {
|
|
||||||
SUPER: 1,
|
|
||||||
ADMIN: 2,
|
|
||||||
STAFF: 3,
|
|
||||||
USER: 4
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export function hasRole(userRoleId: number | null | undefined, requiredRoleId: number): boolean {
|
|
||||||
if (userRoleId == null || userRoleId < 1) return false;
|
|
||||||
return userRoleId <= requiredRoleId;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function canAccessAdmin(userRoleId: number | null | undefined): boolean {
|
|
||||||
return hasRole(userRoleId, ROLES.STAFF);
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
const email = z.string().email('Invalid email address');
|
|
||||||
const phone = z
|
|
||||||
.string()
|
|
||||||
.min(10, 'Phone number must be at least 10 digits')
|
|
||||||
.regex(/^[\d\-\+\(\) ]+$/, 'Invalid phone number format');
|
|
||||||
const name = z.string().min(1, 'Required').max(100, 'Too long');
|
|
||||||
|
|
||||||
export const UserRole = z.object({
|
|
||||||
name: z.string().min(1, 'Required')
|
|
||||||
});
|
|
||||||
|
|
||||||
export const User = z.object({
|
|
||||||
name: z.string().min(1, 'Required'),
|
|
||||||
email,
|
|
||||||
roleId: z.coerce.number().int().min(1)
|
|
||||||
});
|
|
||||||
|
|
||||||
export const Profile = z.object({
|
|
||||||
userId: z.string().min(1, 'Required'),
|
|
||||||
firstName: name,
|
|
||||||
lastName: name,
|
|
||||||
phoneNumber: phone
|
|
||||||
});
|
|
||||||
|
|
||||||
export const Address = z.object({
|
|
||||||
profileId: z.string().min(1),
|
|
||||||
street: z.string().min(1, 'Street is required'),
|
|
||||||
city: z.string().min(1, 'City is required'),
|
|
||||||
state: z.string().min(1, 'State is required'),
|
|
||||||
postalCode: z.string().min(1, 'Postal code is required'),
|
|
||||||
country: z.string().min(1, 'Country is required'),
|
|
||||||
type: z.enum(['Mailing', 'Billing', 'Shipping']).default('Mailing'),
|
|
||||||
isDefaultMailing: z.coerce.boolean().default(false),
|
|
||||||
isDefaultBilling: z.coerce.boolean().default(false),
|
|
||||||
isDefaultShipping: z.coerce.boolean().default(false)
|
|
||||||
});
|
|
||||||
|
|
||||||
export const SolfeggioFrequency = z.object({
|
|
||||||
name: z.string().min(1, 'Required'),
|
|
||||||
value: z.coerce.number().int('Must be a whole number')
|
|
||||||
});
|
|
||||||
|
|
||||||
export const SubscriptionLevel = z.object({
|
|
||||||
name: z.string().min(1, 'Required'),
|
|
||||||
description: z.string().optional().default('')
|
|
||||||
});
|
|
||||||
|
|
||||||
export const ProclamationCategory = z.object({
|
|
||||||
name: z.string().min(1, 'Required'),
|
|
||||||
parentId: z.coerce.number().int().optional()
|
|
||||||
});
|
|
||||||
|
|
||||||
export const Proclamation = z.object({
|
|
||||||
name: z.string().min(1, 'Required'),
|
|
||||||
solfeggioFrequencyId: z.coerce.number().int(),
|
|
||||||
parentCategoryId: z.coerce.number().int(),
|
|
||||||
categoryId: z.coerce.number().int().optional(),
|
|
||||||
subscriptionLevelId: z.coerce.number().int(),
|
|
||||||
content: z.string().optional().default('')
|
|
||||||
});
|
|
||||||
|
|
||||||
export const LicenseKey = z.object({
|
|
||||||
active: z.coerce.boolean(),
|
|
||||||
level: z.string().min(1, 'Required'),
|
|
||||||
userId: z.string().min(1, 'Required')
|
|
||||||
});
|
|
||||||
|
|
||||||
export const UserUpdate = z.object({
|
|
||||||
id: z.string().min(1),
|
|
||||||
name: z.string().min(1, 'Required').optional(),
|
|
||||||
email: z.string().email().optional()
|
|
||||||
});
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
export type PrismaScalarType = 'String' | 'Int' | 'Float' | 'Boolean' | 'DateTime' | 'Tiptap';
|
|
||||||
|
|
||||||
export type RelationOptionConfig = {
|
|
||||||
model: string;
|
|
||||||
labelField: string;
|
|
||||||
valueField: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type AdminField = {
|
|
||||||
name: string;
|
|
||||||
label?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type RelatedRecordConfig = {
|
|
||||||
relationKey: string;
|
|
||||||
label: string;
|
|
||||||
type: 'has-one' | 'has-many';
|
|
||||||
displayFields: string[];
|
|
||||||
editSlug?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ActionResult = {
|
|
||||||
success: boolean;
|
|
||||||
error?: string;
|
|
||||||
fieldErrors?: Record<string, string>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type AdminModelConfig = {
|
|
||||||
slug: string;
|
|
||||||
modelName: string;
|
|
||||||
displayName: string;
|
|
||||||
displayNameSingular: string;
|
|
||||||
idType: 'String' | 'Int';
|
|
||||||
include?: Record<string, boolean | { include: Record<string, boolean> }>;
|
|
||||||
listFields: string[];
|
|
||||||
searchFields: string[];
|
|
||||||
formFields: string[];
|
|
||||||
readonlyFields?: string[];
|
|
||||||
listOnly?: boolean;
|
|
||||||
fieldTypes: Record<string, PrismaScalarType>;
|
|
||||||
relationOptions?: Record<string, RelationOptionConfig>;
|
|
||||||
relatedRecords?: RelatedRecordConfig[];
|
|
||||||
accessFilter?: (viewerRoleId: number) => Record<string, unknown> | undefined;
|
|
||||||
/** Fields that should be excluded from Prisma create/update (virtual/UI-only) */
|
|
||||||
dataOnlyFields?: string[];
|
|
||||||
cascadeFields?: Record<string, { parentField: string }>;
|
|
||||||
};
|
|
||||||
+16
-4
@@ -51,6 +51,7 @@ export function mockUserWithProfile(overrides?: Record<string, unknown>) {
|
|||||||
lastName: 'Doe',
|
lastName: 'Doe',
|
||||||
phoneNumber: '555-0100'
|
phoneNumber: '555-0100'
|
||||||
},
|
},
|
||||||
|
profiles: [{ id: 'profile-1', userId: 'user-1' }],
|
||||||
...overrides
|
...overrides
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -76,6 +77,7 @@ export function mockUserWithProfileAndAddresses(overrides?: Record<string, unkno
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
profiles: [{ id: 'profile-1', userId: 'user-1' }],
|
||||||
...overrides
|
...overrides
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -86,6 +88,8 @@ export function mockLicenseKey(overrides?: Record<string, unknown>) {
|
|||||||
id: 'key-1',
|
id: 'key-1',
|
||||||
key: 'AAAA-BBBB-CCCC-DDDD',
|
key: 'AAAA-BBBB-CCCC-DDDD',
|
||||||
active: true,
|
active: true,
|
||||||
|
createdAt: new Date('2026-01-01T00:00:00.000Z'),
|
||||||
|
lastValidatedAt: null,
|
||||||
level: 'standard',
|
level: 'standard',
|
||||||
userId: 'user-1',
|
userId: 'user-1',
|
||||||
...overrides
|
...overrides
|
||||||
@@ -100,6 +104,7 @@ export function mockProclamation(overrides?: Record<string, unknown>) {
|
|||||||
content: '<p>Test content</p>',
|
content: '<p>Test content</p>',
|
||||||
solfeggioFrequencyId: 1,
|
solfeggioFrequencyId: 1,
|
||||||
categoryId: 1,
|
categoryId: 1,
|
||||||
|
groupId: 1,
|
||||||
price: 0.99,
|
price: 0.99,
|
||||||
solfeggioFrequency: {
|
solfeggioFrequency: {
|
||||||
id: 1,
|
id: 1,
|
||||||
@@ -108,9 +113,7 @@ export function mockProclamation(overrides?: Record<string, unknown>) {
|
|||||||
},
|
},
|
||||||
category: {
|
category: {
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Healing',
|
name: 'Healing'
|
||||||
parentId: null,
|
|
||||||
parent: null
|
|
||||||
},
|
},
|
||||||
...overrides
|
...overrides
|
||||||
};
|
};
|
||||||
@@ -121,7 +124,16 @@ export function mockCategory(overrides?: Record<string, unknown>) {
|
|||||||
return {
|
return {
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Healing',
|
name: 'Healing',
|
||||||
parentId: null,
|
...overrides
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Minimal proclamation group. */
|
||||||
|
export function mockGroup(overrides?: Record<string, unknown>) {
|
||||||
|
return {
|
||||||
|
id: 1,
|
||||||
|
name: 'General',
|
||||||
|
sortOrder: 0,
|
||||||
...overrides
|
...overrides
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,96 +0,0 @@
|
|||||||
import type { PageServerLoad } from './$types';
|
|
||||||
import { prisma } from '$lib/db';
|
|
||||||
import { error, fail, type Actions } from '@sveltejs/kit';
|
|
||||||
import { serverFormatPhoneNumber } from '$lib/utils';
|
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ locals }) => {
|
|
||||||
if (!locals.user?.id) {
|
|
||||||
throw error(403, 'not authorized');
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await prisma.user.findUnique({
|
|
||||||
where: { id: locals.user.id },
|
|
||||||
include: {
|
|
||||||
profile: {
|
|
||||||
include: { addresses: true }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
throw error(403, 'not authorized');
|
|
||||||
}
|
|
||||||
|
|
||||||
return { user };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const actions: Actions = {
|
|
||||||
updateProfile: async ({ request, locals }) => {
|
|
||||||
if (!locals.user?.id) {
|
|
||||||
throw error(403, 'not authorized');
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await request.formData();
|
|
||||||
const firstName = data.get('firstName') as string;
|
|
||||||
const lastName = data.get('lastName') as string;
|
|
||||||
const phoneNumber = serverFormatPhoneNumber(data.get('phoneNumber') as string);
|
|
||||||
|
|
||||||
if (!firstName || !lastName || !phoneNumber) {
|
|
||||||
return fail(400, { error: 'All fields are required.', action: 'updateProfile' });
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await prisma.profile.update({
|
|
||||||
where: { userId: locals.user.id },
|
|
||||||
data: { firstName, lastName, phoneNumber }
|
|
||||||
});
|
|
||||||
return { success: true, action: 'updateProfile' };
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Profile update error:', err);
|
|
||||||
return fail(500, { error: 'Failed to update profile.', action: 'updateProfile' });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
addAddress: async ({ request, locals }) => {
|
|
||||||
if (!locals.user?.id) {
|
|
||||||
throw error(403, 'not authorized');
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await request.formData();
|
|
||||||
const street = data.get('street') as string;
|
|
||||||
const city = data.get('city') as string;
|
|
||||||
const state = data.get('state') as string;
|
|
||||||
const postalCode = data.get('postalCode') as string;
|
|
||||||
const country = data.get('country') as string;
|
|
||||||
|
|
||||||
if (!street || !city || !state || !postalCode || !country) {
|
|
||||||
return fail(400, { error: 'All address fields are required.', action: 'addAddress' });
|
|
||||||
}
|
|
||||||
|
|
||||||
const profile = await prisma.profile.findUnique({
|
|
||||||
where: { userId: locals.user.id },
|
|
||||||
select: { id: true }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!profile) {
|
|
||||||
throw error(400, 'Profile not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await prisma.address.create({
|
|
||||||
data: {
|
|
||||||
profileId: profile.id,
|
|
||||||
street,
|
|
||||||
city,
|
|
||||||
state,
|
|
||||||
postalCode,
|
|
||||||
country
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return { success: true, action: 'addAddress' };
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Address create error:', err);
|
|
||||||
return fail(500, { error: 'Failed to add address.', action: 'addAddress' });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,243 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { clientFormatPhoneNumber } from '$lib/utils';
|
|
||||||
import type { PageProps } from './$types';
|
|
||||||
|
|
||||||
let { data, form }: PageProps = $props();
|
|
||||||
|
|
||||||
let initialPhone = $derived(clientFormatPhoneNumber(data.user.profile?.phoneNumber ?? ''));
|
|
||||||
let phoneNumber = $state('');
|
|
||||||
let phoneInitialized = $state(false);
|
|
||||||
|
|
||||||
$effect(() => {
|
|
||||||
if (!phoneInitialized) {
|
|
||||||
phoneNumber = initialPhone;
|
|
||||||
phoneInitialized = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let showAddressForm = $state(false);
|
|
||||||
|
|
||||||
function handlePhoneInput(event: Event) {
|
|
||||||
const input = event.target as HTMLInputElement;
|
|
||||||
phoneNumber = clientFormatPhoneNumber(input.value);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="flex h-full min-h-0 flex-col gap-8 p-6 dark:text-gray-100">
|
|
||||||
<h1 class="text-2xl font-semibold text-gray-900 dark:text-gray-100">Account</h1>
|
|
||||||
|
|
||||||
<!-- Email (read-only) -->
|
|
||||||
<div>
|
|
||||||
<p class="text-sm font-medium text-gray-700 dark:text-gray-300">Email</p>
|
|
||||||
<p class="mt-1 text-gray-900 dark:text-gray-100">{data.user.email}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Profile Form -->
|
|
||||||
<section>
|
|
||||||
<h2 class="mb-4 text-lg font-medium text-gray-900 dark:text-gray-100">Profile</h2>
|
|
||||||
|
|
||||||
{#if form?.action === 'updateProfile' && form?.success}
|
|
||||||
<div
|
|
||||||
class="mb-4 rounded-lg border border-green-200 bg-green-50 p-3 text-green-700 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-300"
|
|
||||||
>
|
|
||||||
Profile updated successfully.
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{#if form?.action === 'updateProfile' && form?.error}
|
|
||||||
<div
|
|
||||||
class="mb-4 rounded-lg border border-red-200 bg-red-50 p-3 text-red-700 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-300"
|
|
||||||
>
|
|
||||||
{form.error}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<form method="POST" action="?/updateProfile" class="flex max-w-lg flex-col gap-4">
|
|
||||||
<div class="grid grid-cols-2 gap-4">
|
|
||||||
<div>
|
|
||||||
<label for="firstName" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
||||||
First Name
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="firstName"
|
|
||||||
name="firstName"
|
|
||||||
value={data.user.profile?.firstName ?? ''}
|
|
||||||
required
|
|
||||||
class="mt-1 w-full rounded-lg border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 dark:border-gray-600"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label for="lastName" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
||||||
Last Name
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="lastName"
|
|
||||||
name="lastName"
|
|
||||||
value={data.user.profile?.lastName ?? ''}
|
|
||||||
required
|
|
||||||
class="mt-1 w-full rounded-lg border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 dark:border-gray-600"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label for="phoneNumber" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
||||||
Phone Number
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="tel"
|
|
||||||
id="phoneNumber"
|
|
||||||
name="phoneNumber"
|
|
||||||
bind:value={phoneNumber}
|
|
||||||
oninput={handlePhoneInput}
|
|
||||||
required
|
|
||||||
class="mt-1 w-full max-w-xs rounded-lg border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 dark:border-gray-600"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
class="rounded-lg bg-blue-600 px-4 py-2 font-medium text-white transition-colors hover:bg-blue-700 dark:bg-gray-700 dark:text-gray-100 dark:hover:bg-gray-600"
|
|
||||||
>
|
|
||||||
Save Profile
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Addresses -->
|
|
||||||
<section>
|
|
||||||
<div class="mb-4 flex items-center justify-between">
|
|
||||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">Addresses</h2>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="rounded-lg bg-green-600 px-3 py-1.5 text-sm font-medium text-white transition-colors hover:bg-green-700 dark:bg-gray-700 dark:text-gray-100 dark:hover:bg-gray-600"
|
|
||||||
onclick={() => (showAddressForm = !showAddressForm)}
|
|
||||||
>
|
|
||||||
{showAddressForm ? 'Cancel' : 'Add Address'}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if form?.action === 'addAddress' && form?.success}
|
|
||||||
<div
|
|
||||||
class="mb-4 rounded-lg border border-green-200 bg-green-50 p-3 text-green-700 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-300"
|
|
||||||
>
|
|
||||||
Address added successfully.
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{#if form?.action === 'addAddress' && form?.error}
|
|
||||||
<div
|
|
||||||
class="mb-4 rounded-lg border border-red-200 bg-red-50 p-3 text-red-700 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-300"
|
|
||||||
>
|
|
||||||
{form.error}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if showAddressForm}
|
|
||||||
<form
|
|
||||||
method="POST"
|
|
||||||
action="?/addAddress"
|
|
||||||
class="mb-6 max-w-lg rounded-lg border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-800"
|
|
||||||
>
|
|
||||||
<div class="flex flex-col gap-3">
|
|
||||||
<div>
|
|
||||||
<label for="street" class="block text-sm font-medium text-gray-700 dark:text-gray-300"
|
|
||||||
>Street</label
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="street"
|
|
||||||
name="street"
|
|
||||||
required
|
|
||||||
class="mt-1 w-full rounded-lg border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 dark:border-gray-600"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="grid grid-cols-2 gap-3">
|
|
||||||
<div>
|
|
||||||
<label for="city" class="block text-sm font-medium text-gray-700 dark:text-gray-300"
|
|
||||||
>City</label
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="city"
|
|
||||||
name="city"
|
|
||||||
required
|
|
||||||
class="mt-1 w-full rounded-lg border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 dark:border-gray-600"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label for="state" class="block text-sm font-medium text-gray-700 dark:text-gray-300"
|
|
||||||
>State</label
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="state"
|
|
||||||
name="state"
|
|
||||||
required
|
|
||||||
class="mt-1 w-full rounded-lg border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 dark:border-gray-600"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="grid grid-cols-2 gap-3">
|
|
||||||
<div>
|
|
||||||
<label
|
|
||||||
for="postalCode"
|
|
||||||
class="block text-sm font-medium text-gray-700 dark:text-gray-300"
|
|
||||||
>
|
|
||||||
Postal Code
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="postalCode"
|
|
||||||
name="postalCode"
|
|
||||||
required
|
|
||||||
class="mt-1 w-full rounded-lg border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 dark:border-gray-600"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label
|
|
||||||
for="country"
|
|
||||||
class="block text-sm font-medium text-gray-700 dark:text-gray-300">Country</label
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="country"
|
|
||||||
name="country"
|
|
||||||
required
|
|
||||||
value="US"
|
|
||||||
class="mt-1 w-full rounded-lg border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 dark:border-gray-600"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
class="rounded-lg bg-blue-600 px-4 py-2 font-medium text-white transition-colors hover:bg-blue-700 dark:bg-gray-700 dark:text-gray-100 dark:hover:bg-gray-600"
|
|
||||||
>
|
|
||||||
Save Address
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if data.user.profile?.addresses && data.user.profile.addresses.length > 0}
|
|
||||||
<div class="flex flex-col gap-3">
|
|
||||||
{#each data.user.profile.addresses as address}
|
|
||||||
<div
|
|
||||||
class="rounded-lg border border-gray-200 bg-white p-4 dark:border-gray-700 dark:bg-gray-900"
|
|
||||||
>
|
|
||||||
<p class="text-sm text-gray-900 dark:text-gray-100">{address.street}</p>
|
|
||||||
<p class="text-sm text-gray-600 dark:text-gray-400">
|
|
||||||
{address.city}, {address.state}
|
|
||||||
{address.postalCode}
|
|
||||||
</p>
|
|
||||||
<p class="text-sm text-gray-600 dark:text-gray-400">{address.country}</p>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
{:else if !showAddressForm}
|
|
||||||
<p class="text-sm text-gray-500 dark:text-gray-400">No addresses on file.</p>
|
|
||||||
{/if}
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
<!--
|
|
||||||
There should be a form in the middle
|
|
||||||
rounded corners
|
|
||||||
floats above the screen slightly
|
|
||||||
Profile picture in the center at the top, about 1/3-1/2 the width of the form
|
|
||||||
Their email address, not editable, instead is a regular sized black text (or white in dark mode)
|
|
||||||
Input field for their Phone Number
|
|
||||||
Two inputs, side by side, a little margin between them. First and Last name
|
|
||||||
A submit button
|
|
||||||
Save the form then redirect them to their dashboard
|
|
||||||
-->
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import Window from '$lib/components/containers/Window.svelte';
|
|
||||||
import { clientFormatPhoneNumber } from '$lib/utils';
|
|
||||||
import type { PageProps } from './$types';
|
|
||||||
|
|
||||||
let { data }: PageProps = $props();
|
|
||||||
|
|
||||||
let firstName = $state('');
|
|
||||||
let lastName = $state('');
|
|
||||||
|
|
||||||
function splitName(fullName: string | null | undefined): { first: string; last: string } {
|
|
||||||
if (!fullName) return { first: '', last: '' };
|
|
||||||
const parts = fullName.trim().split(/\s+/);
|
|
||||||
return { first: parts[0] ?? '', last: parts.slice(1).join(' ') };
|
|
||||||
}
|
|
||||||
|
|
||||||
$effect(() => {
|
|
||||||
if (data.user?.name && !firstName) {
|
|
||||||
const names = splitName(data.user.name);
|
|
||||||
firstName = names.first;
|
|
||||||
lastName = names.last;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let phoneNumber = $state('');
|
|
||||||
|
|
||||||
function handlePhoneInput(event: Event) {
|
|
||||||
const input = event.target as HTMLInputElement;
|
|
||||||
phoneNumber = clientFormatPhoneNumber(input.value);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Window>
|
|
||||||
<form method="POST" class="border p-6 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-100">
|
|
||||||
<p class="mx-auto my-4 text-center italic">Please finish setting up your profile</p>
|
|
||||||
{#if data.user?.image}
|
|
||||||
<img src={data.user?.image} class="mx-auto w-[8rem] rounded-full" alt="Profile" />
|
|
||||||
{:else}
|
|
||||||
<div class="mx-auto aspect-square w-[8rem] rounded-full bg-slate-900"></div>
|
|
||||||
{/if}
|
|
||||||
<p class="mx-auto my-4 text-center">{data.user?.email}</p>
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<input type="text" name="userId" value={data.user?.id} class="hidden" />
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="firstName"
|
|
||||||
placeholder="First Name"
|
|
||||||
class="my-2"
|
|
||||||
bind:value={firstName}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="lastName"
|
|
||||||
placeholder="Last Name"
|
|
||||||
class="my-2"
|
|
||||||
bind:value={lastName}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="tel"
|
|
||||||
name="phoneNumber"
|
|
||||||
placeholder="Phone Number"
|
|
||||||
class="my-2"
|
|
||||||
bind:value={phoneNumber}
|
|
||||||
oninput={handlePhoneInput}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="bg-blue-300 p-2 px-4 dark:bg-gray-700 dark:text-gray-100"
|
|
||||||
>Submit</button
|
|
||||||
>
|
|
||||||
</form>
|
|
||||||
</Window>
|
|
||||||
@@ -7,27 +7,10 @@ export const load: PageServerLoad = async ({ locals }) => {
|
|||||||
throw error(403, 'not authorized');
|
throw error(403, 'not authorized');
|
||||||
}
|
}
|
||||||
|
|
||||||
const profile = await prisma.profile.findUnique({
|
const licenseKeys = await prisma.licenseKey.findMany({
|
||||||
where: { userId: locals.user.id },
|
where: { userId: locals.user.id },
|
||||||
select: { firstName: true }
|
orderBy: { createdAt: 'desc' }
|
||||||
});
|
});
|
||||||
|
|
||||||
const [keyStats, proclamationCount] = await Promise.all([
|
return { licenseKeys };
|
||||||
prisma.licenseKey.groupBy({
|
|
||||||
by: ['active'],
|
|
||||||
where: { userId: locals.user.id },
|
|
||||||
_count: true
|
|
||||||
}),
|
|
||||||
prisma.proclamation.count()
|
|
||||||
]);
|
|
||||||
|
|
||||||
const activeKeys = keyStats.find((s) => s.active)?._count ?? 0;
|
|
||||||
const totalKeys = keyStats.reduce((sum, s) => sum + s._count, 0);
|
|
||||||
|
|
||||||
return {
|
|
||||||
firstName: profile?.firstName ?? 'User',
|
|
||||||
activeKeys,
|
|
||||||
totalKeys,
|
|
||||||
proclamationCount
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,42 +1,127 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import DashboardSection from '$lib/components/DashboardSection.svelte';
|
||||||
import type { PageProps } from './$types';
|
import type { PageProps } from './$types';
|
||||||
|
|
||||||
let { data }: PageProps = $props();
|
let { data }: PageProps = $props();
|
||||||
|
|
||||||
|
function formatDate(value: Date | string): string {
|
||||||
|
const date = value instanceof Date ? value : new Date(value);
|
||||||
|
return date.toLocaleDateString(undefined, {
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric'
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex h-full min-h-0 flex-col gap-6 p-6 dark:text-gray-100">
|
<div>
|
||||||
<h1 class="text-2xl font-semibold text-gray-900 dark:text-gray-100">
|
<div>
|
||||||
Welcome, {data.firstName}!
|
<h1 class="text-2xl font-bold tracking-tight text-slate-900 sm:text-3xl dark:text-slate-100">
|
||||||
</h1>
|
Dashboard
|
||||||
|
</h1>
|
||||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-3">
|
<p class="mt-1 text-sm text-slate-500 sm:text-base dark:text-slate-400">
|
||||||
<a
|
Manage your account and license keys.
|
||||||
href="/license-keys"
|
</p>
|
||||||
class="rounded-lg border border-gray-200 bg-white p-6 shadow-sm transition-shadow hover:shadow-md dark:border-gray-700 dark:bg-gray-900"
|
|
||||||
>
|
|
||||||
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Active License Keys</p>
|
|
||||||
<p class="mt-2 text-3xl font-bold text-gray-900 dark:text-gray-100">{data.activeKeys}</p>
|
|
||||||
<p class="mt-1 text-sm text-gray-400 dark:text-gray-500">{data.totalKeys} total</p>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a
|
|
||||||
href="/admin/proclamations"
|
|
||||||
class="rounded-lg border border-gray-200 bg-white p-6 shadow-sm transition-shadow hover:shadow-md dark:border-gray-700 dark:bg-gray-900"
|
|
||||||
>
|
|
||||||
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Proclamations</p>
|
|
||||||
<p class="mt-2 text-3xl font-bold text-gray-900 dark:text-gray-100">
|
|
||||||
{data.proclamationCount}
|
|
||||||
</p>
|
|
||||||
<p class="mt-1 text-sm text-gray-400 dark:text-gray-500">Browse all</p>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a
|
|
||||||
href="/account"
|
|
||||||
class="rounded-lg border border-gray-200 bg-white p-6 shadow-sm transition-shadow hover:shadow-md dark:border-gray-700 dark:bg-gray-900"
|
|
||||||
>
|
|
||||||
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Account</p>
|
|
||||||
<p class="mt-2 text-lg font-semibold text-gray-900 dark:text-gray-100">Manage Profile</p>
|
|
||||||
<p class="mt-1 text-sm text-gray-400 dark:text-gray-500">Edit your details</p>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Dashboard sections. This page is the account management surface; personal
|
||||||
|
information (name, email, phone, addresses) lives on the Profile page.
|
||||||
|
Planned sections to add below as <DashboardSection> blocks:
|
||||||
|
- Schedules: create and manage listening schedules.
|
||||||
|
- Devices / playback.
|
||||||
|
- Billing / orders.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<DashboardSection title="License Keys" description="Your active and inactive license keys.">
|
||||||
|
{#if data.licenseKeys.length > 0}
|
||||||
|
<div
|
||||||
|
class="overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900"
|
||||||
|
>
|
||||||
|
<!-- Desktop / tablet: table -->
|
||||||
|
<table class="hidden w-full text-left text-sm sm:table">
|
||||||
|
<thead
|
||||||
|
class="border-b border-slate-200 bg-slate-50 text-xs tracking-wide text-slate-500 uppercase dark:border-slate-800 dark:bg-slate-950 dark:text-slate-400"
|
||||||
|
>
|
||||||
|
<tr>
|
||||||
|
<th class="px-6 py-3 font-semibold">Key</th>
|
||||||
|
<th class="px-6 py-3 font-semibold">Status</th>
|
||||||
|
<th class="px-6 py-3 font-semibold">Created</th>
|
||||||
|
<th class="px-6 py-3 font-semibold">Last verified</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="divide-y divide-slate-100">
|
||||||
|
{#each data.licenseKeys as key (key.key)}
|
||||||
|
<tr class="hover:bg-slate-50 dark:hover:bg-slate-800">
|
||||||
|
<td class="px-6 py-4 font-mono text-slate-800 dark:text-slate-200">{key.key}</td>
|
||||||
|
<td class="px-6 py-4">
|
||||||
|
{#if key.active}
|
||||||
|
<span
|
||||||
|
class="text-lg text-green-600 dark:text-green-400"
|
||||||
|
title="Active"
|
||||||
|
aria-label="Active">☑</span
|
||||||
|
>
|
||||||
|
{:else}
|
||||||
|
<span
|
||||||
|
class="text-lg text-slate-300 dark:text-slate-600"
|
||||||
|
title="Inactive"
|
||||||
|
aria-label="Inactive">☐</span
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
</td>
|
||||||
|
<td class="px-6 py-4 text-slate-600 dark:text-slate-400"
|
||||||
|
>{formatDate(key.createdAt)}</td
|
||||||
|
>
|
||||||
|
<td class="px-6 py-4 text-slate-600 dark:text-slate-400">
|
||||||
|
{key.lastValidatedAt ? formatDate(key.lastValidatedAt) : 'Never'}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- Mobile: card list -->
|
||||||
|
<ul class="divide-y divide-slate-100 sm:hidden">
|
||||||
|
{#each data.licenseKeys as key (key.key)}
|
||||||
|
<li class="flex flex-col gap-2 px-4 py-4">
|
||||||
|
<div class="flex items-center justify-between gap-4">
|
||||||
|
<span class="min-w-0 font-mono text-sm break-all text-slate-800 dark:text-slate-200"
|
||||||
|
>{key.key}</span
|
||||||
|
>
|
||||||
|
<span class="shrink-0">
|
||||||
|
{#if key.active}
|
||||||
|
<span
|
||||||
|
class="text-lg text-green-600 dark:text-green-400"
|
||||||
|
title="Active"
|
||||||
|
aria-label="Active">☑</span
|
||||||
|
>
|
||||||
|
{:else}
|
||||||
|
<span
|
||||||
|
class="text-lg text-slate-300 dark:text-slate-600"
|
||||||
|
title="Inactive"
|
||||||
|
aria-label="Inactive">☐</span
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span class="text-xs text-slate-500 dark:text-slate-400"
|
||||||
|
>Created {formatDate(key.createdAt)}</span
|
||||||
|
>
|
||||||
|
<span class="text-xs text-slate-500 dark:text-slate-400">
|
||||||
|
Last verified {key.lastValidatedAt ? formatDate(key.lastValidatedAt) : 'Never'}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div
|
||||||
|
class="rounded-2xl border border-dashed border-slate-300 bg-white p-8 text-center dark:border-slate-700 dark:bg-slate-900"
|
||||||
|
>
|
||||||
|
<p class="text-sm text-slate-600 dark:text-slate-400">
|
||||||
|
You don't have any license keys yet.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</DashboardSection>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,27 +1,21 @@
|
|||||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||||
import { isHttpError } from '@sveltejs/kit';
|
import { isHttpError } from '@sveltejs/kit';
|
||||||
import { createLocals, mockUser } from '$lib/test-utils';
|
import { createLocals, mockUser, mockLicenseKey } from '$lib/test-utils';
|
||||||
|
|
||||||
vi.mock('$lib/db', () => ({
|
vi.mock('$lib/db', () => ({
|
||||||
prisma: {
|
prisma: {
|
||||||
profile: { findUnique: vi.fn() },
|
licenseKey: { findMany: vi.fn() }
|
||||||
licenseKey: { groupBy: vi.fn() },
|
|
||||||
proclamation: { count: vi.fn() }
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import { prisma } from '$lib/db';
|
import { prisma } from '$lib/db';
|
||||||
import { load } from './+page.server';
|
import { load } from './+page.server';
|
||||||
|
|
||||||
const profileFindUnique = prisma.profile.findUnique as ReturnType<typeof vi.fn>;
|
const licenseKeyFindMany = prisma.licenseKey.findMany as ReturnType<typeof vi.fn>;
|
||||||
const licenseKeyGroupBy = prisma.licenseKey.groupBy as ReturnType<typeof vi.fn>;
|
|
||||||
const proclamationCount = prisma.proclamation.count as ReturnType<typeof vi.fn>;
|
|
||||||
|
|
||||||
describe('(protected)/dashboard page server load', () => {
|
describe('(protected)/dashboard page server load', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
profileFindUnique.mockReset();
|
licenseKeyFindMany.mockReset();
|
||||||
licenseKeyGroupBy.mockReset();
|
|
||||||
proclamationCount.mockReset();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws 403 when locals.user is undefined', async () => {
|
it('throws 403 when locals.user is undefined', async () => {
|
||||||
@@ -31,58 +25,35 @@ describe('(protected)/dashboard page server load', () => {
|
|||||||
expect(isHttpError(err, 403)).toBe(true);
|
expect(isHttpError(err, 403)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns firstName from profile', async () => {
|
it('returns license keys for the authenticated user', async () => {
|
||||||
profileFindUnique.mockResolvedValue({ firstName: 'Jane' });
|
const keys = [
|
||||||
licenseKeyGroupBy.mockResolvedValue([]);
|
mockLicenseKey(),
|
||||||
proclamationCount.mockResolvedValue(0);
|
mockLicenseKey({ id: 'key-2', key: 'EEEE-FFFF-GGGG-HHHH', active: false })
|
||||||
|
];
|
||||||
|
licenseKeyFindMany.mockResolvedValue(keys);
|
||||||
|
|
||||||
const locals = createLocals(mockUser() as any);
|
const locals = createLocals(mockUser() as any);
|
||||||
const result = (await load({ locals } as any)) as any;
|
const result = (await load({ locals } as any)) as any;
|
||||||
expect(result.firstName).toBe('Jane');
|
expect(result.licenseKeys).toEqual(keys);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('defaults firstName to "User" when profile is null', async () => {
|
it('returns empty array when user has no keys', async () => {
|
||||||
profileFindUnique.mockResolvedValue(null);
|
licenseKeyFindMany.mockResolvedValue([]);
|
||||||
licenseKeyGroupBy.mockResolvedValue([]);
|
|
||||||
proclamationCount.mockResolvedValue(0);
|
|
||||||
|
|
||||||
const locals = createLocals(mockUser() as any);
|
const locals = createLocals(mockUser() as any);
|
||||||
const result = (await load({ locals } as any)) as any;
|
const result = (await load({ locals } as any)) as any;
|
||||||
expect(result.firstName).toBe('User');
|
expect(result.licenseKeys).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('computes activeKeys and totalKeys from groupBy results', async () => {
|
it('loads license keys filtered by user, newest first', async () => {
|
||||||
profileFindUnique.mockResolvedValue({ firstName: 'Jane' });
|
licenseKeyFindMany.mockResolvedValue([]);
|
||||||
licenseKeyGroupBy.mockResolvedValue([
|
|
||||||
{ active: true, _count: 3 },
|
|
||||||
{ active: false, _count: 2 }
|
|
||||||
]);
|
|
||||||
proclamationCount.mockResolvedValue(10);
|
|
||||||
|
|
||||||
const locals = createLocals(mockUser() as any);
|
const locals = createLocals(mockUser() as any);
|
||||||
const result = (await load({ locals } as any)) as any;
|
await load({ locals } as any);
|
||||||
expect(result.activeKeys).toBe(3);
|
|
||||||
expect(result.totalKeys).toBe(5);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns 0 for activeKeys when none are active', async () => {
|
expect(licenseKeyFindMany).toHaveBeenCalledWith({
|
||||||
profileFindUnique.mockResolvedValue({ firstName: 'Jane' });
|
where: { userId: 'user-1' },
|
||||||
licenseKeyGroupBy.mockResolvedValue([{ active: false, _count: 4 }]);
|
orderBy: { createdAt: 'desc' }
|
||||||
proclamationCount.mockResolvedValue(0);
|
});
|
||||||
|
|
||||||
const locals = createLocals(mockUser() as any);
|
|
||||||
const result = (await load({ locals } as any)) as any;
|
|
||||||
expect(result.activeKeys).toBe(0);
|
|
||||||
expect(result.totalKeys).toBe(4);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns proclamationCount', async () => {
|
|
||||||
profileFindUnique.mockResolvedValue({ firstName: 'Jane' });
|
|
||||||
licenseKeyGroupBy.mockResolvedValue([]);
|
|
||||||
proclamationCount.mockResolvedValue(42);
|
|
||||||
|
|
||||||
const locals = createLocals(mockUser() as any);
|
|
||||||
const result = (await load({ locals } as any)) as any;
|
|
||||||
expect(result.proclamationCount).toBe(42);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
import type { PageServerLoad } from './$types';
|
|
||||||
import { prisma } from '$lib/db';
|
|
||||||
import { error } from '@sveltejs/kit';
|
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ locals }) => {
|
|
||||||
if (!locals.user?.id) {
|
|
||||||
throw error(403, 'not authorized');
|
|
||||||
}
|
|
||||||
|
|
||||||
const licenseKeys = await prisma.licenseKey.findMany({
|
|
||||||
where: { userId: locals.user.id },
|
|
||||||
orderBy: { key: 'asc' }
|
|
||||||
});
|
|
||||||
|
|
||||||
return { licenseKeys };
|
|
||||||
};
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import type { PageProps } from './$types';
|
|
||||||
|
|
||||||
let { data }: PageProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="flex h-full min-h-0 flex-col gap-6 p-6 dark:text-gray-100">
|
|
||||||
<h1 class="text-2xl font-semibold text-gray-900 dark:text-gray-100">License Keys</h1>
|
|
||||||
|
|
||||||
{#if data.licenseKeys.length > 0}
|
|
||||||
<div class="overflow-x-auto rounded-lg border border-gray-200 dark:border-gray-700">
|
|
||||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
||||||
<thead class="bg-gray-50 dark:bg-gray-800">
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
class="px-6 py-3 text-left text-xs font-medium tracking-wider text-gray-500 uppercase dark:text-gray-400"
|
|
||||||
>
|
|
||||||
Key
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
class="px-6 py-3 text-left text-xs font-medium tracking-wider text-gray-500 uppercase dark:text-gray-400"
|
|
||||||
>
|
|
||||||
Level
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
class="px-6 py-3 text-left text-xs font-medium tracking-wider text-gray-500 uppercase dark:text-gray-400"
|
|
||||||
>
|
|
||||||
Status
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody class="divide-y divide-gray-200 bg-white dark:divide-gray-700 dark:bg-gray-900">
|
|
||||||
{#each data.licenseKeys as key}
|
|
||||||
<tr>
|
|
||||||
<td
|
|
||||||
class="px-6 py-4 font-mono text-sm whitespace-nowrap text-gray-900 dark:text-gray-100"
|
|
||||||
>
|
|
||||||
{key.key}
|
|
||||||
</td>
|
|
||||||
<td class="px-6 py-4 text-sm whitespace-nowrap text-gray-700 dark:text-gray-300">
|
|
||||||
{key.level}
|
|
||||||
</td>
|
|
||||||
<td class="px-6 py-4 text-sm whitespace-nowrap">
|
|
||||||
<span
|
|
||||||
class="inline-flex rounded-full px-2 text-xs leading-5 font-semibold {key.active
|
|
||||||
? 'bg-green-100 text-green-800 dark:bg-gray-700 dark:text-gray-200'
|
|
||||||
: 'bg-red-100 text-red-800 dark:bg-gray-700 dark:text-gray-200'}"
|
|
||||||
>
|
|
||||||
{key.active ? 'Active' : 'Inactive'}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/each}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<div class="flex flex-1 items-center justify-center">
|
|
||||||
<p class="text-lg text-gray-500 dark:text-gray-400">You don't have any license keys yet.</p>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
||||||
import { isHttpError } from '@sveltejs/kit';
|
|
||||||
import { createLocals, mockUser, mockLicenseKey } from '$lib/test-utils';
|
|
||||||
|
|
||||||
vi.mock('$lib/db', () => ({
|
|
||||||
prisma: {
|
|
||||||
licenseKey: { findMany: vi.fn() }
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
import { prisma } from '$lib/db';
|
|
||||||
import { load } from './+page.server';
|
|
||||||
|
|
||||||
const findMany = prisma.licenseKey.findMany as ReturnType<typeof vi.fn>;
|
|
||||||
|
|
||||||
describe('(protected)/license-keys page server load', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
findMany.mockReset();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws 403 when locals.user is undefined', async () => {
|
|
||||||
const err = await Promise.resolve(load({ locals: createLocals() } as any)).catch(
|
|
||||||
(e: unknown) => e
|
|
||||||
);
|
|
||||||
expect(isHttpError(err, 403)).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns license keys for the authenticated user', async () => {
|
|
||||||
const keys = [
|
|
||||||
mockLicenseKey(),
|
|
||||||
mockLicenseKey({ id: 'key-2', key: 'EEEE-FFFF-GGGG-HHHH', active: false })
|
|
||||||
];
|
|
||||||
findMany.mockResolvedValue(keys);
|
|
||||||
|
|
||||||
const locals = createLocals(mockUser() as any);
|
|
||||||
const result = (await load({ locals } as any)) as any;
|
|
||||||
expect(result.licenseKeys).toEqual(keys);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('passes userId in the where clause', async () => {
|
|
||||||
findMany.mockResolvedValue([]);
|
|
||||||
|
|
||||||
const locals = createLocals(mockUser() as any);
|
|
||||||
await load({ locals } as any);
|
|
||||||
|
|
||||||
expect(findMany).toHaveBeenCalledWith({
|
|
||||||
where: { userId: 'user-1' },
|
|
||||||
orderBy: { key: 'asc' }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns empty array when user has no keys', async () => {
|
|
||||||
findMany.mockResolvedValue([]);
|
|
||||||
|
|
||||||
const locals = createLocals(mockUser() as any);
|
|
||||||
const result = (await load({ locals } as any)) as any;
|
|
||||||
expect(result.licenseKeys).toEqual([]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
import type { PageServerLoad } from './$types';
|
||||||
|
import { prisma } from '$lib/db';
|
||||||
|
import { error, fail, type Actions } from '@sveltejs/kit';
|
||||||
|
import { serverFormatPhoneNumber } from '$lib/utils';
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async ({ locals }) => {
|
||||||
|
if (!locals.user?.id) {
|
||||||
|
throw error(403, 'not authorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await prisma.user.findUnique({ where: { id: locals.user.id } });
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw error(403, 'not authorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
const profile = await prisma.profile.findFirst({
|
||||||
|
where: { userId: user.id },
|
||||||
|
include: { addresses: true }
|
||||||
|
});
|
||||||
|
|
||||||
|
return { user: { ...user, profile } };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const actions: Actions = {
|
||||||
|
updateProfile: async ({ request, locals }) => {
|
||||||
|
if (!locals.user?.id) {
|
||||||
|
throw error(403, 'not authorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await request.formData();
|
||||||
|
const firstName = data.get('firstName') as string;
|
||||||
|
const lastName = data.get('lastName') as string;
|
||||||
|
const phoneNumber = serverFormatPhoneNumber(data.get('phoneNumber') as string);
|
||||||
|
|
||||||
|
if (!firstName || !lastName || !phoneNumber) {
|
||||||
|
return fail(400, { error: 'All fields are required.', action: 'updateProfile' });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await prisma.profile.updateMany({
|
||||||
|
where: { userId: locals.user.id },
|
||||||
|
data: { firstName, lastName, phoneNumber }
|
||||||
|
});
|
||||||
|
return { success: true, action: 'updateProfile' };
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Profile update error:', err);
|
||||||
|
return fail(500, { error: 'Failed to update profile.', action: 'updateProfile' });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
addAddress: async ({ request, locals }) => {
|
||||||
|
if (!locals.user?.id) {
|
||||||
|
throw error(403, 'not authorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await request.formData();
|
||||||
|
const street = data.get('street') as string;
|
||||||
|
const city = data.get('city') as string;
|
||||||
|
const state = data.get('state') as string;
|
||||||
|
const postalCode = data.get('postalCode') as string;
|
||||||
|
const country = data.get('country') as string;
|
||||||
|
|
||||||
|
if (!street || !city || !state || !postalCode || !country) {
|
||||||
|
return fail(400, { error: 'All address fields are required.', action: 'addAddress' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const profile = await prisma.profile.findFirst({
|
||||||
|
where: { userId: locals.user.id },
|
||||||
|
select: { id: true }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!profile) {
|
||||||
|
throw error(400, 'Profile not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await prisma.address.create({
|
||||||
|
data: {
|
||||||
|
profileId: profile.id,
|
||||||
|
street,
|
||||||
|
city,
|
||||||
|
state,
|
||||||
|
postalCode,
|
||||||
|
country
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return { success: true, action: 'addAddress' };
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Address create error:', err);
|
||||||
|
return fail(500, { error: 'Failed to add address.', action: 'addAddress' });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updateAddress: async ({ request, locals }) => {
|
||||||
|
if (!locals.user?.id) {
|
||||||
|
throw error(403, 'not authorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await request.formData();
|
||||||
|
const id = data.get('id') as string;
|
||||||
|
const street = data.get('street') as string;
|
||||||
|
const city = data.get('city') as string;
|
||||||
|
const state = data.get('state') as string;
|
||||||
|
const postalCode = data.get('postalCode') as string;
|
||||||
|
const country = data.get('country') as string;
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
return fail(400, { error: 'Missing address id.', action: 'updateAddress' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!street || !city || !state || !postalCode || !country) {
|
||||||
|
return fail(400, { error: 'All address fields are required.', action: 'updateAddress' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const address = await prisma.address.findFirst({
|
||||||
|
where: { id, profile: { userId: locals.user.id } },
|
||||||
|
select: { id: true }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!address) {
|
||||||
|
throw error(404, 'Address not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await prisma.address.update({
|
||||||
|
where: { id },
|
||||||
|
data: { street, city, state, postalCode, country }
|
||||||
|
});
|
||||||
|
return { success: true, action: 'updateAddress' };
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Address update error:', err);
|
||||||
|
return fail(500, { error: 'Failed to update address.', action: 'updateAddress' });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteAddress: async ({ request, locals }) => {
|
||||||
|
if (!locals.user?.id) {
|
||||||
|
throw error(403, 'not authorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await request.formData();
|
||||||
|
const id = data.get('id') as string;
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
return fail(400, { error: 'Missing address id.', action: 'deleteAddress' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const address = await prisma.address.findFirst({
|
||||||
|
where: { id, profile: { userId: locals.user.id } },
|
||||||
|
select: { id: true }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!address) {
|
||||||
|
throw error(404, 'Address not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await prisma.address.delete({ where: { id } });
|
||||||
|
return { success: true, action: 'deleteAddress' };
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Address delete error:', err);
|
||||||
|
return fail(500, { error: 'Failed to delete address.', action: 'deleteAddress' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,337 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { clientFormatPhoneNumber } from '$lib/utils';
|
||||||
|
import type { PageProps } from './$types';
|
||||||
|
|
||||||
|
let { data, form }: PageProps = $props();
|
||||||
|
|
||||||
|
let initialPhone = $derived(clientFormatPhoneNumber(data.user.profile?.phoneNumber ?? ''));
|
||||||
|
let phoneNumber = $state('');
|
||||||
|
let phoneInitialized = $state(false);
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if (!phoneInitialized) {
|
||||||
|
phoneNumber = initialPhone;
|
||||||
|
phoneInitialized = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let showAddressForm = $state(false);
|
||||||
|
let editingAddressId = $state<string | null>(null);
|
||||||
|
let addrStreet = $state('');
|
||||||
|
let addrCity = $state('');
|
||||||
|
let addrState = $state('');
|
||||||
|
let addrPostal = $state('');
|
||||||
|
let addrCountry = $state('US');
|
||||||
|
|
||||||
|
function openAddAddress() {
|
||||||
|
editingAddressId = null;
|
||||||
|
addrStreet = '';
|
||||||
|
addrCity = '';
|
||||||
|
addrState = '';
|
||||||
|
addrPostal = '';
|
||||||
|
addrCountry = 'US';
|
||||||
|
showAddressForm = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function openEditAddress(address: {
|
||||||
|
id: string;
|
||||||
|
street: string;
|
||||||
|
city: string;
|
||||||
|
state: string;
|
||||||
|
postalCode: string;
|
||||||
|
country: string;
|
||||||
|
}) {
|
||||||
|
editingAddressId = address.id;
|
||||||
|
addrStreet = address.street;
|
||||||
|
addrCity = address.city;
|
||||||
|
addrState = address.state;
|
||||||
|
addrPostal = address.postalCode;
|
||||||
|
addrCountry = address.country;
|
||||||
|
showAddressForm = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeAddressForm() {
|
||||||
|
showAddressForm = false;
|
||||||
|
editingAddressId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePhoneInput(event: Event) {
|
||||||
|
const input = event.target as HTMLInputElement;
|
||||||
|
phoneNumber = clientFormatPhoneNumber(input.value);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="mx-auto w-full max-w-3xl">
|
||||||
|
<h1 class="text-2xl font-bold tracking-tight text-slate-900 sm:text-3xl dark:text-slate-100">
|
||||||
|
Profile
|
||||||
|
</h1>
|
||||||
|
<p class="mt-1 text-sm text-slate-500 sm:text-base dark:text-slate-400">
|
||||||
|
Manage your account details and addresses.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- Email (read-only) -->
|
||||||
|
<div
|
||||||
|
class="mt-6 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm sm:p-6 dark:border-slate-800 dark:bg-slate-900"
|
||||||
|
>
|
||||||
|
<p class="text-xs tracking-wide text-slate-500 uppercase dark:text-slate-400">Email</p>
|
||||||
|
<p class="mt-1 text-base break-words text-slate-900 dark:text-slate-100">{data.user.email}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Profile Form -->
|
||||||
|
<section
|
||||||
|
class="mt-6 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm sm:p-6 dark:border-slate-800 dark:bg-slate-900"
|
||||||
|
>
|
||||||
|
<h2 class="text-lg font-semibold text-slate-900 dark:text-slate-100">Profile</h2>
|
||||||
|
|
||||||
|
{#if form?.action === 'updateProfile' && form?.success}
|
||||||
|
<div
|
||||||
|
class="mt-4 rounded-lg bg-green-50 px-4 py-3 text-sm text-green-800 dark:bg-green-950/50 dark:text-green-300"
|
||||||
|
role="status"
|
||||||
|
>
|
||||||
|
Profile updated successfully.
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if form?.action === 'updateProfile' && form?.error}
|
||||||
|
<div
|
||||||
|
class="mt-4 rounded-lg bg-red-50 px-4 py-3 text-sm text-red-700 dark:bg-red-950/50 dark:text-red-300"
|
||||||
|
role="alert"
|
||||||
|
>
|
||||||
|
{form.error}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<form method="POST" action="?/updateProfile" class="mt-5 flex flex-col gap-4">
|
||||||
|
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||||
|
<div class="flex flex-col gap-1.5">
|
||||||
|
<label for="firstName" class="text-sm font-medium text-slate-700 dark:text-slate-300">
|
||||||
|
First Name
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="firstName"
|
||||||
|
name="firstName"
|
||||||
|
value={data.user.profile?.firstName ?? ''}
|
||||||
|
required
|
||||||
|
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-1.5">
|
||||||
|
<label for="lastName" class="text-sm font-medium text-slate-700 dark:text-slate-300">
|
||||||
|
Last Name
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="lastName"
|
||||||
|
name="lastName"
|
||||||
|
value={data.user.profile?.lastName ?? ''}
|
||||||
|
required
|
||||||
|
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-1.5">
|
||||||
|
<label for="phoneNumber" class="text-sm font-medium text-slate-700 dark:text-slate-300">
|
||||||
|
Phone Number
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="tel"
|
||||||
|
id="phoneNumber"
|
||||||
|
name="phoneNumber"
|
||||||
|
bind:value={phoneNumber}
|
||||||
|
oninput={handlePhoneInput}
|
||||||
|
required
|
||||||
|
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="flex h-11 w-full items-center justify-center rounded-lg bg-indigo-600 text-base font-medium text-white hover:bg-indigo-700 sm:w-auto sm:px-8"
|
||||||
|
>
|
||||||
|
Save Profile
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Addresses -->
|
||||||
|
<section
|
||||||
|
class="mt-6 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm sm:p-6 dark:border-slate-800 dark:bg-slate-900"
|
||||||
|
>
|
||||||
|
<div class="flex items-center justify-between gap-4">
|
||||||
|
<h2 class="text-lg font-semibold text-slate-900 dark:text-slate-100">Addresses</h2>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={() => (showAddressForm ? closeAddressForm() : openAddAddress())}
|
||||||
|
class="rounded-lg border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-300 dark:hover:bg-slate-800"
|
||||||
|
>
|
||||||
|
{showAddressForm ? 'Cancel' : 'Add Address'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if form?.action === 'addAddress' && form?.success}
|
||||||
|
<div
|
||||||
|
class="mt-4 rounded-lg bg-green-50 px-4 py-3 text-sm text-green-800 dark:bg-green-950/50 dark:text-green-300"
|
||||||
|
role="status"
|
||||||
|
>
|
||||||
|
Address added successfully.
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if form?.action === 'updateAddress' && form?.success}
|
||||||
|
<div
|
||||||
|
class="mt-4 rounded-lg bg-green-50 px-4 py-3 text-sm text-green-800 dark:bg-green-950/50 dark:text-green-300"
|
||||||
|
role="status"
|
||||||
|
>
|
||||||
|
Address updated successfully.
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if form?.action === 'deleteAddress' && form?.success}
|
||||||
|
<div
|
||||||
|
class="mt-4 rounded-lg bg-green-50 px-4 py-3 text-sm text-green-800 dark:bg-green-950/50 dark:text-green-300"
|
||||||
|
role="status"
|
||||||
|
>
|
||||||
|
Address deleted successfully.
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if (form?.action === 'addAddress' || form?.action === 'updateAddress' || form?.action === 'deleteAddress') && form?.error}
|
||||||
|
<div
|
||||||
|
class="mt-4 rounded-lg bg-red-50 px-4 py-3 text-sm text-red-700 dark:bg-red-950/50 dark:text-red-300"
|
||||||
|
role="alert"
|
||||||
|
>
|
||||||
|
{form.error}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if showAddressForm}
|
||||||
|
<form
|
||||||
|
method="POST"
|
||||||
|
action={editingAddressId ? '?/updateAddress' : '?/addAddress'}
|
||||||
|
class="mt-5 flex flex-col gap-4"
|
||||||
|
>
|
||||||
|
{#if editingAddressId}
|
||||||
|
<input type="hidden" name="id" value={editingAddressId} />
|
||||||
|
{/if}
|
||||||
|
<div class="flex flex-col gap-1.5">
|
||||||
|
<label for="street" class="text-sm font-medium text-slate-700 dark:text-slate-300"
|
||||||
|
>Street</label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="street"
|
||||||
|
name="street"
|
||||||
|
bind:value={addrStreet}
|
||||||
|
required
|
||||||
|
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||||
|
<div class="flex flex-col gap-1.5">
|
||||||
|
<label for="city" class="text-sm font-medium text-slate-700 dark:text-slate-300"
|
||||||
|
>City</label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="city"
|
||||||
|
name="city"
|
||||||
|
bind:value={addrCity}
|
||||||
|
required
|
||||||
|
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-1.5">
|
||||||
|
<label for="state" class="text-sm font-medium text-slate-700 dark:text-slate-300"
|
||||||
|
>State</label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="state"
|
||||||
|
name="state"
|
||||||
|
bind:value={addrState}
|
||||||
|
required
|
||||||
|
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||||
|
<div class="flex flex-col gap-1.5">
|
||||||
|
<label for="postalCode" class="text-sm font-medium text-slate-700 dark:text-slate-300">
|
||||||
|
Postal Code
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="postalCode"
|
||||||
|
name="postalCode"
|
||||||
|
bind:value={addrPostal}
|
||||||
|
required
|
||||||
|
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-1.5">
|
||||||
|
<label for="country" class="text-sm font-medium text-slate-700 dark:text-slate-300"
|
||||||
|
>Country</label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="country"
|
||||||
|
name="country"
|
||||||
|
bind:value={addrCountry}
|
||||||
|
required
|
||||||
|
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="flex h-11 w-full items-center justify-center rounded-lg bg-indigo-600 text-base font-medium text-white hover:bg-indigo-700 sm:w-auto sm:px-8"
|
||||||
|
>
|
||||||
|
{editingAddressId ? 'Update Address' : 'Save Address'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if data.user.profile?.addresses && data.user.profile.addresses.length > 0}
|
||||||
|
<div class="mt-5 grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||||
|
{#each data.user.profile.addresses as address (address.id)}
|
||||||
|
<div
|
||||||
|
class="rounded-lg border border-slate-200 bg-slate-50 p-4 dark:border-slate-800 dark:bg-slate-950"
|
||||||
|
>
|
||||||
|
<p class="text-sm font-medium text-slate-900 dark:text-slate-100">{address.street}</p>
|
||||||
|
<p class="mt-1 text-sm text-slate-600 dark:text-slate-400">
|
||||||
|
{address.city}, {address.state}
|
||||||
|
{address.postalCode}
|
||||||
|
</p>
|
||||||
|
<p class="text-sm text-slate-600 dark:text-slate-400">{address.country}</p>
|
||||||
|
<div class="mt-3 flex gap-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={() => openEditAddress(address)}
|
||||||
|
class="rounded-md border border-slate-300 bg-white px-3 py-1.5 text-xs font-medium text-slate-700 hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-300 dark:hover:bg-slate-800"
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</button>
|
||||||
|
<form
|
||||||
|
method="POST"
|
||||||
|
action="?/deleteAddress"
|
||||||
|
onsubmit={(event) => {
|
||||||
|
if (!confirm('Delete this address?')) event.preventDefault();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<input type="hidden" name="id" value={address.id} />
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="rounded-md border border-red-300 bg-white px-3 py-1.5 text-xs font-medium text-red-600 hover:bg-red-50 dark:border-red-800 dark:bg-slate-900 dark:text-red-400 dark:hover:bg-red-950/50"
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{:else if !showAddressForm}
|
||||||
|
<p class="mt-5 text-sm text-slate-500 dark:text-slate-400">No addresses on file.</p>
|
||||||
|
{/if}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
+188
-20
@@ -10,8 +10,8 @@ import {
|
|||||||
vi.mock('$lib/db', () => ({
|
vi.mock('$lib/db', () => ({
|
||||||
prisma: {
|
prisma: {
|
||||||
user: { findUnique: vi.fn() },
|
user: { findUnique: vi.fn() },
|
||||||
profile: { update: vi.fn(), findUnique: vi.fn() },
|
profile: { findFirst: vi.fn(), updateMany: vi.fn() },
|
||||||
address: { create: vi.fn() }
|
address: { create: vi.fn(), findFirst: vi.fn(), update: vi.fn(), delete: vi.fn() }
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -19,19 +19,25 @@ import { prisma } from '$lib/db';
|
|||||||
import { load, actions } from './+page.server';
|
import { load, actions } from './+page.server';
|
||||||
|
|
||||||
const userFindUnique = prisma.user.findUnique as ReturnType<typeof vi.fn>;
|
const userFindUnique = prisma.user.findUnique as ReturnType<typeof vi.fn>;
|
||||||
const profileUpdate = prisma.profile.update as ReturnType<typeof vi.fn>;
|
const profileFindFirst = prisma.profile.findFirst as ReturnType<typeof vi.fn>;
|
||||||
const profileFindUnique = prisma.profile.findUnique as ReturnType<typeof vi.fn>;
|
const profileUpdateMany = prisma.profile.updateMany as ReturnType<typeof vi.fn>;
|
||||||
const addressCreate = prisma.address.create as ReturnType<typeof vi.fn>;
|
const addressCreate = prisma.address.create as ReturnType<typeof vi.fn>;
|
||||||
|
const addressFindFirst = prisma.address.findFirst as ReturnType<typeof vi.fn>;
|
||||||
|
const addressUpdate = prisma.address.update as ReturnType<typeof vi.fn>;
|
||||||
|
const addressDelete = prisma.address.delete as ReturnType<typeof vi.fn>;
|
||||||
|
|
||||||
const invokeAction = (name: string, request: Request, locals: App.Locals) =>
|
const invokeAction = (name: string, request: Request, locals: App.Locals) =>
|
||||||
(actions as any)[name]({ request, locals } as any);
|
(actions as any)[name]({ request, locals } as any);
|
||||||
|
|
||||||
describe('(protected)/account page server', () => {
|
describe('(protected)/profile page server', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
userFindUnique.mockReset();
|
userFindUnique.mockReset();
|
||||||
profileUpdate.mockReset();
|
profileFindFirst.mockReset();
|
||||||
profileFindUnique.mockReset();
|
profileUpdateMany.mockReset();
|
||||||
addressCreate.mockReset();
|
addressCreate.mockReset();
|
||||||
|
addressFindFirst.mockReset();
|
||||||
|
addressUpdate.mockReset();
|
||||||
|
addressDelete.mockReset();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('load', () => {
|
describe('load', () => {
|
||||||
@@ -51,24 +57,26 @@ describe('(protected)/account page server', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('returns user with profile and addresses', async () => {
|
it('returns user with profile and addresses', async () => {
|
||||||
const user = mockUserWithProfileAndAddresses();
|
const { profile, ...user } = mockUserWithProfileAndAddresses() as any;
|
||||||
userFindUnique.mockResolvedValue(user);
|
userFindUnique.mockResolvedValue(user);
|
||||||
|
profileFindFirst.mockResolvedValue(profile);
|
||||||
|
|
||||||
const locals = createLocals(mockUser() as any);
|
const locals = createLocals(mockUser() as any);
|
||||||
const result = (await load({ locals } as any)) as any;
|
const result = (await load({ locals } as any)) as any;
|
||||||
expect(result.user).toEqual(user);
|
expect(result.user).toEqual({ ...user, profile });
|
||||||
expect(result.user.profile.addresses).toHaveLength(1);
|
expect(result.user.profile.addresses).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('includes addresses in the query', async () => {
|
it('loads the profile with addresses by user id', async () => {
|
||||||
userFindUnique.mockResolvedValue(mockUserWithProfileAndAddresses());
|
userFindUnique.mockResolvedValue(mockUser());
|
||||||
|
profileFindFirst.mockResolvedValue({ id: 'profile-1' });
|
||||||
|
|
||||||
const locals = createLocals(mockUser() as any);
|
const locals = createLocals(mockUser() as any);
|
||||||
await load({ locals } as any);
|
await load({ locals } as any);
|
||||||
|
|
||||||
expect(userFindUnique).toHaveBeenCalledWith({
|
expect(profileFindFirst).toHaveBeenCalledWith({
|
||||||
where: { id: 'user-1' },
|
where: { userId: 'user-1' },
|
||||||
include: { profile: { include: { addresses: true } } }
|
include: { addresses: true }
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -125,7 +133,7 @@ describe('(protected)/account page server', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('updates profile with server-formatted phone number on success', async () => {
|
it('updates profile with server-formatted phone number on success', async () => {
|
||||||
profileUpdate.mockResolvedValue({});
|
profileUpdateMany.mockResolvedValue({});
|
||||||
|
|
||||||
const request = createFormRequest({
|
const request = createFormRequest({
|
||||||
firstName: 'Jane',
|
firstName: 'Jane',
|
||||||
@@ -136,14 +144,14 @@ describe('(protected)/account page server', () => {
|
|||||||
const locals = createLocals(mockUser() as any);
|
const locals = createLocals(mockUser() as any);
|
||||||
const result = await invokeAction('updateProfile', request, locals);
|
const result = await invokeAction('updateProfile', request, locals);
|
||||||
expect(result).toEqual({ success: true, action: 'updateProfile' });
|
expect(result).toEqual({ success: true, action: 'updateProfile' });
|
||||||
expect(profileUpdate).toHaveBeenCalledWith({
|
expect(profileUpdateMany).toHaveBeenCalledWith({
|
||||||
where: { userId: 'user-1' },
|
where: { userId: 'user-1' },
|
||||||
data: { firstName: 'Jane', lastName: 'Smith', phoneNumber: '8175551234' }
|
data: { firstName: 'Jane', lastName: 'Smith', phoneNumber: '8175551234' }
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns fail(500) when prisma update throws', async () => {
|
it('returns fail(500) when prisma update throws', async () => {
|
||||||
profileUpdate.mockRejectedValue(new Error('db error'));
|
profileUpdateMany.mockRejectedValue(new Error('db error'));
|
||||||
|
|
||||||
const request = createFormRequest({
|
const request = createFormRequest({
|
||||||
firstName: 'Jane',
|
firstName: 'Jane',
|
||||||
@@ -190,7 +198,7 @@ describe('(protected)/account page server', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('throws 400 when profile is not found', async () => {
|
it('throws 400 when profile is not found', async () => {
|
||||||
profileFindUnique.mockResolvedValue(null);
|
profileFindFirst.mockResolvedValue(null);
|
||||||
|
|
||||||
const request = createFormRequest({
|
const request = createFormRequest({
|
||||||
street: '123 Main St',
|
street: '123 Main St',
|
||||||
@@ -208,7 +216,7 @@ describe('(protected)/account page server', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('creates address linked to profile on success', async () => {
|
it('creates address linked to profile on success', async () => {
|
||||||
profileFindUnique.mockResolvedValue({ id: 'profile-1' });
|
profileFindFirst.mockResolvedValue({ id: 'profile-1' });
|
||||||
addressCreate.mockResolvedValue({});
|
addressCreate.mockResolvedValue({});
|
||||||
|
|
||||||
const request = createFormRequest({
|
const request = createFormRequest({
|
||||||
@@ -235,7 +243,7 @@ describe('(protected)/account page server', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('returns fail(500) when prisma create throws', async () => {
|
it('returns fail(500) when prisma create throws', async () => {
|
||||||
profileFindUnique.mockResolvedValue({ id: 'profile-1' });
|
profileFindFirst.mockResolvedValue({ id: 'profile-1' });
|
||||||
addressCreate.mockRejectedValue(new Error('db error'));
|
addressCreate.mockRejectedValue(new Error('db error'));
|
||||||
|
|
||||||
const request = createFormRequest({
|
const request = createFormRequest({
|
||||||
@@ -252,4 +260,164 @@ describe('(protected)/account page server', () => {
|
|||||||
expect((result as any).status).toBe(500);
|
expect((result as any).status).toBe(500);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('updateAddress action', () => {
|
||||||
|
it('throws 403 when locals.user is undefined', async () => {
|
||||||
|
const request = createFormRequest({
|
||||||
|
id: 'addr-1',
|
||||||
|
street: '123 Main St',
|
||||||
|
city: 'Dallas',
|
||||||
|
state: 'TX',
|
||||||
|
postalCode: '75001',
|
||||||
|
country: 'US'
|
||||||
|
});
|
||||||
|
|
||||||
|
const err = await Promise.resolve(
|
||||||
|
invokeAction('updateAddress', request, createLocals())
|
||||||
|
).catch((e: unknown) => e);
|
||||||
|
expect(isHttpError(err, 403)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns fail(400) when id is missing', async () => {
|
||||||
|
const request = createFormRequest({
|
||||||
|
id: '',
|
||||||
|
street: '123 Main St',
|
||||||
|
city: 'Dallas',
|
||||||
|
state: 'TX',
|
||||||
|
postalCode: '75001',
|
||||||
|
country: 'US'
|
||||||
|
});
|
||||||
|
|
||||||
|
const locals = createLocals(mockUser() as any);
|
||||||
|
const result = await invokeAction('updateAddress', request, locals);
|
||||||
|
expect(isActionFailure(result)).toBe(true);
|
||||||
|
expect((result as any).data.action).toBe('updateAddress');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws 404 when the address is not owned by the user', async () => {
|
||||||
|
addressFindFirst.mockResolvedValue(null);
|
||||||
|
|
||||||
|
const request = createFormRequest({
|
||||||
|
id: 'addr-1',
|
||||||
|
street: '123 Main St',
|
||||||
|
city: 'Dallas',
|
||||||
|
state: 'TX',
|
||||||
|
postalCode: '75001',
|
||||||
|
country: 'US'
|
||||||
|
});
|
||||||
|
|
||||||
|
const locals = createLocals(mockUser() as any);
|
||||||
|
const err = await Promise.resolve(invokeAction('updateAddress', request, locals)).catch(
|
||||||
|
(e: unknown) => e
|
||||||
|
);
|
||||||
|
expect(isHttpError(err, 404)).toBe(true);
|
||||||
|
expect(addressFindFirst).toHaveBeenCalledWith({
|
||||||
|
where: { id: 'addr-1', profile: { userId: 'user-1' } },
|
||||||
|
select: { id: true }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('updates the owned address on success', async () => {
|
||||||
|
addressFindFirst.mockResolvedValue({ id: 'addr-1' });
|
||||||
|
addressUpdate.mockResolvedValue({});
|
||||||
|
|
||||||
|
const request = createFormRequest({
|
||||||
|
id: 'addr-1',
|
||||||
|
street: '456 Oak Ave',
|
||||||
|
city: 'Austin',
|
||||||
|
state: 'TX',
|
||||||
|
postalCode: '73301',
|
||||||
|
country: 'US'
|
||||||
|
});
|
||||||
|
|
||||||
|
const locals = createLocals(mockUser() as any);
|
||||||
|
const result = await invokeAction('updateAddress', request, locals);
|
||||||
|
expect(result).toEqual({ success: true, action: 'updateAddress' });
|
||||||
|
expect(addressUpdate).toHaveBeenCalledWith({
|
||||||
|
where: { id: 'addr-1' },
|
||||||
|
data: {
|
||||||
|
street: '456 Oak Ave',
|
||||||
|
city: 'Austin',
|
||||||
|
state: 'TX',
|
||||||
|
postalCode: '73301',
|
||||||
|
country: 'US'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns fail(500) when prisma update throws', async () => {
|
||||||
|
addressFindFirst.mockResolvedValue({ id: 'addr-1' });
|
||||||
|
addressUpdate.mockRejectedValue(new Error('db error'));
|
||||||
|
|
||||||
|
const request = createFormRequest({
|
||||||
|
id: 'addr-1',
|
||||||
|
street: '123 Main St',
|
||||||
|
city: 'Dallas',
|
||||||
|
state: 'TX',
|
||||||
|
postalCode: '75001',
|
||||||
|
country: 'US'
|
||||||
|
});
|
||||||
|
|
||||||
|
const locals = createLocals(mockUser() as any);
|
||||||
|
const result = await invokeAction('updateAddress', request, locals);
|
||||||
|
expect(isActionFailure(result)).toBe(true);
|
||||||
|
expect((result as any).status).toBe(500);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('deleteAddress action', () => {
|
||||||
|
it('throws 403 when locals.user is undefined', async () => {
|
||||||
|
const request = createFormRequest({ id: 'addr-1' });
|
||||||
|
|
||||||
|
const err = await Promise.resolve(
|
||||||
|
invokeAction('deleteAddress', request, createLocals())
|
||||||
|
).catch((e: unknown) => e);
|
||||||
|
expect(isHttpError(err, 403)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns fail(400) when id is missing', async () => {
|
||||||
|
const request = createFormRequest({ id: '' });
|
||||||
|
|
||||||
|
const locals = createLocals(mockUser() as any);
|
||||||
|
const result = await invokeAction('deleteAddress', request, locals);
|
||||||
|
expect(isActionFailure(result)).toBe(true);
|
||||||
|
expect((result as any).data.action).toBe('deleteAddress');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws 404 when the address is not owned by the user', async () => {
|
||||||
|
addressFindFirst.mockResolvedValue(null);
|
||||||
|
|
||||||
|
const request = createFormRequest({ id: 'addr-1' });
|
||||||
|
|
||||||
|
const locals = createLocals(mockUser() as any);
|
||||||
|
const err = await Promise.resolve(invokeAction('deleteAddress', request, locals)).catch(
|
||||||
|
(e: unknown) => e
|
||||||
|
);
|
||||||
|
expect(isHttpError(err, 404)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('deletes the owned address on success', async () => {
|
||||||
|
addressFindFirst.mockResolvedValue({ id: 'addr-1' });
|
||||||
|
addressDelete.mockResolvedValue({});
|
||||||
|
|
||||||
|
const request = createFormRequest({ id: 'addr-1' });
|
||||||
|
|
||||||
|
const locals = createLocals(mockUser() as any);
|
||||||
|
const result = await invokeAction('deleteAddress', request, locals);
|
||||||
|
expect(result).toEqual({ success: true, action: 'deleteAddress' });
|
||||||
|
expect(addressDelete).toHaveBeenCalledWith({ where: { id: 'addr-1' } });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns fail(500) when prisma delete throws', async () => {
|
||||||
|
addressFindFirst.mockResolvedValue({ id: 'addr-1' });
|
||||||
|
addressDelete.mockRejectedValue(new Error('db error'));
|
||||||
|
|
||||||
|
const request = createFormRequest({ id: 'addr-1' });
|
||||||
|
|
||||||
|
const locals = createLocals(mockUser() as any);
|
||||||
|
const result = await invokeAction('deleteAddress', request, locals);
|
||||||
|
expect(isActionFailure(result)).toBe(true);
|
||||||
|
expect((result as any).status).toBe(500);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
<!--
|
||||||
|
There should be a form in the middle
|
||||||
|
rounded corners
|
||||||
|
floats above the screen slightly
|
||||||
|
Profile picture in the center at the top, about 1/3-1/2 the width of the form
|
||||||
|
Their email address, not editable, instead is a regular sized black text (or white in dark mode)
|
||||||
|
Input field for their Phone Number
|
||||||
|
Two inputs, side by side, a little margin between them. First and Last name
|
||||||
|
A submit button
|
||||||
|
Save the form then redirect them to their dashboard
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Window from '$lib/components/containers/Window.svelte';
|
||||||
|
import { clientFormatPhoneNumber } from '$lib/utils';
|
||||||
|
import type { PageProps } from './$types';
|
||||||
|
|
||||||
|
let { data }: PageProps = $props();
|
||||||
|
|
||||||
|
let firstName = $state('');
|
||||||
|
let lastName = $state('');
|
||||||
|
|
||||||
|
function splitName(fullName: string | null | undefined): { first: string; last: string } {
|
||||||
|
if (!fullName) return { first: '', last: '' };
|
||||||
|
const parts = fullName.trim().split(/\s+/);
|
||||||
|
return { first: parts[0] ?? '', last: parts.slice(1).join(' ') };
|
||||||
|
}
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if (data.user?.name && !firstName) {
|
||||||
|
const names = splitName(data.user.name);
|
||||||
|
firstName = names.first;
|
||||||
|
lastName = names.last;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let phoneNumber = $state('');
|
||||||
|
|
||||||
|
function handlePhoneInput(event: Event) {
|
||||||
|
const input = event.target as HTMLInputElement;
|
||||||
|
phoneNumber = clientFormatPhoneNumber(input.value);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Window>
|
||||||
|
<form method="POST" class="flex flex-col items-center gap-5">
|
||||||
|
<h1 class="text-center text-2xl font-bold text-slate-900 dark:text-slate-100">
|
||||||
|
Please finish setting up your profile
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
{#if data.user?.image}
|
||||||
|
<img
|
||||||
|
src={data.user?.image}
|
||||||
|
alt="Profile"
|
||||||
|
class="h-24 w-24 rounded-full object-cover shadow-sm sm:h-28 sm:w-28"
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
<div
|
||||||
|
class="flex h-24 w-24 items-center justify-center rounded-full bg-indigo-100 text-3xl font-semibold text-indigo-700 sm:h-28 sm:w-28 dark:bg-indigo-900 dark:text-indigo-300"
|
||||||
|
>
|
||||||
|
{data.user?.name?.charAt(0)?.toUpperCase() ?? '?'}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<p class="text-base text-slate-900 dark:text-slate-100">{data.user?.email}</p>
|
||||||
|
|
||||||
|
<div class="flex w-full flex-col gap-4">
|
||||||
|
<input type="hidden" name="userId" value={data.user?.id} />
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||||
|
<div class="flex flex-col gap-1.5">
|
||||||
|
<label for="firstName" class="text-sm font-medium text-slate-700 dark:text-slate-300">
|
||||||
|
First Name
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="firstName"
|
||||||
|
type="text"
|
||||||
|
name="firstName"
|
||||||
|
placeholder="First Name"
|
||||||
|
bind:value={firstName}
|
||||||
|
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-1.5">
|
||||||
|
<label for="lastName" class="text-sm font-medium text-slate-700 dark:text-slate-300">
|
||||||
|
Last Name
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="lastName"
|
||||||
|
type="text"
|
||||||
|
name="lastName"
|
||||||
|
placeholder="Last Name"
|
||||||
|
bind:value={lastName}
|
||||||
|
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-1.5">
|
||||||
|
<label for="phoneNumber" class="text-sm font-medium text-slate-700 dark:text-slate-300">
|
||||||
|
Phone Number
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="phoneNumber"
|
||||||
|
type="tel"
|
||||||
|
name="phoneNumber"
|
||||||
|
placeholder="(555) 555-5555"
|
||||||
|
bind:value={phoneNumber}
|
||||||
|
oninput={handlePhoneInput}
|
||||||
|
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="mt-1 flex h-11 w-full items-center justify-center rounded-lg bg-indigo-600 text-base font-medium text-white hover:bg-indigo-700"
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</Window>
|
||||||
+2
-2
@@ -27,7 +27,7 @@ import { load, actions } from './+page.server';
|
|||||||
|
|
||||||
const invokeDefault = (request: Request) => actions.default({ request } as any);
|
const invokeDefault = (request: Request) => actions.default({ request } as any);
|
||||||
|
|
||||||
describe('account setup page server load', () => {
|
describe('profile setup page server load', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
create.mockReset();
|
create.mockReset();
|
||||||
});
|
});
|
||||||
@@ -48,7 +48,7 @@ describe('account setup page server load', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('account setup page server action', () => {
|
describe('profile setup page server action', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
create.mockReset();
|
create.mockReset();
|
||||||
});
|
});
|
||||||
@@ -9,14 +9,14 @@ export const load: LayoutServerLoad = async ({ locals, url }) => {
|
|||||||
|
|
||||||
const user = await prisma.user.findUnique({
|
const user = await prisma.user.findUnique({
|
||||||
where: { id: locals.user.id },
|
where: { id: locals.user.id },
|
||||||
select: { id: true, roleId: true, profile: { select: { id: true } } }
|
select: { id: true, roleId: true, profiles: { select: { id: true } } }
|
||||||
});
|
});
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw error(403, 'not authorized');
|
throw error(403, 'not authorized');
|
||||||
}
|
}
|
||||||
if (!user.profile && !url.pathname.startsWith('/account/setup')) {
|
if (user.profiles.length === 0 && !url.pathname.startsWith('/profile/setup')) {
|
||||||
// Skip redirect when already on /account/setup to avoid redirect loop
|
// Skip redirect when already on /profile/setup to avoid redirect loop
|
||||||
throw redirect(303, '/account/setup');
|
throw redirect(303, '/profile/setup');
|
||||||
}
|
}
|
||||||
|
|
||||||
return { userRoleId: user.roleId };
|
return { userRoleId: user.roleId };
|
||||||
|
|||||||
@@ -1,74 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { page } from '$app/state';
|
import type { Snippet } from 'svelte';
|
||||||
import { canAccessAdmin } from '$lib/svelte-admin/roles';
|
|
||||||
import { getAllModelConfigs } from '$lib/svelte-admin/registry';
|
|
||||||
import type { LayoutData } from './$types';
|
|
||||||
|
|
||||||
let { children, data } = $props<{ children: any; data: LayoutData }>();
|
let { children }: { children: Snippet } = $props();
|
||||||
|
|
||||||
const isAdmin = $derived(canAccessAdmin(data.userRoleId));
|
|
||||||
const adminModels = getAllModelConfigs();
|
|
||||||
|
|
||||||
const navLinks = [
|
|
||||||
{ href: '/dashboard', label: 'Dashboard' },
|
|
||||||
{ href: '/account', label: 'Account' },
|
|
||||||
{ href: '/proclamations', label: 'Proclamations' },
|
|
||||||
{ href: '/license-keys', label: 'License Keys' }
|
|
||||||
];
|
|
||||||
|
|
||||||
function isActive(href: string): boolean {
|
|
||||||
return page.url.pathname === href || page.url.pathname.startsWith(href + '/');
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex min-h-0 flex-1">
|
<div class="min-h-screen bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-slate-100">
|
||||||
<!-- Sidebar -->
|
<main class="mx-auto w-full max-w-6xl px-4 py-6 sm:px-6 sm:py-8">
|
||||||
<nav class="mt-12 mr-12 flex flex-col gap-1">
|
|
||||||
{#each navLinks as link}
|
|
||||||
<a
|
|
||||||
href={link.href}
|
|
||||||
class="rounded px-3 py-1.5 text-sm font-medium transition-colors {isActive(link.href)
|
|
||||||
? 'bg-blue-50 text-blue-700 dark:bg-gray-800 dark:text-gray-100'
|
|
||||||
: 'text-gray-600 hover:bg-gray-50 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-100'}"
|
|
||||||
>
|
|
||||||
{link.label}
|
|
||||||
</a>
|
|
||||||
{/each}
|
|
||||||
{#if isAdmin}
|
|
||||||
<div class="mt-4 flex flex-col gap-1">
|
|
||||||
<a
|
|
||||||
href="/admin"
|
|
||||||
class="rounded px-3 py-1.5 text-sm font-medium transition-colors {isActive('/admin')
|
|
||||||
? 'bg-blue-50 text-blue-700 dark:bg-gray-800 dark:text-gray-100'
|
|
||||||
: 'text-gray-700 hover:bg-gray-50 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-100'}"
|
|
||||||
>
|
|
||||||
Admin Panel
|
|
||||||
</a>
|
|
||||||
<div class="ml-3 flex flex-col gap-0.5">
|
|
||||||
{#each adminModels as model}
|
|
||||||
<a
|
|
||||||
href="/admin/{model.slug}"
|
|
||||||
class="rounded px-3 py-1 text-sm transition-colors {isActive('/admin/' + model.slug)
|
|
||||||
? 'bg-blue-50 text-blue-700 dark:bg-gray-800 dark:text-gray-100'
|
|
||||||
: 'text-blue-600 hover:bg-gray-50 hover:text-blue-800 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-100'}"
|
|
||||||
>
|
|
||||||
{model.displayName}
|
|
||||||
</a>
|
|
||||||
{/each}
|
|
||||||
<a
|
|
||||||
href="/admin/api-keys"
|
|
||||||
class="rounded px-3 py-1 text-sm transition-colors {isActive('/admin/api-keys')
|
|
||||||
? 'bg-blue-50 text-blue-700 dark:bg-gray-800 dark:text-gray-100'
|
|
||||||
: 'text-blue-600 hover:bg-gray-50 hover:text-blue-800 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-100'}"
|
|
||||||
>
|
|
||||||
API Keys
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</nav>
|
|
||||||
<!-- Page content -->
|
|
||||||
<div class="min-h-0 flex-1 overflow-auto">
|
|
||||||
{@render children()}
|
{@render children()}
|
||||||
</div>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,25 +1,21 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
|
import { resolve } from '$app/paths';
|
||||||
import Window from '$lib/components/containers/Window.svelte';
|
import Window from '$lib/components/containers/Window.svelte';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Window>
|
<Window>
|
||||||
<div class="flex flex-col items-center gap-4 p-6 text-center">
|
<div>
|
||||||
<h1 class="text-xl font-bold text-gray-900 dark:text-gray-100">
|
<h1>
|
||||||
Admin Error ({$page.status})
|
Admin Error ({$page.status})
|
||||||
</h1>
|
</h1>
|
||||||
<p class="max-w-md text-sm text-gray-600 dark:text-gray-400">
|
<p>
|
||||||
{#if $page.error?.message}
|
{#if $page.error?.message}
|
||||||
{$page.error.message}
|
{$page.error.message}
|
||||||
{:else}
|
{:else}
|
||||||
An unexpected error occurred while loading this admin page.
|
An unexpected error occurred while loading this admin page.
|
||||||
{/if}
|
{/if}
|
||||||
</p>
|
</p>
|
||||||
<a
|
<a href={resolve('/admin')}> Back to Admin </a>
|
||||||
href="/admin"
|
|
||||||
class="mt-2 border border-gray-200 px-4 py-2 text-sm font-medium text-gray-900 hover:bg-gray-50 dark:border-gray-700 dark:text-gray-100 dark:hover:bg-gray-800"
|
|
||||||
>
|
|
||||||
Back to Admin
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</Window>
|
</Window>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import type { LayoutServerLoad } from './$types';
|
import type { LayoutServerLoad } from './$types';
|
||||||
import { error } from '@sveltejs/kit';
|
import { error } from '@sveltejs/kit';
|
||||||
import { prisma } from '$lib/db';
|
import { prisma } from '$lib/db';
|
||||||
import { canAccessAdmin } from '$lib/svelte-admin/roles';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This Layout Server-Side page checks if the user is logged in
|
* This Layout Server-Side page checks if the user is logged in
|
||||||
@@ -17,7 +16,7 @@ export const load: LayoutServerLoad = async ({ locals }) => {
|
|||||||
select: { id: true, name: true, roleId: true, role: { select: { name: true } } }
|
select: { id: true, name: true, roleId: true, role: { select: { name: true } } }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user || !canAccessAdmin(user.roleId)) {
|
if (!user || !user.roleId || user.roleId > 3) {
|
||||||
console.log(
|
console.log(
|
||||||
`Attempted to access /admin via ${user?.id} : ${user?.name} with a role of ${user?.role}`
|
`Attempted to access /admin via ${user?.id} : ${user?.name} with a role of ${user?.role}`
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
import type { PageServerLoad } from './$types';
|
import { redirect } from '@sveltejs/kit';
|
||||||
import { getSerializableModelConfigs } from '$lib/svelte-admin/registry';
|
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ locals }) => {
|
export const load = () => {
|
||||||
const viewerRoleId = (locals.user as { roleId?: number } | null)?.roleId ?? 4;
|
redirect(307, '/admin/profiles');
|
||||||
const models = getSerializableModelConfigs(viewerRoleId, ['license-keys']);
|
|
||||||
return { models };
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import type { PageProps } from './$types';
|
|
||||||
|
|
||||||
let { data }: PageProps = $props();
|
|
||||||
let models = $derived(data.models);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<h1 class="p-6 text-xl font-bold text-gray-900 dark:text-gray-100">Admin Dashboard</h1>
|
|
||||||
<div class="grid grid-cols-1 gap-4 px-6 pb-6 sm:grid-cols-2 lg:grid-cols-3">
|
|
||||||
{#each models as model (model.slug)}
|
|
||||||
<a
|
|
||||||
href={'/admin/' + model.slug}
|
|
||||||
class="block border border-gray-200 p-4 hover:bg-gray-50 dark:border-gray-700 dark:hover:bg-gray-800"
|
|
||||||
>
|
|
||||||
<h2 class="text-lg font-semibold text-gray-900 dark:text-gray-100">{model.displayName}</h2>
|
|
||||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
|
||||||
{model.listFields.length} fields
|
|
||||||
</p>
|
|
||||||
</a>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
import type { PageServerLoad } from './$types';
|
|
||||||
import { error } from '@sveltejs/kit';
|
|
||||||
import { prisma } from '$lib/db';
|
|
||||||
import { rotateApiKey } from '$lib/server/api-key';
|
|
||||||
import { ROLES } from '$lib/svelte-admin/roles';
|
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ locals, parent }) => {
|
|
||||||
const { adminUser } = await parent();
|
|
||||||
if (adminUser.roleId > ROLES.SUPER) {
|
|
||||||
throw error(403, 'Only Super admins can manage API keys');
|
|
||||||
}
|
|
||||||
|
|
||||||
const now = new Date();
|
|
||||||
const activeKey = await prisma.apiKey.findFirst({
|
|
||||||
where: {
|
|
||||||
revokedAt: null,
|
|
||||||
OR: [{ expiresAt: null }, { expiresAt: { gt: now } }]
|
|
||||||
},
|
|
||||||
select: { keyPrefix: true, createdAt: true, expiresAt: true },
|
|
||||||
orderBy: { createdAt: 'desc' }
|
|
||||||
});
|
|
||||||
|
|
||||||
const expiredCount = await prisma.apiKey.count({
|
|
||||||
where: {
|
|
||||||
OR: [{ revokedAt: { not: null, lte: now } }, { expiresAt: { not: null, lte: now } }]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
activeKey: activeKey
|
|
||||||
? {
|
|
||||||
prefix: activeKey.keyPrefix,
|
|
||||||
createdAt: activeKey.createdAt.toISOString(),
|
|
||||||
expiresAt: activeKey.expiresAt?.toISOString() ?? null
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
totalRotated: expiredCount
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
async function viewerRoleId(locals: App.Locals): Promise<number> {
|
|
||||||
if (!locals.user?.id) return 4;
|
|
||||||
const user = await prisma.user.findUnique({
|
|
||||||
where: { id: locals.user.id },
|
|
||||||
select: { roleId: true }
|
|
||||||
});
|
|
||||||
return user?.roleId ?? 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const actions = {
|
|
||||||
rotate: async ({ locals }) => {
|
|
||||||
if ((await viewerRoleId(locals)) > ROLES.SUPER) {
|
|
||||||
return { success: false, error: 'Not authorized' };
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await rotateApiKey(false);
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
newKey: result.plaintext,
|
|
||||||
mode: 'normal' as const,
|
|
||||||
expiresAt: result.previousExpiresAt?.toISOString() ?? null
|
|
||||||
};
|
|
||||||
},
|
|
||||||
rotateEmergency: async ({ locals }) => {
|
|
||||||
if ((await viewerRoleId(locals)) > ROLES.SUPER) {
|
|
||||||
return { success: false, error: 'Not authorized' };
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await rotateApiKey(true);
|
|
||||||
return { success: true, newKey: result.plaintext, mode: 'emergency' as const };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user