Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 180 additions & 0 deletions src/frontend/composables/useDocsContent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
export interface DocSection {
title: string
description: string
href: string
}

export interface CodeExample {
label: string
code: string
}

export default () => {
const overviewCards: DocSection[] = [
{
title: 'Getting started',
description: 'Install the stack, bootstrap the app, and get to a working local setup quickly.',
href: '/docs/getting-started',
},
{
title: 'Self-hosting',
description: 'Configure environment variables, deploy with Docker or Kubernetes, and operate securely.',
href: '/docs/self-hosting',
},
{
title: 'Architecture',
description: 'Understand the Nuxt frontend, Strapi backend, and the credential verification flow.',
href: '/docs/architecture',
},
{
title: 'Contributing',
description: 'Learn the review flow, repo structure, and how to contribute responsibly.',
href: '/docs/contributing',
},
]

const quickLinks: DocSection[] = [
{
title: 'Repository overview',
description: 'Start from the root README and the architecture docs for the project mental model.',
href: 'https://github.com/schroedinger-hat/certo',
},
{
title: 'Docs index',
description: 'Browse the project docs for backend, frontend, security, and standards guidance.',
href: 'https://github.com/schroedinger-hat/certo/tree/main/docs',
},
{
title: 'Contributing guide',
description: 'Review the canonical contribution expectations before opening a PR.',
href: 'https://github.com/schroedinger-hat/certo/blob/main/CONTRIBUTING.md',
},
]

const gettingStartedSections = [
{
title: 'Prerequisites',
content: [
'Node.js 18-22.x for local development',
'npm 6+ for package management',
'A running PostgreSQL instance for production-oriented local testing, or SQLite for simpler setups',
],
},
{
title: 'Clone and install',
content: [
'Clone the repo and install the frontend + backend dependencies',
'cd certo/src/frontend && npm install',
'cd ../backend && npm install',
],
},
{
title: 'Run the stack',
content: [
'Start the backend from src/backend',
'npm run develop',
'In another terminal, start the frontend',
'cd src/frontend && npm run dev',
],
},
]

const selfHostingSections = [
{
title: 'Environment essentials',
content: [
'Set NUXT_PUBLIC_API_URL for the frontend app',
'Set HOST, PORT, APP_KEYS, JWT_SECRET, and DATABASE settings for Strapi',
'Configure FRONTEND_URL and any email provider variables used in credential actions',
],
},
{
title: 'Deployment options',
content: [
'Docker Compose is the easiest path for local or lightweight self-hosting',
'Helm values are available under helm/certo for Kubernetes deployments',
'Use a reverse proxy in front of the app for TLS termination and production hardening',
],
},
{
title: 'Operational checklist',
content: [
'Protect keys and secrets with a real secret manager or environment injection layer',
'Persist uploads and database storage across restarts',
'Review the monitoring and backup docs before exposing the stack publicly',
],
},
]

const contributionSections = [
{
title: 'Repo structure',
content: [
'Frontend logic lives under src/frontend',
'Backend logic and Strapi configuration live under src/backend',
'Project docs, architecture notes, and config guides live under docs',
],
},
{
title: 'Before opening a PR',
content: [
'Read the project docs and current roadmap to confirm scope',
'Keep changes targeted to a single problem or feature area',
'Document any config or operational changes introduced by the patch',
],
},
{
title: 'Validation',
content: [
'Run the relevant frontend unit tests',
'Use the existing backend TypeScript checks where relevant',
'Confirm that security-sensitive changes are documented and not silently reduced in scope',
],
},
]

const architectureSections = [
{
title: 'Frontend',
content: [
'Nuxt 3 provides pages, layouts, auth flows, and public-facing verification views',
'The UI uses Vue 3, Pinia, and the project’s existing component patterns',
],
},
{
title: 'Backend',
content: [
'Strapi 5 hosts the API surface, content models, and permission layer',
'Credential issuance, verification, revocation, and proof validation are implemented there',
],
},
{
title: 'Standards',
content: [
'The platform targets Open Badges 3.0 and W3C Verifiable Credentials flows',
'The docs and roadmap make the standards goal explicit, while known issues document the current gaps',
],
},
]

const environmentVariables: CodeExample[] = [
{
label: 'Frontend',
code: `NUXT_PUBLIC_API_URL=http://localhost:1337\nNUXT_PUBLIC_WEBSITE_URL=http://localhost:3000`,
},
{
label: 'Backend',
code: `HOST=0.0.0.0\nPORT=1337\nAPP_KEYS=your-app-keys\nJWT_SECRET=your-jwt-secret\nADMIN_JWT_SECRET=your-admin-jwt-secret\nDATABASE_CLIENT=postgres\nDATABASE_HOST=localhost`,
},
]

return {
overviewCards,
quickLinks,
gettingStartedSections,
selfHostingSections,
contributionSections,
architectureSections,
environmentVariables,
}
}
1 change: 1 addition & 0 deletions src/frontend/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const WEBSITE_URL = import.meta.env?.NUXT_PUBLIC_WEBSITE_URL
export const HEADER_NAV_LINKS = [
{ name: 'Home', href: '/', i18nKey: 'home' },
{ name: 'About', href: '/about', i18nKey: 'about' },
{ name: 'Docs', href: '/docs', i18nKey: 'docs' },
{ name: 'Dashboard', href: '/dashboard', i18nKey: 'dashboard' },
{ name: 'Issue Badges', href: '/issue', i18nKey: 'issue' },
{ name: 'Verify', href: '/verify', i18nKey: 'verify' },
Expand Down
1 change: 1 addition & 0 deletions src/frontend/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"learnMore": "Learn more",
"about": "About",
"home": "Home",
"docs": "Docs",
"issue": "Issue Badges"
},
"common": {
Expand Down
54 changes: 54 additions & 0 deletions src/frontend/pages/docs/architecture.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<script setup lang="ts">
const { architectureSections } = useDocsContent()

useSeoMeta({
title: 'Architecture',
description: 'Understand the frontend/backend split, the credential lifecycle, and the standards the platform is designed around.',
})
</script>

<template>
<section class="max-w-5xl mx-auto px-4 py-16 sm:px-6 lg:px-8">
<div class="mb-10">
<p class="text-sm font-semibold uppercase tracking-[0.2em] text-primary mb-3">
Architecture
</p>
<h1 class="text-4xl font-bold tracking-tight mb-4">
How Certo is organized
</h1>
<p class="text-lg text-text-secondary max-w-3xl">
Certo combines a Nuxt frontend with a Strapi backend to create a credential platform that focuses on verification, secure issuance, and open standards.
</p>
</div>

<div class="space-y-8 mb-12">
<div
v-for="section in architectureSections"
:key="section.title"
class="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm"
>
<h2 class="text-2xl font-semibold mb-4">
{{ section.title }}
</h2>
<ul class="space-y-3 text-text-secondary text-lg leading-relaxed">
<li v-for="item in section.content" :key="item" class="flex gap-3">
<span class="mt-2 h-2 w-2 rounded-full bg-primary shrink-0" />
<span>{{ item }}</span>
</li>
</ul>
</div>
</div>

<div class="rounded-2xl border border-slate-200 bg-slate-50 p-6">
<h2 class="text-2xl font-semibold mb-4">
High-level flow
</h2>
<ol class="space-y-3 text-text-secondary text-lg leading-relaxed list-decimal list-inside">
<li>Issuers create achievement definitions and issue credentials from the application.</li>
<li>The backend validates permissions, issues the credential, and records the necessary lifecycle metadata.</li>
<li>Recipients can view, share, and verify the credential from the frontend.</li>
<li>Verification checks the credential structure and trust signals against the platform and standards expectations.</li>
</ol>
</div>
</section>
</template>
55 changes: 55 additions & 0 deletions src/frontend/pages/docs/contributing.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<script setup lang="ts">
const { contributionSections } = useDocsContent()

useSeoMeta({
title: 'Contributing',
description: 'Learn the project structure, validation flow, and PR expectations for contributing to Certo.',
})
</script>

<template>
<section class="max-w-5xl mx-auto px-4 py-16 sm:px-6 lg:px-8">
<div class="mb-10">
<p class="text-sm font-semibold uppercase tracking-[0.2em] text-primary mb-3">
Contributing
</p>
<h1 class="text-4xl font-bold tracking-tight mb-4">
Contribute to Certo responsibly
</h1>
<p class="text-lg text-text-secondary max-w-3xl">
Contributions are easiest to review when they stay scoped, are grounded in project docs, and clearly describe the operational impact of the change.
</p>
</div>

<div class="space-y-8 mb-12">
<div
v-for="section in contributionSections"
:key="section.title"
class="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm"
>
<h2 class="text-2xl font-semibold mb-4">
{{ section.title }}
</h2>
<ul class="space-y-3 text-text-secondary text-lg leading-relaxed">
<li v-for="item in section.content" :key="item" class="flex gap-3">
<span class="mt-2 h-2 w-2 rounded-full bg-primary shrink-0" />
<span>{{ item }}</span>
</li>
</ul>
</div>
</div>

<div class="rounded-2xl border border-slate-200 bg-slate-50 p-6">
<h2 class="text-2xl font-semibold mb-4">
Contribution checklist
</h2>
<ol class="space-y-3 text-text-secondary text-lg leading-relaxed list-decimal list-inside">
<li>Read the root documentation and any relevant architecture or security notes before proposing a change.</li>
<li>Keep the patch focused on one user journey, bug fix, or feature area.</li>
<li>Document any configuration or deployment changes that affect operators.</li>
<li>Validate the relevant frontend or backend checks before requesting review.</li>
<li>Reference the project issue or roadmap context when the work touches product or security concerns.</li>
</ol>
</div>
</section>
</template>
69 changes: 69 additions & 0 deletions src/frontend/pages/docs/getting-started.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<script setup lang="ts">
const { gettingStartedSections } = useDocsContent()

useSeoMeta({
title: 'Getting started',
description: 'Set up Certo locally, run the frontend and backend, and get the platform ready for issuing credentials.',
})
</script>

<template>
<section class="max-w-5xl mx-auto px-4 py-16 sm:px-6 lg:px-8">
<div class="mb-10">
<p class="text-sm font-semibold uppercase tracking-[0.2em] text-primary mb-3">
Get started
</p>
<h1 class="text-4xl font-bold tracking-tight mb-4">
Set up Certo locally
</h1>
<p class="text-lg text-text-secondary max-w-3xl">
Certo is a Strapi backend + Nuxt frontend stack. The local development flow is straightforward: install dependencies, configure environment variables, start the backend, then start the frontend.
</p>
</div>

<div class="space-y-8 mb-12">
<div
v-for="(section, index) in gettingStartedSections"
:key="section.title"
class="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm"
>
<p class="text-sm font-semibold uppercase tracking-[0.18em] text-primary mb-3">
Step {{ index + 1 }}
</p>
<h2 class="text-2xl font-semibold mb-4">
{{ section.title }}
</h2>
<ul class="space-y-3 text-text-secondary text-lg leading-relaxed">
<li v-for="item in section.content" :key="item" class="flex gap-3">
<span class="mt-2 h-2 w-2 rounded-full bg-primary shrink-0" />
<span>{{ item }}</span>
</li>
</ul>
</div>
</div>

<div class="rounded-2xl border border-slate-200 bg-slate-50 p-6 mb-10">
<h2 class="text-2xl font-semibold mb-4">
Example setup commands
</h2>
<pre class="overflow-x-auto rounded-xl bg-slate-900 p-4 text-sm text-slate-100"><code>cd certo
cd src/frontend && npm install
cd ../backend && npm install

# Start backend
cd ../backend && npm run develop

# Start frontend in another terminal
cd ../frontend && npm run dev</code></pre>
</div>

<div class="rounded-2xl border border-slate-200 bg-white p-6">
<h2 class="text-2xl font-semibold mb-4">
Default app behavior
</h2>
<p class="text-text-secondary leading-relaxed">
On first run, the development backend seeds sample data for testing, including the default admin account and example certificate content. The project docs describe the default login credentials and expected sample data in more detail, which is useful before you start issuing credential workflows.
</p>
</div>
</section>
</template>
Loading
Loading