@velora-cms/server 0.11.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/LICENSE +202 -0
- package/README.md +65 -0
- package/dist/admin-bundle/assets/__virtual_mf___mfe_internal__velora_admin__mf_owner__1__loadShare__react__loadShare__.js_commonjs-proxy-Ng0fOpdU.js +1 -0
- package/dist/admin-bundle/assets/__virtual_mf___mfe_internal__velora_admin__mf_owner__1__loadShare__react_mf_2_dom__loadShare__.js_commonjs-proxy-oc1i_iBU.js +9 -0
- package/dist/admin-bundle/assets/_virtual_mf-localSharedImportMap___mfe_internal__velora_admin__mf_owner__1-DOKdWYyj.js +1 -0
- package/dist/admin-bundle/assets/_virtual_mf___mfe_internal__velora_admin__mf_owner__1__loadShare___mf_0_velora_mf_2_cms_mf_1_plugin_mf_2_sdk__loadShare__.js-C1-ZAxwO.js +2 -0
- package/dist/admin-bundle/assets/_virtual_mf___mfe_internal__velora_admin__mf_owner__1__loadShare__react__loadShare__.js-BuUBskgj.js +9 -0
- package/dist/admin-bundle/assets/_virtual_mf___mfe_internal__velora_admin__mf_owner__1__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-DMaku-HC.js +9 -0
- package/dist/admin-bundle/assets/_virtual_mf___mfe_internal__velora_admin__mf_owner__1__loadShare__react_mf_2_dom__loadShare__.js-DYfqn12F.js +1 -0
- package/dist/admin-bundle/assets/_virtual_mf___mfe_internal__velora_admin__mf_owner__1__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-BPoj3DC9.js +25 -0
- package/dist/admin-bundle/assets/content-trash.schema-DfXHkC-w.js +72 -0
- package/dist/admin-bundle/assets/hostInit--C4xK6nj.js +1 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-500-normal-Ael50iVv.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-500-normal-Bq9vWWag.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-ext-500-normal-BIfNGwUT.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-ext-500-normal-BqneJy0T.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-latin-ext-500-normal-CAhNIIs5.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-latin-ext-500-normal-CZ70TYgx.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-vietnamese-500-normal-C8zxqsMH.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-vietnamese-500-normal-DZ4AoWbu.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-arabic-400-normal-CyU-ddYS.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-arabic-400-normal-EA1o-u_Q.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-arabic-500-normal-C4MQITzh.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-arabic-500-normal-DPXcppbm.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-arabic-600-normal-0pRdybE_.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-arabic-600-normal-MJZ0liF5.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-400-normal-Bo5KPYvw.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-400-normal-CFZh1unN.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-500-normal-BletK1Hp.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-500-normal-Cd6jVIg7.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-600-normal-Dk5eISD_.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-600-normal-KrqB56Mw.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-ext-400-normal-BeVLg08x.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-ext-400-normal-BpLfq-63.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-ext-500-normal-Ck_W4G78.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-ext-500-normal-eJxPnL1P.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-ext-600-normal-DT29Q8HW.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-ext-600-normal-DjDAeNQD.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-400-normal-BTotfTJu.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-400-normal-DZqxrq2p.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-500-normal-ByOcLdNv.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-500-normal-CocWQlwt.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-600-normal-71GNu3SW.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-600-normal-BGq0mW3O.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-ext-400-normal-Dsrv2Tcn.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-ext-400-normal-g30qAdWV.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-ext-500-normal-Cs5J6C77.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-ext-500-normal-DB5PtV2g.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-ext-600-normal-Bz0x94Yp.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-ext-600-normal-DUMzJB7m.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-greek-400-normal-D9ESIMu3.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-greek-400-normal-_efipK4i.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-greek-500-normal-CuWXN6rf.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-greek-500-normal-JMMifIXV.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-greek-600-normal-D-CqTdkO.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-greek-600-normal-DzTrcv_p.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-latin-ext-400-normal-C5H60-Va.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-latin-ext-400-normal-RBey6euL.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-latin-ext-500-normal-D0aIdm-b.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-latin-ext-500-normal-DakdToA3.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-latin-ext-600-normal-DIrixKbi.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-latin-ext-600-normal-DOrvGEcy.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-vietnamese-400-normal-DG4YqDda.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-vietnamese-400-normal-fK1oJ5dG.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-vietnamese-500-normal-BEb3_waV.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-vietnamese-500-normal-e4dixQRQ.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-vietnamese-600-normal-DgdngZtN.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-vietnamese-600-normal-DpPYBSTl.woff2 +0 -0
- package/dist/admin-bundle/assets/index-BIAF0ShP.js +542 -0
- package/dist/admin-bundle/assets/index-DhMY-0sC.js +148 -0
- package/dist/admin-bundle/assets/index-DnZV_gsg.css +1 -0
- package/dist/admin-bundle/assets/index-Do71v6NZ.js +7 -0
- package/dist/admin-bundle/assets/literata-cyrillic-ext-opsz-italic-5yUt4G_9.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-cyrillic-ext-opsz-normal-C5tfwALZ.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-cyrillic-opsz-italic-Bj0ndbyp.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-cyrillic-opsz-normal-DgmypKXX.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-greek-ext-opsz-italic-DFpO5j-Z.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-greek-ext-opsz-normal-DGm64vGR.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-greek-opsz-italic-CHERNRFq.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-greek-opsz-normal-DFglUmAd.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-latin-ext-opsz-italic-4y8iAqqs.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-latin-ext-opsz-normal-STiTPs0F.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-latin-opsz-italic-CJ93D38p.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-latin-opsz-normal-BHZsWIlc.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-vietnamese-opsz-italic-BdcrZMcO.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-vietnamese-opsz-normal-Gqw5JQlq.woff2 +0 -0
- package/dist/admin-bundle/assets/mf-entry-bootstrap-0-aef015cd.js +41 -0
- package/dist/admin-bundle/assets/remoteEntry-CNyN68bc.js +14 -0
- package/dist/admin-bundle/assets/virtualExposes-BDCSo5OS.js +1 -0
- package/dist/admin-bundle/assets/vite-preload-helper-Cgledu0r.js +1 -0
- package/dist/admin-bundle/index.html +16 -0
- package/dist/app.d.ts +8 -0
- package/dist/app.js +252 -0
- package/dist/auth/api-content-author.d.ts +2 -0
- package/dist/auth/api-content-author.js +27 -0
- package/dist/auth/api-keys.d.ts +7 -0
- package/dist/auth/api-keys.js +85 -0
- package/dist/auth/jwt-payload.d.ts +16 -0
- package/dist/auth/jwt-payload.js +1 -0
- package/dist/auth/preview-tokens.d.ts +7 -0
- package/dist/auth/preview-tokens.js +76 -0
- package/dist/auth/rate-limit.d.ts +16 -0
- package/dist/auth/rate-limit.js +283 -0
- package/dist/auth/require-auth.d.ts +4 -0
- package/dist/auth/require-auth.js +28 -0
- package/dist/auth/route-rate-limits.d.ts +31 -0
- package/dist/auth/route-rate-limits.js +155 -0
- package/dist/auth/tokens.d.ts +15 -0
- package/dist/auth/tokens.js +38 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +6 -0
- package/dist/boot/core-migration-gate.d.ts +12 -0
- package/dist/boot/core-migration-gate.js +52 -0
- package/dist/boot/resolve-boot-mode.d.ts +6 -0
- package/dist/boot/resolve-boot-mode.js +206 -0
- package/dist/bundled-templates/com.velora.blog/manifest.json +39 -0
- package/dist/bundled-templates/com.velora.blog/package.tar.gz +0 -0
- package/dist/bundled-templates/com.velora.blog/sample-content.json +14 -0
- package/dist/config/admin-ui.d.ts +1 -0
- package/dist/config/admin-ui.js +10 -0
- package/dist/config/cors.d.ts +7 -0
- package/dist/config/cors.js +53 -0
- package/dist/config/env-file-path.d.ts +2 -0
- package/dist/config/env-file-path.js +22 -0
- package/dist/config/graphql-limits.d.ts +2 -0
- package/dist/config/graphql-limits.js +26 -0
- package/dist/config/headless.d.ts +1 -0
- package/dist/config/headless.js +8 -0
- package/dist/config/license-revalidation.d.ts +2 -0
- package/dist/config/license-revalidation.js +16 -0
- package/dist/config/load-env.d.ts +1 -0
- package/dist/config/load-env.js +53 -0
- package/dist/config/marketplace.d.ts +11 -0
- package/dist/config/marketplace.js +19 -0
- package/dist/config/preview.d.ts +3 -0
- package/dist/config/preview.js +39 -0
- package/dist/config/public-docs.d.ts +1 -0
- package/dist/config/public-docs.js +9 -0
- package/dist/config/site.d.ts +7 -0
- package/dist/config/site.js +12 -0
- package/dist/config/trust-proxy.d.ts +2 -0
- package/dist/config/trust-proxy.js +42 -0
- package/dist/data-types/validate-data-type-settings.d.ts +1 -0
- package/dist/data-types/validate-data-type-settings.js +33 -0
- package/dist/db/content-db.d.ts +16 -0
- package/dist/db/content-db.js +109 -0
- package/dist/db/core-migrations/index.d.ts +14 -0
- package/dist/db/core-migrations/index.js +184 -0
- package/dist/db/core-migrations/steps.d.ts +4 -0
- package/dist/db/core-migrations/steps.js +66 -0
- package/dist/db/load-prisma-client.d.ts +2 -0
- package/dist/db/load-prisma-client.js +44 -0
- package/dist/db/prisma.d.ts +3 -0
- package/dist/db/prisma.js +67 -0
- package/dist/db/resolve-client-dir.d.ts +2 -0
- package/dist/db/resolve-client-dir.js +83 -0
- package/dist/docs/filter-public-api-spec.d.ts +2 -0
- package/dist/docs/filter-public-api-spec.js +52 -0
- package/dist/docs-bundle/404.html +41 -0
- package/dist/docs-bundle/_astro/MobileTableOfContents.astro_astro_type_script_index_0_lang.BcSo_yiZ.js +1 -0
- package/dist/docs-bundle/_astro/Search.astro_astro_type_script_index_0_lang.CavldbAs.js +1 -0
- package/dist/docs-bundle/_astro/TableOfContents.astro_astro_type_script_index_0_lang.Csloo1VZ.js +1 -0
- package/dist/docs-bundle/_astro/common.DTXA7sXM.css +3 -0
- package/dist/docs-bundle/_astro/ec.0vx5m.js +3 -0
- package/dist/docs-bundle/_astro/ec.w36nc.css +1 -0
- package/dist/docs-bundle/_astro/page.Dwipeu-R.js +1 -0
- package/dist/docs-bundle/_astro/print.ehPL0gv-.css +1 -0
- package/dist/docs-bundle/_astro/starlight-toc.CTCQXEge.js +1 -0
- package/dist/docs-bundle/_astro/ui-core.D-7Scnhs.js +2 -0
- package/dist/docs-bundle/favicon.svg +1 -0
- package/dist/docs-bundle/index.html +1 -0
- package/dist/docs-bundle/pagefind/fragment/en_18a36d7.pf_fragment +0 -0
- package/dist/docs-bundle/pagefind/fragment/en_1d9eb24.pf_fragment +0 -0
- package/dist/docs-bundle/pagefind/fragment/en_1ef320c.pf_fragment +0 -0
- package/dist/docs-bundle/pagefind/fragment/en_3099406.pf_fragment +0 -0
- package/dist/docs-bundle/pagefind/fragment/en_4a5b6fe.pf_fragment +0 -0
- package/dist/docs-bundle/pagefind/fragment/en_9aa652e.pf_fragment +0 -0
- package/dist/docs-bundle/pagefind/fragment/en_baccdf6.pf_fragment +0 -0
- package/dist/docs-bundle/pagefind/fragment/en_d81f3fe.pf_fragment +0 -0
- package/dist/docs-bundle/pagefind/fragment/en_d855550.pf_fragment +0 -0
- package/dist/docs-bundle/pagefind/fragment/en_e6d88d8.pf_fragment +0 -0
- package/dist/docs-bundle/pagefind/fragment/en_e9d3189.pf_fragment +0 -0
- package/dist/docs-bundle/pagefind/index/en_5feeed7.pf_index +0 -0
- package/dist/docs-bundle/pagefind/index/en_c6981f9.pf_index +0 -0
- package/dist/docs-bundle/pagefind/index/en_ce4f6bf.pf_index +0 -0
- package/dist/docs-bundle/pagefind/pagefind-component-ui.css +1509 -0
- package/dist/docs-bundle/pagefind/pagefind-component-ui.js +56 -0
- package/dist/docs-bundle/pagefind/pagefind-entry.json +1 -0
- package/dist/docs-bundle/pagefind/pagefind-highlight.js +1070 -0
- package/dist/docs-bundle/pagefind/pagefind-modular-ui.css +214 -0
- package/dist/docs-bundle/pagefind/pagefind-modular-ui.js +8 -0
- package/dist/docs-bundle/pagefind/pagefind-ui.css +1 -0
- package/dist/docs-bundle/pagefind/pagefind-ui.js +2 -0
- package/dist/docs-bundle/pagefind/pagefind-worker.js +6 -0
- package/dist/docs-bundle/pagefind/pagefind.en_1e94abd852.pf_meta +0 -0
- package/dist/docs-bundle/pagefind/pagefind.js +6 -0
- package/dist/docs-bundle/pagefind/wasm.en.pagefind +0 -0
- package/dist/docs-bundle/pagefind/wasm.unknown.pagefind +0 -0
- package/dist/docs-bundle/v1/index.html +86 -0
- package/dist/docs-bundle/v1/using-velora/build-a-headless-frontend/index.html +413 -0
- package/dist/docs-bundle/v1/using-velora/content-model/index.html +858 -0
- package/dist/docs-bundle/v1/using-velora/editing/index.html +748 -0
- package/dist/docs-bundle/v1/using-velora/getting-started/index.html +588 -0
- package/dist/docs-bundle/v1/using-velora/headless-api/index.html +1620 -0
- package/dist/docs-bundle/v1/using-velora/index.html +215 -0
- package/dist/docs-bundle/v1/using-velora/installation/index.html +886 -0
- package/dist/docs-bundle/v1/using-velora/installing-plugins/index.html +836 -0
- package/dist/docs-bundle/v1/using-velora/media/index.html +468 -0
- package/dist/docs-bundle/v1/using-velora/users-and-roles/index.html +561 -0
- package/dist/graphql/field-count-rule.d.ts +5 -0
- package/dist/graphql/field-count-rule.js +59 -0
- package/dist/graphql/schema.d.ts +12 -0
- package/dist/graphql/schema.js +330 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +180 -0
- package/dist/lib.d.ts +2 -0
- package/dist/lib.js +39 -0
- package/dist/marketplace/install-bundle-members.d.ts +33 -0
- package/dist/marketplace/install-bundle-members.js +137 -0
- package/dist/marketplace/install-from-marketplace.d.ts +60 -0
- package/dist/marketplace/install-from-marketplace.js +581 -0
- package/dist/marketplace/license-revalidation-job.d.ts +38 -0
- package/dist/marketplace/license-revalidation-job.js +139 -0
- package/dist/marketplace/marketplace-client.d.ts +43 -0
- package/dist/marketplace/marketplace-client.js +228 -0
- package/dist/marketplace/revalidate-license.d.ts +39 -0
- package/dist/marketplace/revalidate-license.js +103 -0
- package/dist/media/media-service.d.ts +36 -0
- package/dist/media/media-service.js +162 -0
- package/dist/plugins/boot-plugin-loader.d.ts +32 -0
- package/dist/plugins/boot-plugin-loader.js +57 -0
- package/dist/plugins/hook-dispatcher.d.ts +13 -0
- package/dist/plugins/hook-dispatcher.js +68 -0
- package/dist/plugins/install-built-package.d.ts +36 -0
- package/dist/plugins/install-built-package.js +199 -0
- package/dist/plugins/install-package-tarball.d.ts +25 -0
- package/dist/plugins/install-package-tarball.js +73 -0
- package/dist/plugins/plugin-context.d.ts +5 -0
- package/dist/plugins/plugin-context.js +26 -0
- package/dist/plugins/plugin-package-store.d.ts +31 -0
- package/dist/plugins/plugin-package-store.js +64 -0
- package/dist/plugins/plugin-registry-service.d.ts +227 -0
- package/dist/plugins/plugin-registry-service.js +636 -0
- package/dist/plugins/seed-template-content.d.ts +23 -0
- package/dist/plugins/seed-template-content.js +198 -0
- package/dist/plugins/server-serializers.d.ts +3 -0
- package/dist/plugins/server-serializers.js +16 -0
- package/dist/plugins/simple-storage-api.d.ts +9 -0
- package/dist/plugins/simple-storage-api.js +26 -0
- package/dist/plugins/snapshot-groups.d.ts +16 -0
- package/dist/plugins/snapshot-groups.js +81 -0
- package/dist/plugins/structured-query-builder.d.ts +21 -0
- package/dist/plugins/structured-query-builder.js +60 -0
- package/dist/plugins/structured-snapshot-service.d.ts +16 -0
- package/dist/plugins/structured-snapshot-service.js +286 -0
- package/dist/plugins/structured-storage-api.d.ts +22 -0
- package/dist/plugins/structured-storage-api.js +91 -0
- package/dist/plugins/structured-storage-transaction.d.ts +22 -0
- package/dist/plugins/structured-storage-transaction.js +63 -0
- package/dist/plugins/structured-table-naming.d.ts +1 -0
- package/dist/plugins/structured-table-naming.js +18 -0
- package/dist/plugins/trusted-keys.d.ts +11 -0
- package/dist/plugins/trusted-keys.js +67 -0
- package/dist/plugins/trusted-signing-keys.d.ts +27 -0
- package/dist/plugins/trusted-signing-keys.js +40 -0
- package/dist/plugins/uninstall-bundle.d.ts +29 -0
- package/dist/plugins/uninstall-bundle.js +150 -0
- package/dist/plugins/verify-package.d.ts +36 -0
- package/dist/plugins/verify-package.js +121 -0
- package/dist/routes/admin-static.d.ts +8 -0
- package/dist/routes/admin-static.js +196 -0
- package/dist/routes/api-keys.d.ts +2 -0
- package/dist/routes/api-keys.js +95 -0
- package/dist/routes/api-playground.d.ts +2 -0
- package/dist/routes/api-playground.js +21 -0
- package/dist/routes/auth.d.ts +2 -0
- package/dist/routes/auth.js +102 -0
- package/dist/routes/content.d.ts +3 -0
- package/dist/routes/content.js +471 -0
- package/dist/routes/data-types.d.ts +3 -0
- package/dist/routes/data-types.js +100 -0
- package/dist/routes/docs-static.d.ts +8 -0
- package/dist/routes/docs-static.js +137 -0
- package/dist/routes/document-types.d.ts +3 -0
- package/dist/routes/document-types.js +149 -0
- package/dist/routes/headless-bridge.d.ts +10 -0
- package/dist/routes/headless-bridge.js +207 -0
- package/dist/routes/health.d.ts +2 -0
- package/dist/routes/health.js +15 -0
- package/dist/routes/marketplace-proxy.d.ts +8 -0
- package/dist/routes/marketplace-proxy.js +135 -0
- package/dist/routes/media.d.ts +6 -0
- package/dist/routes/media.js +93 -0
- package/dist/routes/plugin-api-routes.d.ts +12 -0
- package/dist/routes/plugin-api-routes.js +224 -0
- package/dist/routes/plugin-assets.d.ts +8 -0
- package/dist/routes/plugin-assets.js +51 -0
- package/dist/routes/plugin-management.d.ts +7 -0
- package/dist/routes/plugin-management.js +525 -0
- package/dist/routes/plugin-storage.d.ts +8 -0
- package/dist/routes/plugin-storage.js +104 -0
- package/dist/routes/plugins.d.ts +2 -0
- package/dist/routes/plugins.js +36 -0
- package/dist/routes/public-api.d.ts +9 -0
- package/dist/routes/public-api.js +545 -0
- package/dist/routes/published-item.d.ts +57 -0
- package/dist/routes/published-item.js +236 -0
- package/dist/routes/setup.d.ts +9 -0
- package/dist/routes/setup.js +477 -0
- package/dist/routes/snapshots.d.ts +3 -0
- package/dist/routes/snapshots.js +65 -0
- package/dist/routes/templates.d.ts +3 -0
- package/dist/routes/templates.js +37 -0
- package/dist/routes/themes.d.ts +2 -0
- package/dist/routes/themes.js +30 -0
- package/dist/routes/users.d.ts +2 -0
- package/dist/routes/users.js +113 -0
- package/dist/sandbox/register-local-plugin.d.ts +36 -0
- package/dist/sandbox/register-local-plugin.js +108 -0
- package/dist/sandbox/seed-sandbox.d.ts +20 -0
- package/dist/sandbox/seed-sandbox.js +122 -0
- package/dist/scripts/copy-admin-bundle.d.ts +1 -0
- package/dist/scripts/copy-admin-bundle.js +24 -0
- package/dist/scripts/copy-docs-bundle.d.ts +1 -0
- package/dist/scripts/copy-docs-bundle.js +22 -0
- package/dist/scripts/create-api-key.d.ts +5 -0
- package/dist/scripts/create-api-key.js +56 -0
- package/dist/scripts/generate-prisma-clients.d.ts +6 -0
- package/dist/scripts/generate-prisma-clients.js +125 -0
- package/dist/scripts/pack-bundled-templates.d.ts +1 -0
- package/dist/scripts/pack-bundled-templates.js +87 -0
- package/dist/setup/build-connection-string.d.ts +19 -0
- package/dist/setup/build-connection-string.js +14 -0
- package/dist/setup/bundled-templates.d.ts +19 -0
- package/dist/setup/bundled-templates.js +121 -0
- package/dist/setup/ensure-prisma-client.d.ts +3 -0
- package/dist/setup/ensure-prisma-client.js +23 -0
- package/dist/setup/install-starter.d.ts +14 -0
- package/dist/setup/install-starter.js +76 -0
- package/dist/setup/normalize-database-url.d.ts +2 -0
- package/dist/setup/normalize-database-url.js +35 -0
- package/dist/setup/persist-env-config.d.ts +2 -0
- package/dist/setup/persist-env-config.js +63 -0
- package/dist/setup/provision-database.d.ts +2 -0
- package/dist/setup/provision-database.js +89 -0
- package/dist/setup-app.d.ts +4 -0
- package/dist/setup-app.js +43 -0
- package/package.json +96 -0
- package/prisma/schema.prisma +303 -0
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="en" dir="ltr" data-theme="dark" data-has-toc data-has-sidebar class="astro-ewhz5xxr"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><title>Getting started | Velora</title><link rel="shortcut icon" href="/docs/favicon.svg" type="image/svg+xml"/><meta name="generator" content="Astro v7.2.10"/><meta name="generator" content="Starlight v0.41.5"/><meta property="og:title" content="Getting started"/><meta property="og:type" content="article"/><meta property="og:locale" content="en"/><meta property="og:description" content="Zero to a published post — install a fresh Velora instance, run the setup wizard, install the Blog starter, and publish your first post."/><meta property="og:site_name" content="Velora"/><meta name="twitter:card" content="summary_large_image"/><meta name="description" content="Zero to a published post — install a fresh Velora instance, run the setup wizard, install the Blog starter, and publish your first post."/><script>
|
|
2
|
+
window.StarlightThemeProvider = (() => {
|
|
3
|
+
const storedTheme =
|
|
4
|
+
typeof localStorage !== 'undefined' && localStorage.getItem('starlight-theme');
|
|
5
|
+
const theme =
|
|
6
|
+
storedTheme ||
|
|
7
|
+
(window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark');
|
|
8
|
+
document.documentElement.dataset.theme = theme === 'light' ? 'light' : 'dark';
|
|
9
|
+
return {
|
|
10
|
+
updatePickers(theme = storedTheme || 'auto') {
|
|
11
|
+
document.querySelectorAll('starlight-theme-select').forEach((picker) => {
|
|
12
|
+
const select = picker.querySelector('select');
|
|
13
|
+
if (select) select.value = theme;
|
|
14
|
+
/** @type {HTMLTemplateElement | null} */
|
|
15
|
+
const tmpl = document.querySelector(`#theme-icons`);
|
|
16
|
+
const newIcon = tmpl && tmpl.content.querySelector('.' + theme);
|
|
17
|
+
if (newIcon) {
|
|
18
|
+
const oldIcon = picker.querySelector('svg.label-icon');
|
|
19
|
+
if (oldIcon) {
|
|
20
|
+
oldIcon.replaceChildren(...newIcon.cloneNode(true).childNodes);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
})();
|
|
27
|
+
</script><template id="theme-icons"><svg aria-hidden="true" class="light astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M5 12a1 1 0 0 0-1-1H3a1 1 0 0 0 0 2h1a1 1 0 0 0 1-1Zm.64 5-.71.71a1 1 0 0 0 0 1.41 1 1 0 0 0 1.41 0l.71-.71A1 1 0 0 0 5.64 17ZM12 5a1 1 0 0 0 1-1V3a1 1 0 0 0-2 0v1a1 1 0 0 0 1 1Zm5.66 2.34a1 1 0 0 0 .7-.29l.71-.71a1 1 0 1 0-1.41-1.41l-.66.71a1 1 0 0 0 0 1.41 1 1 0 0 0 .66.29Zm-12-.29a1 1 0 0 0 1.41 0 1 1 0 0 0 0-1.41l-.71-.71a1.004 1.004 0 1 0-1.43 1.41l.73.71ZM21 11h-1a1 1 0 0 0 0 2h1a1 1 0 0 0 0-2Zm-2.64 6A1 1 0 0 0 17 18.36l.71.71a1 1 0 0 0 1.41 0 1 1 0 0 0 0-1.41l-.76-.66ZM12 6.5a5.5 5.5 0 1 0 5.5 5.5A5.51 5.51 0 0 0 12 6.5Zm0 9a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7Zm0 3.5a1 1 0 0 0-1 1v1a1 1 0 0 0 2 0v-1a1 1 0 0 0-1-1Z"/></svg><svg aria-hidden="true" class="dark astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M21.64 13a1 1 0 0 0-1.05-.14 8.049 8.049 0 0 1-3.37.73 8.15 8.15 0 0 1-8.14-8.1 8.59 8.59 0 0 1 .25-2A1 1 0 0 0 8 2.36a10.14 10.14 0 1 0 14 11.69 1 1 0 0 0-.36-1.05Zm-9.5 6.69A8.14 8.14 0 0 1 7.08 5.22v.27a10.15 10.15 0 0 0 10.14 10.14 9.784 9.784 0 0 0 2.1-.22 8.11 8.11 0 0 1-7.18 4.32v-.04Z"/></svg><svg aria-hidden="true" class="auto astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M21 14h-1V7a3 3 0 0 0-3-3H7a3 3 0 0 0-3 3v7H3a1 1 0 0 0-1 1v2a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3v-2a1 1 0 0 0-1-1ZM6 7a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v7H6V7Zm14 10a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-1h16v1Z"/></svg></template><link rel="stylesheet" href="/docs/_astro/print.ehPL0gv-.css" media="print"><link rel="stylesheet" href="/docs/_astro/common.DTXA7sXM.css"><script type="module" src="/docs/_astro/page.Dwipeu-R.js"></script></head><body class="astro-ewhz5xxr"><a class="sl-skip-link astro-zufphfvp" href="#_top">Skip to content</a><div class="page sl-flex astro-5auw3bkx"><header class="header astro-5auw3bkx"><div class="header astro-6ytusalw"><div class="title-wrapper sl-flex astro-6ytusalw"><a href="/docs/" class="site-title sl-flex astro-nu4hftpf"><span class="astro-nu4hftpf" translate="no">Velora</span></a></div><div class="sl-flex print:hidden astro-6ytusalw"><site-search class="astro-6ytusalw astro-zwj6qr56" data-translations="{"placeholder":"Search"}"><button data-open-modal disabled aria-label="Search" aria-keyshortcuts="Control+K" class="astro-zwj6qr56"><svg aria-hidden="true" class="astro-zwj6qr56 astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M21.71 20.29 18 16.61A9 9 0 1 0 16.61 18l3.68 3.68a.999.999 0 0 0 1.42 0 1 1 0 0 0 0-1.39ZM11 18a7 7 0 1 1 0-14 7 7 0 0 1 0 14Z"/></svg><span class="sl-hidden md:sl-block astro-zwj6qr56" aria-hidden="true">Search</span><kbd class="sl-hidden md:sl-flex astro-zwj6qr56" style="display: none;"><kbd class="astro-zwj6qr56">Ctrl</kbd><kbd class="astro-zwj6qr56">K</kbd></kbd></button><dialog style="padding:0" aria-label="Search" class="astro-zwj6qr56"><div class="dialog-frame sl-flex astro-zwj6qr56"><button data-close-modal class="sl-flex md:sl-hidden astro-zwj6qr56">Cancel</button><div class="search-container astro-zwj6qr56"><div id="starlight__search" class="astro-zwj6qr56"></div></div></div></dialog></site-search><script>
|
|
28
|
+
(() => {
|
|
29
|
+
const openBtn = document.querySelector('button[data-open-modal]');
|
|
30
|
+
const shortcut = openBtn?.querySelector('kbd');
|
|
31
|
+
if (!openBtn || !(shortcut instanceof HTMLElement)) return;
|
|
32
|
+
const platformKey = shortcut.querySelector('kbd');
|
|
33
|
+
if (platformKey && /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)) {
|
|
34
|
+
platformKey.textContent = '⌘';
|
|
35
|
+
openBtn.setAttribute('aria-keyshortcuts', 'Meta+K');
|
|
36
|
+
}
|
|
37
|
+
shortcut.style.display = '';
|
|
38
|
+
})();
|
|
39
|
+
</script><script type="module" src="/docs/_astro/Search.astro_astro_type_script_index_0_lang.CavldbAs.js"></script></div><div class="sl-hidden md:sl-flex print:hidden right-group astro-6ytusalw"><div class="sl-flex social-icons astro-6ytusalw"></div><starlight-theme-select><label style="--sl-select-width: 6.25em" class="astro-7uz3f5wr"><span class="sr-only astro-7uz3f5wr">Select theme</span><svg aria-hidden="true" class="icon label-icon astro-7uz3f5wr astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M21 14h-1V7a3 3 0 0 0-3-3H7a3 3 0 0 0-3 3v7H3a1 1 0 0 0-1 1v2a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3v-2a1 1 0 0 0-1-1ZM6 7a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v7H6V7Zm14 10a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-1h16v1Z"/></svg><select autocomplete="off" class="astro-7uz3f5wr"><option value="dark" class="astro-7uz3f5wr">Dark</option><option value="light" class="astro-7uz3f5wr">Light</option><option value="auto" selected class="astro-7uz3f5wr">Auto</option></select><svg aria-hidden="true" class="icon caret astro-7uz3f5wr astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M17 9.17a1 1 0 0 0-1.41 0L12 12.71 8.46 9.17a1 1 0 1 0-1.41 1.42l4.24 4.24a1.002 1.002 0 0 0 1.42 0L17 10.59a1.002 1.002 0 0 0 0-1.42Z"/></svg></label></starlight-theme-select><script>
|
|
40
|
+
StarlightThemeProvider.updatePickers();
|
|
41
|
+
</script><script type="module">var e=`starlight-theme`,t=e=>e===`auto`||e===`dark`||e===`light`?e:`auto`,n=()=>t(typeof localStorage<`u`&&localStorage.getItem(e));function r(t){typeof localStorage<`u`&&localStorage.setItem(e,t===`light`||t===`dark`?t:``)}var i=()=>matchMedia(`(prefers-color-scheme: light)`).matches?`light`:`dark`;function a(e){StarlightThemeProvider.updatePickers(e),document.documentElement.dataset.theme=e===`auto`?i():e,r(e)}matchMedia(`(prefers-color-scheme: light)`).addEventListener(`change`,()=>{n()===`auto`&&a(`auto`)});var o=class extends HTMLElement{constructor(){super(),a(n()),this.querySelector(`select`)?.addEventListener(`change`,e=>{e.currentTarget instanceof HTMLSelectElement&&a(t(e.currentTarget.value))})}};customElements.define(`starlight-theme-select`,o);</script><script type="module">var e=class extends HTMLElement{constructor(){super();let e=this.querySelector(`select`);e&&(e.addEventListener(`change`,e=>{e.currentTarget instanceof HTMLSelectElement&&(window.location.pathname=e.currentTarget.value)}),window.addEventListener(`pageshow`,t=>{if(!t.persisted)return;let n=e.querySelector(`option[selected]`)?.index;n!==e.selectedIndex&&(e.selectedIndex=n??0)}))}};customElements.define(`starlight-lang-select`,e);</script></div></div></header><nav class="sidebar print:hidden astro-5auw3bkx" aria-label="Main"><starlight-menu-button class="print:hidden astro-gnkm6dw5"><button aria-expanded="false" aria-label="Menu" aria-controls="starlight__sidebar" class="sl-flex md:sl-hidden astro-gnkm6dw5"><svg aria-hidden="true" class="open-menu astro-gnkm6dw5 astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M3 8h18a1 1 0 1 0 0-2H3a1 1 0 0 0 0 2Zm18 8H3a1 1 0 0 0 0 2h18a1 1 0 0 0 0-2Zm0-5H3a1 1 0 0 0 0 2h18a1 1 0 0 0 0-2Z"/></svg><svg aria-hidden="true" class="close-menu astro-gnkm6dw5 astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="m13.41 12 6.3-6.29a1.004 1.004 0 1 0-1.42-1.42L12 10.59l-6.29-6.3a1.004 1.004 0 0 0-1.42 1.42l6.3 6.29-6.3 6.29a1 1 0 0 0 0 1.42.998.998 0 0 0 1.42 0l6.29-6.3 6.29 6.3a.999.999 0 0 0 1.42 0 1 1 0 0 0 0-1.42L13.41 12Z"/></svg></button></starlight-menu-button><script type="module">var e=class extends HTMLElement{btn=this.querySelector(`button`);constructor(){super(),this.btn.addEventListener(`click`,()=>this.toggleExpanded());let e=this.closest(`nav`);e&&e.addEventListener(`keyup`,e=>this.closeOnEscape(e)),matchMedia(`(min-width: 50em)`).addEventListener(`change`,()=>this.setExpanded(!1))}setExpanded(e){this.setAttribute(`aria-expanded`,String(e)),document.body.toggleAttribute(`data-mobile-menu-expanded`,e),document.querySelectorAll(`.main-frame, .sl-skip-link`).forEach(t=>{t.toggleAttribute(`inert`,e)})}toggleExpanded(){this.setExpanded(this.getAttribute(`aria-expanded`)!==`true`)}closeOnEscape(e){e.code===`Escape`&&(this.setExpanded(!1),this.btn.focus())}};customElements.define(`starlight-menu-button`,e);</script><div id="starlight__sidebar" class="sidebar-pane astro-5auw3bkx"><div class="sidebar-content sl-flex astro-5auw3bkx"><sl-sidebar-state-persist data-hash="03jp7ll" class="astro-pcs3l5kp"><script aria-hidden="true">
|
|
42
|
+
(() => {
|
|
43
|
+
try {
|
|
44
|
+
if (!matchMedia('(min-width: 50em)').matches) return;
|
|
45
|
+
/** @type {HTMLElement | null} */
|
|
46
|
+
const target = document.querySelector('sl-sidebar-state-persist');
|
|
47
|
+
const state = JSON.parse(sessionStorage.getItem('sl-sidebar-state') || '0');
|
|
48
|
+
if (!target || !state || target.dataset.hash !== state.hash) return;
|
|
49
|
+
window._starlightScrollRestore = state.scroll;
|
|
50
|
+
customElements.define(
|
|
51
|
+
'sl-sidebar-restore',
|
|
52
|
+
class SidebarRestore extends HTMLElement {
|
|
53
|
+
connectedCallback() {
|
|
54
|
+
try {
|
|
55
|
+
const idx = parseInt(this.dataset.index || '');
|
|
56
|
+
const details = this.closest('details');
|
|
57
|
+
if (details && typeof state.open[idx] === 'boolean') details.open = state.open[idx];
|
|
58
|
+
} catch {}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
} catch {}
|
|
63
|
+
})();
|
|
64
|
+
</script><ul class="top-level astro-ck7l566j"><li class="astro-ck7l566j"><details open class="astro-ck7l566j"><summary class="astro-ck7l566j"><span class="group-label astro-ck7l566j"><span class="large astro-ck7l566j">Using Velora</span></span><svg aria-hidden="true" class="caret astro-ck7l566j astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.25rem;"><path d="m14.83 11.29-4.24-4.24a1 1 0 1 0-1.42 1.41L12.71 12l-3.54 3.54a1 1 0 0 0 0 1.41 1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.24-4.24a1.002 1.002 0 0 0 0-1.42Z"/></svg></summary><sl-sidebar-restore data-index="0"></sl-sidebar-restore><ul class="astro-ck7l566j"><li class="astro-ck7l566j"><a href="/docs/v1/using-velora/" class="astro-ck7l566j"><span class="astro-ck7l566j">Using Velora</span></a></li><li class="astro-ck7l566j"><a href="/docs/v1/using-velora/getting-started/" aria-current="page" class="astro-ck7l566j"><span class="astro-ck7l566j">Getting started</span></a></li><li class="astro-ck7l566j"><a href="/docs/v1/using-velora/installation/" class="astro-ck7l566j"><span class="astro-ck7l566j">Installation</span></a></li><li class="astro-ck7l566j"><a href="/docs/v1/using-velora/content-model/" class="astro-ck7l566j"><span class="astro-ck7l566j">Document types</span></a></li><li class="astro-ck7l566j"><a href="/docs/v1/using-velora/editing/" class="astro-ck7l566j"><span class="astro-ck7l566j">Content editing</span></a></li><li class="astro-ck7l566j"><a href="/docs/v1/using-velora/media/" class="astro-ck7l566j"><span class="astro-ck7l566j">Media</span></a></li><li class="astro-ck7l566j"><a href="/docs/v1/using-velora/users-and-roles/" class="astro-ck7l566j"><span class="astro-ck7l566j">Users and roles</span></a></li><li class="astro-ck7l566j"><a href="/docs/v1/using-velora/installing-plugins/" class="astro-ck7l566j"><span class="astro-ck7l566j">Installing plugins</span></a></li><li class="astro-ck7l566j"><a href="/docs/v1/using-velora/headless-api/" class="astro-ck7l566j"><span class="astro-ck7l566j">Headless API</span></a></li><li class="astro-ck7l566j"><a href="/docs/v1/using-velora/build-a-headless-frontend/" class="astro-ck7l566j"><span class="astro-ck7l566j">Build a headless frontend</span></a></li></ul></details></li></ul><script aria-hidden="true">
|
|
65
|
+
(() => {
|
|
66
|
+
const scroller = document.getElementById('starlight__sidebar');
|
|
67
|
+
if (!window._starlightScrollRestore || !scroller) return;
|
|
68
|
+
scroller.scrollTop = window._starlightScrollRestore;
|
|
69
|
+
delete window._starlightScrollRestore;
|
|
70
|
+
})();
|
|
71
|
+
</script></sl-sidebar-state-persist><div class="md:sl-hidden"><div class="mobile-preferences sl-flex astro-yfet6llj"><div class="social-icons astro-yfet6llj"></div><starlight-theme-select><label style="--sl-select-width: 6.25em" class="astro-7uz3f5wr"><span class="sr-only astro-7uz3f5wr">Select theme</span><svg aria-hidden="true" class="icon label-icon astro-7uz3f5wr astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M21 14h-1V7a3 3 0 0 0-3-3H7a3 3 0 0 0-3 3v7H3a1 1 0 0 0-1 1v2a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3v-2a1 1 0 0 0-1-1ZM6 7a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v7H6V7Zm14 10a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-1h16v1Z"/></svg><select autocomplete="off" class="astro-7uz3f5wr"><option value="dark" class="astro-7uz3f5wr">Dark</option><option value="light" class="astro-7uz3f5wr">Light</option><option value="auto" selected class="astro-7uz3f5wr">Auto</option></select><svg aria-hidden="true" class="icon caret astro-7uz3f5wr astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M17 9.17a1 1 0 0 0-1.41 0L12 12.71 8.46 9.17a1 1 0 1 0-1.41 1.42l4.24 4.24a1.002 1.002 0 0 0 1.42 0L17 10.59a1.002 1.002 0 0 0 0-1.42Z"/></svg></label></starlight-theme-select><script>
|
|
72
|
+
StarlightThemeProvider.updatePickers();
|
|
73
|
+
</script></div></div></div></div></nav><div class="main-frame astro-5auw3bkx"><script type="module">var e=document.getElementById(`starlight__sidebar`),t=e?.querySelector(`sl-sidebar-state-persist`),n=`sl-sidebar-state`,r=()=>{let r=[],i=t?.dataset.hash||``;try{let e=sessionStorage.getItem(n),t=JSON.parse(e||`{}`);Array.isArray(t.open)&&t.hash===i&&(r=t.open)}catch{}return{hash:i,open:r,scroll:e?.scrollTop||0}},i=e=>{try{sessionStorage.setItem(n,JSON.stringify(e))}catch{}},a=()=>i(r()),o=(e,t)=>{let n=r();n.open[t]=e,i(n)};t?.addEventListener(`click`,e=>{if(!(e.target instanceof Element))return;let t=e.target.closest(`summary`)?.closest(`details`);if(!t)return;let n=t.querySelector(`sl-sidebar-restore`),r=parseInt(n?.dataset.index||``);isNaN(r)||o(!t.open,r)}),addEventListener(`visibilitychange`,()=>{document.visibilityState===`hidden`&&a()});</script><div class="lg:sl-flex astro-perbdhd2"><aside class="right-sidebar-container print:hidden astro-perbdhd2"><div class="right-sidebar astro-perbdhd2"><div class="lg:sl-hidden astro-epm5tkrc"><mobile-starlight-toc data-min-h="2" data-max-h="3" class="astro-bnvebtr4"><nav aria-labelledby="starlight__on-this-page--mobile" class="astro-bnvebtr4"><details id="starlight__mobile-toc" class="astro-bnvebtr4"><summary id="starlight__on-this-page--mobile" class="sl-flex astro-bnvebtr4"><span class="toggle sl-flex astro-bnvebtr4">On this page<svg aria-hidden="true" class="caret astro-bnvebtr4 astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1rem;"><path d="m14.83 11.29-4.24-4.24a1 1 0 1 0-1.42 1.41L12.71 12l-3.54 3.54a1 1 0 0 0 0 1.41 1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.24-4.24a1.002 1.002 0 0 0 0-1.42Z"/></svg></span><span class="display-current astro-bnvebtr4"></span></summary><div class="dropdown astro-bnvebtr4"><ul class="isMobile astro-lptfoybu" style="--depth: 0;"><li style="--depth: 0;" class="astro-lptfoybu"><a href="#_top" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">Overview</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#environment-variables-this-tutorial-sets" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">Environment variables this tutorial sets</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#1-deploy-a-fresh-instance" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">1. Deploy a fresh instance</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#2-run-the-admin-ui" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">2. Run the admin UI</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#3-walk-the-setup-wizard" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">3. Walk the setup wizard</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#4-create-and-publish-a-post" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">4. Create and publish a post</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#5-get-an-api-key-and-run-the-blog-starter-frontend" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">5. Get an API key and run the Blog starter frontend</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#6-read-the-post-back-over-the-api" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">6. Read the post back over the API</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#where-next" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">Where next</span></a></li></ul></div></details></nav></mobile-starlight-toc><script type="module" src="/docs/_astro/MobileTableOfContents.astro_astro_type_script_index_0_lang.BcSo_yiZ.js"></script></div><div class="right-sidebar-panel sl-hidden lg:sl-block astro-epm5tkrc"><div class="sl-container astro-epm5tkrc"><starlight-toc data-min-h="2" data-max-h="3"><nav aria-labelledby="starlight__on-this-page"><h2 id="starlight__on-this-page">On this page</h2><ul class="astro-lptfoybu" style="--depth: 0;"><li style="--depth: 0;" class="astro-lptfoybu"><a href="#_top" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">Overview</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#environment-variables-this-tutorial-sets" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">Environment variables this tutorial sets</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#1-deploy-a-fresh-instance" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">1. Deploy a fresh instance</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#2-run-the-admin-ui" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">2. Run the admin UI</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#3-walk-the-setup-wizard" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">3. Walk the setup wizard</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#4-create-and-publish-a-post" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">4. Create and publish a post</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#5-get-an-api-key-and-run-the-blog-starter-frontend" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">5. Get an API key and run the Blog starter frontend</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#6-read-the-post-back-over-the-api" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">6. Read the post back over the API</span></a></li><li style="--depth: 0;" class="astro-lptfoybu"><a href="#where-next" style="--depth: 0;" class="astro-lptfoybu"><span style="--depth: 0;" class="astro-lptfoybu">Where next</span></a></li></ul></nav></starlight-toc><script type="module" src="/docs/_astro/TableOfContents.astro_astro_type_script_index_0_lang.Csloo1VZ.js"></script></div></div></div></aside><div class="main-pane astro-perbdhd2"><main data-pagefind-body class="astro-ewhz5xxr" lang="en" dir="ltr"><div class="content-panel astro-ahkzlqop"><div class="sl-container astro-ahkzlqop"><h1 id="_top" class="astro-oef2efwn">Getting started</h1></div></div><div class="content-panel astro-ahkzlqop"><div class="sl-container astro-ahkzlqop"><div class="sl-markdown-content"><p>This tutorial takes you from nothing to a published blog post you can see
|
|
74
|
+
on a real frontend: install a fresh Velora instance, walk through the
|
|
75
|
+
setup wizard (first admin account, database, and the Blog starter), write
|
|
76
|
+
and publish a post in the admin UI, view it on the Blog starter site, and
|
|
77
|
+
finally read the same post back over the public headless API.</p>
|
|
78
|
+
<p>This is <strong>not</strong> the <a href="https://docs.velora-cms.com/v1/contributing/local-setup/">Local setup</a> sandbox. That
|
|
79
|
+
page’s <code dir="auto">velora-dev</code> sandbox seeds an admin account automatically and skips
|
|
80
|
+
the setup wizard on purpose — great for the other tutorials, but it never
|
|
81
|
+
shows you the wizard this tutorial is about. Here you’ll deploy a genuinely
|
|
82
|
+
fresh, unconfigured instance instead, the same shape a real deployment
|
|
83
|
+
boots into on first run (see <code dir="auto">DEPLOYMENT.md</code> at the repo root). The
|
|
84
|
+
<a href="https://docs.velora-cms.com/v1/contributing/local-setup/">Local setup</a> page’s prerequisites still
|
|
85
|
+
apply — a checkout of the Velora repo, Node.js 22.12+, and <code dir="auto">pnpm</code> — so
|
|
86
|
+
this walkthrough is a contributor path run from a source-checkout sandbox,
|
|
87
|
+
not the way to try Velora today. A one-command install, <code dir="auto">npm create velora</code>, is coming; until then, the fastest way to see Velora working is
|
|
88
|
+
the hosted demo, and the <a href="/docs/v1/using-velora/build-a-headless-frontend/">headless tutorial</a>
|
|
89
|
+
walks the public API this same Blog content is served through.</p>
|
|
90
|
+
<div class="sl-heading-wrapper level-h2"><h2 id="environment-variables-this-tutorial-sets">Environment variables this tutorial sets</h2><a class="sl-anchor-link" href="#environment-variables-this-tutorial-sets"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “Environment variables this tutorial sets”</span></a></div>
|
|
91
|
+
<p>You’ll set four environment variables along the way, each explained in
|
|
92
|
+
context when it first appears. For reference, all four in one place:</p>
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
<table><thead><tr><th>Name</th><th>Type</th><th>Meaning</th><th>Example</th></tr></thead><tbody><tr><td><code dir="auto">JWT_SECRET</code></td><td>string</td><td>Signs and verifies the admin session tokens; no default, so the server refuses to start without it. Generated once with <code dir="auto">openssl rand -hex 32</code> and exported, so every restart in this tutorial reuses the same value.</td><td><code dir="auto">9f2c…</code> (64 hex chars)</td></tr><tr><td><code dir="auto">DATABASE_URL</code></td><td>string (URL)</td><td>Connection string for the configured database. Left unset in step 1 so the server boots into the setup wizard; the wizard writes it to <code dir="auto">.env</code> itself once setup completes, so you never set it by hand in this tutorial.</td><td><code dir="auto">file:<absolute path></code></td></tr><tr><td><code dir="auto">VELORA_HEADLESS</code></td><td><code dir="auto">"true"</code> | <code dir="auto">"1"</code></td><td>Registers the key-gated public <code dir="auto">/api/v1/*</code> namespace at boot. Off by default; this tutorial turns it on in step 5 so the Blog starter and the API calls in step 6 can read published content.</td><td><code dir="auto">true</code></td></tr><tr><td><code dir="auto">VELORA_API_PROXY</code></td><td>string (origin)</td><td>Where the admin UI’s dev server proxies <code dir="auto">/api</code>, <code dir="auto">/docs</code>, and <code dir="auto">/plugin-assets</code> requests. Defaults to <code dir="auto">http://localhost:3000</code>, which is already where the tutorial’s server listens — set explicitly in step 2 so the admin UI talks to it regardless of that default.</td><td><code dir="auto">http://localhost:3000</code></td></tr></tbody></table>
|
|
129
|
+
<div class="sl-heading-wrapper level-h2"><h2 id="1-deploy-a-fresh-instance">1. Deploy a fresh instance</h2><a class="sl-anchor-link" href="#1-deploy-a-fresh-instance"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “1. Deploy a fresh instance”</span></a></div>
|
|
130
|
+
<p>From the repository root, build the server and package it as a
|
|
131
|
+
self-contained deploy target outside the monorepo:</p>
|
|
132
|
+
<div class="expressive-code"><link rel="stylesheet" href="/docs/_astro/ec.w36nc.css"><script type="module" src="/docs/_astro/ec.0vx5m.js"></script><figure class="frame is-terminal not-content"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">pnpm</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--filter</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">@velora-cms/server</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">build</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">pnpm</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--filter</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">@velora-cms/server</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">deploy</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--legacy</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--prod</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">../velora-getting-started</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="pnpm --filter @velora-cms/server buildpnpm --filter @velora-cms/server deploy --legacy --prod ../velora-getting-started"><div></div></button></div></figure></div>
|
|
133
|
+
<p>Both commands should exit <code dir="auto">0</code>, and <code dir="auto">../velora-getting-started</code> should now
|
|
134
|
+
exist. Run this from a native shell (PowerShell on Windows) rather than
|
|
135
|
+
git-bash — git-bash’s path translation can corrupt the target-directory
|
|
136
|
+
argument. Put the target on the <strong>same drive letter</strong> as the monorepo;
|
|
137
|
+
<code dir="auto">pnpm deploy --legacy</code> fails across drives.</p>
|
|
138
|
+
<p>The target directory now has everything the server needs to run
|
|
139
|
+
standalone: a compiled <code dir="auto">dist/</code>, the bundled Blog starter template, and a
|
|
140
|
+
fully-resolved <code dir="auto">node_modules/</code> — no dependency on the rest of the
|
|
141
|
+
monorepo being present.</p>
|
|
142
|
+
<p>Start it with no <code dir="auto">DATABASE_URL</code> set, so it boots straight into the setup
|
|
143
|
+
wizard. <code dir="auto">JWT_SECRET</code> has no default and the server refuses to boot without
|
|
144
|
+
it, so generate one on the command line and export it, so this terminal
|
|
145
|
+
reuses the same value across the restarts later in this tutorial —
|
|
146
|
+
generating a fresh secret on each restart would invalidate the admin
|
|
147
|
+
session you’re about to create (API keys are unaffected):</p>
|
|
148
|
+
<div class="expressive-code"><figure class="frame is-terminal not-content"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#C5E478;--1:#3B61B0">cd</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">../velora-getting-started</span></div></div><div class="ec-line"><div class="code"><span style="--0:#C792EA;--1:#8844AE">export</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#C5E478;--1:#3B61B0">JWT_SECRET</span><span style="--0:#C792EA;--1:#8844AE">=</span><span style="--0:#D6DEEB;--1:#403F53">$(</span><span style="--0:#82AAFF;--1:#3B61B0">openssl</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">rand</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">-hex</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#F78C6C;--1:#AA0982">32</span><span style="--0:#D6DEEB;--1:#403F53">)</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">node</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">dist/index.js</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="cd ../velora-getting-startedexport JWT_SECRET=$(openssl rand -hex 32)node dist/index.js"><div></div></button></div></figure></div>
|
|
149
|
+
<p>The process stays in the foreground and logs that it is listening on
|
|
150
|
+
<code dir="auto">http://127.0.0.1:3000</code> (port <code dir="auto">3000</code> and host <code dir="auto">127.0.0.1</code> are the defaults
|
|
151
|
+
— see <a href="/docs/v1/using-velora/installation/">Installation</a> to change them). With
|
|
152
|
+
<code dir="auto">DATABASE_URL</code> unset the server resolves the <code dir="auto">setup-full</code> boot mode, so
|
|
153
|
+
only the two setup routes are registered. Asking it for its setup status
|
|
154
|
+
confirms that:</p>
|
|
155
|
+
<div class="expressive-code"><figure class="frame is-terminal not-content"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">curl</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">-s</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">http://localhost:3000/api/setup/status</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="curl -s http://localhost:3000/api/setup/status"><div></div></button></div></figure></div>
|
|
156
|
+
<p>It answers with <code dir="auto">setupRequired: true</code>, the bundled templates it can offer
|
|
157
|
+
you in the wizard, and <code dir="auto">databaseConfigured: false</code>:</p>
|
|
158
|
+
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="json"><code><div class="ec-line"><div class="code"><span style="--0:#D6DEEB;--1:#403F53">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"setupRequired"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#FF6A83;--1:#A24848">true</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"bundledTemplates"</span><span style="--0:#D6DEEB;--1:#403F53">: [</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#D6DEEB;--1:#403F53"> </span></span><span style="--0:#D6DEEB;--1:#403F53">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"pluginId"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">com.velora.blog</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"version"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">0.1.0</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"name"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">Blog</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"description"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">The official Velora blog template — posts, authors and tags, ready to publish.</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"sampleContent"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#FF6A83;--1:#A24848">true</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#D6DEEB;--1:#403F53"> </span></span><span style="--0:#D6DEEB;--1:#403F53">}</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#D6DEEB;--1:#403F53"> </span></span><span style="--0:#D6DEEB;--1:#403F53">],</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"databaseConfigured"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#FF6A83;--1:#A24848">false</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D6DEEB;--1:#403F53">}</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="{ "setupRequired": true, "bundledTemplates": [ { "pluginId": "com.velora.blog", "version": "0.1.0", "name": "Blog", "description": "The official Velora blog template — posts, authors and tags, ready to publish.", "sampleContent": true } ], "databaseConfigured": false}"><div></div></button></div></figure></div>
|
|
159
|
+
<p>Leave the server running — it is the instance the rest of this tutorial
|
|
160
|
+
talks to.</p>
|
|
161
|
+
<div class="sl-heading-wrapper level-h2"><h2 id="2-run-the-admin-ui">2. Run the admin UI</h2><a class="sl-anchor-link" href="#2-run-the-admin-ui"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “2. Run the admin UI”</span></a></div>
|
|
162
|
+
<p>The admin UI is a separate app that talks to the server over <code dir="auto">/api</code>, and
|
|
163
|
+
that’s the shape this tutorial uses so you get the Vite dev server’s hot
|
|
164
|
+
reload — the packaged server you started in step 1 already carries the
|
|
165
|
+
same admin UI built in and serves it itself at <code dir="auto">/velora</code>, which is how a
|
|
166
|
+
deployed instance is actually used day to day. In another terminal, from
|
|
167
|
+
the repository root, point the dev server at the instance you just
|
|
168
|
+
started and run it in dev mode:</p>
|
|
169
|
+
<div class="expressive-code"><figure class="frame is-terminal not-content"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#C5E478;--1:#3B61B0">VELORA_API_PROXY</span><span style="--0:#C792EA;--1:#8844AE">=</span><span style="--0:#ECC48D;--1:#3B61B0">http://localhost:3000</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">pnpm</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--filter</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">@velora-cms/admin-ui</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">dev</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="VELORA_API_PROXY=http://localhost:3000 pnpm --filter @velora-cms/admin-ui dev"><div></div></button></div></figure></div>
|
|
170
|
+
<p>Vite prints a local URL, normally <code dir="auto">http://localhost:5173</code>. Open it: because
|
|
171
|
+
no admin account exists yet, you land straight in the setup wizard instead
|
|
172
|
+
of a login page.</p>
|
|
173
|
+
<div class="sl-heading-wrapper level-h2"><h2 id="3-walk-the-setup-wizard">3. Walk the setup wizard</h2><a class="sl-anchor-link" href="#3-walk-the-setup-wizard"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “3. Walk the setup wizard”</span></a></div>
|
|
174
|
+
<p>The wizard has three steps on a fresh instance. Each one collects a slice
|
|
175
|
+
of the single <code dir="auto">POST /api/setup/complete</code> request it submits at the end.</p>
|
|
176
|
+
<p><strong>Step 1 — admin account.</strong> These four fields are validated in the browser
|
|
177
|
+
before the wizard will advance, and the first three are re-validated by
|
|
178
|
+
<code dir="auto">SetupCompleteRequestSchema</code> on the server:</p>
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
<table><thead><tr><th>Name</th><th>Type</th><th>Meaning</th><th>Example</th></tr></thead><tbody><tr><td>Email</td><td>string (email)</td><td>Sign-in address for the first administrator. Must contain an <code dir="auto">@</code> and a dotted domain.</td><td><code dir="auto">you@example.com</code></td></tr><tr><td>Name</td><td>string (non-empty)</td><td>Display name shown in the admin UI.</td><td><code dir="auto">Ada Lovelace</code></td></tr><tr><td>Password</td><td>string (min 8 chars)</td><td>The account’s password. Shorter values are rejected with “Password must be at least 8 characters.”</td><td><code dir="auto">correct-horse-8</code></td></tr><tr><td>Confirm password</td><td>string</td><td>Must match Password exactly, or the wizard shows “Passwords do not match.”</td><td><code dir="auto">correct-horse-8</code></td></tr></tbody></table>
|
|
215
|
+
<p>Select <strong>Continue</strong> when all four are filled in.</p>
|
|
216
|
+
<p><strong>Step 2 — database.</strong> Pick a <strong>Database type</strong> from the dropdown; the rest
|
|
217
|
+
of the form changes to match. SQLite needs no separate database server, so
|
|
218
|
+
it is the simplest choice for trying Velora locally:</p>
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
<table><thead><tr><th>Name</th><th>Type</th><th>Meaning</th><th>Example</th></tr></thead><tbody><tr><td>Database type</td><td><code dir="auto">postgresql</code> | <code dir="auto">mysql</code> | <code dir="auto">sqlite</code></td><td>Which dialect this instance stores content in. Cannot be changed by the wizard later.</td><td><code dir="auto">sqlite</code></td></tr><tr><td>File path</td><td>string (SQLite only)</td><td>Where the database file lives. A relative path is fine — Velora resolves it to an absolute one before anything uses it.</td><td><code dir="auto">./data/velora.db</code></td></tr><tr><td>Host</td><td>string (PostgreSQL/MySQL)</td><td>Hostname or IP of the database server.</td><td><code dir="auto">localhost</code></td></tr><tr><td>Port</td><td>integer > 0 (PostgreSQL/MySQL)</td><td>Port to connect on. Pre-filled with <code dir="auto">5432</code> for PostgreSQL and <code dir="auto">3306</code> for MySQL.</td><td><code dir="auto">5432</code></td></tr><tr><td>Database name</td><td>string (PostgreSQL/MySQL)</td><td>The existing database to use.</td><td><code dir="auto">velora</code></td></tr><tr><td>Username</td><td>string (PostgreSQL/MySQL)</td><td>Database user. URL-encoded into the connection string.</td><td><code dir="auto">velora</code></td></tr><tr><td>Password</td><td>string (PostgreSQL/MySQL)</td><td>That user’s password. May be empty; URL-encoded, so <code dir="auto">@</code>, <code dir="auto">:</code> and <code dir="auto">/</code> are safe.</td><td><code dir="auto">velora</code></td></tr></tbody></table>
|
|
273
|
+
<p>Select <strong>Continue</strong>.</p>
|
|
274
|
+
<p><strong>Step 3 — starter.</strong> “Start from a template?” lists <strong>Blank site</strong>
|
|
275
|
+
(pre-selected) alongside any bundled templates as cards:</p>
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
<table><thead><tr><th>Name</th><th>Type</th><th>Meaning</th><th>Example</th></tr></thead><tbody><tr><td>Template card</td><td>Blank site, or a bundled template</td><td>Which starter to install during setup. Blank site installs nothing.</td><td><code dir="auto">Blog</code> (badged <strong>Official</strong>)</td></tr><tr><td>Include sample content</td><td>boolean, default on</td><td>Seeds the template’s <code dir="auto">sample-content.json</code> tree — for Blog, a sample author and two sample posts.</td><td>checked</td></tr></tbody></table>
|
|
300
|
+
<p>Select the <strong>Blog</strong> card — the official Blog template: posts, authors, and
|
|
301
|
+
tags, ready to publish. Leave <strong>Include sample content</strong> checked so you get
|
|
302
|
+
a sample author and two sample posts to look at alongside the one you are
|
|
303
|
+
about to write. The button now reads <strong>Install Blog & finish</strong> — select it.</p>
|
|
304
|
+
<p>Setup completes and installs Blog through the same local, offline install
|
|
305
|
+
path a bundled template always uses — no marketplace round-trip. The
|
|
306
|
+
screen that follows says:</p>
|
|
307
|
+
<blockquote>
|
|
308
|
+
<p>Stop the server and run “pnpm dev” again to continue to the login page.</p>
|
|
309
|
+
</blockquote>
|
|
310
|
+
<p>That instruction is generic wizard copy — what it actually means is
|
|
311
|
+
<em>restart the server process</em>. Since this is a deployed target rather than
|
|
312
|
+
the monorepo’s own dev script, do that with the same command you used to
|
|
313
|
+
start it, reusing the <code dir="auto">$JWT_SECRET</code> you exported earlier rather than
|
|
314
|
+
generating a new one — a fresh secret here would invalidate the admin
|
|
315
|
+
session you’re about to log into:</p>
|
|
316
|
+
<div class="expressive-code"><figure class="frame is-terminal not-content"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Ctrl-C the running server, then:</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">node</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">dist/index.js</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="node dist/index.js"><div></div></button></div></figure></div>
|
|
317
|
+
<p>The server comes back up on <code dir="auto">http://localhost:3000</code>, this time in <code dir="auto">full</code>
|
|
318
|
+
mode with the whole API registered. You don’t need to set <code dir="auto">DATABASE_URL</code>
|
|
319
|
+
again — setup wrote it to <code dir="auto">.env</code> in the target directory
|
|
320
|
+
(<code dir="auto">DATABASE_URL=file:<absolute path></code>, one line, only <code dir="auto">DATABASE_URL</code>), and
|
|
321
|
+
the server reads it back on this and every future boot.</p>
|
|
322
|
+
<p>Check that setup is genuinely closed before logging in:</p>
|
|
323
|
+
<div class="expressive-code"><figure class="frame is-terminal not-content"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">curl</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">-s</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">http://localhost:3000/api/setup/status</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="curl -s http://localhost:3000/api/setup/status"><div></div></button></div></figure></div>
|
|
324
|
+
<p><code dir="auto">setupRequired</code> has flipped to <code dir="auto">false</code>, and the template list is empty
|
|
325
|
+
because a configured instance has nothing left to offer:</p>
|
|
326
|
+
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="json"><code><div class="ec-line"><div class="code"><span style="--0:#D6DEEB;--1:#403F53">{</span><span style="--0:#7FDBCA;--1:#096E72">"setupRequired"</span><span style="--0:#D6DEEB;--1:#403F53">:</span><span style="--0:#FF6A83;--1:#A24848">false</span><span style="--0:#D6DEEB;--1:#403F53">,</span><span style="--0:#7FDBCA;--1:#096E72">"bundledTemplates"</span><span style="--0:#D6DEEB;--1:#403F53">:[],</span><span style="--0:#7FDBCA;--1:#096E72">"databaseConfigured"</span><span style="--0:#D6DEEB;--1:#403F53">:</span><span style="--0:#FF6A83;--1:#A24848">true</span><span style="--0:#D6DEEB;--1:#403F53">}</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="{"setupRequired":false,"bundledTemplates":[],"databaseConfigured":true}"><div></div></button></div></figure></div>
|
|
327
|
+
<p>Refresh <code dir="auto">http://localhost:5173</code> and log in with the email and password
|
|
328
|
+
from Step 1.</p>
|
|
329
|
+
<div class="sl-heading-wrapper level-h2"><h2 id="4-create-and-publish-a-post">4. Create and publish a post</h2><a class="sl-anchor-link" href="#4-create-and-publish-a-post"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “4. Create and publish a post”</span></a></div>
|
|
330
|
+
<p>The content tree on the left already has <strong>Jane Doe</strong> (the Blog
|
|
331
|
+
template’s sample Author) at the root, alongside two sample Tags. Select
|
|
332
|
+
Jane Doe, then use the tree’s <strong>+</strong> button to create new content under
|
|
333
|
+
her. Pick document type <strong>Post</strong>, give it a name (e.g. “My first post” —
|
|
334
|
+
this becomes its URL slug), and select <strong>Create</strong>.</p>
|
|
335
|
+
<p>The new post opens in the editor with five fields, exactly as the Blog
|
|
336
|
+
template’s Post document type declares them:</p>
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
<table><thead><tr><th>Name</th><th>Type</th><th>Meaning</th><th>Example</th></tr></thead><tbody><tr><td>Title</td><td>Text, required</td><td>The post’s headline; stored under the data key <code dir="auto">title</code>.</td><td><code dir="auto">My first post</code></td></tr><tr><td>Body</td><td>Rich Text, required</td><td>The post body, edited in the rich-text editor; data key <code dir="auto">body</code>.</td><td><code dir="auto"><p>Hello from Velora.</p></code></td></tr><tr><td>Featured Image</td><td>Image, optional</td><td>Picks a file from the Media Library; data key <code dir="auto">featuredImage</code>.</td><td><em>(left empty)</em></td></tr><tr><td>Published Date</td><td>Date, optional</td><td>A display date for the post, independent of when you publish; data key <code dir="auto">publishedDate</code>.</td><td><code dir="auto">2026-08-31</code></td></tr><tr><td>Tags (comma-separated slugs, e.g. “guides,announcements”)</td><td>Text, optional</td><td>Tag slugs the Blog starter frontend matches against the sample Tags; data key <code dir="auto">tags</code>.</td><td><code dir="auto">guides,announcements</code></td></tr></tbody></table>
|
|
379
|
+
<p>Fill in a title and some body text (and, optionally, <code dir="auto">announcements</code> or
|
|
380
|
+
<code dir="auto">guides</code> in Tags to see it grouped with the sample posts on a tag page).
|
|
381
|
+
Then select <strong>Publish</strong> at the bottom of the editor — not just
|
|
382
|
+
<strong>Save draft</strong>, which would leave the post as an unpublished draft. Content in
|
|
383
|
+
Velora is immutable (see <a href="/docs/v1/using-velora/editing/">Content editing</a>):
|
|
384
|
+
creating the post writes version 1, and <strong>Publish</strong> saves your unsaved
|
|
385
|
+
edits as version 2 and moves the published pointer to it in one step.</p>
|
|
386
|
+
<div class="sl-heading-wrapper level-h2"><h2 id="5-get-an-api-key-and-run-the-blog-starter-frontend">5. Get an API key and run the Blog starter frontend</h2><a class="sl-anchor-link" href="#5-get-an-api-key-and-run-the-blog-starter-frontend"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “5. Get an API key and run the Blog starter frontend”</span></a></div>
|
|
387
|
+
<p>The Blog starter (<code dir="auto">plugins/blog/starter</code>, an Astro site) reads Post,
|
|
388
|
+
Author, and Tag content from Velora’s public headless API, so it needs
|
|
389
|
+
two things from the server: the headless API turned on, and a key.</p>
|
|
390
|
+
<p>Create the key first, while the server is still running: in the admin
|
|
391
|
+
UI, go to <strong>Settings → API Keys</strong>, select <strong>New API key</strong>, name it (e.g.
|
|
392
|
+
“blog-starter”), and select <strong>Create key</strong>. Copy the secret shown — it’s
|
|
393
|
+
shown exactly once, and it starts with <code dir="auto">vk_</code>.</p>
|
|
394
|
+
<p>The headless API is a boot-time flag, so turn it on and restart the
|
|
395
|
+
server, again reusing the same <code dir="auto">$JWT_SECRET</code> so the admin session stays
|
|
396
|
+
valid:</p>
|
|
397
|
+
<div class="expressive-code"><figure class="frame is-terminal not-content"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Ctrl-C the running server, then:</span></div></div><div class="ec-line"><div class="code"><span style="--0:#C5E478;--1:#3B61B0">VELORA_HEADLESS</span><span style="--0:#C792EA;--1:#8844AE">=</span><span style="--0:#ECC48D;--1:#3B61B0">true</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">node</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">dist/index.js</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="VELORA_HEADLESS=true node dist/index.js"><div></div></button></div></figure></div>
|
|
398
|
+
<p>The server boots as before, and this time the key-gated <code dir="auto">/api/v1/*</code>
|
|
399
|
+
namespace is registered — without <code dir="auto">VELORA_HEADLESS=true</code> those routes are
|
|
400
|
+
never registered at all, so they 404 rather than 401.</p>
|
|
401
|
+
<p>Now set up the starter, from <code dir="auto">plugins/blog/starter/</code> in the monorepo:</p>
|
|
402
|
+
<div class="expressive-code"><figure class="frame is-terminal not-content"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">npm</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">install</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">cp</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">.env.example</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">.env</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="npm installcp .env.example .env"><div></div></button></div></figure></div>
|
|
403
|
+
<p><code dir="auto">npm install</code> finishes with the starter’s dependencies in place, and you
|
|
404
|
+
now have a <code dir="auto">.env</code> file with three values to fill in:</p>
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
<table><thead><tr><th>Name</th><th>Type</th><th>Meaning</th><th>Example</th></tr></thead><tbody><tr><td><code dir="auto">VELORA_API_URL</code></td><td>string (origin)</td><td>The running Velora instance the starter fetches from, server-side.</td><td><code dir="auto">http://localhost:3000</code></td></tr><tr><td><code dir="auto">VELORA_API_KEY</code></td><td>string (<code dir="auto">vk_…</code>)</td><td>The API key you just created. Server-side only — never sent to the browser.</td><td><code dir="auto">vk_1a2b3c…</code></td></tr><tr><td><code dir="auto">PUBLIC_VELORA_API_URL</code></td><td>string (origin)</td><td>The origin the contact page posts to <strong>from the visitor’s browser</strong>. Astro inlines any <code dir="auto">PUBLIC_</code>-prefixed value into client code, so it must be safe to publish. Usually the same as <code dir="auto">VELORA_API_URL</code>.</td><td><code dir="auto">http://localhost:3000</code></td></tr></tbody></table>
|
|
435
|
+
<p>Put those three values in <code dir="auto">.env</code>:</p>
|
|
436
|
+
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="ini"><code><div class="ec-line"><div class="code"><span style="--0:#C792EA;--1:#8844AE">VELORA_API_URL</span><span style="--0:#D6DEEB;--1:#403F53">=http://localhost:3000</span></div></div><div class="ec-line"><div class="code"><span style="--0:#C792EA;--1:#8844AE">VELORA_API_KEY</span><span style="--0:#D6DEEB;--1:#403F53">=<the secret you copied></span></div></div><div class="ec-line"><div class="code"><span style="--0:#C792EA;--1:#8844AE">PUBLIC_VELORA_API_URL</span><span style="--0:#D6DEEB;--1:#403F53">=http://localhost:3000</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="VELORA_API_URL=http://localhost:3000VELORA_API_KEY=<the secret you copied>PUBLIC_VELORA_API_URL=http://localhost:3000"><div></div></button></div></figure></div>
|
|
437
|
+
<p>With the file saved, the starter has everything it needs. Run it:</p>
|
|
438
|
+
<div class="expressive-code"><figure class="frame is-terminal not-content"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">npm</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">run</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">dev</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="npm run dev"><div></div></button></div></figure></div>
|
|
439
|
+
<p>Astro prints a local URL, normally <code dir="auto">http://localhost:4321</code>. Open it —
|
|
440
|
+
you’ll see a post list with your new post alongside the Blog template’s
|
|
441
|
+
two sample posts (“Hello, Velora” and “Writing your first post”), newest
|
|
442
|
+
first. Select your post’s title to see its detail page at
|
|
443
|
+
<code dir="auto">/posts/jane-doe/<your post's slug></code>, with the body you wrote rendered and
|
|
444
|
+
Jane Doe linked as the author. Publish an edit in the admin and refresh
|
|
445
|
+
this page — there’s no rebuild step between the CMS and what renders here.</p>
|
|
446
|
+
<div class="sl-heading-wrapper level-h2"><h2 id="6-read-the-post-back-over-the-api">6. Read the post back over the API</h2><a class="sl-anchor-link" href="#6-read-the-post-back-over-the-api"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “6. Read the post back over the API”</span></a></div>
|
|
447
|
+
<p>The starter is just one consumer of the public API; anything that can send
|
|
448
|
+
an HTTP request is another. Ask your instance for the first page of
|
|
449
|
+
published content, passing the same key in an <code dir="auto">Authorization: Bearer</code>
|
|
450
|
+
header:</p>
|
|
451
|
+
<div class="expressive-code"><figure class="frame is-terminal not-content"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">curl</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">-s</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">-H</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#ECC48D;--1:#984E4D">Authorization: Bearer </span><span style="--0:#C5E478;--1:#3B61B0">$VELORA_API_KEY</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#F78C6C;--1:#AA0982">\</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#ECC48D;--1:#984E4D">http://localhost:3000/api/v1/content?limit=5</span><span style="--0:#D9F5DD;--1:#111111">"</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="curl -s -H "Authorization: Bearer $VELORA_API_KEY" \ "http://localhost:3000/api/v1/content?limit=5""><div></div></button></div></figure></div>
|
|
452
|
+
<p>You get back a <code dir="auto">PublicContentListResponse</code>: an <code dir="auto">items</code> array of node
|
|
453
|
+
summaries plus a <code dir="auto">nextCursor</code> for the following page. Your ids, paths and
|
|
454
|
+
timestamps are your own, but the shape is fixed:</p>
|
|
455
|
+
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="json"><code><div class="ec-line"><div class="code"><span style="--0:#D6DEEB;--1:#403F53">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"items"</span><span style="--0:#D6DEEB;--1:#403F53">: [</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#D6DEEB;--1:#403F53"> </span></span><span style="--0:#D6DEEB;--1:#403F53">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"id"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686"><uuid></span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"parentId"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#FF6A83;--1:#A24848">null</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"path"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">/jane-doe</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"depth"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#F78C6C;--1:#AA0982">0</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"sortOrder"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#F78C6C;--1:#AA0982">1</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"documentTypeId"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686"><uuid></span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"locale"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">en</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"publishedAt"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">2026-08-31T09:12:04.118Z</span><span style="--0:#D9F5DD;--1:#111111">"</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#D6DEEB;--1:#403F53"> </span></span><span style="--0:#D6DEEB;--1:#403F53">}</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#D6DEEB;--1:#403F53"> </span></span><span style="--0:#D6DEEB;--1:#403F53">],</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"nextCursor"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#FF6A83;--1:#A24848">null</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D6DEEB;--1:#403F53">}</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="{ "items": [ { "id": "<uuid>", "parentId": null, "path": "/jane-doe", "depth": 0, "sortOrder": 1, "documentTypeId": "<uuid>", "locale": "en", "publishedAt": "2026-08-31T09:12:04.118Z" } ], "nextCursor": null}"><div></div></button></div></figure></div>
|
|
456
|
+
<p>Take the <code dir="auto">id</code> of your post from that list and fetch the item itself, which
|
|
457
|
+
adds the version number and the field data:</p>
|
|
458
|
+
<div class="expressive-code"><figure class="frame is-terminal not-content"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">curl</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">-s</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">-H</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#ECC48D;--1:#984E4D">Authorization: Bearer </span><span style="--0:#C5E478;--1:#3B61B0">$VELORA_API_KEY</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#F78C6C;--1:#AA0982">\</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#ECC48D;--1:#984E4D">http://localhost:3000/api/v1/content/<the post's id></span><span style="--0:#D9F5DD;--1:#111111">"</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="curl -s -H "Authorization: Bearer $VELORA_API_KEY" \ "http://localhost:3000/api/v1/content/<the post's id>""><div></div></button></div></figure></div>
|
|
459
|
+
<p>The response is a <code dir="auto">PublicContentItem</code> — the published version of that one
|
|
460
|
+
node, with your Post fields under <code dir="auto">data</code>:</p>
|
|
461
|
+
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="json"><code><div class="ec-line"><div class="code"><span style="--0:#D6DEEB;--1:#403F53">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"id"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686"><uuid></span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"documentTypeId"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686"><uuid></span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"locale"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">en</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"publishedAt"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">2026-08-31T09:12:04.118Z</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"version"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#F78C6C;--1:#AA0982">2</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"data"</span><span style="--0:#D6DEEB;--1:#403F53">: {</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"title"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">My first post</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"body"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686"><p>Hello from Velora.</p></span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"tags"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">guides</span><span style="--0:#D9F5DD;--1:#111111">"</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#D6DEEB;--1:#403F53"> </span></span><span style="--0:#D6DEEB;--1:#403F53">}</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D6DEEB;--1:#403F53">}</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="{ "id": "<uuid>", "documentTypeId": "<uuid>", "locale": "en", "publishedAt": "2026-08-31T09:12:04.118Z", "version": 2, "data": { "title": "My first post", "body": "<p>Hello from Velora.</p>", "tags": "guides" }}"><div></div></button></div></figure></div>
|
|
462
|
+
<p>Every property in those two responses is defined once, in
|
|
463
|
+
<code dir="auto">packages/api-schemas/src/public-api.schema.ts</code>:</p>
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
<table><thead><tr><th>Name</th><th>Type</th><th>Meaning</th><th>Example</th></tr></thead><tbody><tr><td><code dir="auto">items</code></td><td>array</td><td>One page of published node summaries, tree-ordered.</td><td><code dir="auto">[{ "id": "…", "path": "/jane-doe" }]</code></td></tr><tr><td><code dir="auto">items[].id</code></td><td>string (UUID)</td><td>Node id — pass it to <code dir="auto">/api/v1/content/:id</code>.</td><td><code dir="auto">2e9053b1-a300-493f-a48b-58440cdf3629</code></td></tr><tr><td><code dir="auto">items[].parentId</code></td><td>string (UUID) or <code dir="auto">null</code></td><td>Parent node id; <code dir="auto">null</code> at the root.</td><td><code dir="auto">null</code></td></tr><tr><td><code dir="auto">items[].path</code></td><td>string</td><td>Materialised tree path; all subtree queries use it.</td><td><code dir="auto">/jane-doe/my-first-post</code></td></tr><tr><td><code dir="auto">items[].depth</code></td><td>integer ≥ 0</td><td>Depth in the tree; a root is <code dir="auto">0</code>.</td><td><code dir="auto">1</code></td></tr><tr><td><code dir="auto">items[].sortOrder</code></td><td>integer</td><td>Ordering among siblings.</td><td><code dir="auto">1</code></td></tr><tr><td><code dir="auto">items[].documentTypeId</code></td><td>string (UUID)</td><td>The document type this node instantiates (here, Post).</td><td><code dir="auto">d90012af-c528-4cff-b0c9-cc4c9cfc05c9</code></td></tr><tr><td><code dir="auto">items[].locale</code></td><td>string (≥ 2 chars)</td><td>Locale of this node.</td><td><code dir="auto">en</code></td></tr><tr><td><code dir="auto">items[].publishedAt</code></td><td>string (ISO 8601)</td><td>When the current version was published. Always present on the list surface — never <code dir="auto">null</code> here.</td><td><code dir="auto">2026-08-31T09:12:04.118Z</code></td></tr><tr><td><code dir="auto">nextCursor</code></td><td>string (UUID) or <code dir="auto">null</code></td><td>Pass as <code dir="auto">?cursor=</code> to get the next page; <code dir="auto">null</code> when there is none.</td><td><code dir="auto">45dfb6ca-ffa7-482e-842b-f662c0c1034c</code></td></tr><tr><td><code dir="auto">publishedAt</code> (single item)</td><td>string (ISO 8601) or <code dir="auto">null</code></td><td>When the current version was published. Nullable only on this single-item shape: <code dir="auto">null</code> when a verified preview token serves a genuinely-draft node’s latest version, which has no publication yet.</td><td><code dir="auto">2026-08-31T09:12:04.118Z</code></td></tr><tr><td><code dir="auto">version</code></td><td>integer > 0</td><td>Which version of the node this is — version 2 after the publish in step 4.</td><td><code dir="auto">2</code></td></tr><tr><td><code dir="auto">data</code></td><td>object</td><td>The field values, keyed by field id from the document type.</td><td><code dir="auto">{ "title": "My first post" }</code></td></tr><tr><td><code dir="auto">extensions</code></td><td>object, optional</td><td>Present only when an installed plugin contributed extra response data, keyed by plugin id.</td><td><code dir="auto">{ "com.example.seo": { "score": 82 } }</code></td></tr></tbody></table>
|
|
560
|
+
<p>If you’d rather see this working before you install anything, the same
|
|
561
|
+
call runs against the hosted, read-only demo instance. Its key is
|
|
562
|
+
intentionally public and rate-limited:</p>
|
|
563
|
+
<div class="expressive-code"><figure class="frame is-terminal not-content"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">curl</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">-s</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">-H</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#ECC48D;--1:#984E4D">Authorization: Bearer vk_4ac0a6b78b953a13b8646e6379fcbb6bee1a269af253a14eebce668572019328</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#F78C6C;--1:#AA0982">\</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#ECC48D;--1:#984E4D">https://demo.velora-cms.com/api/v1/content/2e9053b1-a300-493f-a48b-58440cdf3629</span><span style="--0:#D9F5DD;--1:#111111">"</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="curl -s -H "Authorization: Bearer vk_4ac0a6b78b953a13b8646e6379fcbb6bee1a269af253a14eebce668572019328" \ "https://demo.velora-cms.com/api/v1/content/2e9053b1-a300-493f-a48b-58440cdf3629""><div></div></button></div></figure></div>
|
|
564
|
+
<p>That returns one of the demo’s published nodes, verbatim:</p>
|
|
565
|
+
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="json"><code><div class="ec-line"><div class="code"><span style="--0:#D6DEEB;--1:#403F53">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"id"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">2e9053b1-a300-493f-a48b-58440cdf3629</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"documentTypeId"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">d90012af-c528-4cff-b0c9-cc4c9cfc05c9</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"locale"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">en</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"publishedAt"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">2026-08-25T14:31:45.729Z</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"version"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#F78C6C;--1:#AA0982">1</span><span style="--0:#D6DEEB;--1:#403F53">,</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#7FDBCA;--1:#096E72">"data"</span><span style="--0:#D6DEEB;--1:#403F53">: { </span><span style="--0:#7FDBCA;--1:#096E72">"label"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">Announcements</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53">, </span><span style="--0:#7FDBCA;--1:#096E72">"description"</span><span style="--0:#D6DEEB;--1:#403F53">: </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#C789D6;--1:#7C5686">Site news.</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53"> }</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D6DEEB;--1:#403F53">}</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="{ "id": "2e9053b1-a300-493f-a48b-58440cdf3629", "documentTypeId": "d90012af-c528-4cff-b0c9-cc4c9cfc05c9", "locale": "en", "publishedAt": "2026-08-25T14:31:45.729Z", "version": 1, "data": { "label": "Announcements", "description": "Site news." }}"><div></div></button></div></figure></div>
|
|
566
|
+
<div class="sl-heading-wrapper level-h2"><h2 id="where-next">Where next</h2><a class="sl-anchor-link" href="#where-next"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 1 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 0 0-1.42-1.42m8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 1 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 0 0 0 1.42 1 1 0 0 0 1.42 0l3.88-3.89a4.49 4.49 0 0 0 0-6.33M8.83 15.17a1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.92-4.92a1 1 0 1 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42"></path></svg></span><span class="sr-only" data-pagefind-ignore="">Section titled “Where next”</span></a></div>
|
|
567
|
+
<ul>
|
|
568
|
+
<li><a href="/docs/v1/using-velora/installation/">Installation</a> — the wizard’s fields and
|
|
569
|
+
every server environment variable, for a deployment you keep.</li>
|
|
570
|
+
<li><a href="/docs/v1/using-velora/content-model/">Document types</a> — model your own content
|
|
571
|
+
instead of the Blog template’s.</li>
|
|
572
|
+
<li><a href="/docs/v1/using-velora/editing/">Content editing</a> — how saving, publishing,
|
|
573
|
+
and rollback work in more depth.</li>
|
|
574
|
+
<li><a href="/docs/v1/using-velora/media/">Media</a> — uploading, browsing, referencing media
|
|
575
|
+
in content, and where the files are stored.</li>
|
|
576
|
+
<li><a href="/docs/v1/using-velora/users-and-roles/">Users and roles</a> — more accounts, and
|
|
577
|
+
managing the API keys you just created.</li>
|
|
578
|
+
<li><a href="/docs/v1/using-velora/installing-plugins/">Installing plugins</a> — browsing the
|
|
579
|
+
marketplace from the admin, the signing/trust gate, and sideloading.</li>
|
|
580
|
+
<li><a href="/docs/v1/using-velora/headless-api/">Headless API</a> — every <code dir="auto">/api/v1</code> endpoint,
|
|
581
|
+
its parameters, and its response shape.</li>
|
|
582
|
+
<li><a href="/docs/v1/using-velora/build-a-headless-frontend/">Build a headless frontend</a> —
|
|
583
|
+
build your own frontend against that API.</li>
|
|
584
|
+
<li><a href="https://docs.velora-cms.com/v1/building-plugins/build-a-plugin/">Build a plugin</a> — scaffold and install a
|
|
585
|
+
datatype plugin with <code dir="auto">create-velora-plugin</code>.</li>
|
|
586
|
+
<li><code dir="auto">plugins/blog/starter/README.md</code> — the rest of what the Blog starter
|
|
587
|
+
can do, including its contact-form page.</li>
|
|
588
|
+
</ul></div><footer class="sl-flex astro-gjkl2a5w"><div class="meta sl-flex astro-gjkl2a5w"></div><div class="pagination-links print:hidden astro-3tj6ibx2" dir="ltr"><a href="/docs/v1/using-velora/" rel="prev" class="astro-3tj6ibx2"><svg aria-hidden="true" class="astro-3tj6ibx2 astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M17 11H9.41l3.3-3.29a1.004 1.004 0 1 0-1.42-1.42l-5 5a1 1 0 0 0-.21.33 1 1 0 0 0 0 .76 1 1 0 0 0 .21.33l5 5a1.002 1.002 0 0 0 1.639-.325 1 1 0 0 0-.219-1.095L9.41 13H17a1 1 0 0 0 0-2Z"/></svg><span class="astro-3tj6ibx2">Previous<br class="astro-3tj6ibx2"><span class="link-title astro-3tj6ibx2">Using Velora</span></span></a><a href="/docs/v1/using-velora/installation/" rel="next" class="astro-3tj6ibx2"><svg aria-hidden="true" class="astro-3tj6ibx2 astro-24n5ceuf" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M17.92 11.62a1.001 1.001 0 0 0-.21-.33l-5-5a1.003 1.003 0 1 0-1.42 1.42l3.3 3.29H7a1 1 0 0 0 0 2h7.59l-3.3 3.29a1.002 1.002 0 0 0 .325 1.639 1 1 0 0 0 1.095-.219l5-5a1 1 0 0 0 .21-.33 1 1 0 0 0 0-.76Z"/></svg><span class="astro-3tj6ibx2">Next<br class="astro-3tj6ibx2"><span class="link-title astro-3tj6ibx2">Installation</span></span></a></div></footer><div class="velora-bundle-stamp astro-llmj63sw">Velora 0.9.0 · bundled documentation</div></div></div></main></div></div></div></div></body></html>
|