@danielcok17/prisma-db 1.18.0 → 1.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/migrations/migrateIngestedDocuments.d.ts +17 -0
- package/dist/migrations/migrateIngestedDocuments.d.ts.map +1 -0
- package/dist/migrations/migrateIngestedDocuments.js +113 -0
- package/dist/migrations/migrateIngestedDocuments.js.map +1 -0
- package/dist/migrations/migrateMessageFiles.d.ts +14 -0
- package/dist/migrations/migrateMessageFiles.d.ts.map +1 -0
- package/dist/migrations/migrateMessageFiles.js +120 -0
- package/dist/migrations/migrateMessageFiles.js.map +1 -0
- package/package.json +2 -1
- package/prisma/app.prisma +173 -2
- package/prisma/migrations/20260502185044_add_userfile_system/migration.sql +194 -0
- package/prisma/migrations/20260502204440_add_userfile_source_tracking/migration.sql +50 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migrácia IngestedDocument → UserFile
|
|
3
|
+
*
|
|
4
|
+
* IngestedDocument.storageUrl (DO Spaces) → UserFile.storageKey
|
|
5
|
+
* IngestedDocument.qdrantDocId → UserFile.qdrantDocId
|
|
6
|
+
* IngestedDocument status COMPLETED → UserFile status INDEXED
|
|
7
|
+
*
|
|
8
|
+
* PRED spustením:
|
|
9
|
+
* 1. Nastav DATABASE_URL env var
|
|
10
|
+
* 2. Over že UserFile tabuľka existuje
|
|
11
|
+
* 3. Spusti v staging/dev NAJPRV
|
|
12
|
+
*
|
|
13
|
+
* Spustenie: npx tsx src/migrations/migrateIngestedDocuments.ts
|
|
14
|
+
* Rollback: IngestedDocument záznamy sa NEMAŽÚ
|
|
15
|
+
*/
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=migrateIngestedDocuments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrateIngestedDocuments.d.ts","sourceRoot":"","sources":["../../src/migrations/migrateIngestedDocuments.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Migrácia IngestedDocument → UserFile
|
|
4
|
+
*
|
|
5
|
+
* IngestedDocument.storageUrl (DO Spaces) → UserFile.storageKey
|
|
6
|
+
* IngestedDocument.qdrantDocId → UserFile.qdrantDocId
|
|
7
|
+
* IngestedDocument status COMPLETED → UserFile status INDEXED
|
|
8
|
+
*
|
|
9
|
+
* PRED spustením:
|
|
10
|
+
* 1. Nastav DATABASE_URL env var
|
|
11
|
+
* 2. Over že UserFile tabuľka existuje
|
|
12
|
+
* 3. Spusti v staging/dev NAJPRV
|
|
13
|
+
*
|
|
14
|
+
* Spustenie: npx tsx src/migrations/migrateIngestedDocuments.ts
|
|
15
|
+
* Rollback: IngestedDocument záznamy sa NEMAŽÚ
|
|
16
|
+
*/
|
|
17
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
18
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
19
|
+
};
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
const app_1 = require("../../prisma/generated/app");
|
|
22
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
23
|
+
const prisma = new app_1.PrismaClient();
|
|
24
|
+
async function migrateIngestedDocuments() {
|
|
25
|
+
console.log("Začínam migráciu IngestedDocument → UserFile");
|
|
26
|
+
const docs = await prisma.$queryRaw `SELECT * FROM "IngestedDocument" WHERE "storageUrl" IS NOT NULL OR "storagePath" IS NOT NULL`;
|
|
27
|
+
console.log(`Nájdených ${docs.length} IngestedDocument záznamov`);
|
|
28
|
+
let migrated = 0;
|
|
29
|
+
let skipped = 0;
|
|
30
|
+
let errors = 0;
|
|
31
|
+
for (const doc of docs) {
|
|
32
|
+
try {
|
|
33
|
+
// Odvodni storageKey z storageUrl alebo storagePath
|
|
34
|
+
let storageKey = doc.storageUrl || doc.storagePath || null;
|
|
35
|
+
if (!storageKey) {
|
|
36
|
+
console.warn(`IngestedDocument ${doc.id} (${doc.fileName}) nemá storage, skip`);
|
|
37
|
+
skipped++;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
// Ak je to full URL, extrahuj path (key)
|
|
41
|
+
if (storageKey.startsWith("https://")) {
|
|
42
|
+
try {
|
|
43
|
+
const url = new URL(storageKey);
|
|
44
|
+
storageKey = url.pathname.replace(/^\/[^/]+\//, ""); // odober bucket z path
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// nechaj ako je
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Skontroluj dedup podľa qdrantDocId
|
|
51
|
+
if (doc.qdrantDocId) {
|
|
52
|
+
const existing = await prisma.userFile.findFirst({
|
|
53
|
+
where: { qdrantDocId: doc.qdrantDocId },
|
|
54
|
+
});
|
|
55
|
+
if (existing) {
|
|
56
|
+
console.log(`UserFile pre qdrantDocId ${doc.qdrantDocId} už existuje, skip`);
|
|
57
|
+
skipped++;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const userFile = await prisma.userFile.create({
|
|
62
|
+
data: {
|
|
63
|
+
userId: doc.userId,
|
|
64
|
+
storageKey,
|
|
65
|
+
// PLACEHOLDER: SHA-256 nie je dostupné pre migrované IngestedDocument záznamy.
|
|
66
|
+
// Dedup cez checksumSha256 nebude fungovať pre tieto záznamy.
|
|
67
|
+
// Po migrácii: ak je potrebný skutočný hash, stiahnuť súbor z S3 a prepočítať.
|
|
68
|
+
checksumSha256: `migrated_${crypto_1.default.randomUUID()}`,
|
|
69
|
+
fileName: doc.fileName,
|
|
70
|
+
fileType: doc.fileType || "application/pdf",
|
|
71
|
+
fileSize: doc.fileSize || 0,
|
|
72
|
+
status: doc.status === "COMPLETED" ? "INDEXED" : "STORED",
|
|
73
|
+
docType: doc.docType,
|
|
74
|
+
legalArea: doc.legalArea,
|
|
75
|
+
summary: doc.summary,
|
|
76
|
+
references: doc.references || [],
|
|
77
|
+
chunkCount: doc.chunkCount,
|
|
78
|
+
qdrantDocId: doc.qdrantDocId,
|
|
79
|
+
indexingCost: doc.embeddingCost,
|
|
80
|
+
indexedAt: doc.status === "COMPLETED" ? doc.updatedAt : null,
|
|
81
|
+
tags: [],
|
|
82
|
+
createdAt: doc.createdAt,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
// Ak má folderId, vytvor FolderItem
|
|
86
|
+
if (doc.folderId) {
|
|
87
|
+
await prisma.folderItem.create({
|
|
88
|
+
data: {
|
|
89
|
+
folderId: doc.folderId,
|
|
90
|
+
entityType: "USER_FILE",
|
|
91
|
+
entityId: userFile.id,
|
|
92
|
+
addedById: doc.userId,
|
|
93
|
+
sortOrder: 0,
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
console.log(`Migrovaný: ${doc.fileName} (status: ${doc.status} → ${userFile.status})`);
|
|
98
|
+
migrated++;
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
console.error(`Chyba pri migrácii IngestedDocument ${doc.id}:`, err);
|
|
102
|
+
errors++;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
console.log("\nVýsledok:");
|
|
106
|
+
console.log(` Migrovaných: ${migrated}`);
|
|
107
|
+
console.log(` Preskočených: ${skipped}`);
|
|
108
|
+
console.log(` Chýb: ${errors}`);
|
|
109
|
+
}
|
|
110
|
+
migrateIngestedDocuments()
|
|
111
|
+
.catch(console.error)
|
|
112
|
+
.finally(() => prisma.$disconnect());
|
|
113
|
+
//# sourceMappingURL=migrateIngestedDocuments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrateIngestedDocuments.js","sourceRoot":"","sources":["../../src/migrations/migrateIngestedDocuments.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;;;;AAEH,oDAA0D;AAC1D,oDAA4B;AAE5B,MAAM,MAAM,GAAG,IAAI,kBAAY,EAAE,CAAC;AAElC,KAAK,UAAU,wBAAwB;IACrC,OAAO,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC;IAE5D,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,SAAS,CAqBlC,8FAA8F,CAAC;IAEhG,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,CAAC,MAAM,4BAA4B,CAAC,CAAC;IAElE,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,MAAM,GAAG,CAAC,CAAC;IAEf,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,CAAC;YACH,oDAAoD;YACpD,IAAI,UAAU,GAAG,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,WAAW,IAAI,IAAI,CAAC;YAE3D,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,OAAO,CAAC,IAAI,CAAC,oBAAoB,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,QAAQ,sBAAsB,CAAC,CAAC;gBAChF,OAAO,EAAE,CAAC;gBACV,SAAS;YACX,CAAC;YAED,yCAAyC;YACzC,IAAI,UAAU,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBACtC,IAAI,CAAC;oBACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;oBAChC,UAAU,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,CAAC,uBAAuB;gBAC9E,CAAC;gBAAC,MAAM,CAAC;oBACP,gBAAgB;gBAClB,CAAC;YACH,CAAC;YAED,qCAAqC;YACrC,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;gBACpB,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;oBAC/C,KAAK,EAAE,EAAE,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE;iBACxC,CAAC,CAAC;gBACH,IAAI,QAAQ,EAAE,CAAC;oBACb,OAAO,CAAC,GAAG,CAAC,4BAA4B,GAAG,CAAC,WAAW,oBAAoB,CAAC,CAAC;oBAC7E,OAAO,EAAE,CAAC;oBACV,SAAS;gBACX,CAAC;YACH,CAAC;YAED,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAC5C,IAAI,EAAE;oBACJ,MAAM,EAAE,GAAG,CAAC,MAAM;oBAClB,UAAU;oBACV,+EAA+E;oBAC/E,8DAA8D;oBAC9D,+EAA+E;oBAC/E,cAAc,EAAE,YAAY,gBAAM,CAAC,UAAU,EAAE,EAAE;oBACjD,QAAQ,EAAE,GAAG,CAAC,QAAQ;oBACtB,QAAQ,EAAE,GAAG,CAAC,QAAQ,IAAI,iBAAiB;oBAC3C,QAAQ,EAAE,GAAG,CAAC,QAAQ,IAAI,CAAC;oBAC3B,MAAM,EAAE,GAAG,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ;oBACzD,OAAO,EAAE,GAAG,CAAC,OAAO;oBACpB,SAAS,EAAE,GAAG,CAAC,SAAS;oBACxB,OAAO,EAAE,GAAG,CAAC,OAAO;oBACpB,UAAU,EAAE,GAAG,CAAC,UAAU,IAAI,EAAE;oBAChC,UAAU,EAAE,GAAG,CAAC,UAAU;oBAC1B,WAAW,EAAE,GAAG,CAAC,WAAW;oBAC5B,YAAY,EAAE,GAAG,CAAC,aAAa;oBAC/B,SAAS,EAAE,GAAG,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI;oBAC5D,IAAI,EAAE,EAAE;oBACR,SAAS,EAAE,GAAG,CAAC,SAAS;iBACzB;aACF,CAAC,CAAC;YAEH,oCAAoC;YACpC,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;gBACjB,MAAM,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;oBAC7B,IAAI,EAAE;wBACJ,QAAQ,EAAE,GAAG,CAAC,QAAQ;wBACtB,UAAU,EAAE,WAAW;wBACvB,QAAQ,EAAE,QAAQ,CAAC,EAAE;wBACrB,SAAS,EAAE,GAAG,CAAC,MAAM;wBACrB,SAAS,EAAE,CAAC;qBACb;iBACF,CAAC,CAAC;YACL,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,cAAc,GAAG,CAAC,QAAQ,aAAa,GAAG,CAAC,MAAM,MAAM,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;YACvF,QAAQ,EAAE,CAAC;QACb,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,uCAAuC,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;YACrE,MAAM,EAAE,CAAC;QACX,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAC3B,OAAO,CAAC,GAAG,CAAC,kBAAkB,QAAQ,EAAE,CAAC,CAAC;IAC1C,OAAO,CAAC,GAAG,CAAC,mBAAmB,OAAO,EAAE,CAAC,CAAC;IAC1C,OAAO,CAAC,GAAG,CAAC,WAAW,MAAM,EAAE,CAAC,CAAC;AACnC,CAAC;AAED,wBAAwB,EAAE;KACvB,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;KACpB,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migrácia MessageFile (base64 v DB) → UserFile (S3/DO Spaces)
|
|
3
|
+
*
|
|
4
|
+
* PRED spustením:
|
|
5
|
+
* 1. Nastav env vars: DO_SPACES_ENDPOINT, DO_SPACES_REGION, DO_SPACES_BUCKET,
|
|
6
|
+
* DO_SPACES_ACCESS_KEY, DO_SPACES_SECRET_KEY, DATABASE_URL
|
|
7
|
+
* 2. Over že UserFile tabuľka existuje (migrácia 20260502185044_add_userfile_system)
|
|
8
|
+
* 3. Spusti v staging/dev NAJPRV pred prod
|
|
9
|
+
*
|
|
10
|
+
* Spustenie: npx tsx src/migrations/migrateMessageFiles.ts
|
|
11
|
+
* Rollback: MessageFile záznamy sa NEMAŽÚ — rollback = vyčisti UserFile záznamy
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=migrateMessageFiles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrateMessageFiles.d.ts","sourceRoot":"","sources":["../../src/migrations/migrateMessageFiles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Migrácia MessageFile (base64 v DB) → UserFile (S3/DO Spaces)
|
|
4
|
+
*
|
|
5
|
+
* PRED spustením:
|
|
6
|
+
* 1. Nastav env vars: DO_SPACES_ENDPOINT, DO_SPACES_REGION, DO_SPACES_BUCKET,
|
|
7
|
+
* DO_SPACES_ACCESS_KEY, DO_SPACES_SECRET_KEY, DATABASE_URL
|
|
8
|
+
* 2. Over že UserFile tabuľka existuje (migrácia 20260502185044_add_userfile_system)
|
|
9
|
+
* 3. Spusti v staging/dev NAJPRV pred prod
|
|
10
|
+
*
|
|
11
|
+
* Spustenie: npx tsx src/migrations/migrateMessageFiles.ts
|
|
12
|
+
* Rollback: MessageFile záznamy sa NEMAŽÚ — rollback = vyčisti UserFile záznamy
|
|
13
|
+
*/
|
|
14
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
15
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
16
|
+
};
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
const app_1 = require("../../prisma/generated/app");
|
|
19
|
+
const client_s3_1 = require("@aws-sdk/client-s3");
|
|
20
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
21
|
+
const prisma = new app_1.PrismaClient();
|
|
22
|
+
const s3 = new client_s3_1.S3Client({
|
|
23
|
+
endpoint: process.env.DO_SPACES_ENDPOINT || "https://fra1.digitaloceanspaces.com",
|
|
24
|
+
region: process.env.DO_SPACES_REGION || "fra1",
|
|
25
|
+
credentials: {
|
|
26
|
+
accessKeyId: process.env.DO_SPACES_ACCESS_KEY || "",
|
|
27
|
+
secretAccessKey: process.env.DO_SPACES_SECRET_KEY || "",
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
const BUCKET = process.env.DO_SPACES_BUCKET || "smartlex-user-files";
|
|
31
|
+
async function migrateMessageFiles() {
|
|
32
|
+
console.log("Začínam migráciu MessageFile → UserFile");
|
|
33
|
+
// Nájdi všetky FolderItem záznamy s entityType=ATTACHMENT
|
|
34
|
+
const attachments = await prisma.folderItem.findMany({
|
|
35
|
+
where: { entityType: "ATTACHMENT" },
|
|
36
|
+
});
|
|
37
|
+
console.log(`Nájdených ${attachments.length} ATTACHMENT FolderItem záznamov`);
|
|
38
|
+
let migrated = 0;
|
|
39
|
+
let skipped = 0;
|
|
40
|
+
let errors = 0;
|
|
41
|
+
for (const attachment of attachments) {
|
|
42
|
+
try {
|
|
43
|
+
// Načítaj MessageFile cez raw query (Prisma ho môže nemať ako typed model)
|
|
44
|
+
const messageFiles = await prisma.$queryRaw `SELECT id, "fileName", "fileType", "base64Data", "uploadedAt" FROM "MessageFile" WHERE id = ${attachment.entityId}`;
|
|
45
|
+
if (!messageFiles[0]) {
|
|
46
|
+
console.warn(`MessageFile ${attachment.entityId} nenájdený, skip`);
|
|
47
|
+
skipped++;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const mf = messageFiles[0];
|
|
51
|
+
// Dekóduj base64 → buffer
|
|
52
|
+
if (!mf.base64Data) {
|
|
53
|
+
console.warn(`MessageFile ${mf.id} (${mf.fileName}) má prázdny base64Data, skip`);
|
|
54
|
+
skipped++;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const buffer = Buffer.from(mf.base64Data, "base64");
|
|
58
|
+
const checksumSha256 = crypto_1.default.createHash("sha256").update(buffer).digest("hex");
|
|
59
|
+
// Skontroluj dedup podľa checksum
|
|
60
|
+
const existing = await prisma.userFile.findFirst({
|
|
61
|
+
where: { checksumSha256 },
|
|
62
|
+
});
|
|
63
|
+
if (existing) {
|
|
64
|
+
console.log(`UserFile pre checksum ${checksumSha256.substring(0, 8)}... už existuje, aktualizujem FolderItem`);
|
|
65
|
+
await prisma.folderItem.update({
|
|
66
|
+
where: { id: attachment.id },
|
|
67
|
+
data: { entityType: "USER_FILE", entityId: existing.id },
|
|
68
|
+
});
|
|
69
|
+
migrated++;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
// Upload do S3
|
|
73
|
+
const userId = attachment.addedById;
|
|
74
|
+
const uuid = crypto_1.default.randomUUID();
|
|
75
|
+
const safe = (mf.fileName || "file").replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
76
|
+
const storageKey = `users/${userId}/files/migrated_${uuid}_${safe}`;
|
|
77
|
+
await s3.send(new client_s3_1.PutObjectCommand({
|
|
78
|
+
Bucket: BUCKET,
|
|
79
|
+
Key: storageKey,
|
|
80
|
+
Body: buffer,
|
|
81
|
+
ContentType: mf.fileType || "application/octet-stream",
|
|
82
|
+
ContentLength: buffer.length,
|
|
83
|
+
}));
|
|
84
|
+
// Vytvor UserFile
|
|
85
|
+
const userFile = await prisma.userFile.create({
|
|
86
|
+
data: {
|
|
87
|
+
userId,
|
|
88
|
+
storageKey,
|
|
89
|
+
checksumSha256,
|
|
90
|
+
fileName: mf.fileName || "unknown",
|
|
91
|
+
fileType: mf.fileType || "application/octet-stream",
|
|
92
|
+
fileSize: buffer.length,
|
|
93
|
+
status: "STORED",
|
|
94
|
+
tags: [],
|
|
95
|
+
references: [],
|
|
96
|
+
createdAt: mf.uploadedAt,
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
// Aktualizuj FolderItem: ATTACHMENT → USER_FILE
|
|
100
|
+
await prisma.folderItem.update({
|
|
101
|
+
where: { id: attachment.id },
|
|
102
|
+
data: { entityType: "USER_FILE", entityId: userFile.id },
|
|
103
|
+
});
|
|
104
|
+
console.log(`Migrovaný: ${mf.fileName} (${buffer.length} bytes) → ${storageKey}`);
|
|
105
|
+
migrated++;
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
console.error(`Chyba pri migrácii attachment ${attachment.id}:`, err);
|
|
109
|
+
errors++;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
console.log("\nVýsledok migrácie:");
|
|
113
|
+
console.log(` Migrovaných: ${migrated}`);
|
|
114
|
+
console.log(` Preskočených: ${skipped}`);
|
|
115
|
+
console.log(` Chýb: ${errors}`);
|
|
116
|
+
}
|
|
117
|
+
migrateMessageFiles()
|
|
118
|
+
.catch(console.error)
|
|
119
|
+
.finally(() => prisma.$disconnect());
|
|
120
|
+
//# sourceMappingURL=migrateMessageFiles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrateMessageFiles.js","sourceRoot":"","sources":["../../src/migrations/migrateMessageFiles.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danielcok17/prisma-db",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.19.1",
|
|
4
4
|
"description": "Shared Prisma schema for Legal AI applications",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"@prisma/client": "^6.17.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
+
"@aws-sdk/client-s3": "^3.1041.0",
|
|
42
43
|
"@types/node": "^24.3.0",
|
|
43
44
|
"prisma": "^6.17.0",
|
|
44
45
|
"tsx": "^4.0.0",
|
package/prisma/app.prisma
CHANGED
|
@@ -137,6 +137,11 @@ model User {
|
|
|
137
137
|
sequenceEnrollments EmailSequenceEnrollment[]
|
|
138
138
|
referralsSent Referral[] @relation("ReferralsSent")
|
|
139
139
|
referralsReceived Referral[] @relation("ReferralsReceived")
|
|
140
|
+
// Files & folders system
|
|
141
|
+
userFiles UserFile[] @relation("UserFiles")
|
|
142
|
+
reviewTables FileReviewTable[] @relation("UserReviewTables")
|
|
143
|
+
filesTotalBytes BigInt @default(0)
|
|
144
|
+
filesCount Int @default(0)
|
|
140
145
|
|
|
141
146
|
// Multi-brand: compound unique allows same email across brands
|
|
142
147
|
@@unique([email, brand])
|
|
@@ -293,6 +298,7 @@ model Conversation {
|
|
|
293
298
|
user User @relation(fields: [userId], references: [id])
|
|
294
299
|
workflowLogs WorkflowLog[]
|
|
295
300
|
canvasDocuments CanvasDocument[]
|
|
301
|
+
fileUsages UserFileConversation[]
|
|
296
302
|
|
|
297
303
|
@@index([userId])
|
|
298
304
|
@@index([createdAt])
|
|
@@ -325,7 +331,6 @@ model Answer {
|
|
|
325
331
|
WorkflowLog WorkflowLog[]
|
|
326
332
|
files MessageFile[]
|
|
327
333
|
canvasDocumentId String? // No FK constraint - flexible document management
|
|
328
|
-
|
|
329
334
|
@@index([conversationId])
|
|
330
335
|
@@index([messageId])
|
|
331
336
|
@@index([role])
|
|
@@ -890,7 +895,8 @@ enum FolderItemType {
|
|
|
890
895
|
CONVERSATION
|
|
891
896
|
CANVAS_DOCUMENT
|
|
892
897
|
REFERENCE
|
|
893
|
-
ATTACHMENT
|
|
898
|
+
ATTACHMENT // deprecated — kept for migration of old data
|
|
899
|
+
USER_FILE
|
|
894
900
|
}
|
|
895
901
|
|
|
896
902
|
// Folder activity types (for timeline)
|
|
@@ -902,10 +908,41 @@ enum FolderActivityType {
|
|
|
902
908
|
ITEM_ADDED
|
|
903
909
|
ITEM_REMOVED
|
|
904
910
|
ITEM_UPDATED
|
|
911
|
+
ITEM_MOVED
|
|
905
912
|
SHARE_ADDED
|
|
906
913
|
SHARE_REMOVED
|
|
907
914
|
USER_MESSAGE
|
|
908
915
|
USER_NOTE
|
|
916
|
+
FILE_INDEXED
|
|
917
|
+
FILE_INDEX_ERROR
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
enum UserFileStatus {
|
|
921
|
+
STORED
|
|
922
|
+
QUEUED
|
|
923
|
+
INDEXING
|
|
924
|
+
INDEXED
|
|
925
|
+
ERROR
|
|
926
|
+
DELETED
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
enum UserFileUploadSource {
|
|
930
|
+
FOLDER // nahraté cez UI priečinka / prípadu
|
|
931
|
+
CHAT // nahraté z chatovej konverzácie
|
|
932
|
+
STORAGE // nahraté priamo do "Môj archív"
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
enum IndexingJobStatus {
|
|
936
|
+
QUEUED
|
|
937
|
+
RUNNING
|
|
938
|
+
COMPLETED
|
|
939
|
+
FAILED
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
enum FolderType {
|
|
943
|
+
PERSONAL
|
|
944
|
+
MATTER
|
|
945
|
+
KNOWLEDGE_BASE
|
|
909
946
|
}
|
|
910
947
|
|
|
911
948
|
// Folder permission levels
|
|
@@ -1089,6 +1126,137 @@ enum IngestionStatus {
|
|
|
1089
1126
|
ERROR
|
|
1090
1127
|
}
|
|
1091
1128
|
|
|
1129
|
+
model UserFile {
|
|
1130
|
+
id String @id @default(cuid())
|
|
1131
|
+
userId String
|
|
1132
|
+
|
|
1133
|
+
storageKey String @unique
|
|
1134
|
+
storageUrl String?
|
|
1135
|
+
checksumSha256 String
|
|
1136
|
+
|
|
1137
|
+
fileName String
|
|
1138
|
+
fileType String
|
|
1139
|
+
fileSize Int
|
|
1140
|
+
pageCount Int?
|
|
1141
|
+
language String? @default("sk")
|
|
1142
|
+
|
|
1143
|
+
status UserFileStatus @default(STORED)
|
|
1144
|
+
|
|
1145
|
+
docType String?
|
|
1146
|
+
legalArea String?
|
|
1147
|
+
summary String?
|
|
1148
|
+
extractedData Json?
|
|
1149
|
+
references String[] @default([])
|
|
1150
|
+
chunkCount Int?
|
|
1151
|
+
qdrantDocId String? @unique
|
|
1152
|
+
indexingCost Float?
|
|
1153
|
+
indexedAt DateTime?
|
|
1154
|
+
indexError String?
|
|
1155
|
+
|
|
1156
|
+
tags String[] @default([])
|
|
1157
|
+
|
|
1158
|
+
parentFileId String?
|
|
1159
|
+
versionNumber Int @default(1)
|
|
1160
|
+
|
|
1161
|
+
uploadSource UserFileUploadSource @default(STORAGE)
|
|
1162
|
+
uploadContextId String? // polymorphic: folderId | conversationId | null
|
|
1163
|
+
|
|
1164
|
+
createdAt DateTime @default(now())
|
|
1165
|
+
updatedAt DateTime @updatedAt
|
|
1166
|
+
deletedAt DateTime?
|
|
1167
|
+
|
|
1168
|
+
user User @relation("UserFiles", fields: [userId], references: [id], onDelete: Cascade)
|
|
1169
|
+
indexingJobs FileIndexingJob[]
|
|
1170
|
+
deadlines FileDeadline[]
|
|
1171
|
+
parentFile UserFile? @relation("FileVersions", fields: [parentFileId], references: [id], onDelete: SetNull)
|
|
1172
|
+
childVersions UserFile[] @relation("FileVersions")
|
|
1173
|
+
conversationUsages UserFileConversation[]
|
|
1174
|
+
|
|
1175
|
+
@@index([userId])
|
|
1176
|
+
@@index([status])
|
|
1177
|
+
@@index([userId, status])
|
|
1178
|
+
@@index([userId, deletedAt])
|
|
1179
|
+
@@index([qdrantDocId])
|
|
1180
|
+
@@index([uploadSource])
|
|
1181
|
+
@@index([uploadContextId])
|
|
1182
|
+
@@index([checksumSha256])
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
model UserFileConversation {
|
|
1186
|
+
id String @id @default(cuid())
|
|
1187
|
+
fileId String
|
|
1188
|
+
conversationId String
|
|
1189
|
+
createdAt DateTime @default(now())
|
|
1190
|
+
|
|
1191
|
+
file UserFile @relation(fields: [fileId], references: [id], onDelete: Cascade)
|
|
1192
|
+
conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
|
|
1193
|
+
|
|
1194
|
+
@@unique([fileId, conversationId])
|
|
1195
|
+
@@index([fileId])
|
|
1196
|
+
@@index([conversationId])
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
model FileIndexingJob {
|
|
1200
|
+
id String @id @default(cuid())
|
|
1201
|
+
fileId String
|
|
1202
|
+
userId String
|
|
1203
|
+
status IndexingJobStatus @default(QUEUED)
|
|
1204
|
+
attempts Int @default(0)
|
|
1205
|
+
maxAttempts Int @default(3)
|
|
1206
|
+
errorMsg String?
|
|
1207
|
+
startedAt DateTime?
|
|
1208
|
+
finishedAt DateTime?
|
|
1209
|
+
createdAt DateTime @default(now())
|
|
1210
|
+
|
|
1211
|
+
file UserFile @relation(fields: [fileId], references: [id], onDelete: Cascade)
|
|
1212
|
+
|
|
1213
|
+
@@index([fileId])
|
|
1214
|
+
@@index([userId])
|
|
1215
|
+
@@index([status])
|
|
1216
|
+
@@index([createdAt])
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
model FileDeadline {
|
|
1220
|
+
id String @id @default(cuid())
|
|
1221
|
+
fileId String
|
|
1222
|
+
userId String
|
|
1223
|
+
label String
|
|
1224
|
+
deadlineAt DateTime
|
|
1225
|
+
notifyAt DateTime?
|
|
1226
|
+
isNotified Boolean @default(false)
|
|
1227
|
+
isDismissed Boolean @default(false)
|
|
1228
|
+
sourceSection String?
|
|
1229
|
+
sourcePage Int?
|
|
1230
|
+
createdAt DateTime @default(now())
|
|
1231
|
+
|
|
1232
|
+
file UserFile @relation(fields: [fileId], references: [id], onDelete: Cascade)
|
|
1233
|
+
|
|
1234
|
+
@@index([fileId])
|
|
1235
|
+
@@index([userId])
|
|
1236
|
+
@@index([deadlineAt])
|
|
1237
|
+
@@index([notifyAt, isNotified])
|
|
1238
|
+
@@index([userId, isDismissed])
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
model FileReviewTable {
|
|
1242
|
+
id String @id @default(cuid())
|
|
1243
|
+
userId String
|
|
1244
|
+
folderId String?
|
|
1245
|
+
name String
|
|
1246
|
+
columns Json
|
|
1247
|
+
fileIds String[] @default([])
|
|
1248
|
+
results Json?
|
|
1249
|
+
status String @default("draft")
|
|
1250
|
+
createdAt DateTime @default(now())
|
|
1251
|
+
updatedAt DateTime @updatedAt
|
|
1252
|
+
|
|
1253
|
+
user User @relation("UserReviewTables", fields: [userId], references: [id], onDelete: Cascade)
|
|
1254
|
+
|
|
1255
|
+
@@index([userId])
|
|
1256
|
+
@@index([folderId])
|
|
1257
|
+
@@index([status])
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1092
1260
|
enum LifecycleStage {
|
|
1093
1261
|
REGISTERED
|
|
1094
1262
|
ACTIVATED
|
|
@@ -1237,6 +1405,9 @@ model Folder {
|
|
|
1237
1405
|
path String @default("/") // Materialized path: "/parentId/grandparentId/..."
|
|
1238
1406
|
depth Int @default(0)
|
|
1239
1407
|
|
|
1408
|
+
// Folder type
|
|
1409
|
+
folderType FolderType @default(PERSONAL)
|
|
1410
|
+
|
|
1240
1411
|
// Metadata
|
|
1241
1412
|
sortOrder Int @default(0)
|
|
1242
1413
|
isArchived Boolean @default(false)
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
-- CreateEnum
|
|
2
|
+
CREATE TYPE "UserFileStatus" AS ENUM ('STORED', 'QUEUED', 'INDEXING', 'INDEXED', 'ERROR', 'DELETED');
|
|
3
|
+
|
|
4
|
+
-- CreateEnum
|
|
5
|
+
CREATE TYPE "IndexingJobStatus" AS ENUM ('QUEUED', 'RUNNING', 'COMPLETED', 'FAILED');
|
|
6
|
+
|
|
7
|
+
-- CreateEnum
|
|
8
|
+
CREATE TYPE "FolderType" AS ENUM ('PERSONAL', 'MATTER', 'KNOWLEDGE_BASE');
|
|
9
|
+
|
|
10
|
+
-- AlterEnum
|
|
11
|
+
-- This migration adds more than one value to an enum.
|
|
12
|
+
-- With PostgreSQL versions 11 and earlier, this is not possible
|
|
13
|
+
-- in a single migration. This can be worked around by creating
|
|
14
|
+
-- multiple migrations, each migration adding only one value to
|
|
15
|
+
-- the enum.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
ALTER TYPE "FolderActivityType" ADD VALUE 'ITEM_MOVED';
|
|
19
|
+
ALTER TYPE "FolderActivityType" ADD VALUE 'FILE_INDEXED';
|
|
20
|
+
ALTER TYPE "FolderActivityType" ADD VALUE 'FILE_INDEX_ERROR';
|
|
21
|
+
|
|
22
|
+
-- AlterEnum
|
|
23
|
+
ALTER TYPE "FolderItemType" ADD VALUE 'USER_FILE';
|
|
24
|
+
|
|
25
|
+
-- AlterTable
|
|
26
|
+
ALTER TABLE "Folder" ADD COLUMN "folderType" "FolderType" NOT NULL DEFAULT 'PERSONAL';
|
|
27
|
+
|
|
28
|
+
-- AlterTable
|
|
29
|
+
ALTER TABLE "User" ADD COLUMN "filesCount" INTEGER NOT NULL DEFAULT 0,
|
|
30
|
+
ADD COLUMN "filesTotalBytes" BIGINT NOT NULL DEFAULT 0;
|
|
31
|
+
|
|
32
|
+
-- CreateTable
|
|
33
|
+
CREATE TABLE "UserFile" (
|
|
34
|
+
"id" TEXT NOT NULL,
|
|
35
|
+
"userId" TEXT NOT NULL,
|
|
36
|
+
"storageKey" TEXT NOT NULL,
|
|
37
|
+
"storageUrl" TEXT,
|
|
38
|
+
"checksumSha256" TEXT NOT NULL,
|
|
39
|
+
"fileName" TEXT NOT NULL,
|
|
40
|
+
"fileType" TEXT NOT NULL,
|
|
41
|
+
"fileSize" INTEGER NOT NULL,
|
|
42
|
+
"pageCount" INTEGER,
|
|
43
|
+
"language" TEXT DEFAULT 'sk',
|
|
44
|
+
"status" "UserFileStatus" NOT NULL DEFAULT 'STORED',
|
|
45
|
+
"docType" TEXT,
|
|
46
|
+
"legalArea" TEXT,
|
|
47
|
+
"summary" TEXT,
|
|
48
|
+
"extractedData" JSONB,
|
|
49
|
+
"references" TEXT[] DEFAULT ARRAY[]::TEXT[],
|
|
50
|
+
"chunkCount" INTEGER,
|
|
51
|
+
"qdrantDocId" TEXT,
|
|
52
|
+
"indexingCost" DOUBLE PRECISION,
|
|
53
|
+
"indexedAt" TIMESTAMP(3),
|
|
54
|
+
"indexError" TEXT,
|
|
55
|
+
"tags" TEXT[] DEFAULT ARRAY[]::TEXT[],
|
|
56
|
+
"parentFileId" TEXT,
|
|
57
|
+
"versionNumber" INTEGER NOT NULL DEFAULT 1,
|
|
58
|
+
"answerId" TEXT,
|
|
59
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
60
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
61
|
+
"deletedAt" TIMESTAMP(3),
|
|
62
|
+
|
|
63
|
+
CONSTRAINT "UserFile_pkey" PRIMARY KEY ("id")
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
-- CreateTable
|
|
67
|
+
CREATE TABLE "FileIndexingJob" (
|
|
68
|
+
"id" TEXT NOT NULL,
|
|
69
|
+
"fileId" TEXT NOT NULL,
|
|
70
|
+
"userId" TEXT NOT NULL,
|
|
71
|
+
"status" "IndexingJobStatus" NOT NULL DEFAULT 'QUEUED',
|
|
72
|
+
"attempts" INTEGER NOT NULL DEFAULT 0,
|
|
73
|
+
"maxAttempts" INTEGER NOT NULL DEFAULT 3,
|
|
74
|
+
"errorMsg" TEXT,
|
|
75
|
+
"startedAt" TIMESTAMP(3),
|
|
76
|
+
"finishedAt" TIMESTAMP(3),
|
|
77
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
78
|
+
|
|
79
|
+
CONSTRAINT "FileIndexingJob_pkey" PRIMARY KEY ("id")
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
-- CreateTable
|
|
83
|
+
CREATE TABLE "FileDeadline" (
|
|
84
|
+
"id" TEXT NOT NULL,
|
|
85
|
+
"fileId" TEXT NOT NULL,
|
|
86
|
+
"userId" TEXT NOT NULL,
|
|
87
|
+
"label" TEXT NOT NULL,
|
|
88
|
+
"deadlineAt" TIMESTAMP(3) NOT NULL,
|
|
89
|
+
"notifyAt" TIMESTAMP(3),
|
|
90
|
+
"isNotified" BOOLEAN NOT NULL DEFAULT false,
|
|
91
|
+
"isDismissed" BOOLEAN NOT NULL DEFAULT false,
|
|
92
|
+
"sourceSection" TEXT,
|
|
93
|
+
"sourcePage" INTEGER,
|
|
94
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
95
|
+
|
|
96
|
+
CONSTRAINT "FileDeadline_pkey" PRIMARY KEY ("id")
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
-- CreateTable
|
|
100
|
+
CREATE TABLE "FileReviewTable" (
|
|
101
|
+
"id" TEXT NOT NULL,
|
|
102
|
+
"userId" TEXT NOT NULL,
|
|
103
|
+
"folderId" TEXT,
|
|
104
|
+
"name" TEXT NOT NULL,
|
|
105
|
+
"columns" JSONB NOT NULL,
|
|
106
|
+
"fileIds" TEXT[] DEFAULT ARRAY[]::TEXT[],
|
|
107
|
+
"results" JSONB,
|
|
108
|
+
"status" TEXT NOT NULL DEFAULT 'draft',
|
|
109
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
110
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
111
|
+
|
|
112
|
+
CONSTRAINT "FileReviewTable_pkey" PRIMARY KEY ("id")
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
-- CreateIndex
|
|
116
|
+
CREATE UNIQUE INDEX "UserFile_storageKey_key" ON "UserFile"("storageKey");
|
|
117
|
+
|
|
118
|
+
-- CreateIndex
|
|
119
|
+
CREATE UNIQUE INDEX "UserFile_qdrantDocId_key" ON "UserFile"("qdrantDocId");
|
|
120
|
+
|
|
121
|
+
-- CreateIndex
|
|
122
|
+
CREATE INDEX "UserFile_userId_idx" ON "UserFile"("userId");
|
|
123
|
+
|
|
124
|
+
-- CreateIndex
|
|
125
|
+
CREATE INDEX "UserFile_status_idx" ON "UserFile"("status");
|
|
126
|
+
|
|
127
|
+
-- CreateIndex
|
|
128
|
+
CREATE INDEX "UserFile_userId_status_idx" ON "UserFile"("userId", "status");
|
|
129
|
+
|
|
130
|
+
-- CreateIndex
|
|
131
|
+
CREATE INDEX "UserFile_userId_deletedAt_idx" ON "UserFile"("userId", "deletedAt");
|
|
132
|
+
|
|
133
|
+
-- CreateIndex
|
|
134
|
+
CREATE INDEX "UserFile_qdrantDocId_idx" ON "UserFile"("qdrantDocId");
|
|
135
|
+
|
|
136
|
+
-- CreateIndex
|
|
137
|
+
CREATE INDEX "UserFile_answerId_idx" ON "UserFile"("answerId");
|
|
138
|
+
|
|
139
|
+
-- CreateIndex
|
|
140
|
+
CREATE INDEX "UserFile_checksumSha256_idx" ON "UserFile"("checksumSha256");
|
|
141
|
+
|
|
142
|
+
-- CreateIndex
|
|
143
|
+
CREATE INDEX "FileIndexingJob_fileId_idx" ON "FileIndexingJob"("fileId");
|
|
144
|
+
|
|
145
|
+
-- CreateIndex
|
|
146
|
+
CREATE INDEX "FileIndexingJob_userId_idx" ON "FileIndexingJob"("userId");
|
|
147
|
+
|
|
148
|
+
-- CreateIndex
|
|
149
|
+
CREATE INDEX "FileIndexingJob_status_idx" ON "FileIndexingJob"("status");
|
|
150
|
+
|
|
151
|
+
-- CreateIndex
|
|
152
|
+
CREATE INDEX "FileIndexingJob_createdAt_idx" ON "FileIndexingJob"("createdAt");
|
|
153
|
+
|
|
154
|
+
-- CreateIndex
|
|
155
|
+
CREATE INDEX "FileDeadline_fileId_idx" ON "FileDeadline"("fileId");
|
|
156
|
+
|
|
157
|
+
-- CreateIndex
|
|
158
|
+
CREATE INDEX "FileDeadline_userId_idx" ON "FileDeadline"("userId");
|
|
159
|
+
|
|
160
|
+
-- CreateIndex
|
|
161
|
+
CREATE INDEX "FileDeadline_deadlineAt_idx" ON "FileDeadline"("deadlineAt");
|
|
162
|
+
|
|
163
|
+
-- CreateIndex
|
|
164
|
+
CREATE INDEX "FileDeadline_notifyAt_isNotified_idx" ON "FileDeadline"("notifyAt", "isNotified");
|
|
165
|
+
|
|
166
|
+
-- CreateIndex
|
|
167
|
+
CREATE INDEX "FileDeadline_userId_isDismissed_idx" ON "FileDeadline"("userId", "isDismissed");
|
|
168
|
+
|
|
169
|
+
-- CreateIndex
|
|
170
|
+
CREATE INDEX "FileReviewTable_userId_idx" ON "FileReviewTable"("userId");
|
|
171
|
+
|
|
172
|
+
-- CreateIndex
|
|
173
|
+
CREATE INDEX "FileReviewTable_folderId_idx" ON "FileReviewTable"("folderId");
|
|
174
|
+
|
|
175
|
+
-- CreateIndex
|
|
176
|
+
CREATE INDEX "FileReviewTable_status_idx" ON "FileReviewTable"("status");
|
|
177
|
+
|
|
178
|
+
-- AddForeignKey
|
|
179
|
+
ALTER TABLE "UserFile" ADD CONSTRAINT "UserFile_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
180
|
+
|
|
181
|
+
-- AddForeignKey
|
|
182
|
+
ALTER TABLE "UserFile" ADD CONSTRAINT "UserFile_answerId_fkey" FOREIGN KEY ("answerId") REFERENCES "Answer"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
183
|
+
|
|
184
|
+
-- AddForeignKey
|
|
185
|
+
ALTER TABLE "UserFile" ADD CONSTRAINT "UserFile_parentFileId_fkey" FOREIGN KEY ("parentFileId") REFERENCES "UserFile"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
186
|
+
|
|
187
|
+
-- AddForeignKey
|
|
188
|
+
ALTER TABLE "FileIndexingJob" ADD CONSTRAINT "FileIndexingJob_fileId_fkey" FOREIGN KEY ("fileId") REFERENCES "UserFile"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
189
|
+
|
|
190
|
+
-- AddForeignKey
|
|
191
|
+
ALTER TABLE "FileDeadline" ADD CONSTRAINT "FileDeadline_fileId_fkey" FOREIGN KEY ("fileId") REFERENCES "UserFile"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
192
|
+
|
|
193
|
+
-- AddForeignKey
|
|
194
|
+
ALTER TABLE "FileReviewTable" ADD CONSTRAINT "FileReviewTable_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Warnings:
|
|
3
|
+
|
|
4
|
+
- You are about to drop the column `answerId` on the `UserFile` table. All the data in the column will be lost.
|
|
5
|
+
|
|
6
|
+
*/
|
|
7
|
+
-- CreateEnum
|
|
8
|
+
CREATE TYPE "UserFileUploadSource" AS ENUM ('FOLDER', 'CHAT', 'STORAGE');
|
|
9
|
+
|
|
10
|
+
-- DropForeignKey
|
|
11
|
+
ALTER TABLE "public"."UserFile" DROP CONSTRAINT "UserFile_answerId_fkey";
|
|
12
|
+
|
|
13
|
+
-- DropIndex
|
|
14
|
+
DROP INDEX "public"."UserFile_answerId_idx";
|
|
15
|
+
|
|
16
|
+
-- AlterTable
|
|
17
|
+
ALTER TABLE "UserFile" DROP COLUMN "answerId",
|
|
18
|
+
ADD COLUMN "uploadContextId" TEXT,
|
|
19
|
+
ADD COLUMN "uploadSource" "UserFileUploadSource" NOT NULL DEFAULT 'STORAGE';
|
|
20
|
+
|
|
21
|
+
-- CreateTable
|
|
22
|
+
CREATE TABLE "UserFileConversation" (
|
|
23
|
+
"id" TEXT NOT NULL,
|
|
24
|
+
"fileId" TEXT NOT NULL,
|
|
25
|
+
"conversationId" TEXT NOT NULL,
|
|
26
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
27
|
+
|
|
28
|
+
CONSTRAINT "UserFileConversation_pkey" PRIMARY KEY ("id")
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
-- CreateIndex
|
|
32
|
+
CREATE INDEX "UserFileConversation_fileId_idx" ON "UserFileConversation"("fileId");
|
|
33
|
+
|
|
34
|
+
-- CreateIndex
|
|
35
|
+
CREATE INDEX "UserFileConversation_conversationId_idx" ON "UserFileConversation"("conversationId");
|
|
36
|
+
|
|
37
|
+
-- CreateIndex
|
|
38
|
+
CREATE UNIQUE INDEX "UserFileConversation_fileId_conversationId_key" ON "UserFileConversation"("fileId", "conversationId");
|
|
39
|
+
|
|
40
|
+
-- CreateIndex
|
|
41
|
+
CREATE INDEX "UserFile_uploadSource_idx" ON "UserFile"("uploadSource");
|
|
42
|
+
|
|
43
|
+
-- CreateIndex
|
|
44
|
+
CREATE INDEX "UserFile_uploadContextId_idx" ON "UserFile"("uploadContextId");
|
|
45
|
+
|
|
46
|
+
-- AddForeignKey
|
|
47
|
+
ALTER TABLE "UserFileConversation" ADD CONSTRAINT "UserFileConversation_fileId_fkey" FOREIGN KEY ("fileId") REFERENCES "UserFile"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
48
|
+
|
|
49
|
+
-- AddForeignKey
|
|
50
|
+
ALTER TABLE "UserFileConversation" ADD CONSTRAINT "UserFileConversation_conversationId_fkey" FOREIGN KEY ("conversationId") REFERENCES "Conversation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|