@frigear-nu/account 0.0.7 → 0.0.8

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.
Files changed (44) hide show
  1. package/README.md +15 -6
  2. package/dist/module.d.mts +31 -0
  3. package/dist/module.json +1 -1
  4. package/dist/module.mjs +41 -36
  5. package/dist/runtime/app/components/Account/AccountHeader.d.vue.ts +3 -0
  6. package/dist/runtime/app/components/Account/AccountHeader.vue +59 -0
  7. package/dist/runtime/app/components/Account/AccountHeader.vue.d.ts +3 -0
  8. package/dist/runtime/app/components/Account/AccountMenu.vue +7 -2
  9. package/dist/runtime/app/components/Account/IdentificationCode.d.vue.ts +3 -0
  10. package/dist/runtime/app/components/Account/IdentificationCode.vue +82 -0
  11. package/dist/runtime/app/components/Account/IdentificationCode.vue.d.ts +3 -0
  12. package/dist/runtime/app/components/Account/IdentificationCodeDialog.d.vue.ts +20 -0
  13. package/dist/runtime/app/components/Account/IdentificationCodeDialog.vue +16 -0
  14. package/dist/runtime/app/components/Account/IdentificationCodeDialog.vue.d.ts +20 -0
  15. package/dist/runtime/app/composables/useAccount.js +3 -3
  16. package/dist/runtime/app/pages/api-tokens.d.vue.ts +3 -0
  17. package/dist/runtime/app/pages/api-tokens.vue +308 -0
  18. package/dist/runtime/app/pages/api-tokens.vue.d.ts +3 -0
  19. package/dist/runtime/app/pages/documents.d.vue.ts +3 -0
  20. package/dist/runtime/app/pages/documents.vue +38 -0
  21. package/dist/runtime/app/pages/documents.vue.d.ts +3 -0
  22. package/dist/runtime/app/pages/id-code.d.vue.ts +3 -0
  23. package/dist/runtime/app/pages/id-code.vue +13 -0
  24. package/dist/runtime/app/pages/id-code.vue.d.ts +3 -0
  25. package/dist/runtime/app/pages/layout.d.vue.ts +3 -0
  26. package/dist/runtime/app/pages/layout.vue +10 -0
  27. package/dist/runtime/app/pages/layout.vue.d.ts +3 -0
  28. package/dist/runtime/app/pages/profile.vue +188 -46
  29. package/dist/runtime/app/pages/security.vue +180 -60
  30. package/dist/runtime/app/utils/documents.d.ts +5 -0
  31. package/dist/runtime/app/utils/documents.js +7 -0
  32. package/dist/runtime/i18n/lang/da.json +125 -13
  33. package/dist/runtime/i18n/lang/en.json +125 -13
  34. package/dist/runtime/menus.js +36 -0
  35. package/dist/runtime/server/routes/account/id-code.get.js +20 -0
  36. package/package.json +22 -10
  37. package/dist/runtime/server/db/schema.sqlite.d.ts +0 -24
  38. package/dist/runtime/server/db/schema.sqlite.js +0 -4
  39. package/dist/runtime/server/routes/account/change-email.post.js +0 -20
  40. package/dist/runtime/server/routes/account/change-password.post.d.ts +0 -2
  41. package/dist/runtime/server/routes/account/change-password.post.js +0 -26
  42. package/dist/runtime/server/routes/account/profile.post.d.ts +0 -2
  43. package/dist/runtime/server/routes/account/profile.post.js +0 -17
  44. /package/dist/runtime/server/routes/account/{change-email.post.d.ts → id-code.get.d.ts} +0 -0
@@ -39,4 +39,40 @@ export default defineMenuSource(() => {
39
39
  order: 20
40
40
  });
41
41
  }
42
+ if (config?.pages?.apiTokens) {
43
+ extendMenuItem("account", {
44
+ id: "api-tokens",
45
+ label: "API tokens",
46
+ labelKey: "account.menu.apiTokens.label",
47
+ description: "Create tokens for scripts and tools.",
48
+ descriptionKey: "account.menu.apiTokens.description",
49
+ icon: "i-lucide-key-round",
50
+ to: config.pages.apiTokens,
51
+ order: 25
52
+ });
53
+ }
54
+ if (config?.pages?.documents) {
55
+ extendMenuItem("account", {
56
+ id: "documents",
57
+ label: "Documents",
58
+ labelKey: "account.menu.documents.label",
59
+ description: "Handbooks and other files for you.",
60
+ descriptionKey: "account.menu.documents.description",
61
+ icon: "i-lucide-files",
62
+ to: config.pages.documents,
63
+ order: 30
64
+ });
65
+ }
66
+ if (config?.pages?.idCode) {
67
+ extendMenuItem("account", {
68
+ id: "id-code",
69
+ label: "Frigear ID",
70
+ labelKey: "account.identificationCode.title",
71
+ description: "Press here to show your ID.",
72
+ descriptionKey: "account.identificationCode.description",
73
+ icon: "i-lucide-qr-code",
74
+ to: config.pages.idCode,
75
+ order: 5
76
+ });
77
+ }
42
78
  });
@@ -0,0 +1,20 @@
1
+ import { SignJWT, importPKCS8 } from "jose";
2
+ const TTL_SECONDS = 60;
3
+ function normalizePem(raw) {
4
+ return raw.trim().replace(/^["']|["']$/g, "").replace(/\\n/g, "\n").trim();
5
+ }
6
+ export default defineEventHandler(async (event) => {
7
+ const user = await requireUser(event, { sessionOnly: true });
8
+ const config = useRuntimeConfig(event);
9
+ const own = config.account?.idCode;
10
+ const oauth = config.oauthServer;
11
+ const pem = normalizePem(own?.privateKey || oauth?.privateKey || "");
12
+ if (!pem) {
13
+ throw createError({ statusCode: 501, statusMessage: "Frigear ID is not configured: set a signing key." });
14
+ }
15
+ const keyId = own?.keyId || oauth?.keyId || "key-1";
16
+ const issuer = own?.issuer || oauth?.issuer || getRequestURL(event).origin;
17
+ const key = await importPKCS8(pem, "RS256");
18
+ const jwt = await new SignJWT({ typ: "frigear:id" }).setProtectedHeader({ alg: "RS256", kid: keyId }).setIssuer(issuer).setSubject(String(user.id)).setAudience("frigear:id").setIssuedAt().setExpirationTime(`${TTL_SECONDS}s`).setJti(crypto.randomUUID()).sign(key);
19
+ return { idCode: jwt, payload: `urn:frigear:id:${jwt}`, expiresIn: TTL_SECONDS };
20
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frigear-nu/account",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "A module for managing the account area (profile, email, password, security) in the main frigear app.",
5
5
  "homepage": "https://docs.frigear.dev/modules/account",
6
6
  "repository": {
@@ -24,31 +24,44 @@
24
24
  "playground"
25
25
  ],
26
26
  "dependencies": {
27
- "@frigear-nu/users": "1.0.8",
27
+ "@frigear-nu/module-kit": "0.1.7",
28
+ "@frigear-nu/auth": "0.0.3",
29
+ "@frigear-nu/users": "1.0.9",
28
30
  "@libsql/client": "^0.18.0",
29
31
  "@nuxt/kit": "^4.6.0",
32
+ "@vueuse/core": "^15.0.0",
30
33
  "drizzle-kit": "^0.31.11",
31
34
  "drizzle-orm": "^0.45.3",
32
- "zod": "^4.6.5",
33
- "@frigear-nu/menu": "0.0.8",
34
- "@frigear-nu/module-kit": "0.1.6",
35
- "@vueuse/core": "^15.0.0"
35
+ "jose": "^6.1.0",
36
+ "uqr": "^0.1.2",
37
+ "zod": "^4.6.5"
36
38
  },
37
39
  "peerDependencies": {
38
- "@nuxthub/core": "^0.10.8",
39
- "@nuxt/ui": "^4.10.0"
40
+ "@frigear-nu/authorization": "0.0.6",
41
+ "@frigear-nu/menu": "0.0.9",
42
+ "@nuxt/ui": "^4.10.0",
43
+ "@nuxthub/core": "^0.10.8"
40
44
  },
41
45
  "peerDependenciesMeta": {
46
+ "@frigear-nu/authorization": {
47
+ "optional": true
48
+ },
49
+ "@frigear-nu/menu": {
50
+ "optional": true
51
+ },
42
52
  "@nuxt/ui": {
43
53
  "optional": true
44
54
  }
45
55
  },
46
56
  "devDependencies": {
57
+ "@frigear-nu/authorization": "0.0.6",
58
+ "@frigear-nu/menu": "0.0.9",
47
59
  "@nuxt/devtools": "^3.4.2",
48
60
  "@nuxt/eslint-config": "^1.17.0",
49
61
  "@nuxt/module-builder": "^1.0.3",
50
62
  "@nuxt/schema": "^4.6.0",
51
63
  "@nuxt/test-utils": "^4.3.2",
64
+ "@nuxt/ui": "^4.11.0",
52
65
  "@nuxthub/core": "^0.10.8",
53
66
  "@types/node": "latest",
54
67
  "changelogen": "^0.6.2",
@@ -56,8 +69,7 @@
56
69
  "nuxt": "^4.6.1",
57
70
  "typescript": "^6.0.3",
58
71
  "vitest": "^5.0.1",
59
- "vue-tsc": "^3.3.11",
60
- "@nuxt/ui": "^4.11.0"
72
+ "vue-tsc": "^3.3.11"
61
73
  },
62
74
  "scripts": {
63
75
  "postinstall": "nuxt-module-build build --stub && nuxt prepare",
@@ -1,24 +0,0 @@
1
- export declare const users: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
2
- name: "users";
3
- schema: undefined;
4
- columns: {
5
- acceptedTermsAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
6
- name: "accepted_terms_at";
7
- tableName: "users";
8
- dataType: "number";
9
- columnType: "SQLiteInteger";
10
- data: number;
11
- driverParam: number;
12
- notNull: false;
13
- hasDefault: false;
14
- isPrimaryKey: false;
15
- isAutoincrement: false;
16
- hasRuntimeDefault: false;
17
- enumValues: undefined;
18
- baseColumn: never;
19
- identity: undefined;
20
- generated: undefined;
21
- }, {}, {}>;
22
- };
23
- dialect: "sqlite";
24
- }>;
@@ -1,4 +0,0 @@
1
- import { sqliteTable, integer } from "drizzle-orm/sqlite-core";
2
- export const users = sqliteTable("users", {
3
- acceptedTermsAt: integer("accepted_terms_at")
4
- });
@@ -1,20 +0,0 @@
1
- import { db, schema } from "@nuxthub/db";
2
- import { eq } from "drizzle-orm";
3
- import { z } from "zod";
4
- const bodySchema = z.object({
5
- email: z.string().email()
6
- });
7
- export default defineEventHandler(async (event) => {
8
- const { user } = await requireUserSession(event);
9
- const { email } = await readValidatedBody(event, bodySchema.parse);
10
- const existing = await db.query.users.findFirst({ where: (t, { eq: eq2 }) => eq2(t.email, email) });
11
- if (existing && existing.id !== user.id) {
12
- throw createError({ statusCode: 409, statusMessage: "A user with this email already exists." });
13
- }
14
- const [updated] = await db.update(schema.users).set({ email }).where(eq(schema.users.id, user.id)).returning();
15
- if (!updated) {
16
- throw createError({ statusCode: 404, statusMessage: "User not found." });
17
- }
18
- await setUserSession(event, { user: mapUserToSession(updated) });
19
- return { id: updated.id };
20
- });
@@ -1,2 +0,0 @@
1
- declare const _default: any;
2
- export default _default;
@@ -1,26 +0,0 @@
1
- import { db, schema } from "@nuxthub/db";
2
- import { eq } from "drizzle-orm";
3
- import { z } from "zod";
4
- const bodySchema = z.object({
5
- currentPassword: z.string().min(1),
6
- newPassword: z.string().min(8),
7
- confirmNewPassword: z.string().min(8)
8
- }).refine((data) => data.newPassword === data.confirmNewPassword, {
9
- message: "Passwords do not match.",
10
- path: ["confirmNewPassword"]
11
- });
12
- export default defineEventHandler(async (event) => {
13
- const { user } = await requireUserSession(event);
14
- const { currentPassword, newPassword } = await readValidatedBody(event, bodySchema.parse);
15
- const dbUser = await db.query.users.findFirst({ where: (t, { eq: eq2 }) => eq2(t.id, user.id) });
16
- if (!dbUser?.password || !await verifyPassword(dbUser.password, currentPassword)) {
17
- throw createError({ statusCode: 401, statusMessage: "Your current password is incorrect." });
18
- }
19
- const hashed = await hashPassword(newPassword);
20
- const [updated] = await db.update(schema.users).set({ password: hashed }).where(eq(schema.users.id, user.id)).returning();
21
- if (!updated) {
22
- throw createError({ statusCode: 404, statusMessage: "User not found." });
23
- }
24
- await setUserSession(event, { user: mapUserToSession(updated) });
25
- return { id: updated.id };
26
- });
@@ -1,2 +0,0 @@
1
- declare const _default: any;
2
- export default _default;
@@ -1,17 +0,0 @@
1
- import { db, schema } from "@nuxthub/db";
2
- import { eq } from "drizzle-orm";
3
- import { z } from "zod";
4
- const bodySchema = z.object({
5
- name: z.string().min(1),
6
- avatar: z.string().optional()
7
- });
8
- export default defineEventHandler(async (event) => {
9
- const { user } = await requireUserSession(event);
10
- const { name, avatar } = await readValidatedBody(event, bodySchema.parse);
11
- const [updated] = await db.update(schema.users).set({ name, avatar }).where(eq(schema.users.id, user.id)).returning();
12
- if (!updated) {
13
- throw createError({ statusCode: 404, statusMessage: "User not found." });
14
- }
15
- await setUserSession(event, { user: mapUserToSession(updated) });
16
- return { id: updated.id };
17
- });