@plutocms/supabase 0.10.0 → 0.11.1
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/CHANGELOG.md +24 -0
- package/FEATURES.md +3 -0
- package/README.md +10 -1
- package/app/components/auth/AuthSignIn.vue +56 -0
- package/app/components/migrations/MigrationsBanner.vue +26 -12
- package/app/composables/auth.ts +22 -88
- package/app/pages/admin/(auth)/confirm.vue +17 -21
- package/app/pages/admin/(auth)/forgot-password.vue +57 -64
- package/app/pages/admin/(auth)/signup.vue +104 -107
- package/app/pages/admin/(auth)/update-password.vue +75 -82
- package/app/pages/admin/setup.vue +0 -1
- package/app/plugins/pluto-extension.ts +147 -8
- package/app/utils/auth-driver.ts +110 -0
- package/i18n/locales/en.json +2 -18
- package/i18n/locales/pt-BR.json +2 -18
- package/package.json +2 -2
- package/server/utils/typegen.ts +67 -2
- package/app/components/navbar/NavbarAdmin.vue +0 -219
- package/app/components/navbar/NavbarAdminProvider.vue +0 -18
- package/app/middleware/auth.ts +0 -34
- package/app/middleware/setup-check.ts +0 -44
- package/app/pages/admin/(auth)/login.vue +0 -95
- package/app/pages/admin.vue +0 -57
- package/app/plugins/pluto-locale-sync.client.ts +0 -30
- package/app/plugins/pluto-permissions-sync.ts +0 -38
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
interface Form {
|
|
3
|
-
email: string
|
|
4
|
-
password: string
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
definePageMeta({
|
|
8
|
-
layout: 'auth',
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
const { t } = usePlutoI18n()
|
|
12
|
-
|
|
13
|
-
useHead({
|
|
14
|
-
title: () => t('supabase.auth.login.title'),
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
const { isSubmitting, login } = await useAuth()
|
|
18
|
-
|
|
19
|
-
const form = ref<Form>({
|
|
20
|
-
email: '',
|
|
21
|
-
password: '',
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
function submitForm() {
|
|
25
|
-
login(form.value)
|
|
26
|
-
}
|
|
27
|
-
</script>
|
|
28
|
-
|
|
29
|
-
<template>
|
|
30
|
-
<div class="grid min-h-dvh place-items-center p-4">
|
|
31
|
-
<UCard class="w-full max-w-100">
|
|
32
|
-
<form @submit.prevent="submitForm">
|
|
33
|
-
<div class="flex flex-col gap-y-6">
|
|
34
|
-
<h1 class="text-3xl font-bold">{{ t('supabase.auth.login.title') }}</h1>
|
|
35
|
-
|
|
36
|
-
<UFormField :label="t('supabase.auth.fields.email')">
|
|
37
|
-
<UInput
|
|
38
|
-
v-model="form.email"
|
|
39
|
-
placeholder="victor@example.com"
|
|
40
|
-
class="w-full"
|
|
41
|
-
autocomplete="email"
|
|
42
|
-
/>
|
|
43
|
-
</UFormField>
|
|
44
|
-
|
|
45
|
-
<UFormField :label="t('supabase.auth.fields.password')">
|
|
46
|
-
<UInput
|
|
47
|
-
v-model="form.password"
|
|
48
|
-
placeholder="••••"
|
|
49
|
-
class="w-full"
|
|
50
|
-
type="password"
|
|
51
|
-
/>
|
|
52
|
-
</UFormField>
|
|
53
|
-
|
|
54
|
-
<div
|
|
55
|
-
class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between sm:gap-x-4"
|
|
56
|
-
>
|
|
57
|
-
<UFormField>
|
|
58
|
-
<UButton
|
|
59
|
-
:loading="isSubmitting"
|
|
60
|
-
type="submit"
|
|
61
|
-
icon="lucide:log-in"
|
|
62
|
-
class="w-full justify-center sm:w-auto"
|
|
63
|
-
>
|
|
64
|
-
{{ t('supabase.auth.login.submit') }}
|
|
65
|
-
</UButton>
|
|
66
|
-
</UFormField>
|
|
67
|
-
|
|
68
|
-
<UFormField>
|
|
69
|
-
<UButton variant="link" to="/admin/signup" class="px-0">
|
|
70
|
-
{{ t('supabase.auth.login.createAccount') }}
|
|
71
|
-
</UButton>
|
|
72
|
-
</UFormField>
|
|
73
|
-
</div>
|
|
74
|
-
|
|
75
|
-
<div
|
|
76
|
-
class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between"
|
|
77
|
-
>
|
|
78
|
-
<UButton
|
|
79
|
-
variant="link"
|
|
80
|
-
icon="lucide:arrow-left"
|
|
81
|
-
to="/"
|
|
82
|
-
class="px-0"
|
|
83
|
-
>
|
|
84
|
-
{{ t('supabase.auth.login.backToHome') }}
|
|
85
|
-
</UButton>
|
|
86
|
-
|
|
87
|
-
<UButton variant="link" to="/admin/forgot-password" class="px-0">
|
|
88
|
-
{{ t('supabase.auth.login.forgotPassword') }}
|
|
89
|
-
</UButton>
|
|
90
|
-
</div>
|
|
91
|
-
</div>
|
|
92
|
-
</form>
|
|
93
|
-
</UCard>
|
|
94
|
-
</div>
|
|
95
|
-
</template>
|
package/app/pages/admin.vue
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
definePageMeta({
|
|
3
|
-
middleware: ['setup-check', 'auth'],
|
|
4
|
-
layout: 'admin',
|
|
5
|
-
})
|
|
6
|
-
|
|
7
|
-
const route = useRoute()
|
|
8
|
-
const { t } = usePlutoI18n()
|
|
9
|
-
const { isLoggedIn, logout, allowedUnauthenticatedPaths } = await useAuth()
|
|
10
|
-
const toast = useToast()
|
|
11
|
-
|
|
12
|
-
// Loading/clearing permissions on login/logout lives in
|
|
13
|
-
// app/plugins/pluto-permissions-sync.ts now, not here — that plugin runs
|
|
14
|
-
// on every page, not just this one, so a signed-in admin browsing a public
|
|
15
|
-
// page directly (no prior visit to /admin/** this session) still gets
|
|
16
|
-
// their capabilities loaded.
|
|
17
|
-
|
|
18
|
-
const visibility = useDocumentVisibility()
|
|
19
|
-
|
|
20
|
-
watch(visibility, async (current, previous) => {
|
|
21
|
-
if (current === 'visible' && previous === 'hidden') {
|
|
22
|
-
if (
|
|
23
|
-
!isLoggedIn.value &&
|
|
24
|
-
!route.path.startsWith('/admin/setup') &&
|
|
25
|
-
!allowedUnauthenticatedPaths.includes(route.path)
|
|
26
|
-
) {
|
|
27
|
-
await logout({ redirectTo: route.path })
|
|
28
|
-
|
|
29
|
-
toast.add({
|
|
30
|
-
title: t('supabase.auth.toasts.notLoggedIn'),
|
|
31
|
-
description: t('supabase.auth.toasts.notLoggedInDescription'),
|
|
32
|
-
icon: 'lucide:circle-x',
|
|
33
|
-
color: 'error',
|
|
34
|
-
})
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
})
|
|
38
|
-
</script>
|
|
39
|
-
|
|
40
|
-
<template>
|
|
41
|
-
<div class="light:text-zinc-800 h-full dark:text-white">
|
|
42
|
-
<ClientOnly>
|
|
43
|
-
<Container
|
|
44
|
-
v-if="
|
|
45
|
-
isLoggedIn
|
|
46
|
-
&& !route.path.startsWith('/admin/setup')
|
|
47
|
-
&& !route.path.startsWith('/admin/migrations')
|
|
48
|
-
"
|
|
49
|
-
class="pt-4"
|
|
50
|
-
>
|
|
51
|
-
<MigrationsBanner />
|
|
52
|
-
</Container>
|
|
53
|
-
</ClientOnly>
|
|
54
|
-
|
|
55
|
-
<NuxtPage />
|
|
56
|
-
</div>
|
|
57
|
-
</template>
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Loads the signed-in user's stored admin language whenever a session
|
|
3
|
-
* appears: after a login, and on a page load that already has a session.
|
|
4
|
-
* App-wide, like pluto-permissions-sync.ts, so any page gets the language,
|
|
5
|
-
* not only the pages under /admin.
|
|
6
|
-
*
|
|
7
|
-
* Client-only, unlike the permissions sync. A server `$fetch` does not forward
|
|
8
|
-
* the user's cookies, so a server run would read no locale and clear the cookie.
|
|
9
|
-
* The server already resolves the first locale from the cookie or the
|
|
10
|
-
* Accept-Language header. The stored value then replaces it on the client, and
|
|
11
|
-
* the cookie keeps it for the next server render.
|
|
12
|
-
*
|
|
13
|
-
* `useSupabaseSession()` is the same primitive the permissions sync uses.
|
|
14
|
-
* `syncUserLocale()` never throws. With no driver, or on an error, nothing
|
|
15
|
-
* changes.
|
|
16
|
-
*/
|
|
17
|
-
export default defineNuxtPlugin(() => {
|
|
18
|
-
const session = useSupabaseSession()
|
|
19
|
-
const { syncUserLocale } = usePlutoI18n()
|
|
20
|
-
|
|
21
|
-
watch(
|
|
22
|
-
session,
|
|
23
|
-
(current) => {
|
|
24
|
-
if (current) {
|
|
25
|
-
void syncUserLocale()
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
{ immediate: true }
|
|
29
|
-
)
|
|
30
|
-
})
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Loads (or clears) the current user's capabilities whenever their session
|
|
3
|
-
* appears or disappears — app-wide, not page-scoped.
|
|
4
|
-
*
|
|
5
|
-
* This used to live as a `watch` inside `app/pages/admin.vue`. That only
|
|
6
|
-
* ran while `admin.vue` was the mounted page, so a signed-in admin loading
|
|
7
|
-
* a public page directly (a fresh navigation or a page reload, with no
|
|
8
|
-
* earlier visit to `/admin/**` in the same app instance) never triggered a
|
|
9
|
-
* load at all — `usePlutoPermissions().granted` stayed empty, and every
|
|
10
|
-
* `can()` check for that admin read as `false` on that page, even though
|
|
11
|
-
* the same admin's capability-gated UI worked correctly inside `/admin/**`.
|
|
12
|
-
* `NavbarAdminActions.vue`'s "Edit product" quick-link (shown while
|
|
13
|
-
* viewing a live `/product/**` page) is exactly the kind of capability-
|
|
14
|
-
* gated UI that lives outside `/admin/**` by design, so it needs this to
|
|
15
|
-
* fire on every page, not just admin ones.
|
|
16
|
-
*
|
|
17
|
-
* `useSupabaseSession()` (not `useAuth()`) on purpose: it is the same
|
|
18
|
-
* primitive `useAuth()`'s own `isLoggedIn` is built from
|
|
19
|
-
* (`computed(() => !!supabaseSession.value)`), and it is genuinely
|
|
20
|
-
* app-wide reactive state already (managed by `@nuxtjs/supabase`'s own
|
|
21
|
-
* plugin), so reading it directly here needs no extra composable surface.
|
|
22
|
-
*/
|
|
23
|
-
export default defineNuxtPlugin(() => {
|
|
24
|
-
const session = useSupabaseSession()
|
|
25
|
-
const { load, clear } = usePlutoPermissions()
|
|
26
|
-
|
|
27
|
-
watch(
|
|
28
|
-
session,
|
|
29
|
-
(current) => {
|
|
30
|
-
if (current) {
|
|
31
|
-
load()
|
|
32
|
-
} else {
|
|
33
|
-
clear()
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
{ immediate: true }
|
|
37
|
-
)
|
|
38
|
-
})
|