@softure-ai/privacy 0.0.0-stage → 0.1.5
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/LICENSE +21 -0
- package/README.md +272 -2
- package/dist/contract.d.ts +58 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +2 -0
- package/dist/contract.js.map +1 -0
- package/dist/index.d.ts +90 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +52 -0
- package/dist/index.js.map +1 -0
- package/dist/messages/en.d.ts +46 -0
- package/dist/messages/en.d.ts.map +1 -0
- package/dist/messages/en.js +46 -0
- package/dist/messages/en.js.map +1 -0
- package/dist/messages/index.d.ts +99 -0
- package/dist/messages/index.d.ts.map +1 -0
- package/dist/messages/index.js +14 -0
- package/dist/messages/index.js.map +1 -0
- package/dist/messages/pl.d.ts +3 -0
- package/dist/messages/pl.d.ts.map +1 -0
- package/dist/messages/pl.js +46 -0
- package/dist/messages/pl.js.map +1 -0
- package/dist/next/actions.d.ts +4 -0
- package/dist/next/actions.d.ts.map +1 -0
- package/dist/next/actions.js +57 -0
- package/dist/next/actions.js.map +1 -0
- package/dist/next/context.d.ts +4 -0
- package/dist/next/context.d.ts.map +1 -0
- package/dist/next/context.js +13 -0
- package/dist/next/context.js.map +1 -0
- package/dist/next/index.d.ts +5 -0
- package/dist/next/index.d.ts.map +1 -0
- package/dist/next/index.js +7 -0
- package/dist/next/index.js.map +1 -0
- package/dist/next/messages.d.ts +5 -0
- package/dist/next/messages.d.ts.map +1 -0
- package/dist/next/messages.js +7 -0
- package/dist/next/messages.js.map +1 -0
- package/dist/next/pages.d.ts +2 -0
- package/dist/next/pages.d.ts.map +1 -0
- package/dist/next/pages.js +22 -0
- package/dist/next/pages.js.map +1 -0
- package/dist/next/route.d.ts +6 -0
- package/dist/next/route.d.ts.map +1 -0
- package/dist/next/route.js +49 -0
- package/dist/next/route.js.map +1 -0
- package/dist/next/session-cookie.d.ts +3 -0
- package/dist/next/session-cookie.d.ts.map +1 -0
- package/dist/next/session-cookie.js +17 -0
- package/dist/next/session-cookie.js.map +1 -0
- package/dist/options.d.ts +27 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +70 -0
- package/dist/options.js.map +1 -0
- package/dist/schema.d.ts +162 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +17 -0
- package/dist/schema.js.map +1 -0
- package/dist/server/collect.d.ts +16 -0
- package/dist/server/collect.d.ts.map +1 -0
- package/dist/server/collect.js +44 -0
- package/dist/server/collect.js.map +1 -0
- package/dist/server/consents-contributor.d.ts +14 -0
- package/dist/server/consents-contributor.d.ts.map +1 -0
- package/dist/server/consents-contributor.js +40 -0
- package/dist/server/consents-contributor.js.map +1 -0
- package/dist/server/consents.d.ts +42 -0
- package/dist/server/consents.d.ts.map +1 -0
- package/dist/server/consents.js +117 -0
- package/dist/server/consents.js.map +1 -0
- package/dist/server/context.d.ts +5 -0
- package/dist/server/context.d.ts.map +1 -0
- package/dist/server/context.js +2 -0
- package/dist/server/context.js.map +1 -0
- package/dist/server/contributors.d.ts +23 -0
- package/dist/server/contributors.d.ts.map +1 -0
- package/dist/server/contributors.js +38 -0
- package/dist/server/contributors.js.map +1 -0
- package/dist/server/erase.d.ts +10 -0
- package/dist/server/erase.d.ts.map +1 -0
- package/dist/server/erase.js +42 -0
- package/dist/server/erase.js.map +1 -0
- package/dist/server/health.d.ts +3 -0
- package/dist/server/health.d.ts.map +1 -0
- package/dist/server/health.js +11 -0
- package/dist/server/health.js.map +1 -0
- package/dist/server/index.d.ts +11 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +12 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/legal-documents.d.ts +9 -0
- package/dist/server/legal-documents.d.ts.map +1 -0
- package/dist/server/legal-documents.js +18 -0
- package/dist/server/legal-documents.js.map +1 -0
- package/dist/server/options.d.ts +15 -0
- package/dist/server/options.d.ts.map +1 -0
- package/dist/server/options.js +20 -0
- package/dist/server/options.js.map +1 -0
- package/dist/server/rate-limits.d.ts +13 -0
- package/dist/server/rate-limits.d.ts.map +1 -0
- package/dist/server/rate-limits.js +28 -0
- package/dist/server/rate-limits.js.map +1 -0
- package/dist/server/registration-consent.d.ts +19 -0
- package/dist/server/registration-consent.d.ts.map +1 -0
- package/dist/server/registration-consent.js +28 -0
- package/dist/server/registration-consent.js.map +1 -0
- package/dist/server/self-service.d.ts +29 -0
- package/dist/server/self-service.d.ts.map +1 -0
- package/dist/server/self-service.js +37 -0
- package/dist/server/self-service.js.map +1 -0
- package/dist/ui/delete-account-form.d.ts +16 -0
- package/dist/ui/delete-account-form.d.ts.map +1 -0
- package/dist/ui/delete-account-form.js +19 -0
- package/dist/ui/delete-account-form.js.map +1 -0
- package/dist/ui/index.d.ts +4 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +7 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/legal-document.d.ts +51 -0
- package/dist/ui/legal-document.d.ts.map +1 -0
- package/dist/ui/legal-document.js +50 -0
- package/dist/ui/legal-document.js.map +1 -0
- package/dist/ui/legal-footer.d.ts +18 -0
- package/dist/ui/legal-footer.d.ts.map +1 -0
- package/dist/ui/legal-footer.js +13 -0
- package/dist/ui/legal-footer.js.map +1 -0
- package/migrations/0001_create_consents.sql +37 -0
- package/module.json +15 -0
- package/package.json +64 -4
- package/src/contract.ts +70 -0
- package/src/index.ts +73 -0
- package/src/messages/en.ts +45 -0
- package/src/messages/index.ts +18 -0
- package/src/messages/pl.ts +47 -0
- package/src/next/actions.ts +61 -0
- package/src/next/context.ts +14 -0
- package/src/next/index.ts +6 -0
- package/src/next/messages.ts +9 -0
- package/src/next/next-modules.d.ts +12 -0
- package/src/next/pages.tsx +36 -0
- package/src/next/route.ts +50 -0
- package/src/next/session-cookie.ts +18 -0
- package/src/options.ts +87 -0
- package/src/schema.ts +18 -0
- package/src/server/collect.ts +58 -0
- package/src/server/consents-contributor.ts +48 -0
- package/src/server/consents.ts +142 -0
- package/src/server/context.ts +5 -0
- package/src/server/contributors.ts +58 -0
- package/src/server/erase.ts +42 -0
- package/src/server/health.ts +12 -0
- package/src/server/index.ts +41 -0
- package/src/server/legal-documents.ts +24 -0
- package/src/server/options.ts +33 -0
- package/src/server/rate-limits.ts +32 -0
- package/src/server/registration-consent.ts +43 -0
- package/src/server/self-service.ts +59 -0
- package/src/ui/delete-account-form.tsx +58 -0
- package/src/ui/index.ts +21 -0
- package/src/ui/legal-document.tsx +189 -0
- package/src/ui/legal-footer.tsx +50 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { DeleteAccountErrorCode } from "../contract.js";
|
|
2
|
+
import { en } from "./en.js";
|
|
3
|
+
/** Complete default dictionaries; apps pass partial overrides per locale. */
|
|
4
|
+
export declare const privacyMessages: {
|
|
5
|
+
en: {
|
|
6
|
+
page: {
|
|
7
|
+
title: string;
|
|
8
|
+
lead: string;
|
|
9
|
+
};
|
|
10
|
+
export: {
|
|
11
|
+
title: string;
|
|
12
|
+
description: string;
|
|
13
|
+
button: string;
|
|
14
|
+
};
|
|
15
|
+
delete: {
|
|
16
|
+
title: string;
|
|
17
|
+
description: string;
|
|
18
|
+
password: string;
|
|
19
|
+
confirm: string;
|
|
20
|
+
submit: string;
|
|
21
|
+
pending: string;
|
|
22
|
+
};
|
|
23
|
+
legal: {
|
|
24
|
+
contents: string;
|
|
25
|
+
version: string;
|
|
26
|
+
effectiveFrom: string;
|
|
27
|
+
changes: string;
|
|
28
|
+
footer: string;
|
|
29
|
+
};
|
|
30
|
+
errors: {
|
|
31
|
+
privacy: {
|
|
32
|
+
export_failed: string;
|
|
33
|
+
export_too_large: string;
|
|
34
|
+
deletion_refused: string;
|
|
35
|
+
password_invalid: string;
|
|
36
|
+
confirmation_required: string;
|
|
37
|
+
};
|
|
38
|
+
auth: {
|
|
39
|
+
unauthenticated: string;
|
|
40
|
+
};
|
|
41
|
+
security: {
|
|
42
|
+
rate_limited: string;
|
|
43
|
+
};
|
|
44
|
+
core: {
|
|
45
|
+
database_failed: string;
|
|
46
|
+
unexpected: string;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
pl: {
|
|
51
|
+
page: {
|
|
52
|
+
title: string;
|
|
53
|
+
lead: string;
|
|
54
|
+
};
|
|
55
|
+
export: {
|
|
56
|
+
title: string;
|
|
57
|
+
description: string;
|
|
58
|
+
button: string;
|
|
59
|
+
};
|
|
60
|
+
delete: {
|
|
61
|
+
title: string;
|
|
62
|
+
description: string;
|
|
63
|
+
password: string;
|
|
64
|
+
confirm: string;
|
|
65
|
+
submit: string;
|
|
66
|
+
pending: string;
|
|
67
|
+
};
|
|
68
|
+
legal: {
|
|
69
|
+
contents: string;
|
|
70
|
+
version: string;
|
|
71
|
+
effectiveFrom: string;
|
|
72
|
+
changes: string;
|
|
73
|
+
footer: string;
|
|
74
|
+
};
|
|
75
|
+
errors: {
|
|
76
|
+
privacy: {
|
|
77
|
+
export_failed: string;
|
|
78
|
+
export_too_large: string;
|
|
79
|
+
deletion_refused: string;
|
|
80
|
+
password_invalid: string;
|
|
81
|
+
confirmation_required: string;
|
|
82
|
+
};
|
|
83
|
+
auth: {
|
|
84
|
+
unauthenticated: string;
|
|
85
|
+
};
|
|
86
|
+
security: {
|
|
87
|
+
rate_limited: string;
|
|
88
|
+
};
|
|
89
|
+
core: {
|
|
90
|
+
database_failed: string;
|
|
91
|
+
unexpected: string;
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
export type PrivacyMessages = typeof en;
|
|
97
|
+
/** The copy for an error code; an unknown code gets the generic failure. */
|
|
98
|
+
export declare function getPrivacyErrorMessage(messages: PrivacyMessages, code: DeleteAccountErrorCode): string;
|
|
99
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/messages/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAC7D,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAG7B,6EAA6E;AAC7E,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAa,CAAC;AAE1C,MAAM,MAAM,eAAe,GAAG,OAAO,EAAE,CAAC;AAExC,4EAA4E;AAC5E,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,sBAAsB,GAAG,MAAM,CAOtG"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { en } from "./en.js";
|
|
2
|
+
import { pl } from "./pl.js";
|
|
3
|
+
/** Complete default dictionaries; apps pass partial overrides per locale. */
|
|
4
|
+
export const privacyMessages = { en, pl };
|
|
5
|
+
/** The copy for an error code; an unknown code gets the generic failure. */
|
|
6
|
+
export function getPrivacyErrorMessage(messages, code) {
|
|
7
|
+
const separator = code.lastIndexOf(".");
|
|
8
|
+
const namespace = code.slice(0, separator);
|
|
9
|
+
const name = code.slice(separator + 1);
|
|
10
|
+
const groups = messages.errors;
|
|
11
|
+
const group = Object.hasOwn(groups, namespace) ? groups[namespace] : undefined;
|
|
12
|
+
return (group !== undefined && Object.hasOwn(group, name) ? group[name] : undefined) ?? messages.errors.core.unexpected;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/messages/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAE7B,6EAA6E;AAC7E,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;AAI1C,4EAA4E;AAC5E,MAAM,UAAU,sBAAsB,CAAC,QAAyB,EAAE,IAA4B;IAC5F,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAoE,CAAC;IAC7F,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/E,OAAO,CAAC,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;AAC1H,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pl.d.ts","sourceRoot":"","sources":["../../src/messages/pl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAElC,eAAO,MAAM,EAAE,EAAE,OAAO,EA4CvB,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export const pl = {
|
|
2
|
+
page: {
|
|
3
|
+
title: "Twoje dane",
|
|
4
|
+
lead: "Pobierz kopię danych, które ta aplikacja o Tobie przechowuje, albo usuń konto.",
|
|
5
|
+
},
|
|
6
|
+
export: {
|
|
7
|
+
title: "Pobierz swoje dane",
|
|
8
|
+
description: "Plik JSON ze wszystkim, co ta aplikacja przechowuje o Twoim koncie.",
|
|
9
|
+
button: "Pobierz moje dane",
|
|
10
|
+
},
|
|
11
|
+
delete: {
|
|
12
|
+
title: "Usuń konto",
|
|
13
|
+
description: "To usunie Twoje konto i dane oraz wyloguje Cię na wszystkich urządzeniach. Tego nie da się cofnąć.",
|
|
14
|
+
password: "Obecne hasło",
|
|
15
|
+
confirm: "Rozumiem, że moje konto i moje dane zostaną usunięte na zawsze.",
|
|
16
|
+
submit: "Usuń moje konto",
|
|
17
|
+
pending: "Usuwanie…",
|
|
18
|
+
},
|
|
19
|
+
legal: {
|
|
20
|
+
contents: "Spis treści",
|
|
21
|
+
version: "Wersja",
|
|
22
|
+
effectiveFrom: "Obowiązuje od",
|
|
23
|
+
changes: "Historia zmian",
|
|
24
|
+
footer: "Informacje prawne",
|
|
25
|
+
},
|
|
26
|
+
errors: {
|
|
27
|
+
privacy: {
|
|
28
|
+
export_failed: "Nie udało się zebrać Twoich danych. Spróbuj ponownie za chwilę.",
|
|
29
|
+
export_too_large: "Twoich danych jest za dużo, by pobrać je tutaj. Napisz do nas, a prześlemy je.",
|
|
30
|
+
deletion_refused: "Nie można teraz usunąć konta, bo część jego danych musimy jeszcze przechowywać. Napisz do nas po szczegóły.",
|
|
31
|
+
password_invalid: "To nie jest Twoje obecne hasło.",
|
|
32
|
+
confirmation_required: "Potwierdź, że chcesz usunąć konto.",
|
|
33
|
+
},
|
|
34
|
+
auth: {
|
|
35
|
+
unauthenticated: "Twoja sesja wygasła. Zaloguj się ponownie.",
|
|
36
|
+
},
|
|
37
|
+
security: {
|
|
38
|
+
rate_limited: "Zbyt wiele prób. Odczekaj chwilę i spróbuj ponownie.",
|
|
39
|
+
},
|
|
40
|
+
core: {
|
|
41
|
+
database_failed: "Coś poszło nie tak po naszej stronie. Spróbuj ponownie za chwilę.",
|
|
42
|
+
unexpected: "Coś poszło nie tak po naszej stronie. Spróbuj ponownie za chwilę.",
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=pl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pl.js","sourceRoot":"","sources":["../../src/messages/pl.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,EAAE,GAAc;IAC3B,IAAI,EAAE;QACJ,KAAK,EAAE,YAAY;QACnB,IAAI,EAAE,gFAAgF;KACvF;IACD,MAAM,EAAE;QACN,KAAK,EAAE,oBAAoB;QAC3B,WAAW,EAAE,qEAAqE;QAClF,MAAM,EAAE,mBAAmB;KAC5B;IACD,MAAM,EAAE;QACN,KAAK,EAAE,YAAY;QACnB,WAAW,EAAE,oGAAoG;QACjH,QAAQ,EAAE,cAAc;QACxB,OAAO,EAAE,iEAAiE;QAC1E,MAAM,EAAE,iBAAiB;QACzB,OAAO,EAAE,WAAW;KACrB;IACD,KAAK,EAAE;QACL,QAAQ,EAAE,aAAa;QACvB,OAAO,EAAE,QAAQ;QACjB,aAAa,EAAE,eAAe;QAC9B,OAAO,EAAE,gBAAgB;QACzB,MAAM,EAAE,mBAAmB;KAC5B;IACD,MAAM,EAAE;QACN,OAAO,EAAE;YACP,aAAa,EAAE,iEAAiE;YAChF,gBAAgB,EAAE,gFAAgF;YAClG,gBAAgB,EAAE,6GAA6G;YAC/H,gBAAgB,EAAE,iCAAiC;YACnD,qBAAqB,EAAE,oCAAoC;SAC5D;QACD,IAAI,EAAE;YACJ,eAAe,EAAE,4CAA4C;SAC9D;QACD,QAAQ,EAAE;YACR,YAAY,EAAE,sDAAsD;SACrE;QACD,IAAI,EAAE;YACJ,eAAe,EAAE,mEAAmE;YACpF,UAAU,EAAE,mEAAmE;SAChF;KACF;CACF,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { DeleteAccountFormState } from "../contract.js";
|
|
2
|
+
/** Deletes the signed-in user's account, ends the browser's session and goes to `afterDelete`. */
|
|
3
|
+
export declare function deleteAccountAction(_previous: DeleteAccountFormState, formData: FormData): Promise<DeleteAccountFormState>;
|
|
4
|
+
//# sourceMappingURL=actions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/next/actions.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAA8C,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AA4BzG,kGAAkG;AAClG,wBAAsB,mBAAmB,CAAC,SAAS,EAAE,sBAAsB,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAqBhI"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
// The account deletion action. The user comes from the session (never from a bound argument or
|
|
3
|
+
// the form, which the client controls, docs/02 §8) before the form is read; unexpected failures
|
|
4
|
+
// become `safeError` codes. Next refuses an action whose Origin does not match the host.
|
|
5
|
+
import { getCurrentUser } from "@softure-ai/auth/next";
|
|
6
|
+
import { errorLogLabel, safeError } from "@softure-ai/core";
|
|
7
|
+
import { getSoftureConfig } from "@softure-ai/core/next";
|
|
8
|
+
import { redirect } from "next/navigation";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
import { getPrivacyRoutes } from "../server/options.js";
|
|
11
|
+
import { deleteOwnAccount } from "../server/self-service.js";
|
|
12
|
+
import { getPrivacyContext } from "./context.js";
|
|
13
|
+
import { clearSessionCookie } from "./session-cookie.js";
|
|
14
|
+
/** Longer values are cut: auth refuses them anyway, and nothing huge is hashed. */
|
|
15
|
+
const MAX_FIELD_LENGTH = 4096;
|
|
16
|
+
const deleteAccountInput = z.object({
|
|
17
|
+
password: z
|
|
18
|
+
.string()
|
|
19
|
+
.catch("")
|
|
20
|
+
.transform((value) => value.slice(0, MAX_FIELD_LENGTH)),
|
|
21
|
+
// A checked HTML checkbox sends "on" (or its value); an unchecked one sends nothing.
|
|
22
|
+
confirm: z.string().nullable().catch(null),
|
|
23
|
+
});
|
|
24
|
+
const FIELD_OF = {
|
|
25
|
+
"privacy.password_invalid": "password",
|
|
26
|
+
"privacy.confirmation_required": "confirm",
|
|
27
|
+
};
|
|
28
|
+
function failure(error) {
|
|
29
|
+
const field = FIELD_OF[error];
|
|
30
|
+
return field === undefined ? { status: "error", error } : { status: "error", error, field };
|
|
31
|
+
}
|
|
32
|
+
/** Deletes the signed-in user's account, ends the browser's session and goes to `afterDelete`. */
|
|
33
|
+
export async function deleteAccountAction(_previous, formData) {
|
|
34
|
+
const config = getSoftureConfig();
|
|
35
|
+
let result;
|
|
36
|
+
try {
|
|
37
|
+
const user = await getCurrentUser();
|
|
38
|
+
if (user === null)
|
|
39
|
+
return failure("auth.unauthenticated");
|
|
40
|
+
// Every field has a `catch`, so parsing cannot fail.
|
|
41
|
+
const input = deleteAccountInput.parse({ password: formData.get("password"), confirm: formData.get("confirm") });
|
|
42
|
+
result = await deleteOwnAccount(await getPrivacyContext(config), {
|
|
43
|
+
userId: user.id,
|
|
44
|
+
password: input.password,
|
|
45
|
+
isConfirmed: input.confirm !== null,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
console.error(`@softure-ai/privacy: account deletion failed: ${errorLogLabel(error)}`);
|
|
50
|
+
return failure(safeError(error).error);
|
|
51
|
+
}
|
|
52
|
+
if (!result.ok)
|
|
53
|
+
return failure(result.error);
|
|
54
|
+
await clearSessionCookie(config);
|
|
55
|
+
redirect(getPrivacyRoutes(config).afterDelete);
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=actions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actions.js","sourceRoot":"","sources":["../../src/next/actions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/next/context.ts"],"names":[],"mappings":"AAEA,OAAO,EAAe,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,wBAAsB,iBAAiB,CAAC,MAAM,GAAE,aAAkC,GAAG,OAAO,CAAC,cAAc,CAAC,CAM3G"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// What the adapter hands the server functions: the registered config, the process-wide database
|
|
2
|
+
// handle and the wall clock. Request scope (cookies, headers) stays in this folder.
|
|
3
|
+
import { systemClock } from "@softure-ai/core";
|
|
4
|
+
import { getSoftureConfig } from "@softure-ai/core/next";
|
|
5
|
+
import { getSharedDatabase } from "@softure-ai/db";
|
|
6
|
+
export async function getPrivacyContext(config = getSoftureConfig()) {
|
|
7
|
+
if (config.database === null) {
|
|
8
|
+
throw new Error("@softure-ai/privacy: softure.config.ts has no database; privacy needs one");
|
|
9
|
+
}
|
|
10
|
+
const { db } = await getSharedDatabase(config.database.url);
|
|
11
|
+
return { db, clock: systemClock, config };
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/next/context.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,oFAAoF;AACpF,OAAO,EAAE,WAAW,EAAsB,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGnD,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,SAAwB,gBAAgB,EAAE;IAChF,IAAI,MAAM,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,2EAA2E,CAAC,CAAC;IAC/F,CAAC;IACD,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,iBAAiB,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC5D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/next/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// The Next.js adapter of @softure-ai/privacy: the page, the delete action and the export route
|
|
2
|
+
// (docs/02-module-standard.md §8).
|
|
3
|
+
export { deleteAccountAction } from "./actions.js";
|
|
4
|
+
export { getPrivacyMessages } from "./messages.js";
|
|
5
|
+
export { PrivacyPage } from "./pages.js";
|
|
6
|
+
export { exportRoute } from "./route.js";
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/next/index.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,mCAAmC;AACnC,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { SoftureConfig } from "@softure-ai/core";
|
|
2
|
+
import type { PrivacyMessages } from "../messages/index.js";
|
|
3
|
+
/** The module's copy in the app's locale, with the app's overrides applied. */
|
|
4
|
+
export declare function getPrivacyMessages(config: SoftureConfig): PrivacyMessages;
|
|
5
|
+
//# sourceMappingURL=messages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/next/messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAG5D,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,GAAG,eAAe,CAGzE"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { getPrivacyModule } from "../server/options.js";
|
|
2
|
+
/** The module's copy in the app's locale, with the app's overrides applied. */
|
|
3
|
+
export function getPrivacyMessages(config) {
|
|
4
|
+
// The module factory merged the dictionaries; their shape is the module's own.
|
|
5
|
+
return getPrivacyModule(config).messages[config.locale];
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=messages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/next/messages.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,+EAA+E;AAC/E,MAAM,UAAU,kBAAkB,CAAC,MAAqB;IACtD,+EAA+E;IAC/E,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAoB,CAAC;AAC7E,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pages.d.ts","sourceRoot":"","sources":["../../src/next/pages.tsx"],"names":[],"mappings":"AAeA,wBAAsB,WAAW,yCAoBhC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// The privacy page, ready to mount with one line:
|
|
3
|
+
// `export { PrivacyPage as default } from "@softure-ai/privacy/next"` in app/account/privacy/page.tsx.
|
|
4
|
+
// A server component: without a session it redirects to the login page and back.
|
|
5
|
+
import { requireUser } from "@softure-ai/auth/next";
|
|
6
|
+
import { getSoftureConfig } from "@softure-ai/core/next";
|
|
7
|
+
import { ButtonLink, Card } from "@softure-ai/ui";
|
|
8
|
+
import { getPrivacyRoutes } from "../server/options.js";
|
|
9
|
+
import { DeleteAccountForm } from "../ui/delete-account-form.js";
|
|
10
|
+
import { deleteAccountAction } from "./actions.js";
|
|
11
|
+
import { getPrivacyMessages } from "./messages.js";
|
|
12
|
+
const LAYOUT_CLASS = "sft:mx-auto sft:box-border sft:flex sft:w-full sft:flex-col sft:gap-4 sft:sm:max-w-md sft:px-4 sft:py-4";
|
|
13
|
+
const DESCRIPTION_CLASS = "sft:m-0 sft:text-sm sft:text-muted";
|
|
14
|
+
const SECTION_CLASS = "sft:flex sft:flex-col sft:gap-3";
|
|
15
|
+
export async function PrivacyPage() {
|
|
16
|
+
const config = getSoftureConfig();
|
|
17
|
+
const routes = getPrivacyRoutes(config);
|
|
18
|
+
await requireUser({ next: routes.account });
|
|
19
|
+
const messages = getPrivacyMessages(config);
|
|
20
|
+
return (_jsxs("main", { className: LAYOUT_CLASS, children: [_jsx(Card, { title: messages.page.title, subtitle: messages.page.lead, variant: "lead", children: _jsxs("div", { className: SECTION_CLASS, children: [_jsx("p", { className: DESCRIPTION_CLASS, children: messages.export.description }), _jsx(ButtonLink, { href: routes.export, download: true, variant: "secondary", children: messages.export.button })] }) }), _jsx(Card, { title: messages.delete.title, children: _jsx(DeleteAccountForm, { action: deleteAccountAction, messages: messages, locale: config.locale }) })] }));
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=pages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pages.js","sourceRoot":"","sources":["../../src/next/pages.tsx"],"names":[],"mappings":";AAAA,kDAAkD;AAClD,uGAAuG;AACvG,iFAAiF;AACjF,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEnD,MAAM,YAAY,GAAG,yGAAyG,CAAC;AAC/H,MAAM,iBAAiB,GAAG,oCAAoC,CAAC;AAC/D,MAAM,aAAa,GAAG,iCAAiC,CAAC;AAExD,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;IAClC,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,WAAW,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAC5C,OAAO,CACL,gBAAM,SAAS,EAAE,YAAY,aAC3B,KAAC,IAAI,IAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAC,MAAM,YAC5E,eAAK,SAAS,EAAE,aAAa,aAC3B,YAAG,SAAS,EAAE,iBAAiB,YAAG,QAAQ,CAAC,MAAM,CAAC,WAAW,GAAK,EAClE,KAAC,UAAU,IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,QAAC,OAAO,EAAC,WAAW,YAC1D,QAAQ,CAAC,MAAM,CAAC,MAAM,GACZ,IACT,GACD,EACP,KAAC,IAAI,IAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,YAChC,KAAC,iBAAiB,IAAC,MAAM,EAAE,mBAAmB,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,GAAI,GACxF,IACF,CACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The signed-in user's data as a JSON download. 401 without a session, 429 with `retry-after` over
|
|
3
|
+
* the `privacy-export` limit, 413 over `export.maxBytes`, 500 when a contributor fails.
|
|
4
|
+
*/
|
|
5
|
+
export declare function exportRoute(): Promise<Response>;
|
|
6
|
+
//# sourceMappingURL=route.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../src/next/route.ts"],"names":[],"mappings":"AAsBA;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,QAAQ,CAAC,CAuBrD"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// The export route handler. Mount with a rename:
|
|
2
|
+
// `export { exportRoute as GET } from "@softure-ai/privacy/next"` in app/api/privacy/export/route.ts.
|
|
3
|
+
// The user comes from the session cookie only; the answer is a JSON attachment that is never cached.
|
|
4
|
+
import { getCurrentUser } from "@softure-ai/auth/next";
|
|
5
|
+
import { errorLogLabel, safeError } from "@softure-ai/core";
|
|
6
|
+
import { getSoftureConfig } from "@softure-ai/core/next";
|
|
7
|
+
import { getPrivacyOptions } from "../server/options.js";
|
|
8
|
+
import { exportOwnData } from "../server/self-service.js";
|
|
9
|
+
import { getPrivacyContext } from "./context.js";
|
|
10
|
+
const NO_STORE = { "cache-control": "no-store", "x-content-type-options": "nosniff" };
|
|
11
|
+
function fail(error, status, headers = {}) {
|
|
12
|
+
return Response.json({ error }, { status, headers: { ...NO_STORE, ...headers } });
|
|
13
|
+
}
|
|
14
|
+
/** `account-data-2026-10-03.json`: the date of the download in the app's time zone. */
|
|
15
|
+
function getFileName(config, now) {
|
|
16
|
+
const date = new Intl.DateTimeFormat("en-CA", { timeZone: config.timezone, year: "numeric", month: "2-digit", day: "2-digit" }).format(now);
|
|
17
|
+
return `${getPrivacyOptions(config).export.fileName}-${date}.json`;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The signed-in user's data as a JSON download. 401 without a session, 429 with `retry-after` over
|
|
21
|
+
* the `privacy-export` limit, 413 over `export.maxBytes`, 500 when a contributor fails.
|
|
22
|
+
*/
|
|
23
|
+
export async function exportRoute() {
|
|
24
|
+
try {
|
|
25
|
+
const config = getSoftureConfig();
|
|
26
|
+
const user = await getCurrentUser();
|
|
27
|
+
if (user === null)
|
|
28
|
+
return fail("auth.unauthenticated", 401);
|
|
29
|
+
const ctx = await getPrivacyContext(config);
|
|
30
|
+
const result = await exportOwnData(ctx, { userId: user.id });
|
|
31
|
+
if (!result.ok) {
|
|
32
|
+
if ("retryAfterSeconds" in result)
|
|
33
|
+
return fail(result.error, 429, { "retry-after": String(result.retryAfterSeconds) });
|
|
34
|
+
return fail(result.error, result.error === "privacy.export_too_large" ? 413 : 500);
|
|
35
|
+
}
|
|
36
|
+
return new Response(result.value.json, {
|
|
37
|
+
headers: {
|
|
38
|
+
...NO_STORE,
|
|
39
|
+
"content-type": "application/json; charset=utf-8",
|
|
40
|
+
"content-disposition": `attachment; filename="${getFileName(config, ctx.clock.now())}"`,
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
console.error(`@softure-ai/privacy: export failed: ${errorLogLabel(error)}`);
|
|
46
|
+
return fail(safeError(error).error, 500);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route.js","sourceRoot":"","sources":["../../src/next/route.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,sGAAsG;AACtG,qGAAqG;AACrG,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,SAAS,EAAsB,MAAM,kBAAkB,CAAC;AAChF,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjD,MAAM,QAAQ,GAAG,EAAE,eAAe,EAAE,UAAU,EAAE,wBAAwB,EAAE,SAAS,EAAE,CAAC;AAEtF,SAAS,IAAI,CAAC,KAAa,EAAE,MAAc,EAAE,UAAkC,EAAE;IAC/E,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,EAAE,EAAE,CAAC,CAAC;AACpF,CAAC;AAED,uFAAuF;AACvF,SAAS,WAAW,CAAC,MAAqB,EAAE,GAAS;IACnD,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC5I,OAAO,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,QAAQ,IAAI,IAAI,OAAO,CAAC;AACrE,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,MAAM,cAAc,EAAE,CAAC;QACpC,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC,sBAAsB,EAAE,GAAG,CAAC,CAAC;QAE5D,MAAM,GAAG,GAAG,MAAM,iBAAiB,CAAC,MAAM,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7D,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,IAAI,mBAAmB,IAAI,MAAM;gBAAE,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC;YACvH,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,KAAK,0BAA0B,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACrF,CAAC;QACD,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE;YACrC,OAAO,EAAE;gBACP,GAAG,QAAQ;gBACX,cAAc,EAAE,iCAAiC;gBACjD,qBAAqB,EAAE,yBAAyB,WAAW,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,GAAG;aACxF;SACF,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,uCAAuC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC7E,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC3C,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-cookie.d.ts","sourceRoot":"","sources":["../../src/next/session-cookie.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGtD,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAW7E"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Ending the browser's session cookie after the account is gone. The database sessions are
|
|
2
|
+
// deleted with the account; this removes the cookie with the attributes auth set it with.
|
|
3
|
+
import { getSessionCookie } from "@softure-ai/auth";
|
|
4
|
+
import { cookies } from "next/headers";
|
|
5
|
+
export async function clearSessionCookie(config) {
|
|
6
|
+
const cookie = getSessionCookie(config);
|
|
7
|
+
const store = await cookies();
|
|
8
|
+
store.set(cookie.name, "", {
|
|
9
|
+
httpOnly: cookie.httpOnly,
|
|
10
|
+
secure: cookie.secure,
|
|
11
|
+
sameSite: cookie.sameSite,
|
|
12
|
+
path: cookie.path,
|
|
13
|
+
domain: cookie.domain,
|
|
14
|
+
maxAge: 0,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=session-cookie.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-cookie.js","sourceRoot":"","sources":["../../src/next/session-cookie.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,0FAA0F;AAC1F,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,MAAqB;IAC5D,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,MAAM,OAAO,EAAE,CAAC;IAC9B,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,EAAE;QACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,MAAM,EAAE,CAAC;KACV,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/** Kebab-case, like a module id: contributor ids are the keys of the export. */
|
|
3
|
+
export declare const CONTRIBUTOR_ID_PATTERN: RegExp;
|
|
4
|
+
/** A document version as the app writes it, e.g. `2026-10-01` or `1.2`. */
|
|
5
|
+
export declare const DOCUMENT_VERSION_PATTERN: RegExp;
|
|
6
|
+
/** The export size limit when the app sets none. */
|
|
7
|
+
export declare const DEFAULT_EXPORT_MAX_BYTES: number;
|
|
8
|
+
export declare const privacyOptionsSchema: z.ZodObject<{
|
|
9
|
+
contributors: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
10
|
+
id: z.ZodString;
|
|
11
|
+
exportUserData: z.ZodOptional<z.ZodCustom<(context: import("@softure-ai/core").ModuleContext, userId: string) => Promise<import("@softure-ai/core").Result<unknown>>, (context: import("@softure-ai/core").ModuleContext, userId: string) => Promise<import("@softure-ai/core").Result<unknown>>>>;
|
|
12
|
+
deleteUserData: z.ZodOptional<z.ZodCustom<(context: import("@softure-ai/core").ModuleContext, userId: string) => Promise<import("@softure-ai/core").Result<undefined>>, (context: import("@softure-ai/core").ModuleContext, userId: string) => Promise<import("@softure-ai/core").Result<undefined>>>>;
|
|
13
|
+
}, z.core.$strict>>>;
|
|
14
|
+
documents: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
15
|
+
id: z.ZodString;
|
|
16
|
+
version: z.ZodString;
|
|
17
|
+
}, z.core.$strict>>>;
|
|
18
|
+
export: z.ZodPrefault<z.ZodObject<{
|
|
19
|
+
maxBytes: z.ZodDefault<z.ZodNumber>;
|
|
20
|
+
fileName: z.ZodDefault<z.ZodString>;
|
|
21
|
+
}, z.core.$strict>>;
|
|
22
|
+
}, z.core.$strict>;
|
|
23
|
+
export type PrivacyOptionsInput = z.input<typeof privacyOptionsSchema>;
|
|
24
|
+
export type PrivacyOptions = z.output<typeof privacyOptionsSchema>;
|
|
25
|
+
export type AppPrivacyContributor = PrivacyOptions["contributors"][number];
|
|
26
|
+
export type LegalDocumentDeclaration = PrivacyOptions["documents"][number];
|
|
27
|
+
//# sourceMappingURL=options.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,gFAAgF;AAChF,eAAO,MAAM,sBAAsB,QAAoC,CAAC;AAExE,2EAA2E;AAC3E,eAAO,MAAM,wBAAwB,QAAsC,CAAC;AAI5E,oDAAoD;AACpD,eAAO,MAAM,wBAAwB,QAAgB,CAAC;AA8BtD,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;kBAsC7B,CAAC;AAEL,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACvE,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACnE,MAAM,MAAM,qBAAqB,GAAG,cAAc,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAC3E,MAAM,MAAM,wBAAwB,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC"}
|
package/dist/options.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/** Kebab-case, like a module id: contributor ids are the keys of the export. */
|
|
3
|
+
export const CONTRIBUTOR_ID_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
4
|
+
/** A document version as the app writes it, e.g. `2026-10-01` or `1.2`. */
|
|
5
|
+
export const DOCUMENT_VERSION_PATTERN = /^[0-9A-Za-z][0-9A-Za-z._-]{0,31}$/;
|
|
6
|
+
const MEBIBYTE = 1024 * 1024;
|
|
7
|
+
/** The export size limit when the app sets none. */
|
|
8
|
+
export const DEFAULT_EXPORT_MAX_BYTES = 10 * MEBIBYTE;
|
|
9
|
+
const isFunction = (value) => typeof value === "function";
|
|
10
|
+
const appContributorSchema = z
|
|
11
|
+
.strictObject({
|
|
12
|
+
/** Names the contributor's part of the export and its log lines, e.g. `profile`. */
|
|
13
|
+
id: z.string().max(64, "must be at most 64 characters").regex(CONTRIBUTOR_ID_PATTERN, "must be kebab-case, e.g. user-profile"),
|
|
14
|
+
/** The user's data held by the app, as plain JSON values (dates become ISO strings). */
|
|
15
|
+
exportUserData: z.custom(isFunction, "must be a function").optional(),
|
|
16
|
+
/** Deletes (or anonymises) that data. An `Err` refuses the deletion of the whole account. */
|
|
17
|
+
deleteUserData: z.custom(isFunction, "must be a function").optional(),
|
|
18
|
+
})
|
|
19
|
+
.refine((contributor) => contributor.exportUserData !== undefined || contributor.deleteUserData !== undefined, {
|
|
20
|
+
message: "needs exportUserData, deleteUserData or both",
|
|
21
|
+
});
|
|
22
|
+
const legalDocumentSchema = z.strictObject({
|
|
23
|
+
/** Names the document in consent records and in `getLegalDocument`, e.g. `terms`. */
|
|
24
|
+
id: z.string().max(64, "must be at most 64 characters").regex(CONTRIBUTOR_ID_PATTERN, "must be kebab-case, e.g. privacy-policy"),
|
|
25
|
+
/**
|
|
26
|
+
* The version in force, stamped on every consent given to the document. Change it whenever the
|
|
27
|
+
* published text changes, e.g. to the date the new text takes effect.
|
|
28
|
+
*/
|
|
29
|
+
version: z.string().regex(DOCUMENT_VERSION_PATTERN, "must be 1-32 letters, digits, '.', '_' or '-', e.g. 2026-10-01"),
|
|
30
|
+
});
|
|
31
|
+
export const privacyOptionsSchema = z
|
|
32
|
+
.strictObject({
|
|
33
|
+
/**
|
|
34
|
+
* The app's own contributors, next to the ones of its modules. They export after the modules
|
|
35
|
+
* and delete before them, so app tables that reference module tables go first.
|
|
36
|
+
*/
|
|
37
|
+
contributors: z.array(appContributorSchema).default([]),
|
|
38
|
+
/** The app's legal documents (terms, privacy policy) and their current versions. */
|
|
39
|
+
documents: z.array(legalDocumentSchema).default([]),
|
|
40
|
+
export: z
|
|
41
|
+
.strictObject({
|
|
42
|
+
/** The largest export, in bytes of JSON; a larger one is refused instead of sent. */
|
|
43
|
+
maxBytes: z
|
|
44
|
+
.number()
|
|
45
|
+
.int()
|
|
46
|
+
.min(1024)
|
|
47
|
+
.max(100 * MEBIBYTE)
|
|
48
|
+
.default(DEFAULT_EXPORT_MAX_BYTES),
|
|
49
|
+
/** The download's file name before the date, e.g. `account-data-2026-10-03.json`. */
|
|
50
|
+
fileName: z.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/, "must be 1-64 lowercase letters, digits or -").default("account-data"),
|
|
51
|
+
})
|
|
52
|
+
.prefault({}),
|
|
53
|
+
})
|
|
54
|
+
.superRefine((options, context) => {
|
|
55
|
+
const seen = new Set();
|
|
56
|
+
options.contributors.forEach((contributor, index) => {
|
|
57
|
+
if (seen.has(contributor.id)) {
|
|
58
|
+
context.addIssue({ code: "custom", path: ["contributors", index, "id"], message: `"${contributor.id}" is registered twice` });
|
|
59
|
+
}
|
|
60
|
+
seen.add(contributor.id);
|
|
61
|
+
});
|
|
62
|
+
const documentIds = new Set();
|
|
63
|
+
options.documents.forEach((document, index) => {
|
|
64
|
+
if (documentIds.has(document.id)) {
|
|
65
|
+
context.addIssue({ code: "custom", path: ["documents", index, "id"], message: `"${document.id}" is declared twice` });
|
|
66
|
+
}
|
|
67
|
+
documentIds.add(document.id);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
//# sourceMappingURL=options.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"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"}
|