@hiai-gg/docsmint 0.4.5 → 0.4.6
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.
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
|
+
accounts,
|
|
2
3
|
attachments,
|
|
3
4
|
auditLog,
|
|
5
|
+
categories,
|
|
4
6
|
documentEmbeddings,
|
|
5
7
|
documents,
|
|
8
|
+
folders,
|
|
6
9
|
lifecycleOperations,
|
|
10
|
+
sessions,
|
|
7
11
|
shareLinks,
|
|
12
|
+
tags,
|
|
13
|
+
users,
|
|
8
14
|
versions,
|
|
9
15
|
} from "@hiai-docs/db/schema";
|
|
10
16
|
import type { TenantTransaction } from "@hiai-docs/db/with-tenant";
|
|
@@ -121,6 +127,14 @@ function subjectHash(actorUserId: string): string {
|
|
|
121
127
|
return new Bun.CryptoHasher("sha256").update(actorUserId).digest("hex");
|
|
122
128
|
}
|
|
123
129
|
|
|
130
|
+
export function lifecycleTombstoneEmail(actorUserId: string): string {
|
|
131
|
+
const hash = new Bun.CryptoHasher("sha256")
|
|
132
|
+
.update("docsmint:privacy-tombstone:v1\0")
|
|
133
|
+
.update(actorUserId)
|
|
134
|
+
.digest("hex");
|
|
135
|
+
return `deleted-${hash}@invalid.local`;
|
|
136
|
+
}
|
|
137
|
+
|
|
124
138
|
function checksumLine(
|
|
125
139
|
hash: Bun.CryptoHasher,
|
|
126
140
|
record: UserDataExportRecord,
|
|
@@ -317,6 +331,92 @@ export function createPersistentLifecycleService(
|
|
|
317
331
|
checksumLine(hash, manifest);
|
|
318
332
|
recordCount += 1;
|
|
319
333
|
yield manifest;
|
|
334
|
+
const actor = await withActor(ctx.actorUserId, (tx) =>
|
|
335
|
+
tx
|
|
336
|
+
.select({
|
|
337
|
+
id: users.id,
|
|
338
|
+
email: users.email,
|
|
339
|
+
name: users.name,
|
|
340
|
+
emailVerified: users.emailVerified,
|
|
341
|
+
image: users.image,
|
|
342
|
+
createdAt: users.createdAt,
|
|
343
|
+
updatedAt: users.updatedAt,
|
|
344
|
+
})
|
|
345
|
+
.from(users)
|
|
346
|
+
.where(eq(users.id, ctx.actorUserId))
|
|
347
|
+
.limit(1),
|
|
348
|
+
);
|
|
349
|
+
for (const profile of actor) {
|
|
350
|
+
const record: UserDataExportRecord = {
|
|
351
|
+
type: "data",
|
|
352
|
+
domain: "account",
|
|
353
|
+
resourceType: "user",
|
|
354
|
+
resourceId: profile.id,
|
|
355
|
+
workspaceId: null,
|
|
356
|
+
payload: {
|
|
357
|
+
email: profile.email,
|
|
358
|
+
name: profile.name,
|
|
359
|
+
emailVerified: profile.emailVerified,
|
|
360
|
+
image: profile.image,
|
|
361
|
+
createdAt: profile.createdAt,
|
|
362
|
+
updatedAt: profile.updatedAt,
|
|
363
|
+
},
|
|
364
|
+
};
|
|
365
|
+
checksumLine(hash, record);
|
|
366
|
+
recordCount += 1;
|
|
367
|
+
yield record;
|
|
368
|
+
}
|
|
369
|
+
const actorFolders = await withActor(ctx.actorUserId, (tx) =>
|
|
370
|
+
tx.select().from(folders).where(eq(folders.ownerId, ctx.actorUserId)),
|
|
371
|
+
);
|
|
372
|
+
for (const folder of actorFolders) {
|
|
373
|
+
const record: UserDataExportRecord = {
|
|
374
|
+
type: "data",
|
|
375
|
+
domain: "folders",
|
|
376
|
+
resourceType: "folder",
|
|
377
|
+
resourceId: folder.id,
|
|
378
|
+
workspaceId: folder.workspaceId,
|
|
379
|
+
payload: folder,
|
|
380
|
+
};
|
|
381
|
+
checksumLine(hash, record);
|
|
382
|
+
recordCount += 1;
|
|
383
|
+
yield record;
|
|
384
|
+
}
|
|
385
|
+
const actorTags = await withActor(ctx.actorUserId, (tx) =>
|
|
386
|
+
tx.select().from(tags).where(eq(tags.ownerId, ctx.actorUserId)),
|
|
387
|
+
);
|
|
388
|
+
for (const tag of actorTags) {
|
|
389
|
+
const record: UserDataExportRecord = {
|
|
390
|
+
type: "data",
|
|
391
|
+
domain: "tags",
|
|
392
|
+
resourceType: "tag",
|
|
393
|
+
resourceId: tag.id,
|
|
394
|
+
workspaceId: tag.workspaceId,
|
|
395
|
+
payload: tag,
|
|
396
|
+
};
|
|
397
|
+
checksumLine(hash, record);
|
|
398
|
+
recordCount += 1;
|
|
399
|
+
yield record;
|
|
400
|
+
}
|
|
401
|
+
const actorCategories = await withActor(ctx.actorUserId, (tx) =>
|
|
402
|
+
tx
|
|
403
|
+
.select()
|
|
404
|
+
.from(categories)
|
|
405
|
+
.where(eq(categories.ownerId, ctx.actorUserId)),
|
|
406
|
+
);
|
|
407
|
+
for (const category of actorCategories) {
|
|
408
|
+
const record: UserDataExportRecord = {
|
|
409
|
+
type: "data",
|
|
410
|
+
domain: "categories",
|
|
411
|
+
resourceType: "category",
|
|
412
|
+
resourceId: category.id,
|
|
413
|
+
workspaceId: category.workspaceId,
|
|
414
|
+
payload: category,
|
|
415
|
+
};
|
|
416
|
+
checksumLine(hash, record);
|
|
417
|
+
recordCount += 1;
|
|
418
|
+
yield record;
|
|
419
|
+
}
|
|
320
420
|
const ownedDocuments = await withActor(ctx.actorUserId, (tx) =>
|
|
321
421
|
tx
|
|
322
422
|
.select()
|
|
@@ -529,6 +629,48 @@ export function createPersistentLifecycleService(
|
|
|
529
629
|
ctx.signal,
|
|
530
630
|
),
|
|
531
631
|
);
|
|
632
|
+
await runStep(
|
|
633
|
+
operation,
|
|
634
|
+
ctx.actorUserId,
|
|
635
|
+
leaseOwner,
|
|
636
|
+
"remove_subject_folders",
|
|
637
|
+
deletedByDomain,
|
|
638
|
+
() =>
|
|
639
|
+
dbDelete((tx) =>
|
|
640
|
+
tx
|
|
641
|
+
.delete(folders)
|
|
642
|
+
.where(eq(folders.ownerId, ctx.actorUserId))
|
|
643
|
+
.returning({ id: folders.id }),
|
|
644
|
+
),
|
|
645
|
+
);
|
|
646
|
+
await runStep(
|
|
647
|
+
operation,
|
|
648
|
+
ctx.actorUserId,
|
|
649
|
+
leaseOwner,
|
|
650
|
+
"remove_subject_tags",
|
|
651
|
+
deletedByDomain,
|
|
652
|
+
() =>
|
|
653
|
+
dbDelete((tx) =>
|
|
654
|
+
tx
|
|
655
|
+
.delete(tags)
|
|
656
|
+
.where(eq(tags.ownerId, ctx.actorUserId))
|
|
657
|
+
.returning({ id: tags.id }),
|
|
658
|
+
),
|
|
659
|
+
);
|
|
660
|
+
await runStep(
|
|
661
|
+
operation,
|
|
662
|
+
ctx.actorUserId,
|
|
663
|
+
leaseOwner,
|
|
664
|
+
"remove_subject_categories",
|
|
665
|
+
deletedByDomain,
|
|
666
|
+
() =>
|
|
667
|
+
dbDelete((tx) =>
|
|
668
|
+
tx
|
|
669
|
+
.delete(categories)
|
|
670
|
+
.where(eq(categories.ownerId, ctx.actorUserId))
|
|
671
|
+
.returning({ id: categories.id }),
|
|
672
|
+
),
|
|
673
|
+
);
|
|
532
674
|
await runStep(
|
|
533
675
|
operation,
|
|
534
676
|
ctx.actorUserId,
|
|
@@ -582,6 +724,55 @@ export function createPersistentLifecycleService(
|
|
|
582
724
|
deletedByDomain,
|
|
583
725
|
async () => (await step.purge?.(ctx))?.deletedCount ?? 0,
|
|
584
726
|
);
|
|
727
|
+
await runStep(
|
|
728
|
+
operation,
|
|
729
|
+
ctx.actorUserId,
|
|
730
|
+
leaseOwner,
|
|
731
|
+
"remove_auth_sessions",
|
|
732
|
+
deletedByDomain,
|
|
733
|
+
() =>
|
|
734
|
+
dbDelete((tx) =>
|
|
735
|
+
tx
|
|
736
|
+
.delete(sessions)
|
|
737
|
+
.where(eq(sessions.userId, ctx.actorUserId))
|
|
738
|
+
.returning({ id: sessions.id }),
|
|
739
|
+
),
|
|
740
|
+
);
|
|
741
|
+
await runStep(
|
|
742
|
+
operation,
|
|
743
|
+
ctx.actorUserId,
|
|
744
|
+
leaseOwner,
|
|
745
|
+
"remove_auth_accounts",
|
|
746
|
+
deletedByDomain,
|
|
747
|
+
() =>
|
|
748
|
+
dbDelete((tx) =>
|
|
749
|
+
tx
|
|
750
|
+
.delete(accounts)
|
|
751
|
+
.where(eq(accounts.userId, ctx.actorUserId))
|
|
752
|
+
.returning({ id: accounts.id }),
|
|
753
|
+
),
|
|
754
|
+
);
|
|
755
|
+
await runStep(
|
|
756
|
+
operation,
|
|
757
|
+
ctx.actorUserId,
|
|
758
|
+
leaseOwner,
|
|
759
|
+
"tombstone_subject_user",
|
|
760
|
+
deletedByDomain,
|
|
761
|
+
() =>
|
|
762
|
+
dbDelete((tx) =>
|
|
763
|
+
tx
|
|
764
|
+
.update(users)
|
|
765
|
+
.set({
|
|
766
|
+
email: lifecycleTombstoneEmail(ctx.actorUserId),
|
|
767
|
+
name: null,
|
|
768
|
+
image: null,
|
|
769
|
+
emailVerified: false,
|
|
770
|
+
updatedAt: new Date(),
|
|
771
|
+
})
|
|
772
|
+
.where(eq(users.id, ctx.actorUserId))
|
|
773
|
+
.returning({ id: users.id }),
|
|
774
|
+
),
|
|
775
|
+
);
|
|
585
776
|
await runStep(
|
|
586
777
|
operation,
|
|
587
778
|
ctx.actorUserId,
|
package/dist/backend/index.js
CHANGED
|
@@ -214361,7 +214361,7 @@ var swaggerConfig = {
|
|
|
214361
214361
|
documentation: {
|
|
214362
214362
|
info: {
|
|
214363
214363
|
title: "DocsMint API",
|
|
214364
|
-
version: "0.4.
|
|
214364
|
+
version: "0.4.6",
|
|
214365
214365
|
description: "Self-hosted AI-first documentation platform. Full-text + semantic search, version history, sharing, and folder organization.",
|
|
214366
214366
|
contact: { name: "HiAi-gg", url: "https://github.com/HiAi-gg/docsmint" },
|
|
214367
214367
|
license: {
|
|
@@ -538,6 +538,10 @@ function tokenHash(token) {
|
|
|
538
538
|
function subjectHash(actorUserId) {
|
|
539
539
|
return new Bun.CryptoHasher("sha256").update(actorUserId).digest("hex");
|
|
540
540
|
}
|
|
541
|
+
function lifecycleTombstoneEmail(actorUserId) {
|
|
542
|
+
const hash = new Bun.CryptoHasher("sha256").update("docsmint:privacy-tombstone:v1\x00").update(actorUserId).digest("hex");
|
|
543
|
+
return `deleted-${hash}@invalid.local`;
|
|
544
|
+
}
|
|
541
545
|
function checksumLine(hash, record) {
|
|
542
546
|
hash.update(`${JSON.stringify(record)}
|
|
543
547
|
`, "utf8");
|
|
@@ -634,6 +638,77 @@ function createPersistentLifecycleService(runtime, database, hostSteps = []) {
|
|
|
634
638
|
checksumLine(hash, manifest);
|
|
635
639
|
recordCount += 1;
|
|
636
640
|
yield manifest;
|
|
641
|
+
const actor = await withActor(ctx.actorUserId, (tx) => tx.select({
|
|
642
|
+
id: users.id,
|
|
643
|
+
email: users.email,
|
|
644
|
+
name: users.name,
|
|
645
|
+
emailVerified: users.emailVerified,
|
|
646
|
+
image: users.image,
|
|
647
|
+
createdAt: users.createdAt,
|
|
648
|
+
updatedAt: users.updatedAt
|
|
649
|
+
}).from(users).where(eq(users.id, ctx.actorUserId)).limit(1));
|
|
650
|
+
for (const profile of actor) {
|
|
651
|
+
const record = {
|
|
652
|
+
type: "data",
|
|
653
|
+
domain: "account",
|
|
654
|
+
resourceType: "user",
|
|
655
|
+
resourceId: profile.id,
|
|
656
|
+
workspaceId: null,
|
|
657
|
+
payload: {
|
|
658
|
+
email: profile.email,
|
|
659
|
+
name: profile.name,
|
|
660
|
+
emailVerified: profile.emailVerified,
|
|
661
|
+
image: profile.image,
|
|
662
|
+
createdAt: profile.createdAt,
|
|
663
|
+
updatedAt: profile.updatedAt
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
checksumLine(hash, record);
|
|
667
|
+
recordCount += 1;
|
|
668
|
+
yield record;
|
|
669
|
+
}
|
|
670
|
+
const actorFolders = await withActor(ctx.actorUserId, (tx) => tx.select().from(folders).where(eq(folders.ownerId, ctx.actorUserId)));
|
|
671
|
+
for (const folder of actorFolders) {
|
|
672
|
+
const record = {
|
|
673
|
+
type: "data",
|
|
674
|
+
domain: "folders",
|
|
675
|
+
resourceType: "folder",
|
|
676
|
+
resourceId: folder.id,
|
|
677
|
+
workspaceId: folder.workspaceId,
|
|
678
|
+
payload: folder
|
|
679
|
+
};
|
|
680
|
+
checksumLine(hash, record);
|
|
681
|
+
recordCount += 1;
|
|
682
|
+
yield record;
|
|
683
|
+
}
|
|
684
|
+
const actorTags = await withActor(ctx.actorUserId, (tx) => tx.select().from(tags).where(eq(tags.ownerId, ctx.actorUserId)));
|
|
685
|
+
for (const tag of actorTags) {
|
|
686
|
+
const record = {
|
|
687
|
+
type: "data",
|
|
688
|
+
domain: "tags",
|
|
689
|
+
resourceType: "tag",
|
|
690
|
+
resourceId: tag.id,
|
|
691
|
+
workspaceId: tag.workspaceId,
|
|
692
|
+
payload: tag
|
|
693
|
+
};
|
|
694
|
+
checksumLine(hash, record);
|
|
695
|
+
recordCount += 1;
|
|
696
|
+
yield record;
|
|
697
|
+
}
|
|
698
|
+
const actorCategories = await withActor(ctx.actorUserId, (tx) => tx.select().from(categories).where(eq(categories.ownerId, ctx.actorUserId)));
|
|
699
|
+
for (const category of actorCategories) {
|
|
700
|
+
const record = {
|
|
701
|
+
type: "data",
|
|
702
|
+
domain: "categories",
|
|
703
|
+
resourceType: "category",
|
|
704
|
+
resourceId: category.id,
|
|
705
|
+
workspaceId: category.workspaceId,
|
|
706
|
+
payload: category
|
|
707
|
+
};
|
|
708
|
+
checksumLine(hash, record);
|
|
709
|
+
recordCount += 1;
|
|
710
|
+
yield record;
|
|
711
|
+
}
|
|
637
712
|
const ownedDocuments = await withActor(ctx.actorUserId, (tx) => tx.select().from(documents).where(eq(documents.ownerId, ctx.actorUserId)));
|
|
638
713
|
for (const document of ownedDocuments) {
|
|
639
714
|
throwIfAborted(ctx.signal);
|
|
@@ -733,12 +808,24 @@ function createPersistentLifecycleService(runtime, database, hostSteps = []) {
|
|
|
733
808
|
storageKey: attachments.storageKey
|
|
734
809
|
}).from(attachments).where(inArray(attachments.documentId, documentIds))) : [];
|
|
735
810
|
await runStep(operation, ctx.actorUserId, leaseOwner, "delete_attachment_objects", deletedByDomain, () => runtime.deleteObjects(objectRows.map((row) => row.storageKey), ctx.signal));
|
|
811
|
+
await runStep(operation, ctx.actorUserId, leaseOwner, "remove_subject_folders", deletedByDomain, () => dbDelete((tx) => tx.delete(folders).where(eq(folders.ownerId, ctx.actorUserId)).returning({ id: folders.id })));
|
|
812
|
+
await runStep(operation, ctx.actorUserId, leaseOwner, "remove_subject_tags", deletedByDomain, () => dbDelete((tx) => tx.delete(tags).where(eq(tags.ownerId, ctx.actorUserId)).returning({ id: tags.id })));
|
|
813
|
+
await runStep(operation, ctx.actorUserId, leaseOwner, "remove_subject_categories", deletedByDomain, () => dbDelete((tx) => tx.delete(categories).where(eq(categories.ownerId, ctx.actorUserId)).returning({ id: categories.id })));
|
|
736
814
|
await runStep(operation, ctx.actorUserId, leaseOwner, "remove_attachment_rows", deletedByDomain, () => objectRows.length ? dbDelete((tx) => tx.delete(attachments).where(inArray(attachments.id, objectRows.map((row) => row.id))).returning({ id: attachments.id })) : Promise.resolve(0));
|
|
737
815
|
await runStep(operation, ctx.actorUserId, leaseOwner, "remove_subject_documents", deletedByDomain, () => dbDelete((tx) => tx.delete(documents).where(eq(documents.ownerId, ctx.actorUserId)).returning({ id: documents.id })));
|
|
738
816
|
await runStep(operation, ctx.actorUserId, leaseOwner, "clear_redis_state", deletedByDomain, () => runtime.clearAccountRedisState(ctx.actorUserId, ctx.signal));
|
|
739
817
|
for (const step of orderedSteps)
|
|
740
818
|
if (step.purge)
|
|
741
819
|
await runStep(operation, ctx.actorUserId, leaseOwner, `host:${step.id}`, deletedByDomain, async () => (await step.purge?.(ctx))?.deletedCount ?? 0);
|
|
820
|
+
await runStep(operation, ctx.actorUserId, leaseOwner, "remove_auth_sessions", deletedByDomain, () => dbDelete((tx) => tx.delete(sessions).where(eq(sessions.userId, ctx.actorUserId)).returning({ id: sessions.id })));
|
|
821
|
+
await runStep(operation, ctx.actorUserId, leaseOwner, "remove_auth_accounts", deletedByDomain, () => dbDelete((tx) => tx.delete(accounts).where(eq(accounts.userId, ctx.actorUserId)).returning({ id: accounts.id })));
|
|
822
|
+
await runStep(operation, ctx.actorUserId, leaseOwner, "tombstone_subject_user", deletedByDomain, () => dbDelete((tx) => tx.update(users).set({
|
|
823
|
+
email: lifecycleTombstoneEmail(ctx.actorUserId),
|
|
824
|
+
name: null,
|
|
825
|
+
image: null,
|
|
826
|
+
emailVerified: false,
|
|
827
|
+
updatedAt: new Date
|
|
828
|
+
}).where(eq(users.id, ctx.actorUserId)).returning({ id: users.id })));
|
|
742
829
|
await runStep(operation, ctx.actorUserId, leaseOwner, "write_deletion_audit", deletedByDomain, async () => {
|
|
743
830
|
await withActor(ctx.actorUserId, (tx) => tx.insert(auditLog).values({
|
|
744
831
|
actorId: ctx.actorUserId,
|
|
@@ -803,6 +890,7 @@ function createPersistentLifecycleRuntime(options) {
|
|
|
803
890
|
}
|
|
804
891
|
export {
|
|
805
892
|
requireLeaseWrite,
|
|
893
|
+
lifecycleTombstoneEmail,
|
|
806
894
|
createPersistentLifecycleService,
|
|
807
895
|
createPersistentLifecycleRuntime,
|
|
808
896
|
bindPersistentLifecycle,
|
package/package.json
CHANGED
|
@@ -24,7 +24,7 @@ import { registerSearch } from "./commands/search.js";
|
|
|
24
24
|
import { registerSnapshot } from "./commands/snapshot.js";
|
|
25
25
|
import { registerUpdate } from "./commands/update.js";
|
|
26
26
|
|
|
27
|
-
const VERSION = "0.4.
|
|
27
|
+
const VERSION = "0.4.6";
|
|
28
28
|
|
|
29
29
|
const program = new Command();
|
|
30
30
|
program
|