@lacrous/omnicloud 0.2.2 → 0.2.3

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.
@@ -0,0 +1,106 @@
1
+ -- CreateSchema
2
+ CREATE SCHEMA IF NOT EXISTS "public";
3
+
4
+ -- CreateTable
5
+ CREATE TABLE "User" (
6
+ "id" TEXT NOT NULL,
7
+ "telegramUserId" BIGINT NOT NULL,
8
+ "username" TEXT,
9
+ "firstName" TEXT,
10
+ "lastName" TEXT,
11
+ "phone" TEXT,
12
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
13
+ "updatedAt" TIMESTAMP(3) NOT NULL,
14
+
15
+ CONSTRAINT "User_pkey" PRIMARY KEY ("id")
16
+ );
17
+
18
+ -- CreateTable
19
+ CREATE TABLE "TelegramSession" (
20
+ "id" TEXT NOT NULL,
21
+ "userId" TEXT NOT NULL,
22
+ "stringSession" TEXT NOT NULL,
23
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
24
+ "updatedAt" TIMESTAMP(3) NOT NULL,
25
+
26
+ CONSTRAINT "TelegramSession_pkey" PRIMARY KEY ("id")
27
+ );
28
+
29
+ -- CreateTable
30
+ CREATE TABLE "Storage" (
31
+ "id" TEXT NOT NULL,
32
+ "userId" TEXT NOT NULL,
33
+ "provider" TEXT NOT NULL DEFAULT 'telegram',
34
+ "title" TEXT NOT NULL,
35
+ "telegramChatId" BIGINT NOT NULL,
36
+ "telegramAccessHash" BIGINT NOT NULL,
37
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
38
+ "updatedAt" TIMESTAMP(3) NOT NULL,
39
+
40
+ CONSTRAINT "Storage_pkey" PRIMARY KEY ("id")
41
+ );
42
+
43
+ -- CreateTable
44
+ CREATE TABLE "Folder" (
45
+ "id" TEXT NOT NULL,
46
+ "userId" TEXT NOT NULL,
47
+ "parentId" TEXT,
48
+ "name" TEXT NOT NULL,
49
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
50
+ "updatedAt" TIMESTAMP(3) NOT NULL,
51
+
52
+ CONSTRAINT "Folder_pkey" PRIMARY KEY ("id")
53
+ );
54
+
55
+ -- CreateTable
56
+ CREATE TABLE "File" (
57
+ "id" TEXT NOT NULL,
58
+ "userId" TEXT NOT NULL,
59
+ "folderId" TEXT,
60
+ "name" TEXT NOT NULL,
61
+ "size" BIGINT NOT NULL,
62
+ "mimeType" TEXT NOT NULL,
63
+ "sha256" TEXT NOT NULL,
64
+ "telegramMessageId" BIGINT NOT NULL,
65
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
66
+ "updatedAt" TIMESTAMP(3) NOT NULL,
67
+
68
+ CONSTRAINT "File_pkey" PRIMARY KEY ("id")
69
+ );
70
+
71
+ -- CreateIndex
72
+ CREATE UNIQUE INDEX "User_telegramUserId_key" ON "User"("telegramUserId");
73
+
74
+ -- CreateIndex
75
+ CREATE UNIQUE INDEX "TelegramSession_userId_key" ON "TelegramSession"("userId");
76
+
77
+ -- CreateIndex
78
+ CREATE UNIQUE INDEX "Storage_userId_provider_key" ON "Storage"("userId", "provider");
79
+
80
+ -- CreateIndex
81
+ CREATE INDEX "Folder_userId_parentId_idx" ON "Folder"("userId", "parentId");
82
+
83
+ -- CreateIndex
84
+ CREATE INDEX "File_userId_folderId_idx" ON "File"("userId", "folderId");
85
+
86
+ -- CreateIndex
87
+ CREATE INDEX "File_userId_name_idx" ON "File"("userId", "name");
88
+
89
+ -- AddForeignKey
90
+ ALTER TABLE "TelegramSession" ADD CONSTRAINT "TelegramSession_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
91
+
92
+ -- AddForeignKey
93
+ ALTER TABLE "Storage" ADD CONSTRAINT "Storage_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
94
+
95
+ -- AddForeignKey
96
+ ALTER TABLE "Folder" ADD CONSTRAINT "Folder_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
97
+
98
+ -- AddForeignKey
99
+ ALTER TABLE "Folder" ADD CONSTRAINT "Folder_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "Folder"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
100
+
101
+ -- AddForeignKey
102
+ ALTER TABLE "File" ADD CONSTRAINT "File_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
103
+
104
+ -- AddForeignKey
105
+ ALTER TABLE "File" ADD CONSTRAINT "File_folderId_fkey" FOREIGN KEY ("folderId") REFERENCES "Folder"("id") ON DELETE SET NULL ON UPDATE CASCADE;
106
+
@@ -0,0 +1,120 @@
1
+ -- DropIndex
2
+ DROP INDEX "File_userId_folderId_idx";
3
+
4
+ -- DropIndex
5
+ DROP INDEX "Folder_userId_parentId_idx";
6
+
7
+ -- AlterTable
8
+ ALTER TABLE "File" ADD COLUMN "currentVersionId" TEXT,
9
+ ADD COLUMN "deletedAt" TIMESTAMP(3),
10
+ ADD COLUMN "starred" BOOLEAN NOT NULL DEFAULT false,
11
+ ADD COLUMN "versionCount" INTEGER NOT NULL DEFAULT 0;
12
+
13
+ -- AlterTable
14
+ ALTER TABLE "Folder" ADD COLUMN "deletedAt" TIMESTAMP(3),
15
+ ADD COLUMN "starred" BOOLEAN NOT NULL DEFAULT false;
16
+
17
+ -- CreateTable
18
+ CREATE TABLE "FileVersion" (
19
+ "id" TEXT NOT NULL,
20
+ "fileId" TEXT NOT NULL,
21
+ "userId" TEXT NOT NULL,
22
+ "versionNumber" INTEGER NOT NULL,
23
+ "size" BIGINT NOT NULL,
24
+ "mimeType" TEXT NOT NULL,
25
+ "sha256" TEXT NOT NULL,
26
+ "telegramMessageId" BIGINT NOT NULL,
27
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
28
+
29
+ CONSTRAINT "FileVersion_pkey" PRIMARY KEY ("id")
30
+ );
31
+
32
+ -- CreateTable
33
+ CREATE TABLE "ActivityEvent" (
34
+ "id" TEXT NOT NULL,
35
+ "userId" TEXT NOT NULL,
36
+ "action" TEXT NOT NULL,
37
+ "resourceType" TEXT NOT NULL,
38
+ "resourceId" TEXT NOT NULL,
39
+ "resourceName" TEXT,
40
+ "metadata" JSONB,
41
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
42
+
43
+ CONSTRAINT "ActivityEvent_pkey" PRIMARY KEY ("id")
44
+ );
45
+
46
+ -- CreateIndex
47
+ CREATE INDEX "FileVersion_fileId_idx" ON "FileVersion"("fileId");
48
+
49
+ -- CreateIndex
50
+ CREATE INDEX "FileVersion_userId_idx" ON "FileVersion"("userId");
51
+
52
+ -- CreateIndex
53
+ CREATE UNIQUE INDEX "FileVersion_fileId_versionNumber_key" ON "FileVersion"("fileId", "versionNumber");
54
+
55
+ -- CreateIndex
56
+ CREATE INDEX "ActivityEvent_userId_createdAt_idx" ON "ActivityEvent"("userId", "createdAt");
57
+
58
+ -- CreateIndex
59
+ CREATE INDEX "ActivityEvent_userId_resourceId_createdAt_idx" ON "ActivityEvent"("userId", "resourceId", "createdAt");
60
+
61
+ -- CreateIndex
62
+ CREATE INDEX "ActivityEvent_createdAt_idx" ON "ActivityEvent"("createdAt");
63
+
64
+ -- CreateIndex
65
+ CREATE INDEX "File_userId_folderId_deletedAt_idx" ON "File"("userId", "folderId", "deletedAt");
66
+
67
+ -- CreateIndex
68
+ CREATE INDEX "File_userId_deletedAt_idx" ON "File"("userId", "deletedAt");
69
+
70
+ -- CreateIndex
71
+ CREATE INDEX "File_userId_starred_idx" ON "File"("userId", "starred");
72
+
73
+ -- CreateIndex
74
+ CREATE INDEX "File_userId_size_idx" ON "File"("userId", "size");
75
+
76
+ -- CreateIndex
77
+ CREATE INDEX "File_userId_mimeType_idx" ON "File"("userId", "mimeType");
78
+
79
+ -- CreateIndex
80
+ CREATE INDEX "Folder_userId_parentId_deletedAt_idx" ON "Folder"("userId", "parentId", "deletedAt");
81
+
82
+ -- CreateIndex
83
+ CREATE INDEX "Folder_userId_deletedAt_idx" ON "Folder"("userId", "deletedAt");
84
+
85
+ -- CreateIndex
86
+ CREATE INDEX "Folder_userId_name_idx" ON "Folder"("userId", "name");
87
+
88
+ -- CreateIndex
89
+ CREATE INDEX "Folder_userId_starred_idx" ON "Folder"("userId", "starred");
90
+
91
+ -- AddForeignKey
92
+ ALTER TABLE "FileVersion" ADD CONSTRAINT "FileVersion_fileId_fkey" FOREIGN KEY ("fileId") REFERENCES "File"("id") ON DELETE CASCADE ON UPDATE CASCADE;
93
+
94
+ -- AddForeignKey
95
+ ALTER TABLE "FileVersion" ADD CONSTRAINT "FileVersion_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
96
+
97
+ -- AddForeignKey
98
+ ALTER TABLE "ActivityEvent" ADD CONSTRAINT "ActivityEvent_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
99
+
100
+
101
+ -- Backfill: give every pre-v0.2 file a version 1 row so its version history is
102
+ -- complete, then point the file at it. Safe to run on an empty table.
103
+ INSERT INTO "FileVersion" ("id", "fileId", "userId", "versionNumber", "size", "mimeType", "sha256", "telegramMessageId", "createdAt")
104
+ SELECT
105
+ 'v1_' || f."id",
106
+ f."id",
107
+ f."userId",
108
+ 1,
109
+ f."size",
110
+ f."mimeType",
111
+ f."sha256",
112
+ f."telegramMessageId",
113
+ f."createdAt"
114
+ FROM "File" f
115
+ WHERE NOT EXISTS (SELECT 1 FROM "FileVersion" v WHERE v."fileId" = f."id");
116
+
117
+ UPDATE "File" f
118
+ SET "currentVersionId" = 'v1_' || f."id", "versionCount" = 1
119
+ WHERE f."versionCount" = 0
120
+ AND EXISTS (SELECT 1 FROM "FileVersion" v WHERE v."id" = 'v1_' || f."id");
@@ -0,0 +1,6 @@
1
+ -- AlterTable
2
+ ALTER TABLE "File" ADD COLUMN "trashBatchId" TEXT;
3
+
4
+ -- AlterTable
5
+ ALTER TABLE "Folder" ADD COLUMN "trashBatchId" TEXT;
6
+
@@ -0,0 +1,27 @@
1
+ -- CreateTable
2
+ CREATE TABLE "BrowserSession" (
3
+ "id" TEXT NOT NULL,
4
+ "userId" TEXT NOT NULL,
5
+ "tokenHash" TEXT NOT NULL,
6
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
7
+ "expiresAt" TIMESTAMP(3) NOT NULL,
8
+ "revokedAt" TIMESTAMP(3),
9
+ "lastUsedAt" TIMESTAMP(3),
10
+ "userAgent" TEXT,
11
+ "ip" TEXT,
12
+
13
+ CONSTRAINT "BrowserSession_pkey" PRIMARY KEY ("id")
14
+ );
15
+
16
+ -- CreateIndex
17
+ CREATE UNIQUE INDEX "BrowserSession_tokenHash_key" ON "BrowserSession"("tokenHash");
18
+
19
+ -- CreateIndex
20
+ CREATE INDEX "BrowserSession_userId_revokedAt_idx" ON "BrowserSession"("userId", "revokedAt");
21
+
22
+ -- CreateIndex
23
+ CREATE INDEX "BrowserSession_expiresAt_idx" ON "BrowserSession"("expiresAt");
24
+
25
+ -- AddForeignKey
26
+ ALTER TABLE "BrowserSession" ADD CONSTRAINT "BrowserSession_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
27
+
@@ -0,0 +1,26 @@
1
+ -- CreateTable
2
+ CREATE TABLE "UploadOperation" (
3
+ "id" TEXT NOT NULL,
4
+ "userId" TEXT NOT NULL,
5
+ "operationId" TEXT NOT NULL,
6
+ "status" TEXT NOT NULL,
7
+ "telegramMessageId" BIGINT,
8
+ "sha256" TEXT,
9
+ "size" BIGINT,
10
+ "fileId" TEXT,
11
+ "error" TEXT,
12
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
13
+ "updatedAt" TIMESTAMP(3) NOT NULL,
14
+
15
+ CONSTRAINT "UploadOperation_pkey" PRIMARY KEY ("id")
16
+ );
17
+
18
+ -- CreateIndex
19
+ CREATE INDEX "UploadOperation_status_updatedAt_idx" ON "UploadOperation"("status", "updatedAt");
20
+
21
+ -- CreateIndex
22
+ CREATE UNIQUE INDEX "UploadOperation_userId_operationId_key" ON "UploadOperation"("userId", "operationId");
23
+
24
+ -- AddForeignKey
25
+ ALTER TABLE "UploadOperation" ADD CONSTRAINT "UploadOperation_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
26
+
@@ -0,0 +1,3 @@
1
+ # Please do not edit this file manually
2
+ # It should be added in your version-control system (e.g., Git)
3
+ provider = "postgresql"
@@ -0,0 +1,194 @@
1
+ // OmniCloud metadata schema (v0.2).
2
+ //
3
+ // Telegram stores the actual binary file content; PostgreSQL stores the
4
+ // logical representation of the user's cloud (users, folders, file metadata,
5
+ // versions, trash state, stars, activity events, storage registrations and
6
+ // Telegram sessions).
7
+
8
+ generator client {
9
+ provider = "prisma-client-js"
10
+ }
11
+
12
+ datasource db {
13
+ provider = "postgresql"
14
+ url = env("DATABASE_URL")
15
+ }
16
+
17
+ model User {
18
+ id String @id @default(cuid())
19
+ telegramUserId BigInt @unique
20
+ username String?
21
+ firstName String?
22
+ lastName String?
23
+ phone String?
24
+ createdAt DateTime @default(now())
25
+ updatedAt DateTime @updatedAt
26
+
27
+ telegramSession TelegramSession?
28
+ storages Storage[]
29
+ folders Folder[]
30
+ files File[]
31
+ fileVersions FileVersion[]
32
+ activityEvents ActivityEvent[]
33
+ browserSessions BrowserSession[]
34
+ uploadOperations UploadOperation[]
35
+ }
36
+
37
+ /// A signed-in browser session. Only the SHA-256 of the cookie token is stored,
38
+ /// so revoking the row ends the session on the next request.
39
+ model BrowserSession {
40
+ id String @id @default(cuid())
41
+ userId String
42
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
43
+ tokenHash String @unique
44
+ createdAt DateTime @default(now())
45
+ expiresAt DateTime
46
+ revokedAt DateTime?
47
+ lastUsedAt DateTime?
48
+ userAgent String?
49
+ ip String?
50
+
51
+ @@index([userId, revokedAt])
52
+ @@index([expiresAt])
53
+ }
54
+
55
+ /// One logical upload, keyed by a client-supplied id so a retry never stores a
56
+ /// second Telegram object. `telegramMessageId` is recorded as soon as Telegram
57
+ /// accepts the object, so recovery can commit the file without uploading again.
58
+ model UploadOperation {
59
+ id String @id @default(cuid())
60
+ userId String
61
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
62
+ operationId String
63
+ status String
64
+ telegramMessageId BigInt?
65
+ sha256 String?
66
+ size BigInt?
67
+ fileId String?
68
+ error String?
69
+ createdAt DateTime @default(now())
70
+ updatedAt DateTime @updatedAt
71
+
72
+ @@unique([userId, operationId])
73
+ @@index([status, updatedAt])
74
+ }
75
+
76
+ /// MTProto session string for the user's connected Telegram account.
77
+ /// Never exposed through the API and never logged.
78
+ model TelegramSession {
79
+ id String @id @default(cuid())
80
+ userId String @unique
81
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
82
+ stringSession String
83
+ createdAt DateTime @default(now())
84
+ updatedAt DateTime @updatedAt
85
+ }
86
+
87
+ /// The user's private storage channel used as the backing object store.
88
+ model Storage {
89
+ id String @id @default(cuid())
90
+ userId String
91
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
92
+ provider String @default("telegram")
93
+ title String
94
+ telegramChatId BigInt
95
+ telegramAccessHash BigInt
96
+ createdAt DateTime @default(now())
97
+ updatedAt DateTime @updatedAt
98
+
99
+ @@unique([userId, provider])
100
+ }
101
+
102
+ /// Folders are virtual OmniCloud objects — they have no physical
103
+ /// representation on Telegram. `deletedAt` is the Trash marker.
104
+ model Folder {
105
+ id String @id @default(cuid())
106
+ userId String
107
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
108
+ parentId String?
109
+ parent Folder? @relation("FolderTree", fields: [parentId], references: [id], onDelete: Restrict)
110
+ children Folder[] @relation("FolderTree")
111
+ name String
112
+ starred Boolean @default(false)
113
+ deletedAt DateTime?
114
+ /// Groups everything one trash operation moved, for exact restore.
115
+ trashBatchId String?
116
+ createdAt DateTime @default(now())
117
+ updatedAt DateTime @updatedAt
118
+ files File[]
119
+
120
+ @@index([userId, parentId, deletedAt])
121
+ @@index([userId, deletedAt])
122
+ @@index([userId, name])
123
+ @@index([userId, starred])
124
+ }
125
+
126
+ /// File metadata. `telegramMessageId` points at the current version's message
127
+ /// in the user's storage channel; `currentVersionId` links the version row
128
+ /// that content is served from.
129
+ model File {
130
+ id String @id @default(cuid())
131
+ userId String
132
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
133
+ folderId String?
134
+ folder Folder? @relation(fields: [folderId], references: [id], onDelete: SetNull)
135
+ name String
136
+ size BigInt
137
+ mimeType String
138
+ sha256 String
139
+ telegramMessageId BigInt
140
+ starred Boolean @default(false)
141
+ deletedAt DateTime?
142
+ /// Groups everything one trash operation moved, for exact restore.
143
+ trashBatchId String?
144
+ currentVersionId String?
145
+ versionCount Int @default(0)
146
+ createdAt DateTime @default(now())
147
+ updatedAt DateTime @updatedAt
148
+
149
+ versions FileVersion[]
150
+
151
+ @@index([userId, folderId, deletedAt])
152
+ @@index([userId, deletedAt])
153
+ @@index([userId, name])
154
+ @@index([userId, starred])
155
+ @@index([userId, size])
156
+ @@index([userId, mimeType])
157
+ }
158
+
159
+ /// Version foundation for v0.3's version history. Every upload creates a row;
160
+ /// replacing a file appends a version and repoints the file's current version.
161
+ model FileVersion {
162
+ id String @id @default(cuid())
163
+ fileId String
164
+ file File @relation(fields: [fileId], references: [id], onDelete: Cascade)
165
+ userId String
166
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
167
+ versionNumber Int
168
+ size BigInt
169
+ mimeType String
170
+ sha256 String
171
+ telegramMessageId BigInt
172
+ createdAt DateTime @default(now())
173
+
174
+ @@unique([fileId, versionNumber])
175
+ @@index([fileId])
176
+ @@index([userId])
177
+ }
178
+
179
+ /// Structured activity/audit events. Metadata never contains credentials.
180
+ model ActivityEvent {
181
+ id String @id @default(cuid())
182
+ userId String
183
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
184
+ action String
185
+ resourceType String
186
+ resourceId String
187
+ resourceName String?
188
+ metadata Json?
189
+ createdAt DateTime @default(now())
190
+
191
+ @@index([userId, createdAt])
192
+ @@index([userId, resourceId, createdAt])
193
+ @@index([createdAt])
194
+ }