@giancarlosgza/blog-admin-layer
v0.4.6
Published
Nuxt layer for a Firebase blog admin: post editor and workflow, media, authors, tags, settings, analytics and AI tools
Readme
@giancarlosgza/blog-admin-layer
Nuxt 4 layer for the admin of a Firebase blog: post editor (Tiptap) with drafts, review workflow,
scheduling and publishing, media library, authors, tags, series, settings, admin users, Plausible
analytics and Gemini AI tools (titles, excerpts, tags, SEO, rewrites, content recommendations).
It runs as its own SSR app (e.g. blog-admin.example.com) next to the public blog, against the
same Firebase project. The Firestore schema is in docs/schema.md.
Use it
npm install @giancarlosgza/blog-admin-layer// nuxt.config.ts
export default defineNuxtConfig({
extends: ['@giancarlosgza/blog-admin-layer'],
css: ['@/assets/css/material-symbols.generated.css'],
site: { url: process.env.NUXT_SITE_URL, name: 'My Blog Admin' },
pwa: { manifest: { name: 'My Blog Admin', short_name: 'Admin' }, workbox: { cacheId: 'my-blog-admin' } }
})// package.json
"scripts": {
"prebuild": "blog-admin-subset-icons",
"predev": "blog-admin-subset-icons"
}Environment (see playground/admin/.env.example):
NUXT_PUBLIC_VUEFIRE_CONFIG_*: Firebase web config;GOOGLE_APPLICATION_CREDENTIALS: service account.NUXT_SITE_URL: the admin's own origin, read at runtime through the site config (NUXT_PUBLIC_SITE_URLalso works). It must match the deployed URL, because/api/ai/generaterejects other origins.NUXT_BLOG_URL: the public blog URL, including its base path when the blog is embedded (NUXT_PUBLIC_BLOG_URLalso works).- Optional:
NUXT_GEMINI_KEY(AI tools),NUXT_PLAUSIBLE_TOKEN+NUXT_PUBLIC_PLAUSIBLE_*(analytics).
Public files the site provides: favicon.ico, logo-wordmark.svg, icon-pwa.png, avatar.png and the
PWA icons (optionally generated with pwa: { pwaAssets: { config: true } } and a pwa-assets.config.ts).
Customize
Brand:
admin.*in the site'sapp/app.config.ts(names, default post color, login texts, legal links, AI prompt context and recommendation chips). Every key is documented in this package'sapp/app.config.ts.Login page: the layer's
/auth/loginis a neutral card (Google and email/password sign-in, the blog name andadmin.login.staffNotice). For your own design, addapp/pages/auth/login.vueto the site: it replaces the layer's page and keeps the sign-in logic throughuseAdminSignIn():<script setup lang="ts"> definePageMeta({ layout: 'auth' }) const { user, error, loading, redirectTo, signInWithGoogle, signInWithEmail, signOut } = useAdminSignIn() </script>It signs in with Firebase Auth and returns to
?redirect=; theauthmiddleware, the staff check (admins/{uid}.role) and the session cookie stay in the layer.Models and helpers: add
@giancarlosgza/blog-coreto the site's own dependencies when site code imports its types (e.g.WidgetDescriptor) or calls its auto-imported helpers; with pnpm a layer's dependencies are not visible to the site.Widgets: editors can insert the widgets the site registers by providing
blogWidgetRegistry({ descriptors, preview? }) from a plugin. Without one, "Insert widget" is hidden.Styles: the layer loads
@colorffy/cssand the admin styles first, so site tokens in the same cascade layers override the defaults:@layer base.tokens { :root { ... } }for the Colorffy defaults (--cffy-*). Start the site's first stylesheet with the full layer order (@layer reset, abstracts, base, components, layout, utilities, media-queries, overrides, pages;): in dev the site's styles can load before the layer's.
Firestore and Storage rules, indexes and the scheduled-publish Cloud Function are in
@giancarlosgza/blog-firebase; deploy them from the admin repo.
Inside the layer, files import each other through the #admin/... alias. Shared models and helpers come
from @giancarlosgza/blog-core; its helpers are auto-imported in app and server code.
