@plutocms/supabase 0.0.1-alpha.1 → 0.0.1-alpha.10

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.
@@ -24,7 +24,7 @@ const maybeDevUrl = computed(() => {
24
24
  const items = ref<DropdownMenuItem[][]>([
25
25
  [
26
26
  {
27
- label: `${user.value?.first_name} ${user.value?.last_name}` || '',
27
+ label: `${user.value?.display_name} (${user.value?.username})` || 'User',
28
28
  avatar: {
29
29
  icon: 'lucide:user',
30
30
  class: 'bg-green-600',
@@ -53,7 +53,7 @@ const items = ref<DropdownMenuItem[][]>([
53
53
 
54
54
  [
55
55
  {
56
- label: 'GitHub',
56
+ label: 'Pluto on GitHub',
57
57
  icon: 'i-simple-icons-github',
58
58
  type: 'link',
59
59
  to: 'https://github.com/ojvribeiro/pluto',
@@ -135,7 +135,7 @@ defineShortcuts(extractShortcuts(items.value))
135
135
  <UDropdownMenu
136
136
  :items="items"
137
137
  :ui="{
138
- content: 'w-48',
138
+ content: 'w-64',
139
139
  }"
140
140
  :modal="false"
141
141
  >
@@ -148,7 +148,7 @@ defineShortcuts(extractShortcuts(items.value))
148
148
  root: 'rounded-xl',
149
149
  icon: 'text-white text-lg',
150
150
  }"
151
- :alt="user?.first_name || ''"
151
+ :alt="user?.value?.display_name || 'User Avatar'"
152
152
  size="sm"
153
153
  icon="lucide:user"
154
154
  class="bg-green-600"
@@ -157,7 +157,7 @@ defineShortcuts(extractShortcuts(items.value))
157
157
  <span
158
158
  class="light:text-zinc-800 text-sm font-bold dark:text-white"
159
159
  >
160
- {{ user?.first_name }}
160
+ {{ user?.display_name.split(' ')[0] || 'User' }}
161
161
  </span>
162
162
  </div>
163
163
  </button>
@@ -23,7 +23,7 @@ export async function useAuth(authOptions?: PlutoSupabaseAuthOptions) {
23
23
  isSubmitting.value = true
24
24
 
25
25
  try {
26
- const { error } = await supabase.auth.signInWithPassword({
26
+ const { data, error } = await supabase.auth.signInWithPassword({
27
27
  email: form.email,
28
28
  password: form.password,
29
29
  })
@@ -36,6 +36,22 @@ export async function useAuth(authOptions?: PlutoSupabaseAuthOptions) {
36
36
  return
37
37
  }
38
38
 
39
+ if (data.user && data.user.user_metadata.is_admin) {
40
+ // Set the first_setup flag in the healthcheck table to false
41
+ const { error: healthcheckError } = await supabase
42
+ .from('healthcheck')
43
+ .update({ config_value: 'false' })
44
+ .eq('config_name', 'first_setup')
45
+
46
+ if (healthcheckError) {
47
+ return {
48
+ success: false,
49
+ message: healthcheckError.message,
50
+ error: healthcheckError,
51
+ }
52
+ }
53
+ }
54
+
39
55
  return navigateTo(
40
56
  (authOptions?.route?.query.redirect as string) ?? '/admin/home'
41
57
  )
@@ -1,5 +1,9 @@
1
+ import type { RouteLocationNormalizedGeneric } from 'vue-router'
2
+
1
3
  export default defineNuxtRouteMiddleware(async (to, from) => {
2
- const { isLoggedIn, logout } = await useAuth({ route: to })
4
+ const { isLoggedIn, logout } = await useAuth({
5
+ route: to as RouteLocationNormalizedGeneric,
6
+ })
3
7
 
4
8
  if (
5
9
  isLoggedIn.value &&
@@ -1,24 +1,36 @@
1
- export default defineNuxtRouteMiddleware(async (to, from) => {
2
- if (to.path !== '/admin/setup' && to.path.startsWith('/admin')) {
3
- try {
4
- const data = await $fetch('/api/settings/first_setup')
5
-
6
- if (
7
- data.is_first_setup === true &&
8
- from.path.startsWith('/admin') &&
9
- to.path !== '/admin/setup'
10
- ) {
1
+ export default defineNuxtRouteMiddleware(async (to, _from) => {
2
+ if (!to.path.startsWith('/admin')) {
3
+ return
4
+ }
5
+
6
+ try {
7
+ const data = await $fetch('/api/settings/first_setup')
8
+
9
+ if (!data.success) {
10
+ // Cannot determine setup state — send to setup page
11
+ if (to.path !== '/admin/setup') {
12
+ return navigateTo('/admin/setup')
13
+ }
14
+ return
15
+ }
16
+
17
+ if (data.is_first_setup) {
18
+ // Setup not done yet — only allow the setup page
19
+ if (to.path !== '/admin/setup') {
11
20
  return navigateTo('/admin/setup')
12
21
  }
13
-
14
- if (
15
- data.is_first_setup === false &&
16
- from.path.startsWith('/admin') &&
17
- to.path === '/admin/setup'
18
- ) {
19
- return navigateTo('/')
22
+ } else {
23
+ // Setup already done — block access to setup page
24
+ if (to.path === '/admin/setup') {
25
+ return navigateTo('/admin/login')
20
26
  }
21
- } catch(error) {
27
+ }
28
+ } catch (error) {
29
+ if (import.meta.dev) {
30
+ console.error('Error checking first setup status:', error)
31
+ }
32
+
33
+ if (to.path !== '/admin/setup') {
22
34
  return navigateTo('/admin/setup')
23
35
  }
24
36
  }
@@ -9,8 +9,8 @@ useHead({
9
9
 
10
10
  const form = ref({
11
11
  email: '',
12
- first_name: '',
13
- last_name: '',
12
+ username: '',
13
+ display_name: '',
14
14
  password: '',
15
15
  confirm_password: '',
16
16
  })
@@ -32,8 +32,8 @@ async function submitForm() {
32
32
  method: 'POST',
33
33
  body: {
34
34
  email: form.value.email,
35
- first_name: form.value.first_name,
36
- last_name: form.value.last_name,
35
+ username: form.value.username,
36
+ display_name: form.value.display_name,
37
37
  password: form.value.password,
38
38
  },
39
39
  })
@@ -60,23 +60,27 @@ async function submitForm() {
60
60
  type="email"
61
61
  placeholder="victor@example.com"
62
62
  class="w-full"
63
+ required
63
64
  />
64
65
  </UFormField>
65
66
 
66
67
  <div class="flex gap-x-4">
67
- <UFormField label="First Name">
68
+ <UFormField label="Username">
68
69
  <UInput
69
- v-model="form.first_name"
70
- placeholder="e.g: John"
70
+ v-model="form.username"
71
+ placeholder="e.g: johndoe"
71
72
  class="w-full"
73
+ minlength="3"
74
+ required
72
75
  />
73
76
  </UFormField>
74
77
 
75
- <UFormField label="Last Name">
78
+ <UFormField label="Display Name">
76
79
  <UInput
77
- v-model="form.last_name"
78
- placeholder="e.g: Doe"
80
+ v-model="form.display_name"
81
+ placeholder="e.g: John Doe"
79
82
  class="w-full"
83
+ required
80
84
  />
81
85
  </UFormField>
82
86
  </div>
@@ -87,6 +91,7 @@ async function submitForm() {
87
91
  placeholder="••••"
88
92
  class="w-full"
89
93
  type="password"
94
+ required
90
95
  />
91
96
  </UFormField>
92
97
 
@@ -96,6 +101,7 @@ async function submitForm() {
96
101
  placeholder="••••"
97
102
  class="w-full"
98
103
  type="password"
104
+ required
99
105
  />
100
106
  </UFormField>
101
107
 
@@ -23,11 +23,6 @@ const items = ref<StepperItem[]>([
23
23
  icon: 'i-lucide-database',
24
24
  },
25
25
 
26
- /* {
27
- title: 'Admin Account',
28
- icon: 'i-lucide-user',
29
- }, */
30
-
31
26
  {
32
27
  title: 'Complete Setup',
33
28
  icon: 'i-lucide-check',
@@ -61,7 +56,15 @@ const currentStepId = computed(() => {
61
56
  return undefined
62
57
  })
63
58
 
64
- async function completeSetup() {
59
+ const currentStepSubmitLabel = computed(() => {
60
+ if (currentStep.value === 0) {
61
+ return 'Set up database'
62
+ }
63
+
64
+ return 'Submit'
65
+ })
66
+
67
+ async function completeDatabaseSetup() {
65
68
  try {
66
69
  isSettingUpDatabase.value = true
67
70
 
@@ -112,8 +115,8 @@ async function completeSetup() {
112
115
  }
113
116
 
114
117
  function handleStepChange(step: number) {
115
- if (step === 1) {
116
- completeSetup()
118
+ if (step === 0) {
119
+ completeDatabaseSetup()
117
120
  }
118
121
  }
119
122
  </script>
@@ -123,20 +126,14 @@ function handleStepChange(step: number) {
123
126
  <div class="flex justify-center items-center h-screen">
124
127
  <UCard class="w-[500px]">
125
128
  <div class="flex flex-col gap-y-8">
126
- <UStepper
127
- ref="stepper"
128
- v-model="currentStep"
129
- :items="items"
130
- disabled
131
- linear
132
- />
129
+ <UStepper ref="stepper" v-model="currentStep" :items="items" linear />
133
130
 
134
131
  <div>
135
132
  <section v-if="currentStep === 0">
136
133
  <UForm
137
134
  id="database-setup-form"
138
135
  class="flex flex-col gap-y-4"
139
- @submit="handleStepChange(currentStep + 1)"
136
+ @submit="handleStepChange(currentStep)"
140
137
  >
141
138
  <UFormField label="Supabase connection string" required>
142
139
  <UInput
@@ -259,21 +256,28 @@ function handleStepChange(step: number) {
259
256
  </UForm>
260
257
  </section>
261
258
 
262
- <section v-else-if="currentStep === 1">
259
+ <section v-if="currentStep === 1">
263
260
  <div class="flex flex-col gap-y-4">
264
- <h1 class="text-2xl font-bold text-center">
265
- Setup Complete 🎉
266
- </h1>
261
+ <hgroup class="text-center">
262
+ <span class="text-3xl">🎉</span>
263
+
264
+ <h1 class="font-bold text-2xl">Setup Complete</h1>
265
+ </hgroup>
267
266
 
268
- <p>
269
- Your database has been set up successfully! 🎉 You can now log
270
- in to the admin panel using your Supabase credentials.
267
+ <p class="text-balance text-center text-lg">
268
+ Your database is ready. Create your admin account by signing
269
+ up.
271
270
  </p>
272
271
 
273
272
  <p class="text-center">
274
- <ULink to="/admin/login" class="underline">
275
- Go to login page</ULink
273
+ <UButton
274
+ icon="lucide:arrow-right"
275
+ to="/admin/signup"
276
+ color="primary"
277
+ trailing
276
278
  >
279
+ Go to sign up
280
+ </UButton>
277
281
  </p>
278
282
  </div>
279
283
  </section>
@@ -286,10 +290,9 @@ function handleStepChange(step: number) {
286
290
  :form="currentStepId"
287
291
  :loading="currentLoading"
288
292
  leading-icon="lucide:check"
289
- trailing-icon="lucide:arrow-right"
290
293
  type="submit"
291
294
  >
292
- Submit and continue
295
+ {{ currentStepSubmitLabel }}
293
296
  </UButton>
294
297
  </div>
295
298
  </div>
@@ -14,6 +14,7 @@ watch(visibility, async (current, previous) => {
14
14
  if (current === 'visible' && previous === 'hidden') {
15
15
  if (
16
16
  !isLoggedIn.value &&
17
+ !route.path.startsWith('/admin/setup') &&
17
18
  route.path.startsWith('/admin/') &&
18
19
  route.path !== '/admin/login' &&
19
20
  route.path !== '/admin/signup'
package/bun.lock CHANGED
@@ -6,6 +6,8 @@
6
6
  "name": "supabase",
7
7
  "dependencies": {
8
8
  "@nuxtjs/supabase": "2.0.3",
9
+ "@plutocms/pluto": "^0.0.1-alpha.4",
10
+ "@plutocms/utils": "^0.0.1-alpha.5",
9
11
  "postgres": "^3.4.8",
10
12
  "supabase": "^2.76.15",
11
13
  "yup": "1.7.1",
@@ -14,8 +16,6 @@
14
16
  "@antfu/eslint-config": "^6.2.0",
15
17
  "@nuxt/eslint": "^1.15.1",
16
18
  "@nuxt/ui": "^4.4.0",
17
- "@plutocms/pluto": "^0.0.1-alpha.3",
18
- "@plutocms/utils": "^0.0.1-alpha.4",
19
19
  "@types/bun": "^1.3.9",
20
20
  "@vueuse/nuxt": "^14.2.1",
21
21
  "eslint": "^9.39.1",
@@ -29,8 +29,10 @@
29
29
  "trustedDependencies": [
30
30
  "@parcel/watcher",
31
31
  "esbuild",
32
- "vue-demi",
33
32
  "unrs-resolver",
33
+ "@plutocms/pluto",
34
+ "@plutocms/utils",
35
+ "vue-demi",
34
36
  "supabase",
35
37
  ],
36
38
  "packages": {
@@ -448,9 +450,9 @@
448
450
 
449
451
  "@pkgr/core": ["@pkgr/core@0.2.9", "", {}, "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA=="],
450
452
 
451
- "@plutocms/pluto": ["@plutocms/pluto@0.0.1-alpha.3", "", {}, "sha512-spWbocJJF3QVtwlXUV4mw/RUNDPGzDFx+IWXnatdECdnI/RHSO7A0FLAXEEgX2MsCjyGKZio38FQ2c8xV+TD6A=="],
453
+ "@plutocms/pluto": ["@plutocms/pluto@0.0.1-alpha.4", "", { "dependencies": { "@plutocms/utils": "^0.0.1-alpha.5", "@vueuse/nuxt": "^14.2.1" } }, "sha512-Z2PKZmgjOsTttIWTBFnO9Eb46FYphA1mSTdEbsSu7i5XEfSMbCeTGGiGf0Vr/BUJnzZj5zZ3xG+outVNVUEtMA=="],
452
454
 
453
- "@plutocms/utils": ["@plutocms/utils@0.0.1-alpha.4", "", {}, "sha512-Qwcu8aR59ZY0sk8UDYNew4fCWescQg8pmeYxEjXmlLi8TesYM08cq37EN6TWsf4GE6IjoTLcAZWaBEgHFH7BHA=="],
455
+ "@plutocms/utils": ["@plutocms/utils@0.0.1-alpha.5", "", { "dependencies": { "@nuxt/eslint": "^1.15.2" } }, "sha512-CEWAFZxCUOFAnxzUzhjCn66Q9qQ17L7toHCOdDFbkVKzMC6BudMBbl6bx5c9jzxGtqQ4fRmy2WwDoeZcYYAqjQ=="],
454
456
 
455
457
  "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="],
456
458
 
@@ -2290,6 +2292,8 @@
2290
2292
 
2291
2293
  "@parcel/watcher-wasm/napi-wasm": ["napi-wasm@1.1.3", "", { "bundled": true }, "sha512-h/4nMGsHjZDCYmQVNODIrYACVJ+I9KItbG+0si6W/jSjdA9JbWDoU4LLeMXVcEQGHjttI2tuXqDrbGF7qkUHHg=="],
2292
2294
 
2295
+ "@plutocms/utils/@nuxt/eslint": ["@nuxt/eslint@1.15.2", "", { "dependencies": { "@eslint/config-inspector": "^1.4.2", "@nuxt/devtools-kit": "^3.2.1", "@nuxt/eslint-config": "1.15.2", "@nuxt/eslint-plugin": "1.15.2", "@nuxt/kit": "^4.3.1", "chokidar": "^5.0.0", "eslint-flat-config-utils": "^3.0.1", "eslint-typegen": "^2.3.1", "find-up": "^8.0.0", "get-port-please": "^3.2.0", "mlly": "^1.8.0", "pathe": "^2.0.3", "unimport": "^5.6.0" }, "peerDependencies": { "eslint": "^9.0.0 || ^10.0.0", "eslint-webpack-plugin": "^4.1.0", "vite-plugin-eslint2": "^5.0.0" }, "optionalPeers": ["eslint-webpack-plugin", "vite-plugin-eslint2"] }, "sha512-LwDavQoLl+y0sIDqWEYbOnM6FOmXVIYSEjuvkO1hgAqhb0CvG3hgTnfE1qkf1jOAZp3CZGP+6rxRAJ0dxhueIQ=="],
2296
+
2293
2297
  "@poppinss/dumper/supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="],
2294
2298
 
2295
2299
  "@rollup/plugin-commonjs/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
@@ -2546,6 +2550,14 @@
2546
2550
 
2547
2551
  "@nuxtjs/color-mode/pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
2548
2552
 
2553
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config": ["@nuxt/eslint-config@1.15.2", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@clack/prompts": "^1.0.1", "@eslint/js": "^9.39.3", "@nuxt/eslint-plugin": "1.15.2", "@stylistic/eslint-plugin": "^5.9.0", "@typescript-eslint/eslint-plugin": "^8.56.1", "@typescript-eslint/parser": "^8.56.1", "eslint-config-flat-gitignore": "^2.2.1", "eslint-flat-config-utils": "^3.0.1", "eslint-merge-processors": "^2.0.0", "eslint-plugin-import-lite": "^0.5.2", "eslint-plugin-import-x": "^4.16.1", "eslint-plugin-jsdoc": "^62.7.1", "eslint-plugin-regexp": "^3.0.0", "eslint-plugin-unicorn": "^63.0.0", "eslint-plugin-vue": "^10.8.0", "eslint-processor-vue-blocks": "^2.0.0", "globals": "^17.3.0", "local-pkg": "^1.1.2", "pathe": "^2.0.3", "vue-eslint-parser": "^10.4.0" }, "peerDependencies": { "eslint": "^9.0.0 || ^10.0.0", "eslint-plugin-format": "*" }, "optionalPeers": ["eslint-plugin-format"] }, "sha512-vS6mWB87tYjB8h3TxG/QziaZ6CGJpEOBd7N/j+64/tjNipUJzNgKwDzyGoOifNqyDDnlvgi6T3m9XpeYm4qRaA=="],
2554
+
2555
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-plugin": ["@nuxt/eslint-plugin@1.15.2", "", { "dependencies": { "@typescript-eslint/types": "^8.56.1", "@typescript-eslint/utils": "^8.56.1" }, "peerDependencies": { "eslint": "^9.0.0 || ^10.0.0" } }, "sha512-LZ4gEcPP5GjzAkb6Kk04a4v0vvkTLOpmnEvdDatnkSlxtQLUSwX8v11vcDGXL92ZQ98dFoC1Q1IA6Tz3jdFIig=="],
2556
+
2557
+ "@plutocms/utils/@nuxt/eslint/eslint-flat-config-utils": ["eslint-flat-config-utils@3.0.1", "", { "dependencies": { "@eslint/config-helpers": "^0.5.2", "pathe": "^2.0.3" } }, "sha512-VMA3u86bLzNAwD/7DkLtQ9lolgIOx2Sj0kTMMnBvrvEz7w0rQj4aGCR+lqsqtld63gKiLyT4BnQZ3gmGDXtvjg=="],
2558
+
2559
+ "@plutocms/utils/@nuxt/eslint/eslint-typegen": ["eslint-typegen@2.3.1", "", { "dependencies": { "json-schema-to-typescript-lite": "^15.0.0", "ohash": "^2.0.11" }, "peerDependencies": { "eslint": "^9.0.0 || ^10.0.0" } }, "sha512-zVdh8rThBvv2o5T/K524Fr5iy1Jo0q09rHL7y7FbOhgMB177T2gw+shxfC4ChCEqdq6/y6LJA4j8Fbr/Xls9aw=="],
2560
+
2549
2561
  "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
2550
2562
 
2551
2563
  "archiver-utils/glob/jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
@@ -2654,6 +2666,34 @@
2654
2666
 
2655
2667
  "@nuxt/eslint/eslint-flat-config-utils/@eslint/config-helpers/@eslint/core": ["@eslint/core@1.1.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw=="],
2656
2668
 
2669
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@clack/prompts": ["@clack/prompts@1.0.1", "", { "dependencies": { "@clack/core": "1.0.1", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-/42G73JkuYdyWZ6m8d/CJtBrGl1Hegyc7Fy78m5Ob+jF85TOUmLR5XLce/U3LxYAw0kJ8CT5aI99RIvPHcGp/Q=="],
2670
+
2671
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@eslint/js": ["@eslint/js@9.39.3", "", {}, "sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw=="],
2672
+
2673
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@stylistic/eslint-plugin": ["@stylistic/eslint-plugin@5.9.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/types": "^8.56.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "estraverse": "^5.3.0", "picomatch": "^4.0.3" }, "peerDependencies": { "eslint": "^9.0.0 || ^10.0.0" } }, "sha512-FqqSkvDMYJReydrMhlugc71M76yLLQWNfmGq+SIlLa7N3kHp8Qq8i2PyWrVNAfjOyOIY+xv9XaaYwvVW7vroMA=="],
2674
+
2675
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.56.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/type-utils": "8.56.1", "@typescript-eslint/utils": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.56.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A=="],
2676
+
2677
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/parser": ["@typescript-eslint/parser@8.56.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/typescript-estree": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg=="],
2678
+
2679
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/eslint-config-flat-gitignore": ["eslint-config-flat-gitignore@2.2.1", "", { "dependencies": { "@eslint/compat": "^2.0.2" }, "peerDependencies": { "eslint": "^9.5.0 || ^10.0.0" } }, "sha512-wA5EqN0era7/7Gt5Botlsfin/UNY0etJSEeBgbUlFLFrBi47rAN//+39fI7fpYcl8RENutlFtvp/zRa/M/pZNg=="],
2680
+
2681
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/eslint-plugin-import-lite": ["eslint-plugin-import-lite@0.5.2", "", { "peerDependencies": { "eslint": ">=9.0.0" } }, "sha512-XvfdWOC5dSLEI9krIPRlNmKSI2ViIE9pVylzfV9fCq0ZpDaNeUk6o0wZv0OzN83QdadgXp1NsY0qjLINxwYCsw=="],
2682
+
2683
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/eslint-plugin-jsdoc": ["eslint-plugin-jsdoc@62.7.1", "", { "dependencies": { "@es-joy/jsdoccomment": "~0.84.0", "@es-joy/resolve.exports": "1.2.0", "are-docs-informative": "^0.0.2", "comment-parser": "1.4.5", "debug": "^4.4.3", "escape-string-regexp": "^4.0.0", "espree": "^11.1.0", "esquery": "^1.7.0", "html-entities": "^2.6.0", "object-deep-merge": "^2.0.0", "parse-imports-exports": "^0.2.4", "semver": "^7.7.4", "spdx-expression-parse": "^4.0.0", "to-valid-identifier": "^1.0.0" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0" } }, "sha512-4Zvx99Q7d1uggYBUX/AIjvoyqXhluGbbKrRmG8SQTLprPFg6fa293tVJH1o1GQwNe3lUydd8ZHzn37OaSncgSQ=="],
2684
+
2685
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/eslint-plugin-regexp": ["eslint-plugin-regexp@3.0.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.11.0", "comment-parser": "^1.4.0", "jsdoc-type-pratt-parser": "^7.0.0", "refa": "^0.12.1", "regexp-ast-analysis": "^0.7.1", "scslre": "^0.3.0" }, "peerDependencies": { "eslint": ">=9.38.0" } }, "sha512-iW7hgAV8NOG6E2dz+VeKpq67YLQ9jaajOKYpoOSic2/q8y9BMdXBKkSR9gcMtbqEhNQzdW41E3wWzvhp8ExYwQ=="],
2686
+
2687
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/eslint-plugin-unicorn": ["eslint-plugin-unicorn@63.0.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "@eslint-community/eslint-utils": "^4.9.0", "change-case": "^5.4.4", "ci-info": "^4.3.1", "clean-regexp": "^1.0.0", "core-js-compat": "^3.46.0", "find-up-simple": "^1.0.1", "globals": "^16.4.0", "indent-string": "^5.0.0", "is-builtin-module": "^5.0.0", "jsesc": "^3.1.0", "pluralize": "^8.0.0", "regexp-tree": "^0.1.27", "regjsparser": "^0.13.0", "semver": "^7.7.3", "strip-indent": "^4.1.1" }, "peerDependencies": { "eslint": ">=9.38.0" } }, "sha512-Iqecl9118uQEXYh7adylgEmGfkn5es3/mlQTLLkd4pXkIk9CTGrAbeUux+YljSa2ohXCBmQQ0+Ej1kZaFgcfkA=="],
2688
+
2689
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/globals": ["globals@17.3.0", "", {}, "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw=="],
2690
+
2691
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-plugin/@typescript-eslint/types": ["@typescript-eslint/types@8.56.1", "", {}, "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw=="],
2692
+
2693
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-plugin/@typescript-eslint/utils": ["@typescript-eslint/utils@8.56.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/typescript-estree": "8.56.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA=="],
2694
+
2695
+ "@plutocms/utils/@nuxt/eslint/eslint-flat-config-utils/@eslint/config-helpers": ["@eslint/config-helpers@0.5.2", "", { "dependencies": { "@eslint/core": "^1.1.0" } }, "sha512-a5MxrdDXEvqnIq+LisyCX6tQMPF/dSJpCfBgBauY+pNZ28yCtSsTvyTYrMhaI+LK26bVyCJfJkT0u8KIj2i1dQ=="],
2696
+
2657
2697
  "archiver-utils/glob/jackspeak/@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
2658
2698
 
2659
2699
  "archiver-utils/glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
@@ -2666,6 +2706,48 @@
2666
2706
 
2667
2707
  "glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.2", "", { "dependencies": { "jackspeak": "^4.2.3" } }, "sha512-x0K50QvKQ97fdEz2kPehIerj+YTeptKF9hyYkKf6egnwmMWAkADiO0QCzSp0R5xN8FTZgYaBfSaue46Ej62nMg=="],
2668
2708
 
2709
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@clack/prompts/@clack/core": ["@clack/core@1.0.1", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-WKeyK3NOBwDOzagPR5H08rFk9D/WuN705yEbuZvKqlkmoLM2woKtXb10OO2k1NoSU4SFG947i2/SCYh+2u5e4g=="],
2710
+
2711
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@stylistic/eslint-plugin/@typescript-eslint/types": ["@typescript-eslint/types@8.56.1", "", {}, "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw=="],
2712
+
2713
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1" } }, "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w=="],
2714
+
2715
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "@typescript-eslint/typescript-estree": "8.56.1", "@typescript-eslint/utils": "8.56.1", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg=="],
2716
+
2717
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/eslint-plugin/@typescript-eslint/utils": ["@typescript-eslint/utils@8.56.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/typescript-estree": "8.56.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA=="],
2718
+
2719
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/eslint-plugin/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw=="],
2720
+
2721
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
2722
+
2723
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/parser/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1" } }, "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w=="],
2724
+
2725
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/parser/@typescript-eslint/types": ["@typescript-eslint/types@8.56.1", "", {}, "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw=="],
2726
+
2727
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/parser/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.56.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.56.1", "@typescript-eslint/tsconfig-utils": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg=="],
2728
+
2729
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/parser/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw=="],
2730
+
2731
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/eslint-config-flat-gitignore/@eslint/compat": ["@eslint/compat@2.0.2", "", { "dependencies": { "@eslint/core": "^1.1.0" }, "peerDependencies": { "eslint": "^8.40 || 9 || 10" }, "optionalPeers": ["eslint"] }, "sha512-pR1DoD0h3HfF675QZx0xsyrsU8q70Z/plx7880NOhS02NuWLgBCOMDL787nUeQ7EWLkxv3bPQJaarjcPQb2Dwg=="],
2732
+
2733
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/eslint-plugin-jsdoc/@es-joy/jsdoccomment": ["@es-joy/jsdoccomment@0.84.0", "", { "dependencies": { "@types/estree": "^1.0.8", "@typescript-eslint/types": "^8.54.0", "comment-parser": "1.4.5", "esquery": "^1.7.0", "jsdoc-type-pratt-parser": "~7.1.1" } }, "sha512-0xew1CxOam0gV5OMjh2KjFQZsKL2bByX1+q4j3E73MpYIdyUxcZb/xQct9ccUb+ve5KGUYbCUxyPnYB7RbuP+w=="],
2734
+
2735
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/eslint-plugin-jsdoc/comment-parser": ["comment-parser@1.4.5", "", {}, "sha512-aRDkn3uyIlCFfk5NUA+VdwMmMsh8JGhc4hapfV4yxymHGQ3BVskMQfoXGpCo5IoBuQ9tS5iiVKhCpTcB4pW4qw=="],
2736
+
2737
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/eslint-plugin-jsdoc/espree": ["espree@11.1.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^5.0.0" } }, "sha512-WFWYhO1fV4iYkqOOvq8FbqIhr2pYfoDY0kCotMkDeNtGpiGGkZ1iov2u8ydjtgM8yF8rzK7oaTbw2NAzbAbehw=="],
2738
+
2739
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/eslint-plugin-regexp/comment-parser": ["comment-parser@1.4.5", "", {}, "sha512-aRDkn3uyIlCFfk5NUA+VdwMmMsh8JGhc4hapfV4yxymHGQ3BVskMQfoXGpCo5IoBuQ9tS5iiVKhCpTcB4pW4qw=="],
2740
+
2741
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/eslint-plugin-regexp/jsdoc-type-pratt-parser": ["jsdoc-type-pratt-parser@7.1.1", "", {}, "sha512-/2uqY7x6bsrpi3i9LVU6J89352C0rpMk0as8trXxCtvd4kPk1ke/Eyif6wqfSLvoNJqcDG9Vk4UsXgygzCt2xA=="],
2742
+
2743
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/eslint-plugin-unicorn/globals": ["globals@16.5.0", "", {}, "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ=="],
2744
+
2745
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1" } }, "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w=="],
2746
+
2747
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.56.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.56.1", "@typescript-eslint/tsconfig-utils": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg=="],
2748
+
2749
+ "@plutocms/utils/@nuxt/eslint/eslint-flat-config-utils/@eslint/config-helpers/@eslint/core": ["@eslint/core@1.1.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw=="],
2750
+
2669
2751
  "archiver-utils/glob/jackspeak/@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
2670
2752
 
2671
2753
  "archiver-utils/glob/jackspeak/@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
@@ -2674,6 +2756,44 @@
2674
2756
 
2675
2757
  "eslint/find-up/locate-path/p-locate/p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
2676
2758
 
2759
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager/@typescript-eslint/types": ["@typescript-eslint/types@8.56.1", "", {}, "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw=="],
2760
+
2761
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils/@typescript-eslint/types": ["@typescript-eslint/types@8.56.1", "", {}, "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw=="],
2762
+
2763
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.56.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.56.1", "@typescript-eslint/tsconfig-utils": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg=="],
2764
+
2765
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/types": ["@typescript-eslint/types@8.56.1", "", {}, "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw=="],
2766
+
2767
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.56.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.56.1", "@typescript-eslint/tsconfig-utils": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg=="],
2768
+
2769
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/eslint-plugin/@typescript-eslint/visitor-keys/@typescript-eslint/types": ["@typescript-eslint/types@8.56.1", "", {}, "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw=="],
2770
+
2771
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/eslint-plugin/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.0", "", {}, "sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q=="],
2772
+
2773
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/parser/@typescript-eslint/typescript-estree/@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.56.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.56.1", "@typescript-eslint/types": "^8.56.1", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ=="],
2774
+
2775
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/parser/@typescript-eslint/typescript-estree/@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.56.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ=="],
2776
+
2777
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/parser/@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
2778
+
2779
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/parser/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.0", "", {}, "sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q=="],
2780
+
2781
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/eslint-config-flat-gitignore/@eslint/compat/@eslint/core": ["@eslint/core@1.1.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw=="],
2782
+
2783
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/eslint-plugin-jsdoc/@es-joy/jsdoccomment/jsdoc-type-pratt-parser": ["jsdoc-type-pratt-parser@7.1.1", "", {}, "sha512-/2uqY7x6bsrpi3i9LVU6J89352C0rpMk0as8trXxCtvd4kPk1ke/Eyif6wqfSLvoNJqcDG9Vk4UsXgygzCt2xA=="],
2784
+
2785
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/eslint-plugin-jsdoc/espree/eslint-visitor-keys": ["eslint-visitor-keys@5.0.0", "", {}, "sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q=="],
2786
+
2787
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/scope-manager/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw=="],
2788
+
2789
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.56.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.56.1", "@typescript-eslint/types": "^8.56.1", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ=="],
2790
+
2791
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.56.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ=="],
2792
+
2793
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw=="],
2794
+
2795
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
2796
+
2677
2797
  "archiver-utils/glob/jackspeak/@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
2678
2798
 
2679
2799
  "archiver-utils/glob/jackspeak/@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
@@ -2681,5 +2801,37 @@
2681
2801
  "archiver-utils/glob/jackspeak/@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
2682
2802
 
2683
2803
  "eslint/find-up/locate-path/p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
2804
+
2805
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.56.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.56.1", "@typescript-eslint/types": "^8.56.1", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ=="],
2806
+
2807
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.56.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ=="],
2808
+
2809
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
2810
+
2811
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.56.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.56.1", "@typescript-eslint/types": "^8.56.1", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ=="],
2812
+
2813
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.56.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ=="],
2814
+
2815
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
2816
+
2817
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/parser/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.2", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw=="],
2818
+
2819
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/scope-manager/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.0", "", {}, "sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q=="],
2820
+
2821
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.0", "", {}, "sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q=="],
2822
+
2823
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.2", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw=="],
2824
+
2825
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.2", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw=="],
2826
+
2827
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.2", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw=="],
2828
+
2829
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/parser/@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.2", "", { "dependencies": { "jackspeak": "^4.2.3" } }, "sha512-x0K50QvKQ97fdEz2kPehIerj+YTeptKF9hyYkKf6egnwmMWAkADiO0QCzSp0R5xN8FTZgYaBfSaue46Ej62nMg=="],
2830
+
2831
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.2", "", { "dependencies": { "jackspeak": "^4.2.3" } }, "sha512-x0K50QvKQ97fdEz2kPehIerj+YTeptKF9hyYkKf6egnwmMWAkADiO0QCzSp0R5xN8FTZgYaBfSaue46Ej62nMg=="],
2832
+
2833
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.2", "", { "dependencies": { "jackspeak": "^4.2.3" } }, "sha512-x0K50QvKQ97fdEz2kPehIerj+YTeptKF9hyYkKf6egnwmMWAkADiO0QCzSp0R5xN8FTZgYaBfSaue46Ej62nMg=="],
2834
+
2835
+ "@plutocms/utils/@nuxt/eslint/@nuxt/eslint-config/@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.2", "", { "dependencies": { "jackspeak": "^4.2.3" } }, "sha512-x0K50QvKQ97fdEz2kPehIerj+YTeptKF9hyYkKf6egnwmMWAkADiO0QCzSp0R5xN8FTZgYaBfSaue46Ej62nMg=="],
2684
2836
  }
2685
2837
  }
package/nuxt.config.ts CHANGED
@@ -1,7 +1,3 @@
1
- const cwd = process.cwd()
2
-
3
- const isDev = cwd.includes('supabase')
4
-
5
1
  export default defineNuxtConfig({
6
2
  extends: [['@plutocms/pluto', { install: true }], '@plutocms/utils'],
7
3
 
@@ -37,19 +33,17 @@ export default defineNuxtConfig({
37
33
  },
38
34
  },
39
35
 
40
- supabase: !isDev
41
- ? {
42
- types: '#shared',
43
- cookiePrefix: 'access_token',
44
-
45
- redirectOptions: {
46
- login: '/admin/login',
47
- callback: '/auth/confirm',
48
- include: ['/admin/signup'],
49
- saveRedirectToCookie: false,
50
- },
51
-
52
- redirect: false,
53
- }
54
- : undefined,
36
+ supabase: {
37
+ types: '#shared/types/supabase',
38
+ cookiePrefix: 'access_token',
39
+
40
+ redirectOptions: {
41
+ login: '/admin/login',
42
+ callback: '/auth/confirm',
43
+ include: ['/admin/signup'],
44
+ saveRedirectToCookie: false,
45
+ },
46
+
47
+ redirect: false,
48
+ },
55
49
  })
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@plutocms/supabase",
3
3
  "type": "module",
4
- "version": "0.0.1-alpha.1",
4
+ "version": "0.0.1-alpha.10",
5
5
  "main": "./nuxt.config.ts",
6
6
  "scripts": {
7
7
  "build": "nuxt build",
@@ -16,6 +16,8 @@
16
16
  },
17
17
  "dependencies": {
18
18
  "@nuxtjs/supabase": "2.0.3",
19
+ "@plutocms/pluto": "^0.0.1-alpha.4",
20
+ "@plutocms/utils": "^0.0.1-alpha.5",
19
21
  "postgres": "^3.4.8",
20
22
  "supabase": "^2.76.15",
21
23
  "yup": "1.7.1"
@@ -24,8 +26,6 @@
24
26
  "@antfu/eslint-config": "^6.2.0",
25
27
  "@nuxt/eslint": "^1.15.1",
26
28
  "@nuxt/ui": "^4.4.0",
27
- "@plutocms/pluto": "^0.0.1-alpha.3",
28
- "@plutocms/utils": "^0.0.1-alpha.4",
29
29
  "@types/bun": "^1.3.9",
30
30
  "@vueuse/nuxt": "^14.2.1",
31
31
  "eslint": "^9.39.1",
@@ -36,6 +36,8 @@
36
36
  },
37
37
  "trustedDependencies": [
38
38
  "@parcel/watcher",
39
+ "@plutocms/pluto",
40
+ "@plutocms/utils",
39
41
  "esbuild",
40
42
  "supabase",
41
43
  "unrs-resolver",
package/public/schema.sql CHANGED
@@ -14,7 +14,7 @@ end
14
14
  $$;
15
15
 
16
16
  --- Create settings table
17
- create table public.settings (
17
+ create table if not exists public.settings (
18
18
  id bigint generated by default as identity not null,
19
19
  setting_name public.TSettings not null,
20
20
  setting_value text not null,
@@ -26,21 +26,60 @@ create table public.settings (
26
26
  insert into public.settings (setting_name, setting_value) values
27
27
  ('website_title', 'My Website'),
28
28
  ('website_url', 'https://mywebsite.test'),
29
- ('website_description', 'This is my website.');
29
+ ('website_description', 'This is my website.')
30
+ on conflict (setting_name) do nothing;
30
31
 
31
32
  alter table public.settings enable row level security;
32
33
 
33
34
  -- Policies for settings table
34
- create policy "Enable insert for authenticated users only"
35
- on public.settings
36
- to authenticated, dashboard_user
37
- with check (true);
35
+ do $$
36
+ begin
37
+ if not exists (
38
+ select 1 from pg_policies where tablename = 'settings' and policyname = 'Enable insert for authenticated users only'
39
+ ) then
40
+ create policy "Enable insert for authenticated users only"
41
+ on public.settings
42
+ for insert
43
+ to authenticated, dashboard_user
44
+ with check (true);
45
+ end if;
46
+ end
47
+ $$;
48
+
49
+ do $$
50
+ begin
51
+ if not exists (
52
+ select 1 from pg_policies where tablename = 'settings' and policyname = 'Enable read access for authenticated users on settings'
53
+ ) then
54
+ create policy "Enable read access for authenticated users on settings"
55
+ on public.settings
56
+ for select
57
+ to authenticated, dashboard_user
58
+ using (true);
59
+ end if;
60
+ end
61
+ $$;
62
+
63
+ do $$
64
+ begin
65
+ if not exists (
66
+ select 1 from pg_policies where tablename = 'settings' and policyname = 'Enable update for authenticated users on settings'
67
+ ) then
68
+ create policy "Enable update for authenticated users on settings"
69
+ on public.settings
70
+ for update
71
+ to authenticated, dashboard_user
72
+ using (true)
73
+ with check (true);
74
+ end if;
75
+ end
76
+ $$;
38
77
 
39
78
  --- Create enum type for healthcheck names
40
79
  do $$
41
80
  begin
42
81
  if not exists (select 1 from pg_type where typname = 'thealthcheck') then
43
- create type Healthcheck as enum (
82
+ create type THealthcheck as enum (
44
83
  'first_setup'
45
84
  );
46
85
  end if;
@@ -48,56 +87,115 @@ end
48
87
  $$;
49
88
 
50
89
  --- Create healthcheck table (name/value format like settings)
51
- create table public.healthcheck (
90
+ create table if not exists public.healthcheck (
52
91
  id bigint generated by default as identity not null,
53
- check_name public.Healthcheck not null,
54
- check_value text not null,
92
+ config_name public.THealthcheck not null,
93
+ config_value text not null,
55
94
  constraint healthcheck_pkey primary key (id),
56
- constraint healthcheck_check_name_key unique (check_name)
95
+ constraint healthcheck_config_name_key unique (config_name)
57
96
  ) TABLESPACE pg_default;
58
97
 
59
98
  --- Insert default healthcheck values
60
- insert into public.healthcheck (check_name, check_value) values
61
- ('first_setup', 'true');
99
+ insert into public.healthcheck (config_name, config_value) values
100
+ ('first_setup', 'true')
101
+ on conflict (config_name) do nothing;
62
102
 
63
103
  alter table public.healthcheck enable row level security;
64
104
 
65
105
  -- Allow read access to healthcheck for public and dashboard_user (no login required)
66
- create policy "Enable read access for healthcheck to public and dashboard_user"
67
- on public.healthcheck
68
- for select
69
- to public, dashboard_user
70
- using (true);
106
+ do $$
107
+ begin
108
+ if not exists (
109
+ select 1 from pg_policies where tablename = 'healthcheck' and policyname = 'Enable read access for healthcheck to public'
110
+ ) then
111
+ create policy "Enable read access for healthcheck to public"
112
+ on public.healthcheck
113
+ for select
114
+ to public
115
+ using (true);
116
+ end if;
117
+ end
118
+ $$;
119
+
120
+ -- Allow update access to healthcheck for authenticated users and dashboard_user
121
+ do $$
122
+ begin
123
+ if not exists (
124
+ select 1 from pg_policies where tablename = 'healthcheck' and policyname = 'Enable update for authenticated users on healthcheck'
125
+ ) then
126
+ create policy "Enable update for authenticated users on healthcheck"
127
+ on public.healthcheck
128
+ for update
129
+ to authenticated, dashboard_user
130
+ using (true)
131
+ with check (true);
132
+ end if;
133
+ end
134
+ $$;
71
135
 
72
136
  -- Profiles
73
- create table profiles (
137
+ create table if not exists public.profiles (
74
138
  id uuid references auth.users on delete cascade not null primary key,
75
139
  updated_at timestamp with time zone,
76
- first_name text,
77
- last_name text,
78
- constraint first_name_length check (char_length(first_name) >= 3)
140
+ email text,
141
+ username text,
142
+ display_name text,
143
+ is_admin boolean not null default false,
144
+ constraint username_length check (char_length(username) >= 3)
79
145
  );
80
146
 
81
147
  alter table public.profiles enable row level security;
82
148
 
83
- create policy "Enable read access for authenticated users"
84
- on public.profiles
85
- to authenticated, dashboard_user
86
- for select using (true);
149
+ do $$
150
+ begin
151
+ if not exists (
152
+ select 1 from pg_policies where tablename = 'profiles' and policyname = 'Enable read access for authenticated users'
153
+ ) then
154
+ create policy "Enable read access for authenticated users"
155
+ on public.profiles
156
+ for select
157
+ to authenticated, dashboard_user
158
+ using (true);
159
+ end if;
160
+ end
161
+ $$;
87
162
 
88
163
  --- inserts a row into public.profiles
89
- create function public.handle_new_user()
164
+ --- automatically grants admin to the first user if no profiles exist yet
165
+ create or replace function public.handle_new_user()
90
166
  returns trigger
91
167
  language plpgsql
92
168
  security definer set search_path = ''
93
169
  as $$
170
+ declare
171
+ profile_count int;
172
+ should_be_admin boolean;
94
173
  begin
95
- insert into public.profiles (id, first_name, last_name)
96
- values (new.id, new.raw_user_meta_data ->> 'first_name', new.raw_user_meta_data ->> 'last_name');
174
+ select count(*) into profile_count from public.profiles;
175
+
176
+ should_be_admin := profile_count = 0
177
+ or coalesce((new.raw_user_meta_data ->> 'is_admin')::boolean, false);
178
+
179
+ insert into public.profiles (id, email, username, display_name, is_admin)
180
+ values (
181
+ new.id,
182
+ new.email,
183
+ new.raw_user_meta_data ->> 'username',
184
+ new.raw_user_meta_data ->> 'display_name',
185
+ should_be_admin
186
+ );
97
187
  return new;
98
188
  end;
99
189
  $$;
100
190
  --- trigger the function every time a user is created
101
- create trigger on_auth_user_created
102
- after insert on auth.users
103
- for each row execute procedure public.handle_new_user();
191
+ do $$
192
+ begin
193
+ if not exists (
194
+ select 1 from pg_trigger where tgname = 'on_auth_user_created'
195
+ ) then
196
+ create trigger on_auth_user_created
197
+ after insert on auth.users
198
+ for each row execute procedure public.handle_new_user();
199
+ end if;
200
+ end
201
+ $$;
@@ -10,7 +10,7 @@ export default defineEventHandler(async (event) => {
10
10
 
11
11
  if (error) {
12
12
  return {
13
- success: false,
13
+ success: false as const,
14
14
  message: 'Error retrieving healthcheck settings',
15
15
  error: error.message,
16
16
  }
@@ -32,7 +32,7 @@ export default defineEventHandler(async (event) => {
32
32
  })
33
33
 
34
34
  return {
35
- success: true,
35
+ success: true as const,
36
36
  message: 'Healthcheck settings retrieved successfully',
37
37
  is_first_setup: isFirstSetup.first_setup === 'true',
38
38
  }
@@ -5,6 +5,88 @@ interface Payload {
5
5
  connectionString: string
6
6
  }
7
7
 
8
+ /**
9
+ * Splits a SQL string into individual statements, correctly handling
10
+ * $$ dollar-quoted blocks, single-quoted strings, and -- comments.
11
+ */
12
+ function splitStatements(sql: string): string[] {
13
+ const statements: string[] = []
14
+ let current = ''
15
+ let i = 0
16
+
17
+ while (i < sql.length) {
18
+ // Check for dollar-quoting ($$)
19
+ if (sql[i] === '$' && sql[i + 1] === '$') {
20
+ current += '$$'
21
+ i += 2
22
+ // Read until closing $$
23
+ while (i < sql.length) {
24
+ if (sql[i] === '$' && sql[i + 1] === '$') {
25
+ current += '$$'
26
+ i += 2
27
+ break
28
+ }
29
+ current += sql[i]
30
+ i++
31
+ }
32
+ continue
33
+ }
34
+
35
+ // Check for single-quoted strings (handle '' escapes)
36
+ if (sql[i] === `'`) {
37
+ current += sql[i]
38
+ i++
39
+ while (i < sql.length) {
40
+ if (sql[i] === `'` && sql[i + 1] === `'`) {
41
+ // Escaped quote
42
+ current += `''`
43
+ i += 2
44
+ continue
45
+ }
46
+ if (sql[i] === `'`) {
47
+ current += sql[i]
48
+ i++
49
+ break
50
+ }
51
+ current += sql[i]
52
+ i++
53
+ }
54
+ continue
55
+ }
56
+
57
+ // Check for single-line comments
58
+ if (sql[i] === '-' && sql[i + 1] === '-') {
59
+ while (i < sql.length && sql[i] !== '\n') {
60
+ current += sql[i]
61
+ i++
62
+ }
63
+ continue
64
+ }
65
+
66
+ // Statement terminator
67
+ if (sql[i] === ';') {
68
+ current += ';'
69
+ const trimmed = current.trim()
70
+ if (trimmed && trimmed !== ';') {
71
+ statements.push(trimmed)
72
+ }
73
+ current = ''
74
+ i++
75
+ continue
76
+ }
77
+
78
+ current += sql[i]
79
+ i++
80
+ }
81
+
82
+ const trimmed = current.trim()
83
+ if (trimmed && trimmed !== ';') {
84
+ statements.push(trimmed)
85
+ }
86
+
87
+ return statements
88
+ }
89
+
8
90
  export default defineEventHandler(async (event) => {
9
91
  const body = await readBody<Payload>(event)
10
92
 
@@ -30,16 +112,16 @@ export default defineEventHandler(async (event) => {
30
112
  const sql = postgres(connectionString)
31
113
 
32
114
  try {
33
- /* await sql.begin(async (sql) => {
34
- await sql.unsafe(schema)
35
- }) */
36
-
37
- // Set the first_setup flag in the healthcheck table to false
38
- await sql`
39
- UPDATE healthcheck
40
- SET config_value = 'false'
41
- WHERE config_name = 'first_setup';
42
- `
115
+ const statements = splitStatements(schema)
116
+
117
+ for (const statement of statements) {
118
+ await sql.unsafe(statement)
119
+ }
120
+
121
+ // Mark first_setup as complete
122
+ await sql.unsafe(
123
+ `UPDATE public.healthcheck SET config_value = 'false' WHERE config_name = 'first_setup'`
124
+ )
43
125
 
44
126
  return {
45
127
  success: true,
@@ -1,28 +1,46 @@
1
1
  import { serverSupabaseClient } from '#supabase/server'
2
2
 
3
+ interface Payload {
4
+ username: string
5
+ display_name: string
6
+ email: string
7
+ password: string
8
+ }
9
+
3
10
  export default defineEventHandler(async (event) => {
4
11
  const client = await serverSupabaseClient<Database>(event)
5
12
 
6
- const body = await readBody(event)
13
+ const body = await readBody<Payload>(event)
14
+
15
+ if (!body.username || body.username.length < 3) {
16
+ return {
17
+ success: false,
18
+ message: 'Username must be at least 3 characters.',
19
+ }
20
+ }
7
21
 
8
22
  const { data, error } = await client.auth.signUp({
9
23
  email: body.email,
10
24
  password: body.password,
11
25
  options: {
12
26
  data: {
13
- first_name: body.first_name,
14
- last_name: body.last_name,
27
+ username: body.username,
28
+ display_name: body.display_name,
15
29
  },
16
30
  },
17
31
  })
18
32
 
19
33
  if (error) {
20
- throw createError({
21
- statusCode: Number.parseInt(error.code ?? '500'),
22
- cause: error.cause,
34
+ return {
35
+ success: false,
23
36
  message: error.message,
24
- })
37
+ error,
38
+ }
25
39
  }
26
40
 
27
- return { message: 'User registered successfully', data }
41
+ return {
42
+ success: true,
43
+ message: 'User registered successfully',
44
+ data,
45
+ }
28
46
  })