create-nuxt-base 1.2.0 → 2.1.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.
Files changed (46) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/nuxt-base-template/.github/workflows/test.yml +90 -0
  3. package/nuxt-base-template/app/components/Modal/ModalBackupCodes.vue +2 -1
  4. package/nuxt-base-template/app/components/Upload/TusFileUpload.vue +7 -7
  5. package/nuxt-base-template/app/interfaces/user.interface.ts +5 -12
  6. package/nuxt-base-template/app/layouts/default.vue +1 -1
  7. package/nuxt-base-template/app/middleware/admin.global.ts +2 -2
  8. package/nuxt-base-template/app/middleware/auth.global.ts +2 -2
  9. package/nuxt-base-template/app/middleware/guest.global.ts +2 -2
  10. package/nuxt-base-template/app/pages/app/index.vue +1 -1
  11. package/nuxt-base-template/app/pages/app/settings/security.vue +54 -43
  12. package/nuxt-base-template/app/pages/auth/2fa.vue +2 -3
  13. package/nuxt-base-template/app/pages/auth/forgot-password.vue +2 -1
  14. package/nuxt-base-template/app/pages/auth/login.vue +6 -4
  15. package/nuxt-base-template/app/pages/auth/register.vue +85 -61
  16. package/nuxt-base-template/app/pages/auth/reset-password.vue +2 -1
  17. package/nuxt-base-template/docs/pages/docs.vue +1 -1
  18. package/nuxt-base-template/nuxt.config.ts +50 -1
  19. package/nuxt-base-template/package-lock.json +1311 -2920
  20. package/nuxt-base-template/package.json +27 -2
  21. package/nuxt-base-template/playwright.config.ts +1 -1
  22. package/nuxt-base-template/tests/e2e/auth.spec.ts +467 -0
  23. package/nuxt-base-template/tests/unit/auth/auth.spec.ts +439 -0
  24. package/nuxt-base-template/tests/unit/auth/error-translation.spec.ts +279 -0
  25. package/nuxt-base-template/tests/unit/mocks/auth-client.mock.ts +165 -0
  26. package/nuxt-base-template/tests/unit/mocks/nuxt-imports.ts +105 -0
  27. package/nuxt-base-template/tests/unit/setup.ts +56 -0
  28. package/nuxt-base-template/vitest.config.ts +25 -0
  29. package/package.json +1 -1
  30. package/nuxt-base-template/app/components/Transition/TransitionFade.vue +0 -27
  31. package/nuxt-base-template/app/components/Transition/TransitionFadeScale.vue +0 -27
  32. package/nuxt-base-template/app/components/Transition/TransitionSlide.vue +0 -12
  33. package/nuxt-base-template/app/components/Transition/TransitionSlideBottom.vue +0 -12
  34. package/nuxt-base-template/app/components/Transition/TransitionSlideRevert.vue +0 -12
  35. package/nuxt-base-template/app/composables/use-better-auth.ts +0 -597
  36. package/nuxt-base-template/app/composables/use-file.ts +0 -71
  37. package/nuxt-base-template/app/composables/use-share.ts +0 -38
  38. package/nuxt-base-template/app/composables/use-tus-upload.ts +0 -278
  39. package/nuxt-base-template/app/composables/use-tw.ts +0 -1
  40. package/nuxt-base-template/app/interfaces/upload.interface.ts +0 -58
  41. package/nuxt-base-template/app/lib/auth-client.ts +0 -229
  42. package/nuxt-base-template/app/lib/auth-state.ts +0 -206
  43. package/nuxt-base-template/app/plugins/auth-interceptor.client.ts +0 -151
  44. package/nuxt-base-template/app/utils/crypto.ts +0 -44
  45. package/nuxt-base-template/tests/iam.spec.ts +0 -247
  46. /package/nuxt-base-template/tests/{init.spec.ts → e2e/init.spec.ts} +0 -0
@@ -2,16 +2,13 @@
2
2
  // ============================================================================
3
3
  // Imports
4
4
  // ============================================================================
5
- import type { AuthFormField, FormSubmitEvent } from '@nuxt/ui';
6
- import type { InferOutput } from 'valibot';
7
-
8
5
  import * as v from 'valibot';
9
6
 
10
7
  // ============================================================================
11
8
  // Composables
12
9
  // ============================================================================
13
10
  const toast = useToast();
14
- const { signUp, signIn, registerPasskey } = useBetterAuth();
11
+ const { signUp, signIn, registerPasskey } = useLtAuth();
15
12
 
16
13
  // ============================================================================
17
14
  // Page Meta
@@ -27,36 +24,13 @@ const loading = ref<boolean>(false);
27
24
  const showPasskeyPrompt = ref<boolean>(false);
28
25
  const passkeyLoading = ref<boolean>(false);
29
26
 
30
- const fields: AuthFormField[] = [
31
- {
32
- label: 'Name',
33
- name: 'name',
34
- placeholder: 'Name eingeben',
35
- required: true,
36
- type: 'text',
37
- },
38
- {
39
- label: 'E-Mail',
40
- name: 'email',
41
- placeholder: 'E-Mail eingeben',
42
- required: true,
43
- type: 'email',
44
- },
45
- {
46
- label: 'Passwort',
47
- name: 'password',
48
- placeholder: 'Passwort eingeben',
49
- required: true,
50
- type: 'password',
51
- },
52
- {
53
- label: 'Passwort bestätigen',
54
- name: 'confirmPassword',
55
- placeholder: 'Passwort wiederholen',
56
- required: true,
57
- type: 'password',
58
- },
59
- ];
27
+ // Form state - using refs for direct v-model binding
28
+ const formState = reactive({
29
+ name: '',
30
+ email: '',
31
+ password: '',
32
+ confirmPassword: '',
33
+ });
60
34
 
61
35
  const schema = v.pipe(
62
36
  v.object({
@@ -71,20 +45,30 @@ const schema = v.pipe(
71
45
  ),
72
46
  );
73
47
 
74
- type Schema = InferOutput<typeof schema>;
75
-
76
48
  // ============================================================================
77
49
  // Functions
78
50
  // ============================================================================
79
- async function onSubmit(payload: FormSubmitEvent<Schema>): Promise<void> {
51
+ async function onSubmit(): Promise<void> {
52
+ // Validate
53
+ const result = v.safeParse(schema, formState);
54
+ if (!result.success) {
55
+ const firstError = result.issues[0];
56
+ toast.add({
57
+ color: 'error',
58
+ description: firstError.message,
59
+ title: 'Validierungsfehler',
60
+ });
61
+ return;
62
+ }
63
+
80
64
  loading.value = true;
81
65
 
82
66
  try {
83
67
  // Step 1: Sign up
84
68
  const signUpResult = await signUp.email({
85
- email: payload.data.email,
86
- name: payload.data.name,
87
- password: payload.data.password,
69
+ email: formState.email,
70
+ name: formState.name,
71
+ password: formState.password,
88
72
  });
89
73
 
90
74
  const signUpError = 'error' in signUpResult ? signUpResult.error : null;
@@ -100,8 +84,8 @@ async function onSubmit(payload: FormSubmitEvent<Schema>): Promise<void> {
100
84
 
101
85
  // Step 2: Sign in to create session (required for passkey registration)
102
86
  const signInResult = await signIn.email({
103
- email: payload.data.email,
104
- password: payload.data.password,
87
+ email: formState.email,
88
+ password: formState.password,
105
89
  });
106
90
 
107
91
  const signInError = 'error' in signInResult ? signInResult.error : null;
@@ -168,25 +152,65 @@ async function skipPasskey(): Promise<void> {
168
152
  <template>
169
153
  <UPageCard class="w-md" variant="naked">
170
154
  <template v-if="!showPasskeyPrompt">
171
- <UAuthForm
172
- :schema="schema"
173
- title="Registrieren"
174
- icon="i-lucide-user-plus"
175
- :fields="fields"
176
- :loading="loading"
177
- :submit="{
178
- label: 'Konto erstellen',
179
- block: true,
180
- }"
181
- @submit="onSubmit"
182
- >
183
- <template #footer>
184
- <p class="text-center text-sm text-muted">
185
- Bereits ein Konto?
186
- <ULink to="/auth/login" class="text-primary font-medium">Anmelden</ULink>
187
- </p>
188
- </template>
189
- </UAuthForm>
155
+ <div class="flex flex-col items-center gap-4">
156
+ <UIcon name="i-lucide-user-plus" class="size-12 text-primary" />
157
+ <h1 class="text-2xl font-semibold">Registrieren</h1>
158
+ </div>
159
+
160
+ <form class="mt-6 flex flex-col gap-4" @submit.prevent="onSubmit">
161
+ <UFormField label="Name" name="name" required class="w-full">
162
+ <UInput
163
+ v-model="formState.name"
164
+ name="name"
165
+ type="text"
166
+ placeholder="Name eingeben"
167
+ autocomplete="name"
168
+ class="w-full"
169
+ />
170
+ </UFormField>
171
+
172
+ <UFormField label="E-Mail" name="email" required class="w-full">
173
+ <UInput
174
+ v-model="formState.email"
175
+ name="email"
176
+ type="email"
177
+ placeholder="E-Mail eingeben"
178
+ autocomplete="email"
179
+ class="w-full"
180
+ />
181
+ </UFormField>
182
+
183
+ <UFormField label="Passwort" name="password" required class="w-full">
184
+ <UInput
185
+ v-model="formState.password"
186
+ name="password"
187
+ type="password"
188
+ placeholder="Passwort eingeben"
189
+ autocomplete="new-password"
190
+ class="w-full"
191
+ />
192
+ </UFormField>
193
+
194
+ <UFormField label="Passwort bestätigen" name="confirmPassword" required class="w-full">
195
+ <UInput
196
+ v-model="formState.confirmPassword"
197
+ name="confirmPassword"
198
+ type="password"
199
+ placeholder="Passwort wiederholen"
200
+ autocomplete="new-password"
201
+ class="w-full"
202
+ />
203
+ </UFormField>
204
+
205
+ <UButton type="submit" block :loading="loading">
206
+ Konto erstellen
207
+ </UButton>
208
+ </form>
209
+
210
+ <p class="mt-4 text-center text-sm text-muted">
211
+ Bereits ein Konto?
212
+ <ULink to="/auth/login" class="text-primary font-medium">Anmelden</ULink>
213
+ </p>
190
214
  </template>
191
215
 
192
216
  <template v-else>
@@ -7,7 +7,8 @@ import type { InferOutput } from 'valibot';
7
7
 
8
8
  import * as v from 'valibot';
9
9
 
10
- import { authClient } from '~/lib/auth-client';
10
+ // Auth client from @lenne.tech/nuxt-extensions (auto-imported as ltAuthClient)
11
+ const authClient = ltAuthClient;
11
12
 
12
13
  // ============================================================================
13
14
  // Composables
@@ -182,7 +182,7 @@ async function handleFormSubmit(): Promise<void> {
182
182
  }
183
183
 
184
184
  async function handleShare(): Promise<void> {
185
- const { share } = useShare();
185
+ const { share } = useLtShare();
186
186
  await share('Nuxt Base Starter', 'Check out this Nuxt Base Starter Template!', window.location.href);
187
187
 
188
188
  toast.add({
@@ -67,12 +67,40 @@ export default defineNuxtConfig({
67
67
  dirs: ['./states', './stores', './forms', './interfaces', './base', './plugins'],
68
68
  },
69
69
 
70
+ // ============================================================================
71
+ // lenne.tech Nuxt Extensions
72
+ // ============================================================================
73
+ ltExtensions: {
74
+ auth: {
75
+ enabled: true,
76
+ // baseURL is used in production mode for cross-origin API requests
77
+ // In dev mode, Nuxt proxy is used (baseURL is ignored, requests go through /api/iam)
78
+ // In production, requests go directly to baseURL + basePath (e.g., https://api.example.com/iam)
79
+ baseURL: process.env.API_URL || 'http://localhost:3000',
80
+ basePath: '/iam',
81
+ loginPath: '/auth/login',
82
+ twoFactorRedirectPath: '/auth/2fa',
83
+ enableAdmin: true,
84
+ enableTwoFactor: true,
85
+ enablePasskey: true,
86
+ interceptor: {
87
+ enabled: true,
88
+ publicPaths: ['/auth/login', '/auth/register', '/auth/forgot-password', '/auth/reset-password'],
89
+ },
90
+ },
91
+ tus: {
92
+ defaultEndpoint: '/files/upload',
93
+ defaultChunkSize: 5 * 1024 * 1024,
94
+ },
95
+ },
96
+
70
97
  // ============================================================================
71
98
  // Nuxt Modules
72
99
  // ============================================================================
73
100
  modules: [
101
+ '@lenne.tech/nuxt-extensions', // Auth, Upload, Transitions
74
102
  '@nuxt/test-utils/module', // E2E testing with Playwright
75
- '@lenne.tech/bug.lt', // Bug reporting to Linear
103
+ // '@lenne.tech/bug.lt', // Bug reporting to Linear - TEMPORARILY DISABLED FOR TESTING
76
104
  '@vueuse/nuxt', // Vue composition utilities
77
105
  'dayjs-nuxt', // Date/time handling
78
106
  '@nuxt/image', // Image optimization
@@ -158,5 +186,26 @@ export default defineNuxtConfig({
158
186
  exclude: ['@tailwindcss/vite', 'lightningcss', '@vue/devtools-core', '@vue/devtools-kit', '@internationalized/date'],
159
187
  },
160
188
  plugins: [tailwindcss()],
189
+ server: {
190
+ proxy: {
191
+ // IAM proxy via /api prefix (nuxt-extensions adds /api in dev mode)
192
+ // Must be before /api to match more specifically
193
+ '/api/iam': {
194
+ target: 'http://localhost:3000',
195
+ changeOrigin: true,
196
+ rewrite: (path) => path.replace(/^\/api/, ''),
197
+ },
198
+ // API proxy - no rewrite, backend expects /api/... paths
199
+ '/api': {
200
+ target: 'http://localhost:3000',
201
+ changeOrigin: true,
202
+ },
203
+ // IAM proxy for direct BetterAuth endpoints (SSR mode)
204
+ '/iam': {
205
+ target: 'http://localhost:3000',
206
+ changeOrigin: true,
207
+ },
208
+ },
209
+ },
161
210
  },
162
211
  });