nuxt-users 1.48.1 → 1.49.0
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 +3 -4
- package/dist/cli.mjs +2 -2
- package/dist/module.d.mts +2 -2
- package/dist/module.d.ts +2 -2
- package/dist/module.json +2 -2
- package/dist/module.mjs +5 -2
- package/dist/runtime/components/NUsersLoginForm.d.vue.ts +7 -7
- package/dist/runtime/components/NUsersLoginForm.vue +10 -58
- package/dist/runtime/components/NUsersLoginForm.vue.d.ts +7 -7
- package/dist/runtime/components/NUsersPasswordInput.d.vue.ts +24 -0
- package/dist/runtime/components/NUsersPasswordInput.vue +92 -0
- package/dist/runtime/components/NUsersPasswordInput.vue.d.ts +24 -0
- package/dist/runtime/components/NUsersRegisterForm.d.vue.ts +8 -8
- package/dist/runtime/components/NUsersRegisterForm.vue +5 -7
- package/dist/runtime/components/NUsersRegisterForm.vue.d.ts +8 -8
- package/dist/runtime/components/NUsersResetPasswordForm.vue +12 -13
- package/dist/runtime/components/NUsersUserForm.vue +142 -5
- package/dist/runtime/locales/en.js +7 -1
- package/dist/runtime/locales/hu.js +12 -1
- package/dist/runtime/server/api/nuxt-users/[id].patch.js +4 -2
- package/dist/runtime/server/api/nuxt-users/index.post.js +6 -3
- package/dist/runtime/server/services/password.js +4 -3
- package/dist/runtime/server/utils/db.js +1 -1
- package/dist/runtime/server/utils/user.js +11 -2
- package/dist/shared/{nuxt-users.9p2ye9P4.mjs → nuxt-users.Cvjgn5KJ.mjs} +1 -1
- package/dist/shared/{nuxt-users.bLYb3Bz5.d.mts → nuxt-users.DtwPhils.d.mts} +13 -1
- package/dist/shared/{nuxt-users.bLYb3Bz5.d.ts → nuxt-users.DtwPhils.d.ts} +13 -1
- package/dist/shared/{nuxt-users.BsNZbqYb.mjs → nuxt-users.KrHIaTTL.mjs} +2 -1
- package/dist/utils.d.mts +26 -3
- package/dist/utils.d.ts +26 -3
- package/dist/utils.mjs +49 -2
- package/package.json +4 -5
package/README.md
CHANGED
|
@@ -4,11 +4,10 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/nuxt-users)
|
|
5
5
|
[](https://github.com/rrd108/nuxt-users/blob/main/LICENSE)
|
|
6
6
|
[](https://github.com/rrd108/nuxt-users/actions/workflows/ci.yml)
|
|
7
|
-
[](https://nuxt.com)
|
|
7
|
+
[](https://nuxt.com)
|
|
9
8
|
|
|
10
9
|
|
|
11
|
-
A user authentication module for Nuxt
|
|
10
|
+
A user authentication module for Nuxt with database support for SQLite, MySQL, and PostgreSQL.
|
|
12
11
|
|
|
13
12
|
## Features
|
|
14
13
|
|
|
@@ -36,7 +35,7 @@ A user authentication module for Nuxt 3 and Nuxt 4 with database support for SQL
|
|
|
36
35
|
- ⚡ **Developer Experience**
|
|
37
36
|
- Zero-config setup with sensible defaults
|
|
38
37
|
- TypeScript support throughout
|
|
39
|
-
- Nuxt
|
|
38
|
+
- Nuxt compatibility
|
|
40
39
|
- Easy customization and extension
|
|
41
40
|
|
|
42
41
|
## Agent Skill
|
package/dist/cli.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { defineCommand, runMain } from 'citty';
|
|
3
3
|
import { readFileSync } from 'node:fs';
|
|
4
|
-
import { d as defaultOptions } from './shared/nuxt-users.
|
|
4
|
+
import { d as defaultOptions } from './shared/nuxt-users.KrHIaTTL.mjs';
|
|
5
5
|
import { defu } from 'defu';
|
|
6
6
|
import { runMigrations, getAppliedMigrations } from '../dist/runtime/server/utils/migrate.js';
|
|
7
7
|
import { createUser as createUser$1 } from '../dist/runtime/server/utils/user.js';
|
|
8
|
-
import {
|
|
8
|
+
import { g as getPasswordValidationOptions, v as validatePassword } from './shared/nuxt-users.Cvjgn5KJ.mjs';
|
|
9
9
|
import '../dist/runtime/locales/index.js';
|
|
10
10
|
import '../dist/runtime/utils/locale.js';
|
|
11
11
|
import '../dist/runtime/utils/deep-merge.js';
|
package/dist/module.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
-
import {
|
|
2
|
+
import { ModuleOptions, RuntimeModuleOptions } from './utils.mjs';
|
|
3
3
|
export * from '../dist/runtime/server/internal/index.js';
|
|
4
4
|
|
|
5
5
|
declare const defaultOptions: ModuleOptions;
|
|
6
6
|
|
|
7
|
-
declare const _default: _nuxt_schema.NuxtModule<
|
|
7
|
+
declare const _default: _nuxt_schema.NuxtModule<RuntimeModuleOptions, RuntimeModuleOptions, false>;
|
|
8
8
|
|
|
9
9
|
export { _default as default, defaultOptions };
|
package/dist/module.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
-
import {
|
|
2
|
+
import { ModuleOptions, RuntimeModuleOptions } from './utils.js';
|
|
3
3
|
export * from '../dist/runtime/server/internal/index.js';
|
|
4
4
|
|
|
5
5
|
declare const defaultOptions: ModuleOptions;
|
|
6
6
|
|
|
7
|
-
declare const _default: _nuxt_schema.NuxtModule<
|
|
7
|
+
declare const _default: _nuxt_schema.NuxtModule<RuntimeModuleOptions, RuntimeModuleOptions, false>;
|
|
8
8
|
|
|
9
9
|
export { _default as default, defaultOptions };
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineNuxtModule, createResolver, addPlugin, addImportsDir, addServerImportsDir, addServerScanDir, addServerHandler, addServerPlugin, addRouteMiddleware, addComponentsDir } from '@nuxt/kit';
|
|
2
2
|
import { defu } from 'defu';
|
|
3
|
-
import { d as defaultOptions } from './shared/nuxt-users.
|
|
3
|
+
import { d as defaultOptions } from './shared/nuxt-users.KrHIaTTL.mjs';
|
|
4
4
|
export * from '../dist/runtime/server/internal/index.js';
|
|
5
5
|
|
|
6
6
|
const module$1 = defineNuxtModule({
|
|
@@ -22,12 +22,14 @@ const module$1 = defineNuxtModule({
|
|
|
22
22
|
}
|
|
23
23
|
const configuredConnector = nuxt.options.runtimeConfig.nuxtUsers?.connector || options.connector;
|
|
24
24
|
runtimeConfigOptions.connector = configuredConnector || defaultOptions.connector;
|
|
25
|
+
runtimeConfigOptions.auth.allowCustomRoles = runtimeConfigOptions.auth.allowCustomRoles ?? defaultOptions.auth.allowCustomRoles;
|
|
25
26
|
nuxt.options.runtimeConfig.nuxtUsers = {
|
|
26
27
|
...runtimeConfigOptions,
|
|
27
28
|
auth: {
|
|
28
29
|
tokenExpiration: runtimeConfigOptions.auth.tokenExpiration,
|
|
29
30
|
rememberMeExpiration: runtimeConfigOptions.auth.rememberMeExpiration,
|
|
30
31
|
permissions: runtimeConfigOptions.auth.permissions,
|
|
32
|
+
allowCustomRoles: runtimeConfigOptions.auth.allowCustomRoles,
|
|
31
33
|
...runtimeConfigOptions.auth.google && { google: runtimeConfigOptions.auth.google },
|
|
32
34
|
whitelist: (() => {
|
|
33
35
|
const combinedWhitelist = [...defaultOptions.auth?.whitelist || [], ...runtimeConfigOptions.auth?.whitelist || []];
|
|
@@ -99,7 +101,8 @@ const module$1 = defineNuxtModule({
|
|
|
99
101
|
}
|
|
100
102
|
return combinedWhitelist;
|
|
101
103
|
})(),
|
|
102
|
-
permissions: runtimeConfigOptions.auth?.permissions || defaultOptions.auth.permissions
|
|
104
|
+
permissions: runtimeConfigOptions.auth?.permissions || defaultOptions.auth.permissions,
|
|
105
|
+
allowCustomRoles: runtimeConfigOptions.auth.allowCustomRoles
|
|
103
106
|
},
|
|
104
107
|
apiBasePath: runtimeConfigOptions.apiBasePath || defaultOptions.apiBasePath
|
|
105
108
|
};
|
|
@@ -16,9 +16,9 @@ interface Props {
|
|
|
16
16
|
forgotPasswordLabel?: string;
|
|
17
17
|
forgotPasswordSendingLabel?: string;
|
|
18
18
|
}
|
|
19
|
-
declare var __VLS_1: {}, __VLS_3: {}, __VLS_5: {},
|
|
19
|
+
declare var __VLS_1: {}, __VLS_3: {}, __VLS_5: {}, __VLS_12: {}, __VLS_14: {}, __VLS_16: {}, __VLS_18: {
|
|
20
20
|
message: string;
|
|
21
|
-
},
|
|
21
|
+
}, __VLS_20: {
|
|
22
22
|
error: string;
|
|
23
23
|
};
|
|
24
24
|
type __VLS_Slots = {} & {
|
|
@@ -28,15 +28,15 @@ type __VLS_Slots = {} & {
|
|
|
28
28
|
} & {
|
|
29
29
|
'password-field'?: (props: typeof __VLS_5) => any;
|
|
30
30
|
} & {
|
|
31
|
-
'remember-me'?: (props: typeof
|
|
31
|
+
'remember-me'?: (props: typeof __VLS_12) => any;
|
|
32
32
|
} & {
|
|
33
|
-
'submit-button'?: (props: typeof
|
|
33
|
+
'submit-button'?: (props: typeof __VLS_14) => any;
|
|
34
34
|
} & {
|
|
35
|
-
footer?: (props: typeof
|
|
35
|
+
footer?: (props: typeof __VLS_16) => any;
|
|
36
36
|
} & {
|
|
37
|
-
'success-message'?: (props: typeof
|
|
37
|
+
'success-message'?: (props: typeof __VLS_18) => any;
|
|
38
38
|
} & {
|
|
39
|
-
'error-message'?: (props: typeof
|
|
39
|
+
'error-message'?: (props: typeof __VLS_20) => any;
|
|
40
40
|
};
|
|
41
41
|
declare const __VLS_base: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
42
42
|
error: (error: string) => any;
|
|
@@ -38,7 +38,6 @@ const apiEndpoint = computed(() => props.apiEndpoint || `${nuxtUsers?.apiBasePat
|
|
|
38
38
|
const forgotPasswordEndpoint = computed(() => props.forgotPasswordEndpoint || `${nuxtUsers?.apiBasePath}/password/forgot`);
|
|
39
39
|
const isLoading = ref(false);
|
|
40
40
|
const isForgotPasswordLoading = ref(false);
|
|
41
|
-
const showPassword = ref(false);
|
|
42
41
|
const error = ref("");
|
|
43
42
|
const forgotPasswordMessage = ref("");
|
|
44
43
|
const formData = ref({
|
|
@@ -132,63 +131,16 @@ const handleForgotPassword = async () => {
|
|
|
132
131
|
<slot name="password-field">
|
|
133
132
|
<div class="n-users-form-group">
|
|
134
133
|
<label for="password">{{ props.passwordLabel || t("login.passwordLabel") }}</label>
|
|
135
|
-
<
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
<button
|
|
146
|
-
type="button"
|
|
147
|
-
class="n-users-password-toggle"
|
|
148
|
-
:title="showPassword ? props.hidePasswordLabel || t('login.hidePassword') : props.showPasswordLabel || t('login.showPassword')"
|
|
149
|
-
@click="showPassword = !showPassword"
|
|
150
|
-
>
|
|
151
|
-
<svg
|
|
152
|
-
v-if="!showPassword"
|
|
153
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
154
|
-
width="20"
|
|
155
|
-
height="20"
|
|
156
|
-
viewBox="0 0 24 24"
|
|
157
|
-
fill="none"
|
|
158
|
-
stroke="currentColor"
|
|
159
|
-
stroke-width="2"
|
|
160
|
-
stroke-linecap="round"
|
|
161
|
-
stroke-linejoin="round"
|
|
162
|
-
>
|
|
163
|
-
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24" />
|
|
164
|
-
<line
|
|
165
|
-
x1="1"
|
|
166
|
-
y1="1"
|
|
167
|
-
x2="23"
|
|
168
|
-
y2="23"
|
|
169
|
-
/>
|
|
170
|
-
</svg>
|
|
171
|
-
<svg
|
|
172
|
-
v-if="showPassword"
|
|
173
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
174
|
-
width="20"
|
|
175
|
-
height="20"
|
|
176
|
-
viewBox="0 0 24 24"
|
|
177
|
-
fill="none"
|
|
178
|
-
stroke="currentColor"
|
|
179
|
-
stroke-width="2"
|
|
180
|
-
stroke-linecap="round"
|
|
181
|
-
stroke-linejoin="round"
|
|
182
|
-
>
|
|
183
|
-
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
|
|
184
|
-
<circle
|
|
185
|
-
cx="12"
|
|
186
|
-
cy="12"
|
|
187
|
-
r="3"
|
|
188
|
-
/>
|
|
189
|
-
</svg>
|
|
190
|
-
</button>
|
|
191
|
-
</div>
|
|
134
|
+
<NUsersPasswordInput
|
|
135
|
+
id="password"
|
|
136
|
+
v-model="formData.password"
|
|
137
|
+
name="password"
|
|
138
|
+
:placeholder="props.passwordPlaceholder || t('login.passwordPlaceholder')"
|
|
139
|
+
required
|
|
140
|
+
:minlength="passwordValidation?.minLength || 8"
|
|
141
|
+
:show-password-label="props.showPasswordLabel"
|
|
142
|
+
:hide-password-label="props.hidePasswordLabel"
|
|
143
|
+
/>
|
|
192
144
|
</div>
|
|
193
145
|
</slot>
|
|
194
146
|
|
|
@@ -16,9 +16,9 @@ interface Props {
|
|
|
16
16
|
forgotPasswordLabel?: string;
|
|
17
17
|
forgotPasswordSendingLabel?: string;
|
|
18
18
|
}
|
|
19
|
-
declare var __VLS_1: {}, __VLS_3: {}, __VLS_5: {},
|
|
19
|
+
declare var __VLS_1: {}, __VLS_3: {}, __VLS_5: {}, __VLS_12: {}, __VLS_14: {}, __VLS_16: {}, __VLS_18: {
|
|
20
20
|
message: string;
|
|
21
|
-
},
|
|
21
|
+
}, __VLS_20: {
|
|
22
22
|
error: string;
|
|
23
23
|
};
|
|
24
24
|
type __VLS_Slots = {} & {
|
|
@@ -28,15 +28,15 @@ type __VLS_Slots = {} & {
|
|
|
28
28
|
} & {
|
|
29
29
|
'password-field'?: (props: typeof __VLS_5) => any;
|
|
30
30
|
} & {
|
|
31
|
-
'remember-me'?: (props: typeof
|
|
31
|
+
'remember-me'?: (props: typeof __VLS_12) => any;
|
|
32
32
|
} & {
|
|
33
|
-
'submit-button'?: (props: typeof
|
|
33
|
+
'submit-button'?: (props: typeof __VLS_14) => any;
|
|
34
34
|
} & {
|
|
35
|
-
footer?: (props: typeof
|
|
35
|
+
footer?: (props: typeof __VLS_16) => any;
|
|
36
36
|
} & {
|
|
37
|
-
'success-message'?: (props: typeof
|
|
37
|
+
'success-message'?: (props: typeof __VLS_18) => any;
|
|
38
38
|
} & {
|
|
39
|
-
'error-message'?: (props: typeof
|
|
39
|
+
'error-message'?: (props: typeof __VLS_20) => any;
|
|
40
40
|
};
|
|
41
41
|
declare const __VLS_base: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
42
42
|
error: (error: string) => any;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
modelValue?: string;
|
|
3
|
+
id?: string;
|
|
4
|
+
name?: string;
|
|
5
|
+
placeholder?: string;
|
|
6
|
+
required?: boolean;
|
|
7
|
+
minlength?: number;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
autocomplete?: string;
|
|
10
|
+
inputClass?: string | Record<string, boolean> | Array<string | Record<string, boolean>>;
|
|
11
|
+
showPasswordLabel?: string;
|
|
12
|
+
hidePasswordLabel?: string;
|
|
13
|
+
}
|
|
14
|
+
declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
15
|
+
"update:modelValue": (value: string) => any;
|
|
16
|
+
}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
|
|
17
|
+
"onUpdate:modelValue"?: ((value: string) => any) | undefined;
|
|
18
|
+
}>, {
|
|
19
|
+
disabled: boolean;
|
|
20
|
+
required: boolean;
|
|
21
|
+
modelValue: string;
|
|
22
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
23
|
+
declare const _default: typeof __VLS_export;
|
|
24
|
+
export default _default;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref, computed } from "vue";
|
|
3
|
+
import { useNuxtUsersLocale } from "../composables/useNuxtUsersLocale";
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
modelValue: { type: String, required: false, default: "" },
|
|
6
|
+
id: { type: String, required: false },
|
|
7
|
+
name: { type: String, required: false },
|
|
8
|
+
placeholder: { type: String, required: false },
|
|
9
|
+
required: { type: Boolean, required: false, default: false },
|
|
10
|
+
minlength: { type: Number, required: false },
|
|
11
|
+
disabled: { type: Boolean, required: false, default: false },
|
|
12
|
+
autocomplete: { type: String, required: false },
|
|
13
|
+
inputClass: { type: [String, Object, Array], required: false },
|
|
14
|
+
showPasswordLabel: { type: String, required: false },
|
|
15
|
+
hidePasswordLabel: { type: String, required: false }
|
|
16
|
+
});
|
|
17
|
+
const emit = defineEmits(["update:modelValue"]);
|
|
18
|
+
const { t } = useNuxtUsersLocale();
|
|
19
|
+
const showPassword = ref(false);
|
|
20
|
+
const toggleTitle = computed(
|
|
21
|
+
() => showPassword.value ? props.hidePasswordLabel || t("common.hidePassword") : props.showPasswordLabel || t("common.showPassword")
|
|
22
|
+
);
|
|
23
|
+
const handleInput = (event) => {
|
|
24
|
+
const target = event.target;
|
|
25
|
+
emit("update:modelValue", target.value);
|
|
26
|
+
};
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<template>
|
|
30
|
+
<div class="n-users-password-wrapper">
|
|
31
|
+
<input
|
|
32
|
+
:id="id"
|
|
33
|
+
:value="modelValue"
|
|
34
|
+
:type="showPassword ? 'text' : 'password'"
|
|
35
|
+
:name="name"
|
|
36
|
+
:placeholder="placeholder"
|
|
37
|
+
:required="required"
|
|
38
|
+
:minlength="minlength"
|
|
39
|
+
:disabled="disabled"
|
|
40
|
+
:autocomplete="autocomplete"
|
|
41
|
+
:class="inputClass"
|
|
42
|
+
@input="handleInput"
|
|
43
|
+
>
|
|
44
|
+
<button
|
|
45
|
+
type="button"
|
|
46
|
+
class="n-users-password-toggle"
|
|
47
|
+
:title="toggleTitle"
|
|
48
|
+
:disabled="disabled"
|
|
49
|
+
@click="showPassword = !showPassword"
|
|
50
|
+
>
|
|
51
|
+
<svg
|
|
52
|
+
v-if="!showPassword"
|
|
53
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
54
|
+
width="20"
|
|
55
|
+
height="20"
|
|
56
|
+
viewBox="0 0 24 24"
|
|
57
|
+
fill="none"
|
|
58
|
+
stroke="currentColor"
|
|
59
|
+
stroke-width="2"
|
|
60
|
+
stroke-linecap="round"
|
|
61
|
+
stroke-linejoin="round"
|
|
62
|
+
>
|
|
63
|
+
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24" />
|
|
64
|
+
<line
|
|
65
|
+
x1="1"
|
|
66
|
+
y1="1"
|
|
67
|
+
x2="23"
|
|
68
|
+
y2="23"
|
|
69
|
+
/>
|
|
70
|
+
</svg>
|
|
71
|
+
<svg
|
|
72
|
+
v-if="showPassword"
|
|
73
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
74
|
+
width="20"
|
|
75
|
+
height="20"
|
|
76
|
+
viewBox="0 0 24 24"
|
|
77
|
+
fill="none"
|
|
78
|
+
stroke="currentColor"
|
|
79
|
+
stroke-width="2"
|
|
80
|
+
stroke-linecap="round"
|
|
81
|
+
stroke-linejoin="round"
|
|
82
|
+
>
|
|
83
|
+
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
|
|
84
|
+
<circle
|
|
85
|
+
cx="12"
|
|
86
|
+
cy="12"
|
|
87
|
+
r="3"
|
|
88
|
+
/>
|
|
89
|
+
</svg>
|
|
90
|
+
</button>
|
|
91
|
+
</div>
|
|
92
|
+
</template>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
modelValue?: string;
|
|
3
|
+
id?: string;
|
|
4
|
+
name?: string;
|
|
5
|
+
placeholder?: string;
|
|
6
|
+
required?: boolean;
|
|
7
|
+
minlength?: number;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
autocomplete?: string;
|
|
10
|
+
inputClass?: string | Record<string, boolean> | Array<string | Record<string, boolean>>;
|
|
11
|
+
showPasswordLabel?: string;
|
|
12
|
+
hidePasswordLabel?: string;
|
|
13
|
+
}
|
|
14
|
+
declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
15
|
+
"update:modelValue": (value: string) => any;
|
|
16
|
+
}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
|
|
17
|
+
"onUpdate:modelValue"?: ((value: string) => any) | undefined;
|
|
18
|
+
}>, {
|
|
19
|
+
disabled: boolean;
|
|
20
|
+
required: boolean;
|
|
21
|
+
modelValue: string;
|
|
22
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
23
|
+
declare const _default: typeof __VLS_export;
|
|
24
|
+
export default _default;
|
|
@@ -25,9 +25,9 @@ interface RegistrationFormData {
|
|
|
25
25
|
password: string;
|
|
26
26
|
confirmPassword: string;
|
|
27
27
|
}
|
|
28
|
-
declare var __VLS_1: {}, __VLS_3: {}, __VLS_5: {}, __VLS_7: {},
|
|
28
|
+
declare var __VLS_1: {}, __VLS_3: {}, __VLS_5: {}, __VLS_7: {}, __VLS_14: {}, __VLS_21: {}, __VLS_28: {}, __VLS_30: {}, __VLS_32: {
|
|
29
29
|
message: string;
|
|
30
|
-
},
|
|
30
|
+
}, __VLS_34: {
|
|
31
31
|
error: string;
|
|
32
32
|
};
|
|
33
33
|
type __VLS_Slots = {} & {
|
|
@@ -39,17 +39,17 @@ type __VLS_Slots = {} & {
|
|
|
39
39
|
} & {
|
|
40
40
|
'password-field'?: (props: typeof __VLS_7) => any;
|
|
41
41
|
} & {
|
|
42
|
-
'password-strength'?: (props: typeof
|
|
42
|
+
'password-strength'?: (props: typeof __VLS_14) => any;
|
|
43
43
|
} & {
|
|
44
|
-
'confirm-password-field'?: (props: typeof
|
|
44
|
+
'confirm-password-field'?: (props: typeof __VLS_21) => any;
|
|
45
45
|
} & {
|
|
46
|
-
'submit-button'?: (props: typeof
|
|
46
|
+
'submit-button'?: (props: typeof __VLS_28) => any;
|
|
47
47
|
} & {
|
|
48
|
-
footer?: (props: typeof
|
|
48
|
+
footer?: (props: typeof __VLS_30) => any;
|
|
49
49
|
} & {
|
|
50
|
-
'success-message'?: (props: typeof
|
|
50
|
+
'success-message'?: (props: typeof __VLS_32) => any;
|
|
51
51
|
} & {
|
|
52
|
-
'error-message'?: (props: typeof
|
|
52
|
+
'error-message'?: (props: typeof __VLS_34) => any;
|
|
53
53
|
};
|
|
54
54
|
declare const __VLS_base: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
55
55
|
error: (error: string) => any;
|
|
@@ -143,15 +143,14 @@ const handleSubmit = async () => {
|
|
|
143
143
|
<slot name="password-field">
|
|
144
144
|
<div class="n-users-form-group">
|
|
145
145
|
<label for="password">{{ props.passwordLabel || t("register.passwordLabel") }}</label>
|
|
146
|
-
<
|
|
146
|
+
<NUsersPasswordInput
|
|
147
147
|
id="password"
|
|
148
148
|
v-model="formData.password"
|
|
149
|
-
type="password"
|
|
150
149
|
name="password"
|
|
151
150
|
:placeholder="props.passwordPlaceholder || t('register.passwordPlaceholder')"
|
|
152
151
|
required
|
|
153
152
|
:minlength="passwordValidation?.minLength || 8"
|
|
154
|
-
|
|
153
|
+
/>
|
|
155
154
|
</div>
|
|
156
155
|
</slot>
|
|
157
156
|
|
|
@@ -170,15 +169,14 @@ const handleSubmit = async () => {
|
|
|
170
169
|
<slot name="confirm-password-field">
|
|
171
170
|
<div class="n-users-form-group">
|
|
172
171
|
<label for="confirmPassword">{{ props.confirmPasswordLabel || t("register.confirmPasswordLabel") }}</label>
|
|
173
|
-
<
|
|
172
|
+
<NUsersPasswordInput
|
|
174
173
|
id="confirmPassword"
|
|
175
174
|
v-model="formData.confirmPassword"
|
|
176
|
-
type="password"
|
|
177
175
|
name="confirmPassword"
|
|
178
176
|
:placeholder="props.confirmPasswordPlaceholder || t('register.confirmPasswordPlaceholder')"
|
|
179
177
|
required
|
|
180
|
-
:class="{ 'n-users-input-error': !passwordsMatch }"
|
|
181
|
-
|
|
178
|
+
:input-class="{ 'n-users-input-error': !passwordsMatch }"
|
|
179
|
+
/>
|
|
182
180
|
<small
|
|
183
181
|
v-if="!passwordsMatch && formData.confirmPassword"
|
|
184
182
|
class="n-users-error-text"
|
|
@@ -25,9 +25,9 @@ interface RegistrationFormData {
|
|
|
25
25
|
password: string;
|
|
26
26
|
confirmPassword: string;
|
|
27
27
|
}
|
|
28
|
-
declare var __VLS_1: {}, __VLS_3: {}, __VLS_5: {}, __VLS_7: {},
|
|
28
|
+
declare var __VLS_1: {}, __VLS_3: {}, __VLS_5: {}, __VLS_7: {}, __VLS_14: {}, __VLS_21: {}, __VLS_28: {}, __VLS_30: {}, __VLS_32: {
|
|
29
29
|
message: string;
|
|
30
|
-
},
|
|
30
|
+
}, __VLS_34: {
|
|
31
31
|
error: string;
|
|
32
32
|
};
|
|
33
33
|
type __VLS_Slots = {} & {
|
|
@@ -39,17 +39,17 @@ type __VLS_Slots = {} & {
|
|
|
39
39
|
} & {
|
|
40
40
|
'password-field'?: (props: typeof __VLS_7) => any;
|
|
41
41
|
} & {
|
|
42
|
-
'password-strength'?: (props: typeof
|
|
42
|
+
'password-strength'?: (props: typeof __VLS_14) => any;
|
|
43
43
|
} & {
|
|
44
|
-
'confirm-password-field'?: (props: typeof
|
|
44
|
+
'confirm-password-field'?: (props: typeof __VLS_21) => any;
|
|
45
45
|
} & {
|
|
46
|
-
'submit-button'?: (props: typeof
|
|
46
|
+
'submit-button'?: (props: typeof __VLS_28) => any;
|
|
47
47
|
} & {
|
|
48
|
-
footer?: (props: typeof
|
|
48
|
+
footer?: (props: typeof __VLS_30) => any;
|
|
49
49
|
} & {
|
|
50
|
-
'success-message'?: (props: typeof
|
|
50
|
+
'success-message'?: (props: typeof __VLS_32) => any;
|
|
51
51
|
} & {
|
|
52
|
-
'error-message'?: (props: typeof
|
|
52
|
+
'error-message'?: (props: typeof __VLS_34) => any;
|
|
53
53
|
};
|
|
54
54
|
declare const __VLS_base: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
55
55
|
error: (error: string) => any;
|
|
@@ -144,14 +144,14 @@ const handleSubmit = () => {
|
|
|
144
144
|
for="currentPassword"
|
|
145
145
|
class="n-users-form-label"
|
|
146
146
|
>{{ props.currentPasswordLabel || t("resetPassword.currentPasswordLabel") }}</label>
|
|
147
|
-
<
|
|
147
|
+
<NUsersPasswordInput
|
|
148
148
|
id="currentPassword"
|
|
149
149
|
v-model="passwordForm.currentPassword"
|
|
150
|
-
|
|
151
|
-
class="n-users-form-input"
|
|
150
|
+
input-class="n-users-form-input"
|
|
152
151
|
required
|
|
153
152
|
:disabled="isPasswordLoading"
|
|
154
|
-
|
|
153
|
+
autocomplete="current-password"
|
|
154
|
+
/>
|
|
155
155
|
</div>
|
|
156
156
|
|
|
157
157
|
<div class="n-users-form-group">
|
|
@@ -159,16 +159,15 @@ const handleSubmit = () => {
|
|
|
159
159
|
for="newPassword"
|
|
160
160
|
class="n-users-form-label"
|
|
161
161
|
>{{ props.newPasswordLabel || t("resetPassword.newPasswordLabel") }}</label>
|
|
162
|
-
<
|
|
162
|
+
<NUsersPasswordInput
|
|
163
163
|
id="newPassword"
|
|
164
164
|
v-model="passwordForm.newPassword"
|
|
165
|
-
|
|
166
|
-
class="n-users-form-input"
|
|
165
|
+
:input-class="['n-users-form-input', { error: passwordValidation.errors.value.length > 0 && !!passwordForm.newPassword }]"
|
|
167
166
|
required
|
|
168
167
|
:minlength="moduleOptions.passwordValidation?.minLength || 8"
|
|
169
|
-
:class="{ error: passwordValidation.errors.value.length > 0 && passwordForm.newPassword }"
|
|
170
168
|
:disabled="isPasswordLoading"
|
|
171
|
-
|
|
169
|
+
autocomplete="new-password"
|
|
170
|
+
/>
|
|
172
171
|
|
|
173
172
|
<NUsersPasswordStrengthIndicator
|
|
174
173
|
:password="passwordForm.newPassword"
|
|
@@ -189,14 +188,14 @@ const handleSubmit = () => {
|
|
|
189
188
|
for="newPasswordConfirmation"
|
|
190
189
|
class="n-users-form-label"
|
|
191
190
|
>{{ props.confirmPasswordLabel || t("resetPassword.confirmPasswordLabel") }}</label>
|
|
192
|
-
<
|
|
191
|
+
<NUsersPasswordInput
|
|
193
192
|
id="newPasswordConfirmation"
|
|
194
193
|
v-model="passwordForm.newPasswordConfirmation"
|
|
195
|
-
|
|
196
|
-
class="n-users-form-input"
|
|
194
|
+
input-class="n-users-form-input"
|
|
197
195
|
required
|
|
198
196
|
:disabled="isPasswordLoading"
|
|
199
|
-
|
|
197
|
+
autocomplete="new-password"
|
|
198
|
+
/>
|
|
200
199
|
</div>
|
|
201
200
|
|
|
202
201
|
<div
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { ref, computed, watch } from "vue";
|
|
3
|
+
import { getAvailableRoles, MAX_ROLE_LENGTH } from "nuxt-users/utils";
|
|
3
4
|
import { usePasswordValidation } from "../composables/usePasswordValidation";
|
|
5
|
+
import { useNuxtUsersLocale } from "../composables/useNuxtUsersLocale";
|
|
4
6
|
import { useRuntimeConfig } from "#imports";
|
|
7
|
+
const CUSTOM_ROLE_OPTION = "__custom__";
|
|
5
8
|
const props = defineProps({
|
|
6
9
|
user: { type: null, required: false }
|
|
7
10
|
});
|
|
@@ -12,19 +15,55 @@ const emptyFormData = {
|
|
|
12
15
|
role: "user",
|
|
13
16
|
password: ""
|
|
14
17
|
};
|
|
15
|
-
const formData = ref(props.user ?? emptyFormData);
|
|
18
|
+
const formData = ref(props.user ?? { ...emptyFormData });
|
|
16
19
|
const { public: { nuxtUsers } } = useRuntimeConfig();
|
|
17
20
|
const moduleOptions = nuxtUsers;
|
|
18
21
|
const passwordValidation = usePasswordValidation(moduleOptions);
|
|
22
|
+
const { t } = useNuxtUsersLocale();
|
|
19
23
|
const nameError = ref("");
|
|
24
|
+
const roleError = ref("");
|
|
25
|
+
const roleSelectValue = ref("");
|
|
20
26
|
const isEditMode = computed(() => !!props.user);
|
|
21
27
|
const title = computed(() => isEditMode.value ? "Edit User" : "Create User");
|
|
22
28
|
const submitText = computed(() => isEditMode.value ? "Update" : "Create");
|
|
29
|
+
const availableRoles = computed(() => getAvailableRoles(moduleOptions.auth?.permissions));
|
|
30
|
+
const allowCustomRoles = computed(() => moduleOptions.auth?.allowCustomRoles === true);
|
|
31
|
+
const useRoleSelect = computed(() => availableRoles.value.length > 0);
|
|
32
|
+
const roleOptions = computed(() => {
|
|
33
|
+
const roles = [...availableRoles.value];
|
|
34
|
+
const currentRole = formData.value.role;
|
|
35
|
+
if (currentRole && !roles.includes(currentRole) && currentRole !== CUSTOM_ROLE_OPTION) {
|
|
36
|
+
roles.push(currentRole);
|
|
37
|
+
}
|
|
38
|
+
return roles;
|
|
39
|
+
});
|
|
40
|
+
const isCustomRoleSelected = computed(() => roleSelectValue.value === CUSTOM_ROLE_OPTION);
|
|
41
|
+
const syncRoleSelectFromForm = () => {
|
|
42
|
+
if (!useRoleSelect.value) {
|
|
43
|
+
roleSelectValue.value = "";
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const currentRole = formData.value.role || "";
|
|
47
|
+
if (roleOptions.value.includes(currentRole)) {
|
|
48
|
+
roleSelectValue.value = currentRole;
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (allowCustomRoles.value) {
|
|
52
|
+
roleSelectValue.value = CUSTOM_ROLE_OPTION;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
roleSelectValue.value = roleOptions.value[0] || "user";
|
|
56
|
+
formData.value.role = roleSelectValue.value;
|
|
57
|
+
};
|
|
23
58
|
watch(() => props.user, (newUser) => {
|
|
24
59
|
if (newUser) {
|
|
25
60
|
formData.value = { ...newUser };
|
|
26
61
|
}
|
|
27
|
-
|
|
62
|
+
if (!newUser) {
|
|
63
|
+
formData.value = { ...emptyFormData };
|
|
64
|
+
}
|
|
65
|
+
syncRoleSelectFromForm();
|
|
66
|
+
}, { immediate: true });
|
|
28
67
|
watch(() => formData.value.name, (name) => {
|
|
29
68
|
nameError.value = "";
|
|
30
69
|
if (!name || name.trim().length < 3) {
|
|
@@ -39,12 +78,55 @@ watch(() => formData.value.password, (newPassword) => {
|
|
|
39
78
|
passwordValidation.clearValidation();
|
|
40
79
|
}
|
|
41
80
|
});
|
|
81
|
+
watch(roleSelectValue, (value) => {
|
|
82
|
+
roleError.value = "";
|
|
83
|
+
if (!useRoleSelect.value) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (value === CUSTOM_ROLE_OPTION) {
|
|
87
|
+
if (!allowCustomRoles.value) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (roleOptions.value.includes(formData.value.role)) {
|
|
91
|
+
formData.value.role = "";
|
|
92
|
+
}
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
formData.value.role = value;
|
|
96
|
+
});
|
|
97
|
+
const validateRole = () => {
|
|
98
|
+
roleError.value = "";
|
|
99
|
+
const role = (formData.value.role || "").trim();
|
|
100
|
+
if (!role) {
|
|
101
|
+
roleError.value = "Role is required";
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
if (role.length > MAX_ROLE_LENGTH) {
|
|
105
|
+
roleError.value = `Role must be at most ${MAX_ROLE_LENGTH} characters`;
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
if (!useRoleSelect.value) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
if (allowCustomRoles.value && isCustomRoleSelected.value) {
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
if (roleOptions.value.includes(role)) {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
roleError.value = `Invalid role. Allowed roles: ${availableRoles.value.join(", ")}`;
|
|
118
|
+
return false;
|
|
119
|
+
};
|
|
42
120
|
const handleSubmit = async () => {
|
|
43
121
|
const userData = { ...formData.value };
|
|
44
122
|
if (!userData.name || userData.name.trim().length < 3) {
|
|
45
123
|
console.log("name is too short");
|
|
46
124
|
return;
|
|
47
125
|
}
|
|
126
|
+
if (!validateRole()) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
userData.role = (userData.role || "").trim();
|
|
48
130
|
if (isEditMode.value && !userData.password) {
|
|
49
131
|
delete userData.password;
|
|
50
132
|
}
|
|
@@ -67,6 +149,7 @@ const handleSubmit = async () => {
|
|
|
67
149
|
}
|
|
68
150
|
emit("submit", userData);
|
|
69
151
|
formData.value = { ...emptyFormData };
|
|
152
|
+
syncRoleSelectFromForm();
|
|
70
153
|
} catch (error) {
|
|
71
154
|
console.error("Failed to save user:", error);
|
|
72
155
|
emit("error", error);
|
|
@@ -82,6 +165,7 @@ const handleSubmit = async () => {
|
|
|
82
165
|
<div class="n-users-form-group">
|
|
83
166
|
<label for="name">Name:</label>
|
|
84
167
|
<input
|
|
168
|
+
id="name"
|
|
85
169
|
v-model="formData.name"
|
|
86
170
|
name="name"
|
|
87
171
|
type="text"
|
|
@@ -100,6 +184,7 @@ const handleSubmit = async () => {
|
|
|
100
184
|
<div class="n-users-form-group">
|
|
101
185
|
<label for="email">Email:</label>
|
|
102
186
|
<input
|
|
187
|
+
id="email"
|
|
103
188
|
v-model="formData.email"
|
|
104
189
|
type="email"
|
|
105
190
|
required
|
|
@@ -108,21 +193,73 @@ const handleSubmit = async () => {
|
|
|
108
193
|
|
|
109
194
|
<div class="n-users-form-group">
|
|
110
195
|
<label for="role">Role:</label>
|
|
196
|
+
|
|
197
|
+
<template v-if="useRoleSelect">
|
|
198
|
+
<select
|
|
199
|
+
id="role"
|
|
200
|
+
v-model="roleSelectValue"
|
|
201
|
+
name="role"
|
|
202
|
+
required
|
|
203
|
+
:class="{ error: roleError }"
|
|
204
|
+
>
|
|
205
|
+
<option
|
|
206
|
+
v-for="role in roleOptions"
|
|
207
|
+
:key="role"
|
|
208
|
+
:value="role"
|
|
209
|
+
>
|
|
210
|
+
{{ role }}
|
|
211
|
+
</option>
|
|
212
|
+
<option
|
|
213
|
+
v-if="allowCustomRoles"
|
|
214
|
+
:value="CUSTOM_ROLE_OPTION"
|
|
215
|
+
>
|
|
216
|
+
{{ t("userForm.customRoleOption") }}
|
|
217
|
+
</option>
|
|
218
|
+
</select>
|
|
219
|
+
|
|
220
|
+
<input
|
|
221
|
+
v-if="allowCustomRoles && isCustomRoleSelected"
|
|
222
|
+
id="custom-role"
|
|
223
|
+
v-model="formData.role"
|
|
224
|
+
type="text"
|
|
225
|
+
name="custom-role"
|
|
226
|
+
:maxlength="MAX_ROLE_LENGTH"
|
|
227
|
+
required
|
|
228
|
+
:placeholder="t('userForm.customRolePlaceholder')"
|
|
229
|
+
:class="{ error: roleError }"
|
|
230
|
+
>
|
|
231
|
+
</template>
|
|
232
|
+
|
|
111
233
|
<input
|
|
234
|
+
v-else
|
|
235
|
+
id="role"
|
|
112
236
|
v-model="formData.role"
|
|
113
237
|
type="text"
|
|
238
|
+
name="role"
|
|
239
|
+
:maxlength="MAX_ROLE_LENGTH"
|
|
240
|
+
required
|
|
241
|
+
:class="{ error: roleError }"
|
|
242
|
+
>
|
|
243
|
+
|
|
244
|
+
<small
|
|
245
|
+
v-if="roleError"
|
|
246
|
+
class="n-users-error-text"
|
|
114
247
|
>
|
|
248
|
+
{{ roleError }}
|
|
249
|
+
</small>
|
|
115
250
|
</div>
|
|
116
251
|
|
|
117
252
|
<div class="n-users-form-group">
|
|
118
253
|
<label for="password">
|
|
119
254
|
{{ isEditMode ? "Password (leave blank to keep current)" : "Password:" }}
|
|
120
255
|
</label>
|
|
121
|
-
<
|
|
256
|
+
<NUsersPasswordInput
|
|
257
|
+
id="password"
|
|
122
258
|
v-model="formData.password"
|
|
123
|
-
|
|
259
|
+
name="password"
|
|
124
260
|
:required="!isEditMode"
|
|
125
|
-
|
|
261
|
+
autocomplete="new-password"
|
|
262
|
+
/>
|
|
126
263
|
|
|
127
264
|
<NUsersPasswordStrengthIndicator
|
|
128
265
|
:password="formData.password"
|
|
@@ -17,7 +17,9 @@ export const en = {
|
|
|
17
17
|
edit: "Edit",
|
|
18
18
|
delete: "Delete",
|
|
19
19
|
loading: "Loading...",
|
|
20
|
-
error: "Error"
|
|
20
|
+
error: "Error",
|
|
21
|
+
showPassword: "Show password",
|
|
22
|
+
hidePassword: "Hide password"
|
|
21
23
|
},
|
|
22
24
|
login: {
|
|
23
25
|
title: "Welcome Back",
|
|
@@ -146,5 +148,9 @@ export const en = {
|
|
|
146
148
|
users: "users",
|
|
147
149
|
previous: "Previous",
|
|
148
150
|
next: "Next"
|
|
151
|
+
},
|
|
152
|
+
userForm: {
|
|
153
|
+
customRoleOption: "Custom\u2026",
|
|
154
|
+
customRolePlaceholder: "Enter custom role"
|
|
149
155
|
}
|
|
150
156
|
};
|
|
@@ -18,7 +18,9 @@ const huBase = {
|
|
|
18
18
|
edit: "Szerkeszt\xE9s",
|
|
19
19
|
delete: "T\xF6rl\xE9s",
|
|
20
20
|
loading: "Bet\xF6lt\xE9s...",
|
|
21
|
-
error: "Hiba"
|
|
21
|
+
error: "Hiba",
|
|
22
|
+
showPassword: "Jelsz\xF3 megjelen\xEDt\xE9se",
|
|
23
|
+
hidePassword: "Jelsz\xF3 elrejt\xE9se"
|
|
22
24
|
},
|
|
23
25
|
login: {
|
|
24
26
|
passwordLabel: "Jelsz\xF3",
|
|
@@ -128,6 +130,9 @@ const huBase = {
|
|
|
128
130
|
users: "felhaszn\xE1l\xF3",
|
|
129
131
|
previous: "El\u0151z\u0151",
|
|
130
132
|
next: "K\xF6vetkez\u0151"
|
|
133
|
+
},
|
|
134
|
+
userForm: {
|
|
135
|
+
customRoleOption: "Egyedi\u2026"
|
|
131
136
|
}
|
|
132
137
|
};
|
|
133
138
|
const huInformalOverrides = {
|
|
@@ -165,6 +170,9 @@ const huInformalOverrides = {
|
|
|
165
170
|
processingMessage: "K\xE9rj\xFCk, v\xE1rj, am\xEDg feldolgozzuk az e-mail meger\u0151s\xEDt\xE9s\xE9t.",
|
|
166
171
|
successMessage: "Az e-mail c\xEDmed meger\u0151s\xEDt\xE9sre ker\xFClt \xE9s a fi\xF3kod akt\xEDv.",
|
|
167
172
|
errorMessage: "A meger\u0151s\xEDt\u0151 link \xE9rv\xE9nytelen vagy lej\xE1rt. K\xE9rj\xFCk, pr\xF3b\xE1ld \xFAjra regisztr\xE1lni vagy l\xE9pj kapcsolatba az \xFCgyf\xE9lszolg\xE1lattal."
|
|
173
|
+
},
|
|
174
|
+
userForm: {
|
|
175
|
+
customRolePlaceholder: "Add meg az egyedi szerepk\xF6rt"
|
|
168
176
|
}
|
|
169
177
|
};
|
|
170
178
|
const huFormalOverrides = {
|
|
@@ -202,6 +210,9 @@ const huFormalOverrides = {
|
|
|
202
210
|
processingMessage: "K\xE9rj\xFCk, v\xE1rjon, am\xEDg feldolgozzuk az e-mail meger\u0151s\xEDt\xE9s\xE9t.",
|
|
203
211
|
successMessage: "Az e-mail c\xEDme meger\u0151s\xEDt\xE9sre ker\xFClt \xE9s a fi\xF3kja akt\xEDv.",
|
|
204
212
|
errorMessage: "A meger\u0151s\xEDt\u0151 link \xE9rv\xE9nytelen vagy lej\xE1rt. K\xE9rj\xFCk, pr\xF3b\xE1ljon meg \xFAjra regisztr\xE1lni vagy l\xE9pjen kapcsolatba az \xFCgyf\xE9lszolg\xE1lattal."
|
|
213
|
+
},
|
|
214
|
+
userForm: {
|
|
215
|
+
customRolePlaceholder: "Adja meg az egyedi szerepk\xF6rt"
|
|
205
216
|
}
|
|
206
217
|
};
|
|
207
218
|
export const hu = deepMerge(huBase, huInformalOverrides);
|
|
@@ -23,9 +23,11 @@ export default defineEventHandler(async (event) => {
|
|
|
23
23
|
const updatedUser = await updateUser(userId, body, options);
|
|
24
24
|
return { user: updatedUser };
|
|
25
25
|
} catch (error) {
|
|
26
|
+
const message = error?.message || "Unknown error";
|
|
27
|
+
const isClientError = typeof message === "string" && (message.startsWith("Invalid role") || message.startsWith("Role ") || message === "User not found.");
|
|
26
28
|
throw createError({
|
|
27
|
-
statusCode: 500,
|
|
28
|
-
statusMessage: `Error updating user: ${
|
|
29
|
+
statusCode: isClientError ? message === "User not found." ? 404 : 400 : 500,
|
|
30
|
+
statusMessage: `Error updating user: ${message}`
|
|
29
31
|
});
|
|
30
32
|
}
|
|
31
33
|
});
|
|
@@ -15,13 +15,16 @@ export default defineEventHandler(async (event) => {
|
|
|
15
15
|
const newUser = await createUser({
|
|
16
16
|
email: body.email,
|
|
17
17
|
name: body.name,
|
|
18
|
-
password: body.password
|
|
18
|
+
password: body.password,
|
|
19
|
+
role: body.role
|
|
19
20
|
}, options);
|
|
20
21
|
return { user: newUser };
|
|
21
22
|
} catch (error) {
|
|
23
|
+
const message = error?.message || "Unknown error";
|
|
24
|
+
const isClientError = typeof message === "string" && (message.startsWith("Invalid role") || message.startsWith("Role ") || message.startsWith("Password validation"));
|
|
22
25
|
throw createError({
|
|
23
|
-
statusCode: 500,
|
|
24
|
-
statusMessage: `Error creating user: ${
|
|
26
|
+
statusCode: isClientError ? 400 : 500,
|
|
27
|
+
statusMessage: `Error creating user: ${message}`
|
|
25
28
|
});
|
|
26
29
|
}
|
|
27
30
|
});
|
|
@@ -103,9 +103,10 @@ export const resetPassword = async (token, email, newPassword, options) => {
|
|
|
103
103
|
}
|
|
104
104
|
const now = /* @__PURE__ */ new Date();
|
|
105
105
|
const currentTimeString = now.toISOString().slice(0, 19).replace("T", " ");
|
|
106
|
-
const
|
|
106
|
+
const createdAtString = validTokenRecord.created_at instanceof Date ? validTokenRecord.created_at.toISOString().slice(0, 19).replace("T", " ") : String(validTokenRecord.created_at);
|
|
107
|
+
const [datePart, timePart] = createdAtString.split(/[ T]/);
|
|
107
108
|
if (!datePart || !timePart) {
|
|
108
|
-
console.log(`[Nuxt Users] Invalid timestamp format for token: ${
|
|
109
|
+
console.log(`[Nuxt Users] Invalid timestamp format for token: ${createdAtString}`);
|
|
109
110
|
return false;
|
|
110
111
|
}
|
|
111
112
|
const [year, month, day] = datePart.split("-").map(Number);
|
|
@@ -138,7 +139,7 @@ export const deleteExpiredPasswordResetTokens = async (options) => {
|
|
|
138
139
|
const passwordResetTokensTable = options.tables.passwordResetTokens;
|
|
139
140
|
const expirationDate = /* @__PURE__ */ new Date();
|
|
140
141
|
expirationDate.setHours(expirationDate.getHours() - TOKEN_EXPIRATION_HOURS);
|
|
141
|
-
const expirationDateString = expirationDate.toISOString();
|
|
142
|
+
const expirationDateString = expirationDate.toISOString().slice(0, 19).replace("T", " ");
|
|
142
143
|
await db.sql`
|
|
143
144
|
DELETE FROM {${passwordResetTokensTable}}
|
|
144
145
|
WHERE created_at < ${expirationDateString}
|
|
@@ -26,7 +26,7 @@ export const getConnector = async (name) => {
|
|
|
26
26
|
throw new Error(`Database connector "${name}" not found. Please install the required peer dependency:
|
|
27
27
|
- For sqlite: yarn add better-sqlite3
|
|
28
28
|
- For mysql: yarn add mysql2
|
|
29
|
-
- For postgresql: yarn add pg
|
|
29
|
+
- For postgresql: yarn add pg`, { cause: error });
|
|
30
30
|
}
|
|
31
31
|
throw error;
|
|
32
32
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useDb } from "./db.js";
|
|
2
2
|
import bcrypt from "bcrypt";
|
|
3
|
-
import { validatePassword, getPasswordValidationOptions } from "nuxt-users/utils";
|
|
3
|
+
import { validatePassword, getPasswordValidationOptions, assertValidRole } from "nuxt-users/utils";
|
|
4
4
|
export const createUser = async (userData, options) => {
|
|
5
5
|
const db = await useDb(options);
|
|
6
6
|
const usersTable = options.tables.users;
|
|
@@ -10,7 +10,10 @@ export const createUser = async (userData, options) => {
|
|
|
10
10
|
throw new Error(`Password validation failed: ${passwordValidation.errors.join(", ")}`);
|
|
11
11
|
}
|
|
12
12
|
const hashedPassword = await bcrypt.hash(userData.password, 10);
|
|
13
|
-
const role = userData.role || "user"
|
|
13
|
+
const role = assertValidRole(userData.role || "user", {
|
|
14
|
+
permissions: options.auth.permissions,
|
|
15
|
+
allowCustomRoles: options.auth.allowCustomRoles
|
|
16
|
+
});
|
|
14
17
|
await db.sql`
|
|
15
18
|
INSERT INTO {${usersTable}} (email, name, password, role, created_at, updated_at)
|
|
16
19
|
VALUES (${userData.email}, ${userData.name}, ${hashedPassword}, ${role}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
|
@@ -89,6 +92,12 @@ export const updateUser = async (id, userData, options) => {
|
|
|
89
92
|
}
|
|
90
93
|
return currentUser;
|
|
91
94
|
}
|
|
95
|
+
if (userData.role !== void 0) {
|
|
96
|
+
userData.role = assertValidRole(userData.role, {
|
|
97
|
+
permissions: options.auth.permissions,
|
|
98
|
+
allowCustomRoles: options.auth.allowCustomRoles
|
|
99
|
+
});
|
|
100
|
+
}
|
|
92
101
|
if (userData.active === false) {
|
|
93
102
|
await revokeUserTokens(id, options);
|
|
94
103
|
}
|
|
@@ -167,4 +167,4 @@ const getPasswordStrengthText = (strength) => {
|
|
|
167
167
|
}
|
|
168
168
|
};
|
|
169
169
|
|
|
170
|
-
export {
|
|
170
|
+
export { getPasswordStrengthColor as a, getPasswordStrengthText as b, getPasswordValidationOptions as g, validatePassword as v };
|
|
@@ -140,6 +140,13 @@ interface RuntimeModuleOptions {
|
|
|
140
140
|
* }
|
|
141
141
|
*/
|
|
142
142
|
permissions?: Record<string, (string | Permission)[]>;
|
|
143
|
+
/**
|
|
144
|
+
* Allow assigning roles that are not defined in auth.permissions
|
|
145
|
+
* When false (default), create/update user role must be a key of permissions
|
|
146
|
+
* (or any role if permissions is empty / not yet configured)
|
|
147
|
+
* @default false
|
|
148
|
+
*/
|
|
149
|
+
allowCustomRoles?: boolean;
|
|
143
150
|
/**
|
|
144
151
|
* Google OAuth configuration
|
|
145
152
|
* Enable Google OAuth login/registration
|
|
@@ -225,6 +232,11 @@ interface ModuleOptions {
|
|
|
225
232
|
tokenExpiration: number;
|
|
226
233
|
rememberMeExpiration: number;
|
|
227
234
|
permissions: Record<string, (string | Permission)[]>;
|
|
235
|
+
/**
|
|
236
|
+
* Allow assigning roles that are not defined in auth.permissions
|
|
237
|
+
* @default false
|
|
238
|
+
*/
|
|
239
|
+
allowCustomRoles: boolean;
|
|
228
240
|
google?: GoogleOAuthOptions;
|
|
229
241
|
};
|
|
230
242
|
passwordValidation: {
|
|
@@ -337,4 +349,4 @@ declare const defaultFieldLabels: {
|
|
|
337
349
|
};
|
|
338
350
|
|
|
339
351
|
export { defaultDisplayFields as j, defaultFieldLabels as k };
|
|
340
|
-
export type { DatabaseConfig as D, GoogleOAuthOptions as G, LocaleMessages as L, ModuleOptions as M,
|
|
352
|
+
export type { DatabaseConfig as D, GoogleOAuthOptions as G, LocaleMessages as L, ModuleOptions as M, Permission as P, RuntimeModuleOptions as R, User as U, DatabaseType as a, DisplayFieldsProps as b, LocaleOptions as c, LoginFormData as d, LoginFormProps as e, PasswordResetToken as f, PersonalAccessToken as g, ResetPasswordFormProps as h, UserWithoutPassword as i };
|
|
@@ -140,6 +140,13 @@ interface RuntimeModuleOptions {
|
|
|
140
140
|
* }
|
|
141
141
|
*/
|
|
142
142
|
permissions?: Record<string, (string | Permission)[]>;
|
|
143
|
+
/**
|
|
144
|
+
* Allow assigning roles that are not defined in auth.permissions
|
|
145
|
+
* When false (default), create/update user role must be a key of permissions
|
|
146
|
+
* (or any role if permissions is empty / not yet configured)
|
|
147
|
+
* @default false
|
|
148
|
+
*/
|
|
149
|
+
allowCustomRoles?: boolean;
|
|
143
150
|
/**
|
|
144
151
|
* Google OAuth configuration
|
|
145
152
|
* Enable Google OAuth login/registration
|
|
@@ -225,6 +232,11 @@ interface ModuleOptions {
|
|
|
225
232
|
tokenExpiration: number;
|
|
226
233
|
rememberMeExpiration: number;
|
|
227
234
|
permissions: Record<string, (string | Permission)[]>;
|
|
235
|
+
/**
|
|
236
|
+
* Allow assigning roles that are not defined in auth.permissions
|
|
237
|
+
* @default false
|
|
238
|
+
*/
|
|
239
|
+
allowCustomRoles: boolean;
|
|
228
240
|
google?: GoogleOAuthOptions;
|
|
229
241
|
};
|
|
230
242
|
passwordValidation: {
|
|
@@ -337,4 +349,4 @@ declare const defaultFieldLabels: {
|
|
|
337
349
|
};
|
|
338
350
|
|
|
339
351
|
export { defaultDisplayFields as j, defaultFieldLabels as k };
|
|
340
|
-
export type { DatabaseConfig as D, GoogleOAuthOptions as G, LocaleMessages as L, ModuleOptions as M,
|
|
352
|
+
export type { DatabaseConfig as D, GoogleOAuthOptions as G, LocaleMessages as L, ModuleOptions as M, Permission as P, RuntimeModuleOptions as R, User as U, DatabaseType as a, DisplayFieldsProps as b, LocaleOptions as c, LoginFormData as d, LoginFormProps as e, PasswordResetToken as f, PersonalAccessToken as g, ResetPasswordFormProps as h, UserWithoutPassword as i };
|
package/dist/utils.d.mts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { P as Permission } from './shared/nuxt-users.DtwPhils.mjs';
|
|
2
|
+
export { D as DatabaseConfig, a as DatabaseType, b as DisplayFieldsProps, G as GoogleOAuthOptions, L as LocaleMessages, c as LocaleOptions, d as LoginFormData, e as LoginFormProps, M as ModuleOptions, f as PasswordResetToken, g as PersonalAccessToken, h as ResetPasswordFormProps, R as RuntimeModuleOptions, U as User, i as UserWithoutPassword, j as defaultDisplayFields, k as defaultFieldLabels } from './shared/nuxt-users.DtwPhils.mjs';
|
|
1
3
|
export { defaultLocaleMessages, en, hu, huFormal } from '../dist/runtime/locales/index.js';
|
|
2
4
|
export { getNestedValue, getTranslation } from '../dist/runtime/utils/locale.js';
|
|
3
5
|
export { deepMerge } from '../dist/runtime/utils/deep-merge.js';
|
|
4
|
-
export { D as DatabaseConfig, a as DatabaseType, b as DisplayFieldsProps, G as GoogleOAuthOptions, L as LocaleMessages, c as LocaleOptions, d as LoginFormData, e as LoginFormProps, M as ModuleOptions, P as PasswordResetToken, f as Permission, g as PersonalAccessToken, R as ResetPasswordFormProps, h as RuntimeModuleOptions, U as User, i as UserWithoutPassword, j as defaultDisplayFields, k as defaultFieldLabels } from './shared/nuxt-users.bLYb3Bz5.mjs';
|
|
5
6
|
|
|
6
7
|
interface PasswordValidationResult {
|
|
7
8
|
isValid: boolean;
|
|
@@ -28,5 +29,27 @@ declare const validatePassword: (password: string, options?: PasswordValidationO
|
|
|
28
29
|
declare const getPasswordStrengthColor: (strength: "weak" | "medium" | "strong") => string;
|
|
29
30
|
declare const getPasswordStrengthText: (strength: "weak" | "medium" | "strong") => string;
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
declare const MAX_ROLE_LENGTH = 32;
|
|
33
|
+
type RoleValidationOptions = {
|
|
34
|
+
permissions?: Record<string, (string | Permission)[]>;
|
|
35
|
+
allowCustomRoles?: boolean;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Returns role names defined in auth.permissions (the predefined role catalog).
|
|
39
|
+
*/
|
|
40
|
+
declare const getAvailableRoles: (permissions?: Record<string, (string | Permission)[]>) => string[];
|
|
41
|
+
/**
|
|
42
|
+
* Validates a role against permissions and allowCustomRoles.
|
|
43
|
+
* - Empty permissions: any non-empty role is allowed (bootstrap before RBAC is configured)
|
|
44
|
+
* - allowCustomRoles true: any non-empty role is allowed
|
|
45
|
+
* - otherwise: role must be a key of permissions
|
|
46
|
+
*/
|
|
47
|
+
declare const isValidRole: (role: string | undefined | null, options?: RoleValidationOptions) => boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Asserts role is valid or throws with a clear error message.
|
|
50
|
+
* Returns the trimmed role string.
|
|
51
|
+
*/
|
|
52
|
+
declare const assertValidRole: (role: string | undefined | null, options?: RoleValidationOptions) => string;
|
|
53
|
+
|
|
54
|
+
export { MAX_ROLE_LENGTH, Permission, assertValidRole, getAvailableRoles, getPasswordStrengthColor, getPasswordStrengthText, getPasswordValidationOptions, isValidRole, validatePassword };
|
|
55
|
+
export type { PasswordValidationOptions, PasswordValidationResult, RoleValidationOptions };
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { P as Permission } from './shared/nuxt-users.DtwPhils.js';
|
|
2
|
+
export { D as DatabaseConfig, a as DatabaseType, b as DisplayFieldsProps, G as GoogleOAuthOptions, L as LocaleMessages, c as LocaleOptions, d as LoginFormData, e as LoginFormProps, M as ModuleOptions, f as PasswordResetToken, g as PersonalAccessToken, h as ResetPasswordFormProps, R as RuntimeModuleOptions, U as User, i as UserWithoutPassword, j as defaultDisplayFields, k as defaultFieldLabels } from './shared/nuxt-users.DtwPhils.js';
|
|
1
3
|
export { defaultLocaleMessages, en, hu, huFormal } from '../dist/runtime/locales/index.js';
|
|
2
4
|
export { getNestedValue, getTranslation } from '../dist/runtime/utils/locale.js';
|
|
3
5
|
export { deepMerge } from '../dist/runtime/utils/deep-merge.js';
|
|
4
|
-
export { D as DatabaseConfig, a as DatabaseType, b as DisplayFieldsProps, G as GoogleOAuthOptions, L as LocaleMessages, c as LocaleOptions, d as LoginFormData, e as LoginFormProps, M as ModuleOptions, P as PasswordResetToken, f as Permission, g as PersonalAccessToken, R as ResetPasswordFormProps, h as RuntimeModuleOptions, U as User, i as UserWithoutPassword, j as defaultDisplayFields, k as defaultFieldLabels } from './shared/nuxt-users.bLYb3Bz5.js';
|
|
5
6
|
|
|
6
7
|
interface PasswordValidationResult {
|
|
7
8
|
isValid: boolean;
|
|
@@ -28,5 +29,27 @@ declare const validatePassword: (password: string, options?: PasswordValidationO
|
|
|
28
29
|
declare const getPasswordStrengthColor: (strength: "weak" | "medium" | "strong") => string;
|
|
29
30
|
declare const getPasswordStrengthText: (strength: "weak" | "medium" | "strong") => string;
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
declare const MAX_ROLE_LENGTH = 32;
|
|
33
|
+
type RoleValidationOptions = {
|
|
34
|
+
permissions?: Record<string, (string | Permission)[]>;
|
|
35
|
+
allowCustomRoles?: boolean;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Returns role names defined in auth.permissions (the predefined role catalog).
|
|
39
|
+
*/
|
|
40
|
+
declare const getAvailableRoles: (permissions?: Record<string, (string | Permission)[]>) => string[];
|
|
41
|
+
/**
|
|
42
|
+
* Validates a role against permissions and allowCustomRoles.
|
|
43
|
+
* - Empty permissions: any non-empty role is allowed (bootstrap before RBAC is configured)
|
|
44
|
+
* - allowCustomRoles true: any non-empty role is allowed
|
|
45
|
+
* - otherwise: role must be a key of permissions
|
|
46
|
+
*/
|
|
47
|
+
declare const isValidRole: (role: string | undefined | null, options?: RoleValidationOptions) => boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Asserts role is valid or throws with a clear error message.
|
|
50
|
+
* Returns the trimmed role string.
|
|
51
|
+
*/
|
|
52
|
+
declare const assertValidRole: (role: string | undefined | null, options?: RoleValidationOptions) => string;
|
|
53
|
+
|
|
54
|
+
export { MAX_ROLE_LENGTH, Permission, assertValidRole, getAvailableRoles, getPasswordStrengthColor, getPasswordStrengthText, getPasswordValidationOptions, isValidRole, validatePassword };
|
|
55
|
+
export type { PasswordValidationOptions, PasswordValidationResult, RoleValidationOptions };
|
package/dist/utils.mjs
CHANGED
|
@@ -1,8 +1,55 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { a as getPasswordStrengthColor, b as getPasswordStrengthText, g as getPasswordValidationOptions, v as validatePassword } from './shared/nuxt-users.Cvjgn5KJ.mjs';
|
|
2
2
|
export { defaultLocaleMessages, en, hu, huFormal } from '../dist/runtime/locales/index.js';
|
|
3
3
|
export { getNestedValue, getTranslation } from '../dist/runtime/utils/locale.js';
|
|
4
4
|
export { deepMerge } from '../dist/runtime/utils/deep-merge.js';
|
|
5
5
|
|
|
6
|
+
const MAX_ROLE_LENGTH = 32;
|
|
7
|
+
const getAvailableRoles = (permissions) => {
|
|
8
|
+
if (!permissions) {
|
|
9
|
+
return [];
|
|
10
|
+
}
|
|
11
|
+
return Object.keys(permissions);
|
|
12
|
+
};
|
|
13
|
+
const isValidRole = (role, options = {}) => {
|
|
14
|
+
if (role === void 0 || role === null) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
const trimmed = typeof role === "string" ? role.trim() : "";
|
|
18
|
+
if (!trimmed) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
if (trimmed.length > MAX_ROLE_LENGTH) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
if (options.allowCustomRoles) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
const availableRoles = getAvailableRoles(options.permissions);
|
|
28
|
+
if (availableRoles.length === 0) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
return availableRoles.includes(trimmed);
|
|
32
|
+
};
|
|
33
|
+
const assertValidRole = (role, options = {}) => {
|
|
34
|
+
if (role === void 0 || role === null || typeof role !== "string") {
|
|
35
|
+
throw new Error("Role is required");
|
|
36
|
+
}
|
|
37
|
+
const trimmed = role.trim();
|
|
38
|
+
if (!trimmed) {
|
|
39
|
+
throw new Error("Role is required");
|
|
40
|
+
}
|
|
41
|
+
if (trimmed.length > MAX_ROLE_LENGTH) {
|
|
42
|
+
throw new Error(`Role must be at most ${MAX_ROLE_LENGTH} characters`);
|
|
43
|
+
}
|
|
44
|
+
if (isValidRole(trimmed, options)) {
|
|
45
|
+
return trimmed;
|
|
46
|
+
}
|
|
47
|
+
const availableRoles = getAvailableRoles(options.permissions);
|
|
48
|
+
throw new Error(
|
|
49
|
+
`Invalid role "${trimmed}". Allowed roles: ${availableRoles.join(", ")}`
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
6
53
|
const defaultDisplayFields = ["id", "name", "email", "role", "active", "created_at", "updated_at", "last_login_at"];
|
|
7
54
|
const defaultFieldLabels = {
|
|
8
55
|
id: "ID",
|
|
@@ -15,4 +62,4 @@ const defaultFieldLabels = {
|
|
|
15
62
|
last_login_at: "Last Login"
|
|
16
63
|
};
|
|
17
64
|
|
|
18
|
-
export { defaultDisplayFields, defaultFieldLabels };
|
|
65
|
+
export { MAX_ROLE_LENGTH, assertValidRole, defaultDisplayFields, defaultFieldLabels, getAvailableRoles, isValidRole };
|
package/package.json
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-users",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "A comprehensive user management module for Nuxt
|
|
3
|
+
"version": "1.49.0",
|
|
4
|
+
"description": "A comprehensive user management module for Nuxt applications with authentication, authorization, database support, and CLI tools",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nuxt",
|
|
7
|
-
"nuxt3",
|
|
8
7
|
"nuxt-module",
|
|
9
8
|
"users",
|
|
10
9
|
"authentication",
|
|
@@ -83,7 +82,7 @@
|
|
|
83
82
|
"test:postgresql": "./scripts/test-postgresql.sh",
|
|
84
83
|
"test:nuxt3": "./scripts/test-nuxt3.sh unit && ./scripts/test-nuxt3.sh integration && ./scripts/test-nuxt3.sh sqlite && ./scripts/test-nuxt3.sh mysql && ./scripts/test-nuxt3.sh postgresql",
|
|
85
84
|
"test:nuxt4": "yarn test:types && yarn test:unit && yarn test:integration && yarn test:sqlite && yarn test:mysql && yarn test:postgresql",
|
|
86
|
-
"test": "yarn lint && yarn test:
|
|
85
|
+
"test": "yarn lint && yarn test:nuxt4",
|
|
87
86
|
"test:watch": "vitest watch",
|
|
88
87
|
"db:create-users-table": "tsx src/cli/create-users-table.ts",
|
|
89
88
|
"db:create-user": "tsx src/cli/create-user.ts",
|
|
@@ -100,7 +99,7 @@
|
|
|
100
99
|
"dependencies": {
|
|
101
100
|
"bcrypt": "^6.0.0",
|
|
102
101
|
"citty": "^0.2.0",
|
|
103
|
-
"db0": "^0.
|
|
102
|
+
"db0": "^0.4.0",
|
|
104
103
|
"defu": "^6.1.4",
|
|
105
104
|
"google-auth-library": "^10.4.0",
|
|
106
105
|
"nodemailer": "^7.0.5"
|