@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.
- package/README.md +15 -6
- package/dist/module.d.mts +31 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +41 -36
- package/dist/runtime/app/components/Account/AccountHeader.d.vue.ts +3 -0
- package/dist/runtime/app/components/Account/AccountHeader.vue +59 -0
- package/dist/runtime/app/components/Account/AccountHeader.vue.d.ts +3 -0
- package/dist/runtime/app/components/Account/AccountMenu.vue +7 -2
- package/dist/runtime/app/components/Account/IdentificationCode.d.vue.ts +3 -0
- package/dist/runtime/app/components/Account/IdentificationCode.vue +82 -0
- package/dist/runtime/app/components/Account/IdentificationCode.vue.d.ts +3 -0
- package/dist/runtime/app/components/Account/IdentificationCodeDialog.d.vue.ts +20 -0
- package/dist/runtime/app/components/Account/IdentificationCodeDialog.vue +16 -0
- package/dist/runtime/app/components/Account/IdentificationCodeDialog.vue.d.ts +20 -0
- package/dist/runtime/app/composables/useAccount.js +3 -3
- package/dist/runtime/app/pages/api-tokens.d.vue.ts +3 -0
- package/dist/runtime/app/pages/api-tokens.vue +308 -0
- package/dist/runtime/app/pages/api-tokens.vue.d.ts +3 -0
- package/dist/runtime/app/pages/documents.d.vue.ts +3 -0
- package/dist/runtime/app/pages/documents.vue +38 -0
- package/dist/runtime/app/pages/documents.vue.d.ts +3 -0
- package/dist/runtime/app/pages/id-code.d.vue.ts +3 -0
- package/dist/runtime/app/pages/id-code.vue +13 -0
- package/dist/runtime/app/pages/id-code.vue.d.ts +3 -0
- package/dist/runtime/app/pages/layout.d.vue.ts +3 -0
- package/dist/runtime/app/pages/layout.vue +10 -0
- package/dist/runtime/app/pages/layout.vue.d.ts +3 -0
- package/dist/runtime/app/pages/profile.vue +188 -46
- package/dist/runtime/app/pages/security.vue +180 -60
- package/dist/runtime/app/utils/documents.d.ts +5 -0
- package/dist/runtime/app/utils/documents.js +7 -0
- package/dist/runtime/i18n/lang/da.json +125 -13
- package/dist/runtime/i18n/lang/en.json +125 -13
- package/dist/runtime/menus.js +36 -0
- package/dist/runtime/server/routes/account/id-code.get.js +20 -0
- package/package.json +22 -10
- package/dist/runtime/server/db/schema.sqlite.d.ts +0 -24
- package/dist/runtime/server/db/schema.sqlite.js +0 -4
- package/dist/runtime/server/routes/account/change-email.post.js +0 -20
- package/dist/runtime/server/routes/account/change-password.post.d.ts +0 -2
- package/dist/runtime/server/routes/account/change-password.post.js +0 -26
- package/dist/runtime/server/routes/account/profile.post.d.ts +0 -2
- package/dist/runtime/server/routes/account/profile.post.js +0 -17
- /package/dist/runtime/server/routes/account/{change-email.post.d.ts → id-code.get.d.ts} +0 -0
package/dist/runtime/menus.js
CHANGED
|
@@ -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.
|
|
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/
|
|
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
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
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
|
-
"@
|
|
39
|
-
"@
|
|
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,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,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,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
|
-
});
|
|
File without changes
|