@plutocms/core 0.44.1 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/FEATURES.md +1 -1
- package/app/composables/internal/admin-shell.ts +35 -0
- package/app/middleware/pluto-admin-auth.global.ts +15 -8
- package/app/pages/admin/not-configured.vue +22 -0
- package/i18n/locales/en.json +4 -0
- package/i18n/locales/pt-BR.json +4 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## [0.45.0](https://github.com/plutocms/pluto/compare/v0.44.1...v0.45.0) (2026-11-01)
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
* in production, the admin shows a not-configured page when no auth driver is registered ([#346](https://github.com/plutocms/pluto/issues/346)) ([6f49cb8](https://github.com/plutocms/pluto/commit/6f49cb814d1d3614d8b3b7b92722081ba82549ae))
|
|
10
|
+
|
|
4
11
|
## [0.44.1](https://github.com/plutocms/pluto/compare/v0.44.0...v0.44.1) (2026-11-01)
|
|
5
12
|
|
|
6
13
|
|
package/FEATURES.md
CHANGED
|
@@ -101,7 +101,7 @@ policies shared by every project here.
|
|
|
101
101
|
`defineAsyncComponent`, shows a skeleton per field, widget and settings panel while a chunk
|
|
102
102
|
loads, and warns in dev about a registered component that is not async.
|
|
103
103
|
@.claude/skills/extension-registry/SKILL.md
|
|
104
|
-
- Admin shell: core owns the `/admin` parent route, the `/admin/login` page, the route guard, the admin banners, the navbar and the user menu. A backend layer registers an auth driver, account menu items, site info and link actions. The admin is for staff only: a signed-in user with no capability goes to `/admin/no-access`. The bar shows on `/admin/**` for staff, and on public pages for staff only. A route hides the bar with the page meta `plutoAdminBar: false`, and `isPlutoPreviewPath` marks the preview path. The bar height is the token `--pluto-admin-bar-height`, and `<html>` gets `data-pluto-admin-bar` while the bar shows. A hint cookie puts the bar frame in the server HTML of a public page for staff (the actions and the user menu load after the mount). Content types with `adminBar` get "New <type>" and "Edit <type>" buttons (`usePlutoEditLink`). With two or more types, one "New" menu lists them, grouped by `navGroup`. A `navGroup` also puts related types under one sidebar item (registry bucket `navGroups`, core group `pages`). @.claude/skills/admin-shell/SKILL.md
|
|
104
|
+
- Admin shell: core owns the `/admin` parent route, the `/admin/login` page, the route guard, the admin banners, the navbar and the user menu. A backend layer registers an auth driver, account menu items, site info and link actions. The admin is for staff only: a signed-in user with no capability goes to `/admin/no-access`. In production, an app with no auth driver (the backend is missing or did not load) sends every admin path to `/admin/not-configured`; in dev the admin opens. The bar shows on `/admin/**` for staff, and on public pages for staff only. A route hides the bar with the page meta `plutoAdminBar: false`, and `isPlutoPreviewPath` marks the preview path. The bar height is the token `--pluto-admin-bar-height`, and `<html>` gets `data-pluto-admin-bar` while the bar shows. A hint cookie puts the bar frame in the server HTML of a public page for staff (the actions and the user menu load after the mount). Content types with `adminBar` get "New <type>" and "Edit <type>" buttons (`usePlutoEditLink`). With two or more types, one "New" menu lists them, grouped by `navGroup`. A `navGroup` also puts related types under one sidebar item (registry bucket `navGroups`, core group `pages`). @.claude/skills/admin-shell/SKILL.md
|
|
105
105
|
- Local layers helper: `plutoLayer(name)` from `@plutocms/core/layers` gives a Nuxt `extends` entry.
|
|
106
106
|
With `PLUTO_LOCAL_ROOT`, it uses the local checkouts of the Pluto layers. @.claude/skills/local-layers/SKILL.md
|
|
107
107
|
- Pluto logo: `PlutoLogo` draws the logo inline. The wordmark takes the text color, so the logo works
|
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
export const ADMIN_LOGIN_PATH = '/admin/login'
|
|
10
10
|
export const ADMIN_HOME_PATH = '/admin/home'
|
|
11
11
|
export const ADMIN_NO_ACCESS_PATH = '/admin/no-access'
|
|
12
|
+
export const ADMIN_NOT_CONFIGURED_PATH = '/admin/not-configured'
|
|
12
13
|
|
|
13
14
|
const FAKE_ORIGIN = 'http://pluto.invalid'
|
|
14
15
|
|
|
@@ -111,6 +112,40 @@ function firstString(value: unknown): unknown {
|
|
|
111
112
|
return Array.isArray(value) ? value[0] : value
|
|
112
113
|
}
|
|
113
114
|
|
|
115
|
+
export interface DriverInput {
|
|
116
|
+
path: string
|
|
117
|
+
/** An auth driver is registered. */
|
|
118
|
+
hasDriver: boolean
|
|
119
|
+
/** The app runs in dev (`import.meta.dev`). */
|
|
120
|
+
dev: boolean
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Decides what the route guard does about the auth driver, before the sign-in
|
|
125
|
+
* rules. It returns `undefined` when a driver is registered and the sign-in
|
|
126
|
+
* rules apply. It never touches a path outside `/admin`.
|
|
127
|
+
*
|
|
128
|
+
* - No driver in production: the backend is missing or did not load (both
|
|
129
|
+
* backends register a driver). Every admin path goes to the not-configured
|
|
130
|
+
* page, so the admin pages do not show to everyone.
|
|
131
|
+
* - No driver in dev: allow, so admin pages can be built with no backend.
|
|
132
|
+
* - A driver, on the not-configured page: go to the admin home.
|
|
133
|
+
*/
|
|
134
|
+
export function driverDecision(input: DriverInput): GuardDecision | undefined {
|
|
135
|
+
const { path, hasDriver, dev } = input
|
|
136
|
+
if (!isAdminPath(path)) {
|
|
137
|
+
return { action: 'allow' }
|
|
138
|
+
}
|
|
139
|
+
const onPage = trimSlash(path) === ADMIN_NOT_CONFIGURED_PATH
|
|
140
|
+
if (hasDriver) {
|
|
141
|
+
return onPage ? { action: 'redirect', to: ADMIN_HOME_PATH } : undefined
|
|
142
|
+
}
|
|
143
|
+
if (dev || onPage) {
|
|
144
|
+
return { action: 'allow' }
|
|
145
|
+
}
|
|
146
|
+
return { action: 'redirect', to: ADMIN_NOT_CONFIGURED_PATH }
|
|
147
|
+
}
|
|
148
|
+
|
|
114
149
|
/**
|
|
115
150
|
* Decides what the route guard does. It never touches a path outside `/admin`.
|
|
116
151
|
* A signed-out user on a private admin path goes to the login page, with the
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
|
+
driverDecision,
|
|
2
3
|
guardDecision,
|
|
3
|
-
isAdminPath,
|
|
4
4
|
matchPublicPath,
|
|
5
5
|
staffDecision,
|
|
6
6
|
} from '../composables/internal/admin-shell'
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* The route guard of the admin. It
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* admin path goes to the login page. A
|
|
13
|
-
* goes to the admin home. The driver's own `guard` runs first, so a backend
|
|
9
|
+
* The route guard of the admin. It never touches a path outside `/admin`.
|
|
10
|
+
* With no auth driver, `driverDecision` decides: in production every admin
|
|
11
|
+
* path goes to the not-configured page, in dev the admin opens. With a driver,
|
|
12
|
+
* a signed-out user on a private admin path goes to the login page. A
|
|
13
|
+
* signed-in user on a public auth path goes to the admin home. The driver's own `guard` runs first, so a backend
|
|
14
14
|
* can send the user somewhere else (supabase: the first setup). Then a signed-in
|
|
15
15
|
* user on a private path must be staff (at least one capability, or no
|
|
16
16
|
* permissions driver). Other users go to `/admin/no-access`. The public auth
|
|
@@ -19,8 +19,15 @@ import {
|
|
|
19
19
|
*/
|
|
20
20
|
export default defineNuxtRouteMiddleware(async (to) => {
|
|
21
21
|
const auth = usePlutoAuth()
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
const driver = driverDecision({
|
|
23
|
+
path: to.path,
|
|
24
|
+
hasDriver: auth.hasDriver.value,
|
|
25
|
+
dev: import.meta.dev,
|
|
26
|
+
})
|
|
27
|
+
if (driver) {
|
|
28
|
+
return driver.action === 'redirect'
|
|
29
|
+
? navigateTo(driver.to, { replace: true })
|
|
30
|
+
: undefined
|
|
24
31
|
}
|
|
25
32
|
|
|
26
33
|
try {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// The page for an admin with no auth driver in production: the backend is
|
|
3
|
+
// missing or did not load. The route guard sends every admin path here (see
|
|
4
|
+
// `driverDecision`), so the admin pages do not show to everyone. The frame is
|
|
5
|
+
// PlutoAdminAuthCard. See the admin-shell skill.
|
|
6
|
+
definePageMeta({ layout: 'auth' })
|
|
7
|
+
|
|
8
|
+
const { t } = usePlutoI18n()
|
|
9
|
+
|
|
10
|
+
useHead({ title: () => t('pluto.notConfigured.title') })
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<PlutoAdminAuthCard :title="t('pluto.notConfigured.title')">
|
|
15
|
+
<UAlert
|
|
16
|
+
:description="t('pluto.notConfigured.description')"
|
|
17
|
+
color="warning"
|
|
18
|
+
icon="lucide:plug-zap"
|
|
19
|
+
variant="subtle"
|
|
20
|
+
/>
|
|
21
|
+
</PlutoAdminAuthCard>
|
|
22
|
+
</template>
|
package/i18n/locales/en.json
CHANGED
|
@@ -597,6 +597,10 @@
|
|
|
597
597
|
"goToSite": "Go to the site",
|
|
598
598
|
"signOut": "Sign out"
|
|
599
599
|
},
|
|
600
|
+
"notConfigured": {
|
|
601
|
+
"title": "The admin is not set up",
|
|
602
|
+
"description": "This site has no sign-in for the admin. If you run this site, make sure that the server can load its content backend, and check the environment variables of the server."
|
|
603
|
+
},
|
|
600
604
|
"post": {
|
|
601
605
|
"previewTitle": "Post preview",
|
|
602
606
|
"previewBadge": "Preview",
|
package/i18n/locales/pt-BR.json
CHANGED
|
@@ -597,6 +597,10 @@
|
|
|
597
597
|
"goToSite": "Ir para o site",
|
|
598
598
|
"signOut": "Sair"
|
|
599
599
|
},
|
|
600
|
+
"notConfigured": {
|
|
601
|
+
"title": "O painel não está configurado",
|
|
602
|
+
"description": "Este site não tem login para o painel. Se você administra este site, confira se o servidor consegue carregar o backend de conteúdo e verifique as variáveis de ambiente do servidor."
|
|
603
|
+
},
|
|
600
604
|
"post": {
|
|
601
605
|
"previewTitle": "Prévia do post",
|
|
602
606
|
"previewBadge": "Prévia",
|
package/package.json
CHANGED