@chaaskit/db 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/schema-helpers.d.ts +58 -0
- package/dist/schema-helpers.d.ts.map +1 -0
- package/dist/schema-helpers.js +156 -0
- package/dist/schema-helpers.js.map +1 -0
- package/package.json +51 -0
- package/prisma/schema/base.prisma +584 -0
- package/prisma/schema/custom.prisma +24 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import PrismaClientPkg from '@prisma/client';
|
|
2
|
+
declare const PrismaClient: typeof PrismaClientPkg.PrismaClient;
|
|
3
|
+
declare global {
|
|
4
|
+
var prisma: InstanceType<typeof PrismaClient> | undefined;
|
|
5
|
+
}
|
|
6
|
+
export declare const prisma: PrismaClientPkg.PrismaClient<PrismaClientPkg.Prisma.PrismaClientOptions, unknown, import("@prisma/client/runtime/library").InternalArgs>;
|
|
7
|
+
export declare const Prisma: typeof PrismaClientPkg.Prisma;
|
|
8
|
+
export type { PrismaClient } from '@prisma/client';
|
|
9
|
+
export { prisma as db };
|
|
10
|
+
export { getSchemaFolderPath, getBaseSchemaPath, getBaseSchemaContent, getCustomSchemaContent, copySchemaToProject, schemaExists, hasLegacySchema, initializePrisma, } from './schema-helpers.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,eAAe,MAAM,gBAAgB,CAAC;AAC7C,QAAA,MAAQ,YAAY,qCAAoB,CAAC;AAEzC,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,MAAM,EAAE,YAAY,CAAC,OAAO,YAAY,CAAC,GAAG,SAAS,CAAC;CAC3D;AAED,eAAO,MAAM,MAAM,0IAA0C,CAAC;AAO9D,eAAO,MAAM,MAAM,+BAAyB,CAAC;AAC7C,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,MAAM,IAAI,EAAE,EAAE,CAAC;AAGxB,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACtB,mBAAmB,EACnB,YAAY,EACZ,eAAe,EACf,gBAAgB,GACjB,MAAM,qBAAqB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Use default import for CJS/ESM interop with @prisma/client
|
|
2
|
+
import PrismaClientPkg from '@prisma/client';
|
|
3
|
+
const { PrismaClient } = PrismaClientPkg;
|
|
4
|
+
export const prisma = globalThis.prisma ?? new PrismaClient();
|
|
5
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
6
|
+
globalThis.prisma = prisma;
|
|
7
|
+
}
|
|
8
|
+
// Re-export Prisma namespace and types
|
|
9
|
+
export const Prisma = PrismaClientPkg.Prisma;
|
|
10
|
+
export { prisma as db };
|
|
11
|
+
// Schema helpers for project setup
|
|
12
|
+
export { getSchemaFolderPath, getBaseSchemaPath, getBaseSchemaContent, getCustomSchemaContent, copySchemaToProject, schemaExists, hasLegacySchema, initializePrisma, } from './schema-helpers.js';
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,OAAO,eAAe,MAAM,gBAAgB,CAAC;AAC7C,MAAM,EAAE,YAAY,EAAE,GAAG,eAAe,CAAC;AAOzC,MAAM,CAAC,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;AAE9D,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;IAC1C,UAAU,CAAC,MAAM,GAAG,MAAM,CAAC;AAC7B,CAAC;AAED,uCAAuC;AACvC,MAAM,CAAC,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC;AAE7C,OAAO,EAAE,MAAM,IAAI,EAAE,EAAE,CAAC;AAExB,mCAAmC;AACnC,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACtB,mBAAmB,EACnB,YAAY,EACZ,eAAe,EACf,gBAAgB,GACjB,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the path to the schema folder included with the package.
|
|
3
|
+
* Uses Prisma's multi-file schema feature (prismaSchemaFolder).
|
|
4
|
+
*/
|
|
5
|
+
export declare function getSchemaFolderPath(): string;
|
|
6
|
+
/**
|
|
7
|
+
* Get the path to the base Prisma schema included with the package.
|
|
8
|
+
* @deprecated Use getSchemaFolderPath() for multi-file schema support.
|
|
9
|
+
*/
|
|
10
|
+
export declare function getBaseSchemaPath(): string;
|
|
11
|
+
/**
|
|
12
|
+
* Get the content of the base Prisma schema.
|
|
13
|
+
*/
|
|
14
|
+
export declare function getBaseSchemaContent(): Promise<string>;
|
|
15
|
+
/**
|
|
16
|
+
* Get the content of the custom schema template.
|
|
17
|
+
*/
|
|
18
|
+
export declare function getCustomSchemaContent(): Promise<string>;
|
|
19
|
+
/**
|
|
20
|
+
* Copy the schema folder to a target directory.
|
|
21
|
+
* Creates the prisma/schema directory if it doesn't exist.
|
|
22
|
+
* Only overwrites base.prisma, preserves custom.prisma if it exists.
|
|
23
|
+
*
|
|
24
|
+
* @param targetDir - Directory where prisma/schema/ will be created
|
|
25
|
+
* @returns Object with paths to the copied schemas
|
|
26
|
+
*/
|
|
27
|
+
export declare function copySchemaToProject(targetDir: string): Promise<{
|
|
28
|
+
basePath: string;
|
|
29
|
+
customPath: string;
|
|
30
|
+
customCreated: boolean;
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* Check if a Prisma schema exists in the target directory.
|
|
34
|
+
* Checks for both single-file (legacy) and multi-file schema formats.
|
|
35
|
+
*/
|
|
36
|
+
export declare function schemaExists(targetDir: string): Promise<boolean>;
|
|
37
|
+
/**
|
|
38
|
+
* Check if the target directory has a legacy single-file schema.
|
|
39
|
+
*/
|
|
40
|
+
export declare function hasLegacySchema(targetDir: string): Promise<boolean>;
|
|
41
|
+
/**
|
|
42
|
+
* Initialize Prisma in a project directory.
|
|
43
|
+
* Copies the schema files if they don't already exist.
|
|
44
|
+
*
|
|
45
|
+
* @param targetDir - Project directory
|
|
46
|
+
* @param options.force - If true, overwrites existing base.prisma
|
|
47
|
+
* @returns Object with result information
|
|
48
|
+
*/
|
|
49
|
+
export declare function initializePrisma(targetDir: string, options?: {
|
|
50
|
+
force?: boolean;
|
|
51
|
+
}): Promise<{
|
|
52
|
+
basePath: string;
|
|
53
|
+
customPath: string;
|
|
54
|
+
baseCreated: boolean;
|
|
55
|
+
customCreated: boolean;
|
|
56
|
+
hadLegacySchema: boolean;
|
|
57
|
+
}>;
|
|
58
|
+
//# sourceMappingURL=schema-helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-helpers.d.ts","sourceRoot":"","sources":["../src/schema-helpers.ts"],"names":[],"mappings":"AAOA;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAG5C;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED;;GAEG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC,CAG5D;AAED;;GAEG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CAG9D;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,OAAO,CAAA;CAAE,CAAC,CAyB3E;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAgBtE;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQzE;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAChC,OAAO,CAAC;IACT,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;CAC1B,CAAC,CAkDD"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import fs from 'fs/promises';
|
|
4
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
5
|
+
const __dirname = path.dirname(__filename);
|
|
6
|
+
/**
|
|
7
|
+
* Get the path to the schema folder included with the package.
|
|
8
|
+
* Uses Prisma's multi-file schema feature (prismaSchemaFolder).
|
|
9
|
+
*/
|
|
10
|
+
export function getSchemaFolderPath() {
|
|
11
|
+
// When running from dist, schema is at ../prisma/schema/
|
|
12
|
+
return path.resolve(__dirname, '../prisma/schema');
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Get the path to the base Prisma schema included with the package.
|
|
16
|
+
* @deprecated Use getSchemaFolderPath() for multi-file schema support.
|
|
17
|
+
*/
|
|
18
|
+
export function getBaseSchemaPath() {
|
|
19
|
+
return path.join(getSchemaFolderPath(), 'base.prisma');
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Get the content of the base Prisma schema.
|
|
23
|
+
*/
|
|
24
|
+
export async function getBaseSchemaContent() {
|
|
25
|
+
const schemaPath = getBaseSchemaPath();
|
|
26
|
+
return fs.readFile(schemaPath, 'utf-8');
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get the content of the custom schema template.
|
|
30
|
+
*/
|
|
31
|
+
export async function getCustomSchemaContent() {
|
|
32
|
+
const schemaPath = path.join(getSchemaFolderPath(), 'custom.prisma');
|
|
33
|
+
return fs.readFile(schemaPath, 'utf-8');
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Copy the schema folder to a target directory.
|
|
37
|
+
* Creates the prisma/schema directory if it doesn't exist.
|
|
38
|
+
* Only overwrites base.prisma, preserves custom.prisma if it exists.
|
|
39
|
+
*
|
|
40
|
+
* @param targetDir - Directory where prisma/schema/ will be created
|
|
41
|
+
* @returns Object with paths to the copied schemas
|
|
42
|
+
*/
|
|
43
|
+
export async function copySchemaToProject(targetDir) {
|
|
44
|
+
const sourceFolder = getSchemaFolderPath();
|
|
45
|
+
const targetFolder = path.join(targetDir, 'prisma', 'schema');
|
|
46
|
+
// Ensure target directory exists
|
|
47
|
+
await fs.mkdir(targetFolder, { recursive: true });
|
|
48
|
+
const sourceBasePath = path.join(sourceFolder, 'base.prisma');
|
|
49
|
+
const sourceCustomPath = path.join(sourceFolder, 'custom.prisma');
|
|
50
|
+
const targetBasePath = path.join(targetFolder, 'base.prisma');
|
|
51
|
+
const targetCustomPath = path.join(targetFolder, 'custom.prisma');
|
|
52
|
+
// Always copy base.prisma (overwrites)
|
|
53
|
+
await fs.copyFile(sourceBasePath, targetBasePath);
|
|
54
|
+
// Only copy custom.prisma if it doesn't exist
|
|
55
|
+
let customCreated = false;
|
|
56
|
+
try {
|
|
57
|
+
await fs.access(targetCustomPath);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
await fs.copyFile(sourceCustomPath, targetCustomPath);
|
|
61
|
+
customCreated = true;
|
|
62
|
+
}
|
|
63
|
+
return { basePath: targetBasePath, customPath: targetCustomPath, customCreated };
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Check if a Prisma schema exists in the target directory.
|
|
67
|
+
* Checks for both single-file (legacy) and multi-file schema formats.
|
|
68
|
+
*/
|
|
69
|
+
export async function schemaExists(targetDir) {
|
|
70
|
+
// Check for new multi-file schema
|
|
71
|
+
const schemaFolderPath = path.join(targetDir, 'prisma', 'schema', 'base.prisma');
|
|
72
|
+
try {
|
|
73
|
+
await fs.access(schemaFolderPath);
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Check for legacy single-file schema
|
|
78
|
+
const legacyPath = path.join(targetDir, 'prisma', 'schema.prisma');
|
|
79
|
+
try {
|
|
80
|
+
await fs.access(legacyPath);
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Check if the target directory has a legacy single-file schema.
|
|
90
|
+
*/
|
|
91
|
+
export async function hasLegacySchema(targetDir) {
|
|
92
|
+
const legacyPath = path.join(targetDir, 'prisma', 'schema.prisma');
|
|
93
|
+
try {
|
|
94
|
+
await fs.access(legacyPath);
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Initialize Prisma in a project directory.
|
|
103
|
+
* Copies the schema files if they don't already exist.
|
|
104
|
+
*
|
|
105
|
+
* @param targetDir - Project directory
|
|
106
|
+
* @param options.force - If true, overwrites existing base.prisma
|
|
107
|
+
* @returns Object with result information
|
|
108
|
+
*/
|
|
109
|
+
export async function initializePrisma(targetDir, options = {}) {
|
|
110
|
+
const targetFolder = path.join(targetDir, 'prisma', 'schema');
|
|
111
|
+
const targetBasePath = path.join(targetFolder, 'base.prisma');
|
|
112
|
+
const targetCustomPath = path.join(targetFolder, 'custom.prisma');
|
|
113
|
+
const hadLegacySchema = await hasLegacySchema(targetDir);
|
|
114
|
+
// Check if base already exists
|
|
115
|
+
let baseExists = false;
|
|
116
|
+
try {
|
|
117
|
+
await fs.access(targetBasePath);
|
|
118
|
+
baseExists = true;
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// Base doesn't exist
|
|
122
|
+
}
|
|
123
|
+
if (baseExists && !options.force) {
|
|
124
|
+
// Check custom exists
|
|
125
|
+
let customExists = false;
|
|
126
|
+
try {
|
|
127
|
+
await fs.access(targetCustomPath);
|
|
128
|
+
customExists = true;
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// Custom doesn't exist
|
|
132
|
+
}
|
|
133
|
+
// Create custom if missing
|
|
134
|
+
if (!customExists) {
|
|
135
|
+
const customContent = await getCustomSchemaContent();
|
|
136
|
+
await fs.mkdir(targetFolder, { recursive: true });
|
|
137
|
+
await fs.writeFile(targetCustomPath, customContent);
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
basePath: targetBasePath,
|
|
141
|
+
customPath: targetCustomPath,
|
|
142
|
+
baseCreated: false,
|
|
143
|
+
customCreated: !customExists,
|
|
144
|
+
hadLegacySchema,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
const result = await copySchemaToProject(targetDir);
|
|
148
|
+
return {
|
|
149
|
+
basePath: result.basePath,
|
|
150
|
+
customPath: result.customPath,
|
|
151
|
+
baseCreated: true,
|
|
152
|
+
customCreated: result.customCreated,
|
|
153
|
+
hadLegacySchema,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=schema-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-helpers.js","sourceRoot":"","sources":["../src/schema-helpers.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@chaaskit/db",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Prisma database layer for ChaasKit applications",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Matt Ferrante <@ferrants>",
|
|
7
|
+
"homepage": "https://chaaskit.com",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/ferrants/chaaskit.git",
|
|
11
|
+
"directory": "packages/db"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/ferrants/chaaskit/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": ["chaaskit", "ai", "chat", "saas", "database", "prisma"],
|
|
17
|
+
"type": "module",
|
|
18
|
+
"main": "./dist/index.js",
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"prisma": {
|
|
21
|
+
"schema": "prisma/schema"
|
|
22
|
+
},
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"import": "./dist/index.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"prisma/schema"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsc",
|
|
35
|
+
"dev": "tsc --watch",
|
|
36
|
+
"typecheck": "tsc --noEmit",
|
|
37
|
+
"clean": "rm -rf dist",
|
|
38
|
+
"generate": "prisma generate",
|
|
39
|
+
"push": "prisma db push",
|
|
40
|
+
"migrate": "prisma migrate dev",
|
|
41
|
+
"studio": "prisma studio"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@prisma/client": "^6.0.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/node": "^22.0.0",
|
|
48
|
+
"prisma": "^6.0.0",
|
|
49
|
+
"typescript": "^5.6.0"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Base Schema - @chat-saas/db
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// This file contains the core models provided by @chat-saas/db.
|
|
5
|
+
// DO NOT MODIFY THIS FILE - it will be overwritten by `chat-saas-server db:sync`.
|
|
6
|
+
//
|
|
7
|
+
// For custom models, create or edit `custom.prisma` in this same directory.
|
|
8
|
+
// =============================================================================
|
|
9
|
+
|
|
10
|
+
generator client {
|
|
11
|
+
provider = "prisma-client-js"
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
datasource db {
|
|
15
|
+
provider = "postgresql"
|
|
16
|
+
url = env("DATABASE_URL")
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
model User {
|
|
20
|
+
id String @id @default(cuid())
|
|
21
|
+
email String @unique
|
|
22
|
+
passwordHash String?
|
|
23
|
+
name String?
|
|
24
|
+
avatarUrl String?
|
|
25
|
+
|
|
26
|
+
// Auth
|
|
27
|
+
emailVerified Boolean @default(false)
|
|
28
|
+
oauthProvider String?
|
|
29
|
+
oauthId String?
|
|
30
|
+
|
|
31
|
+
// Admin flag
|
|
32
|
+
isAdmin Boolean @default(false)
|
|
33
|
+
|
|
34
|
+
// Subscription
|
|
35
|
+
plan String @default("free")
|
|
36
|
+
stripeCustomerId String? @unique
|
|
37
|
+
credits Int @default(0)
|
|
38
|
+
messagesThisMonth Int @default(0)
|
|
39
|
+
|
|
40
|
+
// User settings/context (JSON)
|
|
41
|
+
settings Json @default("{}")
|
|
42
|
+
themePreference String?
|
|
43
|
+
|
|
44
|
+
// Relations
|
|
45
|
+
threads Thread[]
|
|
46
|
+
projects Project[]
|
|
47
|
+
teamMemberships TeamMember[]
|
|
48
|
+
feedback MessageFeedback[]
|
|
49
|
+
templates PromptTemplate[]
|
|
50
|
+
magicLinks MagicLink[]
|
|
51
|
+
mcpCredentials MCPCredential[]
|
|
52
|
+
apiKeys ApiKey[]
|
|
53
|
+
documents Document[]
|
|
54
|
+
|
|
55
|
+
// OAuth relations
|
|
56
|
+
oauthClients OAuthClient[]
|
|
57
|
+
oauthAuthCodes OAuthAuthorizationCode[]
|
|
58
|
+
oauthTokens OAuthToken[]
|
|
59
|
+
|
|
60
|
+
// Scheduled prompts
|
|
61
|
+
scheduledPrompts ScheduledPrompt[]
|
|
62
|
+
|
|
63
|
+
// Email verification
|
|
64
|
+
emailVerifications EmailVerification[]
|
|
65
|
+
|
|
66
|
+
createdAt DateTime @default(now())
|
|
67
|
+
updatedAt DateTime @updatedAt
|
|
68
|
+
|
|
69
|
+
@@index([email])
|
|
70
|
+
@@index([stripeCustomerId])
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
model Thread {
|
|
74
|
+
id String @id @default(cuid())
|
|
75
|
+
title String @default("New Chat")
|
|
76
|
+
userId String?
|
|
77
|
+
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
|
|
78
|
+
|
|
79
|
+
// Agent used for this thread (null = default agent)
|
|
80
|
+
agentId String?
|
|
81
|
+
|
|
82
|
+
// For team threads
|
|
83
|
+
teamId String?
|
|
84
|
+
team Team? @relation(fields: [teamId], references: [id], onDelete: SetNull)
|
|
85
|
+
|
|
86
|
+
// For project threads
|
|
87
|
+
projectId String?
|
|
88
|
+
project Project? @relation(fields: [projectId], references: [id], onDelete: SetNull)
|
|
89
|
+
|
|
90
|
+
// Branching support
|
|
91
|
+
parentThreadId String?
|
|
92
|
+
parentMessageId String?
|
|
93
|
+
parentThread Thread? @relation("ThreadBranches", fields: [parentThreadId], references: [id], onDelete: SetNull)
|
|
94
|
+
branches Thread[] @relation("ThreadBranches")
|
|
95
|
+
|
|
96
|
+
// Archived threads (hidden but preserved)
|
|
97
|
+
archivedAt DateTime?
|
|
98
|
+
|
|
99
|
+
// Relations
|
|
100
|
+
messages Message[]
|
|
101
|
+
sharedLinks SharedThread[]
|
|
102
|
+
slackEvents SlackMessageEvent[]
|
|
103
|
+
scheduledPrompts ScheduledPrompt[]
|
|
104
|
+
|
|
105
|
+
createdAt DateTime @default(now())
|
|
106
|
+
updatedAt DateTime @updatedAt
|
|
107
|
+
|
|
108
|
+
@@index([userId])
|
|
109
|
+
@@index([teamId])
|
|
110
|
+
@@index([projectId])
|
|
111
|
+
@@index([parentThreadId])
|
|
112
|
+
@@index([agentId])
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
model Message {
|
|
116
|
+
id String @id @default(cuid())
|
|
117
|
+
threadId String
|
|
118
|
+
thread Thread @relation(fields: [threadId], references: [id], onDelete: Cascade)
|
|
119
|
+
role String // 'user' | 'assistant' | 'system'
|
|
120
|
+
content String
|
|
121
|
+
|
|
122
|
+
// File attachments (JSON array)
|
|
123
|
+
files Json?
|
|
124
|
+
|
|
125
|
+
// Tool calls and results (JSON)
|
|
126
|
+
toolCalls Json?
|
|
127
|
+
toolResults Json?
|
|
128
|
+
|
|
129
|
+
// Token usage
|
|
130
|
+
inputTokens Int?
|
|
131
|
+
outputTokens Int?
|
|
132
|
+
|
|
133
|
+
// Relations
|
|
134
|
+
feedback MessageFeedback[]
|
|
135
|
+
|
|
136
|
+
createdAt DateTime @default(now())
|
|
137
|
+
|
|
138
|
+
@@index([threadId])
|
|
139
|
+
@@index([createdAt])
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
model MessageFeedback {
|
|
143
|
+
id String @id @default(cuid())
|
|
144
|
+
messageId String
|
|
145
|
+
message Message @relation(fields: [messageId], references: [id], onDelete: Cascade)
|
|
146
|
+
userId String
|
|
147
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
148
|
+
type String // 'up' | 'down'
|
|
149
|
+
comment String?
|
|
150
|
+
createdAt DateTime @default(now())
|
|
151
|
+
|
|
152
|
+
@@unique([messageId, userId])
|
|
153
|
+
@@index([messageId])
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
model SharedThread {
|
|
157
|
+
id String @id @default(cuid())
|
|
158
|
+
shareId String @unique @default(cuid())
|
|
159
|
+
threadId String
|
|
160
|
+
thread Thread @relation(fields: [threadId], references: [id], onDelete: Cascade)
|
|
161
|
+
expiresAt DateTime?
|
|
162
|
+
createdAt DateTime @default(now())
|
|
163
|
+
|
|
164
|
+
@@index([shareId])
|
|
165
|
+
@@index([threadId])
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
model PromptTemplate {
|
|
169
|
+
id String @id @default(cuid())
|
|
170
|
+
userId String
|
|
171
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
172
|
+
name String
|
|
173
|
+
prompt String
|
|
174
|
+
variables Json @default("[]")
|
|
175
|
+
createdAt DateTime @default(now())
|
|
176
|
+
updatedAt DateTime @updatedAt
|
|
177
|
+
|
|
178
|
+
@@index([userId])
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
model MagicLink {
|
|
182
|
+
id String @id @default(cuid())
|
|
183
|
+
token String @unique
|
|
184
|
+
userId String
|
|
185
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
186
|
+
expiresAt DateTime
|
|
187
|
+
usedAt DateTime?
|
|
188
|
+
createdAt DateTime @default(now())
|
|
189
|
+
|
|
190
|
+
@@index([token])
|
|
191
|
+
@@index([userId])
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
model PasswordReset {
|
|
195
|
+
id String @id @default(cuid())
|
|
196
|
+
token String @unique
|
|
197
|
+
email String
|
|
198
|
+
expiresAt DateTime
|
|
199
|
+
usedAt DateTime?
|
|
200
|
+
createdAt DateTime @default(now())
|
|
201
|
+
|
|
202
|
+
@@index([token])
|
|
203
|
+
@@index([email])
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Projects - organizational folders for threads with AI context
|
|
207
|
+
model Project {
|
|
208
|
+
id String @id @default(cuid())
|
|
209
|
+
name String
|
|
210
|
+
context String? // AI context for project threads
|
|
211
|
+
color String // hex color from preset list
|
|
212
|
+
sharing String @default("private") // 'private' | 'team'
|
|
213
|
+
userId String // creator
|
|
214
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
215
|
+
teamId String? // optional team association
|
|
216
|
+
team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade)
|
|
217
|
+
threads Thread[]
|
|
218
|
+
documents Document[]
|
|
219
|
+
archivedAt DateTime? // null = active, set = archived
|
|
220
|
+
createdAt DateTime @default(now())
|
|
221
|
+
updatedAt DateTime @updatedAt
|
|
222
|
+
|
|
223
|
+
@@index([userId])
|
|
224
|
+
@@index([teamId])
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Team workspaces
|
|
228
|
+
model Team {
|
|
229
|
+
id String @id @default(cuid())
|
|
230
|
+
name String
|
|
231
|
+
context String? // Additional context passed to AI agent for team threads
|
|
232
|
+
archivedAt DateTime? // null = active, set = archived
|
|
233
|
+
|
|
234
|
+
// Billing fields
|
|
235
|
+
plan String @default("free")
|
|
236
|
+
stripeCustomerId String? @unique
|
|
237
|
+
credits Int @default(0)
|
|
238
|
+
messagesThisMonth Int @default(0)
|
|
239
|
+
|
|
240
|
+
members TeamMember[]
|
|
241
|
+
threads Thread[]
|
|
242
|
+
projects Project[]
|
|
243
|
+
invites TeamInvite[]
|
|
244
|
+
apiKeys ApiKey[]
|
|
245
|
+
documents Document[]
|
|
246
|
+
slackIntegration SlackIntegration?
|
|
247
|
+
scheduledPrompts ScheduledPrompt[]
|
|
248
|
+
createdAt DateTime @default(now())
|
|
249
|
+
updatedAt DateTime @updatedAt
|
|
250
|
+
|
|
251
|
+
@@index([stripeCustomerId])
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
model TeamMember {
|
|
255
|
+
id String @id @default(cuid())
|
|
256
|
+
teamId String
|
|
257
|
+
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
|
|
258
|
+
userId String
|
|
259
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
260
|
+
role String // 'owner' | 'admin' | 'member' | 'viewer'
|
|
261
|
+
|
|
262
|
+
createdAt DateTime @default(now())
|
|
263
|
+
|
|
264
|
+
@@unique([teamId, userId])
|
|
265
|
+
@@index([teamId])
|
|
266
|
+
@@index([userId])
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
model TeamInvite {
|
|
270
|
+
id String @id @default(cuid())
|
|
271
|
+
teamId String
|
|
272
|
+
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
|
|
273
|
+
email String
|
|
274
|
+
role String // 'admin' | 'member' | 'viewer'
|
|
275
|
+
token String @unique
|
|
276
|
+
invitedBy String
|
|
277
|
+
expiresAt DateTime
|
|
278
|
+
acceptedAt DateTime?
|
|
279
|
+
createdAt DateTime @default(now())
|
|
280
|
+
|
|
281
|
+
@@unique([teamId, email])
|
|
282
|
+
@@index([token])
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// MCP Server Credentials
|
|
286
|
+
model MCPCredential {
|
|
287
|
+
id String @id @default(cuid())
|
|
288
|
+
userId String
|
|
289
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
290
|
+
serverId String
|
|
291
|
+
credentialType String // 'api_key' | 'oauth'
|
|
292
|
+
encryptedData String // Encrypted JSON: {apiKey} or {accessToken, refreshToken, expiresAt}
|
|
293
|
+
oauthState String? // Temporary state during OAuth flow
|
|
294
|
+
codeVerifier String? // PKCE verifier (encrypted)
|
|
295
|
+
createdAt DateTime @default(now())
|
|
296
|
+
updatedAt DateTime @updatedAt
|
|
297
|
+
|
|
298
|
+
@@unique([userId, serverId])
|
|
299
|
+
@@index([userId])
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// API Keys for programmatic access
|
|
303
|
+
model ApiKey {
|
|
304
|
+
id String @id @default(cuid())
|
|
305
|
+
userId String
|
|
306
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
307
|
+
teamId String? // Optional: if set, key operates in team context
|
|
308
|
+
team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade)
|
|
309
|
+
name String // User-provided label, e.g. "My CLI Tool"
|
|
310
|
+
keyPrefix String // First 10 chars for display: "sk-abc123..."
|
|
311
|
+
keyHash String // bcrypt hash of full key
|
|
312
|
+
lastUsedAt DateTime?
|
|
313
|
+
expiresAt DateTime? // Optional expiration
|
|
314
|
+
createdAt DateTime @default(now())
|
|
315
|
+
|
|
316
|
+
@@index([userId])
|
|
317
|
+
@@index([teamId])
|
|
318
|
+
@@index([keyPrefix]) // For lookup during auth
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// OAuth Client Registration for MCP Server (RFC 7591)
|
|
322
|
+
model OAuthClient {
|
|
323
|
+
id String @id @default(cuid())
|
|
324
|
+
clientId String @unique // Public client ID
|
|
325
|
+
clientSecretHash String? // bcrypt hash (for confidential clients)
|
|
326
|
+
clientName String
|
|
327
|
+
clientUri String?
|
|
328
|
+
redirectUris String // JSON array of allowed redirect URIs
|
|
329
|
+
grantTypes String @default("[\"authorization_code\",\"refresh_token\"]")
|
|
330
|
+
responseTypes String @default("[\"code\"]")
|
|
331
|
+
tokenEndpointAuth String @default("none") // "none", "client_secret_basic", "client_secret_post"
|
|
332
|
+
isActive Boolean @default(true)
|
|
333
|
+
|
|
334
|
+
// For user-registered clients (null = dynamic registration)
|
|
335
|
+
userId String?
|
|
336
|
+
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
337
|
+
|
|
338
|
+
createdAt DateTime @default(now())
|
|
339
|
+
updatedAt DateTime @updatedAt
|
|
340
|
+
|
|
341
|
+
// Relations
|
|
342
|
+
tokens OAuthToken[]
|
|
343
|
+
authCodes OAuthAuthorizationCode[]
|
|
344
|
+
|
|
345
|
+
@@index([clientId])
|
|
346
|
+
@@index([userId])
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// OAuth Authorization Codes (short-lived, for auth code flow)
|
|
350
|
+
model OAuthAuthorizationCode {
|
|
351
|
+
id String @id @default(cuid())
|
|
352
|
+
code String @unique
|
|
353
|
+
clientId String
|
|
354
|
+
client OAuthClient @relation(fields: [clientId], references: [id], onDelete: Cascade)
|
|
355
|
+
userId String
|
|
356
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
357
|
+
redirectUri String
|
|
358
|
+
scope String?
|
|
359
|
+
codeChallenge String // PKCE code challenge
|
|
360
|
+
codeChallengeMethod String @default("S256")
|
|
361
|
+
expiresAt DateTime
|
|
362
|
+
usedAt DateTime?
|
|
363
|
+
createdAt DateTime @default(now())
|
|
364
|
+
|
|
365
|
+
@@index([code])
|
|
366
|
+
@@index([clientId])
|
|
367
|
+
@@index([userId])
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// OAuth Access/Refresh Tokens
|
|
371
|
+
model OAuthToken {
|
|
372
|
+
id String @id @default(cuid())
|
|
373
|
+
tokenHash String @unique // Hash of access token
|
|
374
|
+
refreshTokenHash String? @unique // Hash of refresh token
|
|
375
|
+
clientId String
|
|
376
|
+
client OAuthClient @relation(fields: [clientId], references: [id], onDelete: Cascade)
|
|
377
|
+
userId String
|
|
378
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
379
|
+
scope String?
|
|
380
|
+
expiresAt DateTime
|
|
381
|
+
refreshExpiresAt DateTime?
|
|
382
|
+
revokedAt DateTime?
|
|
383
|
+
createdAt DateTime @default(now())
|
|
384
|
+
|
|
385
|
+
@@index([tokenHash])
|
|
386
|
+
@@index([refreshTokenHash])
|
|
387
|
+
@@index([clientId])
|
|
388
|
+
@@index([userId])
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Document storage for @-mentionable resources
|
|
392
|
+
model Document {
|
|
393
|
+
id String @id @default(cuid())
|
|
394
|
+
name String // Unique within scope
|
|
395
|
+
content String? @db.Text // For small text docs (stored in DB)
|
|
396
|
+
storageKey String? // For file storage provider (filesystem/S3)
|
|
397
|
+
mimeType String @default("text/plain")
|
|
398
|
+
fileSize Int @default(0)
|
|
399
|
+
charCount Int @default(0) // For hybrid threshold
|
|
400
|
+
|
|
401
|
+
userId String
|
|
402
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
403
|
+
teamId String?
|
|
404
|
+
team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade)
|
|
405
|
+
projectId String?
|
|
406
|
+
project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
|
407
|
+
|
|
408
|
+
createdAt DateTime @default(now())
|
|
409
|
+
updatedAt DateTime @updatedAt
|
|
410
|
+
archivedAt DateTime?
|
|
411
|
+
|
|
412
|
+
@@unique([userId, name, teamId, projectId]) // Unique name per scope
|
|
413
|
+
@@index([userId])
|
|
414
|
+
@@index([teamId])
|
|
415
|
+
@@index([projectId])
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Slack Integration - connects a team to their Slack workspace
|
|
419
|
+
model SlackIntegration {
|
|
420
|
+
id String @id @default(cuid())
|
|
421
|
+
|
|
422
|
+
// Team relationship (one-to-one)
|
|
423
|
+
teamId String @unique
|
|
424
|
+
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
|
|
425
|
+
|
|
426
|
+
// Slack workspace info
|
|
427
|
+
slackWorkspaceId String @unique // Slack's team/workspace ID (T01234567)
|
|
428
|
+
slackWorkspaceName String // Human-readable workspace name
|
|
429
|
+
slackBotUserId String // Bot's user ID for @mention detection
|
|
430
|
+
|
|
431
|
+
// Encrypted credentials
|
|
432
|
+
encryptedTokens String // AES-encrypted JSON: {botToken, accessToken?}
|
|
433
|
+
|
|
434
|
+
// Per-team settings
|
|
435
|
+
notificationChannel String? // Default channel for notifications (#general)
|
|
436
|
+
aiChatEnabled Boolean @default(true)
|
|
437
|
+
|
|
438
|
+
// Status tracking
|
|
439
|
+
status String @default("active") // 'active' | 'disconnected' | 'error'
|
|
440
|
+
statusMessage String? // Error details if status='error'
|
|
441
|
+
|
|
442
|
+
// Audit
|
|
443
|
+
installedBy String // User ID who installed
|
|
444
|
+
installedAt DateTime @default(now())
|
|
445
|
+
updatedAt DateTime @updatedAt
|
|
446
|
+
|
|
447
|
+
// Relations
|
|
448
|
+
messageEvents SlackMessageEvent[]
|
|
449
|
+
|
|
450
|
+
@@index([teamId])
|
|
451
|
+
@@index([slackWorkspaceId])
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Slack Message Events - tracks incoming events for deduplication and retry
|
|
455
|
+
model SlackMessageEvent {
|
|
456
|
+
id String @id @default(cuid())
|
|
457
|
+
|
|
458
|
+
// Integration relationship
|
|
459
|
+
integrationId String
|
|
460
|
+
integration SlackIntegration @relation(fields: [integrationId], references: [id], onDelete: Cascade)
|
|
461
|
+
|
|
462
|
+
// Slack message identifiers
|
|
463
|
+
slackEventId String @unique // Slack's event_id for deduplication
|
|
464
|
+
slackChannelId String // Channel ID (C01234567)
|
|
465
|
+
slackThreadTs String? // Parent message timestamp (for threads)
|
|
466
|
+
slackMessageTs String // This message's timestamp
|
|
467
|
+
slackUserId String // User who sent the message
|
|
468
|
+
messageText String? // Original message text (for debugging)
|
|
469
|
+
|
|
470
|
+
// Processing state
|
|
471
|
+
status String @default("pending") // pending|processing|completed|failed
|
|
472
|
+
retryCount Int @default(0)
|
|
473
|
+
lastError String? // Error message if failed
|
|
474
|
+
|
|
475
|
+
// Internal thread linkage (for context continuity)
|
|
476
|
+
internalThreadId String?
|
|
477
|
+
internalThread Thread? @relation(fields: [internalThreadId], references: [id], onDelete: SetNull)
|
|
478
|
+
|
|
479
|
+
// Response tracking
|
|
480
|
+
responseTs String? // Timestamp of bot's response message
|
|
481
|
+
|
|
482
|
+
// Timing
|
|
483
|
+
createdAt DateTime @default(now())
|
|
484
|
+
updatedAt DateTime @updatedAt
|
|
485
|
+
processedAt DateTime?
|
|
486
|
+
|
|
487
|
+
@@index([integrationId])
|
|
488
|
+
@@index([slackEventId])
|
|
489
|
+
@@index([status, updatedAt]) // For retry queries
|
|
490
|
+
@@index([slackChannelId, slackThreadTs]) // For thread context lookup
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// One-time scheduled jobs (delayed execution)
|
|
494
|
+
// Used by the queue scheduler for jobs that need to run at a specific time
|
|
495
|
+
model ScheduledJob {
|
|
496
|
+
id String @id @default(cuid())
|
|
497
|
+
type String // Job type (e.g., 'email:send')
|
|
498
|
+
payload String @db.Text // JSON job payload
|
|
499
|
+
options String @db.Text // JSON EnqueueOptions
|
|
500
|
+
scheduledFor DateTime // When to enqueue to the queue
|
|
501
|
+
status String @default("scheduled") // scheduled | enqueued | cancelled
|
|
502
|
+
error String? // Error message if failed to enqueue
|
|
503
|
+
createdAt DateTime @default(now())
|
|
504
|
+
updatedAt DateTime @updatedAt
|
|
505
|
+
|
|
506
|
+
@@index([status, scheduledFor])
|
|
507
|
+
@@index([type])
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Recurring jobs (cron-style)
|
|
511
|
+
// Scheduler polls these and enqueues jobs according to schedule
|
|
512
|
+
model RecurringJob {
|
|
513
|
+
id String @id @default(cuid())
|
|
514
|
+
name String @unique // Unique job name: "daily-report", "hourly-cleanup"
|
|
515
|
+
type String // Job type to enqueue (e.g., 'report:generate')
|
|
516
|
+
payload String @db.Text // JSON payload for each job
|
|
517
|
+
options String @db.Text // JSON EnqueueOptions (minus scheduling)
|
|
518
|
+
schedule String // Cron: "0 9 * * *" or interval: "every 1 hour"
|
|
519
|
+
timezone String @default("UTC")
|
|
520
|
+
enabled Boolean @default(true)
|
|
521
|
+
nextRunAt DateTime? // Calculated next run time
|
|
522
|
+
lastRunAt DateTime? // Last successful run
|
|
523
|
+
lastError String? // Last error (if any)
|
|
524
|
+
createdAt DateTime @default(now())
|
|
525
|
+
updatedAt DateTime @updatedAt
|
|
526
|
+
|
|
527
|
+
@@index([enabled, nextRunAt])
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// Scheduled Prompts (Automations)
|
|
531
|
+
// User or team-configurable prompts that run on a schedule
|
|
532
|
+
model ScheduledPrompt {
|
|
533
|
+
id String @id @default(cuid())
|
|
534
|
+
name String // "Daily Summary", "Weekly Report"
|
|
535
|
+
prompt String @db.Text // The prompt text to run
|
|
536
|
+
agentId String? // Agent to use (null = default)
|
|
537
|
+
|
|
538
|
+
// Schedule
|
|
539
|
+
schedule String // Cron: "0 9 * * 1-5" or interval: "every 1 day"
|
|
540
|
+
timezone String @default("UTC")
|
|
541
|
+
enabled Boolean @default(true)
|
|
542
|
+
|
|
543
|
+
// Notification settings
|
|
544
|
+
notifySlack Boolean @default(true)
|
|
545
|
+
notifyEmail Boolean @default(false)
|
|
546
|
+
emailRecipients String? @db.Text // JSON array of emails
|
|
547
|
+
|
|
548
|
+
// Results storage
|
|
549
|
+
threadId String? // Dedicated thread for results (created on first run)
|
|
550
|
+
thread Thread? @relation(fields: [threadId], references: [id], onDelete: SetNull)
|
|
551
|
+
|
|
552
|
+
// Ownership (one of these must be set)
|
|
553
|
+
userId String?
|
|
554
|
+
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
555
|
+
teamId String?
|
|
556
|
+
team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade)
|
|
557
|
+
|
|
558
|
+
// Tracking
|
|
559
|
+
lastRunAt DateTime?
|
|
560
|
+
lastRunStatus String? // "success" | "failed"
|
|
561
|
+
lastError String?
|
|
562
|
+
runCount Int @default(0)
|
|
563
|
+
|
|
564
|
+
createdAt DateTime @default(now())
|
|
565
|
+
updatedAt DateTime @updatedAt
|
|
566
|
+
|
|
567
|
+
@@index([userId])
|
|
568
|
+
@@index([teamId])
|
|
569
|
+
@@index([enabled])
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Email Verification Codes
|
|
573
|
+
model EmailVerification {
|
|
574
|
+
id String @id @default(cuid())
|
|
575
|
+
code String // 6-digit code (hashed)
|
|
576
|
+
userId String
|
|
577
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
578
|
+
expiresAt DateTime
|
|
579
|
+
usedAt DateTime?
|
|
580
|
+
attempts Int @default(0) // Brute force protection
|
|
581
|
+
createdAt DateTime @default(now())
|
|
582
|
+
|
|
583
|
+
@@index([userId])
|
|
584
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Custom Schema - Your Application Models
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// Add your custom models here. This file is preserved during `chat-saas-server db:sync`.
|
|
5
|
+
//
|
|
6
|
+
// You can:
|
|
7
|
+
// - Add new models
|
|
8
|
+
// - Add relations to core models (use the model name, Prisma will find it in base.prisma)
|
|
9
|
+
//
|
|
10
|
+
// Example:
|
|
11
|
+
//
|
|
12
|
+
// model BlogPost {
|
|
13
|
+
// id String @id @default(cuid())
|
|
14
|
+
// title String
|
|
15
|
+
// content String
|
|
16
|
+
// authorId String
|
|
17
|
+
// author User @relation(fields: [authorId], references: [id])
|
|
18
|
+
// createdAt DateTime @default(now())
|
|
19
|
+
// }
|
|
20
|
+
//
|
|
21
|
+
// Note: If you add a relation to a core model (like User above), you may need to
|
|
22
|
+
// add the opposite relation field to that model in base.prisma. After running
|
|
23
|
+
// db:sync, check for validation errors and add any missing relation fields.
|
|
24
|
+
// =============================================================================
|