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

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'