@revealui/cli 0.0.0-canary-20260402004330
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 +22 -0
- package/README.md +99 -0
- package/bin/create-revealui.js +6 -0
- package/bin/revealui.js +6 -0
- package/dist/cli.d.ts +16 -0
- package/dist/cli.js +2769 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2767 -0
- package/dist/index.js.map +1 -0
- package/package.json +72 -0
- package/templates/basic-blog/.env.example +36 -0
- package/templates/basic-blog/_gitignore +26 -0
- package/templates/basic-blog/next.config.mjs +8 -0
- package/templates/basic-blog/package.json +36 -0
- package/templates/basic-blog/postcss.config.mjs +5 -0
- package/templates/basic-blog/revealui.config.ts +19 -0
- package/templates/basic-blog/src/app/globals.css +6 -0
- package/templates/basic-blog/src/app/layout.tsx +15 -0
- package/templates/basic-blog/src/app/page.tsx +57 -0
- package/templates/basic-blog/src/app/posts/[slug]/page.tsx +66 -0
- package/templates/basic-blog/src/app/posts/page.tsx +61 -0
- package/templates/basic-blog/src/collections/Posts.ts +42 -0
- package/templates/basic-blog/src/seed.ts +73 -0
- package/templates/basic-blog/tsconfig.json +34 -0
- package/templates/e-commerce/.env.example +36 -0
- package/templates/e-commerce/_gitignore +26 -0
- package/templates/e-commerce/next.config.mjs +8 -0
- package/templates/e-commerce/package.json +36 -0
- package/templates/e-commerce/postcss.config.mjs +5 -0
- package/templates/e-commerce/revealui.config.ts +20 -0
- package/templates/e-commerce/src/app/globals.css +6 -0
- package/templates/e-commerce/src/app/layout.tsx +15 -0
- package/templates/e-commerce/src/app/page.tsx +82 -0
- package/templates/e-commerce/src/app/products/[slug]/page.tsx +80 -0
- package/templates/e-commerce/src/app/products/page.tsx +72 -0
- package/templates/e-commerce/src/collections/Orders.ts +63 -0
- package/templates/e-commerce/src/collections/Products.ts +50 -0
- package/templates/e-commerce/src/seed.ts +72 -0
- package/templates/e-commerce/tsconfig.json +34 -0
- package/templates/portfolio/.env.example +36 -0
- package/templates/portfolio/_gitignore +26 -0
- package/templates/portfolio/next.config.mjs +8 -0
- package/templates/portfolio/package.json +36 -0
- package/templates/portfolio/postcss.config.mjs +5 -0
- package/templates/portfolio/revealui.config.ts +19 -0
- package/templates/portfolio/src/app/globals.css +6 -0
- package/templates/portfolio/src/app/layout.tsx +15 -0
- package/templates/portfolio/src/app/page.tsx +60 -0
- package/templates/portfolio/src/app/projects/[slug]/page.tsx +95 -0
- package/templates/portfolio/src/app/projects/page.tsx +85 -0
- package/templates/portfolio/src/collections/Projects.ts +49 -0
- package/templates/portfolio/src/seed.ts +73 -0
- package/templates/portfolio/tsconfig.json +34 -0
- package/templates/starter/.env.example +36 -0
- package/templates/starter/_gitignore +26 -0
- package/templates/starter/next.config.mjs +8 -0
- package/templates/starter/package.json +36 -0
- package/templates/starter/postcss.config.mjs +5 -0
- package/templates/starter/revealui.config.ts +18 -0
- package/templates/starter/src/app/globals.css +6 -0
- package/templates/starter/src/app/layout.tsx +15 -0
- package/templates/starter/src/app/page.tsx +18 -0
- package/templates/starter/src/seed.ts +40 -0
- package/templates/starter/tsconfig.json +34 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import Image from 'next/image';
|
|
2
|
+
|
|
3
|
+
const API_URL = process.env.NEXT_PUBLIC_SERVER_URL || 'http://localhost:4000';
|
|
4
|
+
|
|
5
|
+
interface Project {
|
|
6
|
+
id: string;
|
|
7
|
+
title: string;
|
|
8
|
+
slug: string;
|
|
9
|
+
description: unknown;
|
|
10
|
+
tags?: Array<{ tag: string }>;
|
|
11
|
+
link?: string;
|
|
12
|
+
image?: { url: string; alt?: string } | null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function getProject(slug: string): Promise<Project | null> {
|
|
16
|
+
try {
|
|
17
|
+
const res = await fetch(`${API_URL}/api/projects?where[slug][equals]=${slug}&limit=1`, {
|
|
18
|
+
cache: 'no-store',
|
|
19
|
+
});
|
|
20
|
+
if (!res.ok) return null;
|
|
21
|
+
const data = await res.json();
|
|
22
|
+
return data.docs?.[0] ?? null;
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default async function ProjectPage({ params }: { params: Promise<{ slug: string }> }) {
|
|
29
|
+
const { slug } = await params;
|
|
30
|
+
const project = await getProject(slug);
|
|
31
|
+
|
|
32
|
+
if (!project) {
|
|
33
|
+
return (
|
|
34
|
+
<main className="mx-auto max-w-2xl px-4 py-16">
|
|
35
|
+
<h1 className="text-2xl font-bold">Project not found</h1>
|
|
36
|
+
<p className="mt-4">
|
|
37
|
+
<a href="/projects" className="text-accent underline">
|
|
38
|
+
Back to projects
|
|
39
|
+
</a>
|
|
40
|
+
</p>
|
|
41
|
+
</main>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<main className="mx-auto max-w-2xl px-4 py-16">
|
|
47
|
+
<nav className="mb-8">
|
|
48
|
+
<a href="/projects" className="text-sm text-accent underline">
|
|
49
|
+
← Back to projects
|
|
50
|
+
</a>
|
|
51
|
+
</nav>
|
|
52
|
+
<article>
|
|
53
|
+
{project.image?.url && (
|
|
54
|
+
<Image
|
|
55
|
+
src={project.image.url}
|
|
56
|
+
alt={project.image.alt || project.title}
|
|
57
|
+
width={800}
|
|
58
|
+
height={450}
|
|
59
|
+
className="mb-8 aspect-video w-full rounded-lg object-cover"
|
|
60
|
+
/>
|
|
61
|
+
)}
|
|
62
|
+
<h1 className="mb-2 text-3xl font-bold">{project.title}</h1>
|
|
63
|
+
{project.tags && project.tags.length > 0 && (
|
|
64
|
+
<div className="mb-6 flex flex-wrap gap-2">
|
|
65
|
+
{project.tags.map((t) => (
|
|
66
|
+
<span
|
|
67
|
+
key={t.tag}
|
|
68
|
+
className="rounded-full bg-gray-100 px-3 py-1 text-sm text-gray-700"
|
|
69
|
+
>
|
|
70
|
+
{t.tag}
|
|
71
|
+
</span>
|
|
72
|
+
))}
|
|
73
|
+
</div>
|
|
74
|
+
)}
|
|
75
|
+
<div className="prose">
|
|
76
|
+
{typeof project.description === 'string' ? (
|
|
77
|
+
<p>{project.description}</p>
|
|
78
|
+
) : (
|
|
79
|
+
<p className="text-gray-500">Project description will render here.</p>
|
|
80
|
+
)}
|
|
81
|
+
</div>
|
|
82
|
+
{project.link && (
|
|
83
|
+
<a
|
|
84
|
+
href={project.link}
|
|
85
|
+
target="_blank"
|
|
86
|
+
rel="noopener noreferrer"
|
|
87
|
+
className="mt-8 inline-block rounded bg-gray-900 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-gray-700"
|
|
88
|
+
>
|
|
89
|
+
View project →
|
|
90
|
+
</a>
|
|
91
|
+
)}
|
|
92
|
+
</article>
|
|
93
|
+
</main>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import Image from 'next/image';
|
|
2
|
+
|
|
3
|
+
const API_URL = process.env.NEXT_PUBLIC_SERVER_URL || 'http://localhost:4000';
|
|
4
|
+
|
|
5
|
+
interface Project {
|
|
6
|
+
id: string;
|
|
7
|
+
title: string;
|
|
8
|
+
slug: string;
|
|
9
|
+
description: unknown;
|
|
10
|
+
tags?: Array<{ tag: string }>;
|
|
11
|
+
link?: string;
|
|
12
|
+
image?: { url: string; alt?: string } | null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function getProjects(): Promise<Project[]> {
|
|
16
|
+
try {
|
|
17
|
+
const res = await fetch(`${API_URL}/api/projects?sort=-createdAt`, {
|
|
18
|
+
cache: 'no-store',
|
|
19
|
+
});
|
|
20
|
+
if (!res.ok) return [];
|
|
21
|
+
const data = await res.json();
|
|
22
|
+
return data.docs ?? [];
|
|
23
|
+
} catch {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default async function ProjectsPage() {
|
|
29
|
+
const projects = await getProjects();
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<main className="mx-auto max-w-4xl px-4 py-16">
|
|
33
|
+
<h1 className="mb-8 text-3xl font-bold">Projects</h1>
|
|
34
|
+
|
|
35
|
+
{projects.length === 0 ? (
|
|
36
|
+
<p className="text-gray-500">
|
|
37
|
+
No projects yet. Add projects in the{' '}
|
|
38
|
+
<a href="/admin/collections/projects" className="text-accent underline">
|
|
39
|
+
admin panel
|
|
40
|
+
</a>
|
|
41
|
+
, or run <code className="rounded bg-gray-100 px-1">pnpm db:seed</code> to add sample
|
|
42
|
+
data.
|
|
43
|
+
</p>
|
|
44
|
+
) : (
|
|
45
|
+
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2">
|
|
46
|
+
{projects.map((project) => (
|
|
47
|
+
<a
|
|
48
|
+
key={project.id}
|
|
49
|
+
href={`/projects/${project.slug}`}
|
|
50
|
+
className="block rounded-lg border border-gray-200 p-4 transition-shadow hover:shadow-md"
|
|
51
|
+
>
|
|
52
|
+
{project.image?.url && (
|
|
53
|
+
<Image
|
|
54
|
+
src={project.image.url}
|
|
55
|
+
alt={project.image.alt || project.title}
|
|
56
|
+
width={600}
|
|
57
|
+
height={338}
|
|
58
|
+
className="mb-4 aspect-video w-full rounded object-cover"
|
|
59
|
+
/>
|
|
60
|
+
)}
|
|
61
|
+
<h2 className="text-xl font-semibold">{project.title}</h2>
|
|
62
|
+
{project.tags && project.tags.length > 0 && (
|
|
63
|
+
<div className="mt-2 flex flex-wrap gap-2">
|
|
64
|
+
{project.tags.map((t) => (
|
|
65
|
+
<span
|
|
66
|
+
key={t.tag}
|
|
67
|
+
className="rounded-full bg-gray-100 px-2 py-0.5 text-xs text-gray-700"
|
|
68
|
+
>
|
|
69
|
+
{t.tag}
|
|
70
|
+
</span>
|
|
71
|
+
))}
|
|
72
|
+
</div>
|
|
73
|
+
)}
|
|
74
|
+
{project.link && (
|
|
75
|
+
<span className="mt-3 inline-block text-sm text-accent underline">
|
|
76
|
+
View project →
|
|
77
|
+
</span>
|
|
78
|
+
)}
|
|
79
|
+
</a>
|
|
80
|
+
))}
|
|
81
|
+
</div>
|
|
82
|
+
)}
|
|
83
|
+
</main>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { CollectionConfig } from '@revealui/contracts';
|
|
2
|
+
|
|
3
|
+
export const Projects: CollectionConfig = {
|
|
4
|
+
slug: 'projects',
|
|
5
|
+
labels: { singular: 'Project', plural: 'Projects' },
|
|
6
|
+
fields: [
|
|
7
|
+
{
|
|
8
|
+
name: 'title',
|
|
9
|
+
type: 'text',
|
|
10
|
+
required: true,
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: 'slug',
|
|
14
|
+
type: 'text',
|
|
15
|
+
required: true,
|
|
16
|
+
unique: true,
|
|
17
|
+
admin: {
|
|
18
|
+
description: 'URL-friendly identifier (e.g. "my-cool-project")',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'description',
|
|
23
|
+
type: 'richText',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: 'image',
|
|
27
|
+
type: 'upload',
|
|
28
|
+
relationTo: 'media',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'tags',
|
|
32
|
+
type: 'array',
|
|
33
|
+
fields: [
|
|
34
|
+
{
|
|
35
|
+
name: 'tag',
|
|
36
|
+
type: 'text',
|
|
37
|
+
required: true,
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'link',
|
|
43
|
+
type: 'text',
|
|
44
|
+
admin: {
|
|
45
|
+
description: 'External URL for the project (e.g. GitHub repo or live demo)',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seed script for the portfolio template.
|
|
3
|
+
* Creates 3 sample projects via the RevealUI REST API.
|
|
4
|
+
*
|
|
5
|
+
* Usage: pnpm db:seed (requires the dev server to be running)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const API_URL = process.env.NEXT_PUBLIC_SERVER_URL || 'http://localhost:4000';
|
|
9
|
+
|
|
10
|
+
interface SeedProject {
|
|
11
|
+
title: string;
|
|
12
|
+
slug: string;
|
|
13
|
+
description: string;
|
|
14
|
+
tags: Array<{ tag: string }>;
|
|
15
|
+
link: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const projects: SeedProject[] = [
|
|
19
|
+
{
|
|
20
|
+
title: 'Weather Dashboard',
|
|
21
|
+
slug: 'weather-dashboard',
|
|
22
|
+
description:
|
|
23
|
+
'A real-time weather dashboard built with React and a public weather API. Displays forecasts, radar maps, and severe weather alerts.',
|
|
24
|
+
tags: [{ tag: 'React' }, { tag: 'TypeScript' }, { tag: 'API' }],
|
|
25
|
+
link: 'https://github.com/example/weather-dashboard',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
title: 'Task Manager CLI',
|
|
29
|
+
slug: 'task-manager-cli',
|
|
30
|
+
description:
|
|
31
|
+
'A command-line task manager written in TypeScript with SQLite storage, priority sorting, and due date reminders.',
|
|
32
|
+
tags: [{ tag: 'TypeScript' }, { tag: 'Node.js' }, { tag: 'CLI' }],
|
|
33
|
+
link: 'https://github.com/example/task-manager-cli',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
title: 'Design System',
|
|
37
|
+
slug: 'design-system',
|
|
38
|
+
description:
|
|
39
|
+
'A component library with 30+ accessible UI primitives, built with Tailwind CSS and documented with Storybook.',
|
|
40
|
+
tags: [{ tag: 'React' }, { tag: 'Tailwind CSS' }, { tag: 'Storybook' }],
|
|
41
|
+
link: 'https://github.com/example/design-system',
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
const log = (...args: unknown[]) => process.stdout.write(`${args.join(' ')}\n`);
|
|
46
|
+
const logErr = (...args: unknown[]) => process.stderr.write(`${args.join(' ')}\n`);
|
|
47
|
+
|
|
48
|
+
async function seed(): Promise<void> {
|
|
49
|
+
log(`Seeding projects to ${API_URL}...`);
|
|
50
|
+
|
|
51
|
+
for (const project of projects) {
|
|
52
|
+
try {
|
|
53
|
+
const res = await fetch(`${API_URL}/api/projects`, {
|
|
54
|
+
method: 'POST',
|
|
55
|
+
headers: { 'Content-Type': 'application/json' },
|
|
56
|
+
body: JSON.stringify(project),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (res.ok) {
|
|
60
|
+
log(` Created: ${project.title}`);
|
|
61
|
+
} else {
|
|
62
|
+
const error = await res.text();
|
|
63
|
+
logErr(` Failed to create "${project.title}": ${error}`);
|
|
64
|
+
}
|
|
65
|
+
} catch (err) {
|
|
66
|
+
logErr(` Error creating "${project.title}":`, err);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
log('Seeding complete.');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
seed();
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"strict": true,
|
|
5
|
+
"strictNullChecks": true,
|
|
6
|
+
"strictFunctionTypes": true,
|
|
7
|
+
"noImplicitReturns": true,
|
|
8
|
+
"noFallthroughCasesInSwitch": true,
|
|
9
|
+
"noUncheckedIndexedAccess": true,
|
|
10
|
+
"noUnusedLocals": true,
|
|
11
|
+
"noUnusedParameters": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"verbatimModuleSyntax": true,
|
|
15
|
+
"skipLibCheck": true,
|
|
16
|
+
"allowJs": true,
|
|
17
|
+
"jsx": "preserve",
|
|
18
|
+
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
|
19
|
+
"module": "ESNext",
|
|
20
|
+
"moduleResolution": "bundler",
|
|
21
|
+
"resolveJsonModule": true,
|
|
22
|
+
"resolvePackageJsonExports": true,
|
|
23
|
+
"target": "ES2022",
|
|
24
|
+
"noEmit": true,
|
|
25
|
+
"incremental": true,
|
|
26
|
+
"plugins": [{ "name": "next" }],
|
|
27
|
+
"baseUrl": ".",
|
|
28
|
+
"paths": {
|
|
29
|
+
"@/*": ["./src/*"]
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"include": ["src", "next-env.d.ts", "next.config.mjs", "revealui.config.ts"],
|
|
33
|
+
"exclude": ["node_modules"]
|
|
34
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# RevealUI Environment Variables
|
|
2
|
+
# Copy this file to .env.local and fill in your values before running `pnpm dev`
|
|
3
|
+
|
|
4
|
+
# ─── Core ────────────────────────────────────────────────────────────────────
|
|
5
|
+
# 32+ character secret used for signing sessions and tokens
|
|
6
|
+
REVEALUI_SECRET=change-me-to-a-long-random-secret-at-least-32-chars
|
|
7
|
+
|
|
8
|
+
# Public URL of your CMS server (must match NEXT_PUBLIC_SERVER_URL)
|
|
9
|
+
REVEALUI_PUBLIC_SERVER_URL=http://localhost:4000
|
|
10
|
+
NEXT_PUBLIC_SERVER_URL=http://localhost:4000
|
|
11
|
+
|
|
12
|
+
# ─── Database ────────────────────────────────────────────────────────────────
|
|
13
|
+
# PostgreSQL connection string (NeonDB, Supabase, or local Postgres)
|
|
14
|
+
POSTGRES_URL=postgresql://postgres:postgres@localhost:5432/revealui
|
|
15
|
+
|
|
16
|
+
# ─── Storage ─────────────────────────────────────────────────────────────────
|
|
17
|
+
# Vercel Blob token for file uploads (optional — leave placeholder for local dev)
|
|
18
|
+
BLOB_READ_WRITE_TOKEN=vercel_blob_rw_placeholder
|
|
19
|
+
|
|
20
|
+
# ─── Stripe (optional) ───────────────────────────────────────────────────────
|
|
21
|
+
# Use test keys during development: https://dashboard.stripe.com/test/apikeys
|
|
22
|
+
STRIPE_SECRET_KEY=sk_test_placeholder
|
|
23
|
+
STRIPE_WEBHOOK_SECRET=whsec_placeholder
|
|
24
|
+
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_placeholder
|
|
25
|
+
|
|
26
|
+
# ─── Admin Bootstrap ─────────────────────────────────────────────────────────
|
|
27
|
+
# Used on first run only to create the initial admin account
|
|
28
|
+
REVEALUI_ADMIN_EMAIL=admin@example.com
|
|
29
|
+
REVEALUI_ADMIN_PASSWORD=changeme-min-12-chars
|
|
30
|
+
|
|
31
|
+
# ─── Branding (Enterprise white-label) ───────────────────────────────────────
|
|
32
|
+
# Customize the admin UI for your brand. Enterprise license required for full white-label.
|
|
33
|
+
# REVEALUI_BRAND_NAME=My CMS
|
|
34
|
+
# REVEALUI_BRAND_LOGO_URL=https://example.com/logo.png
|
|
35
|
+
# REVEALUI_BRAND_PRIMARY_COLOR=#ea580c
|
|
36
|
+
# REVEALUI_SHOW_POWERED_BY=false
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules/
|
|
3
|
+
.pnp
|
|
4
|
+
.pnp.js
|
|
5
|
+
|
|
6
|
+
# Next.js
|
|
7
|
+
.next/
|
|
8
|
+
out/
|
|
9
|
+
build/
|
|
10
|
+
|
|
11
|
+
# Environment variables
|
|
12
|
+
.env
|
|
13
|
+
.env.local
|
|
14
|
+
.env.development.local
|
|
15
|
+
.env.test.local
|
|
16
|
+
.env.production.local
|
|
17
|
+
|
|
18
|
+
# Turbo
|
|
19
|
+
.turbo/
|
|
20
|
+
|
|
21
|
+
# Misc
|
|
22
|
+
.DS_Store
|
|
23
|
+
*.pem
|
|
24
|
+
npm-debug.log*
|
|
25
|
+
yarn-debug.log*
|
|
26
|
+
yarn-error.log*
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{PROJECT_NAME}}",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "next dev --port 4000",
|
|
8
|
+
"build": "next build",
|
|
9
|
+
"start": "next start --port 4000",
|
|
10
|
+
"lint": "biome check .",
|
|
11
|
+
"typecheck": "tsc --noEmit",
|
|
12
|
+
"test": "vitest run",
|
|
13
|
+
"db:init": "revealui cms",
|
|
14
|
+
"db:migrate": "drizzle-kit migrate",
|
|
15
|
+
"db:seed": "tsx src/seed.ts"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@revealui/core": "latest",
|
|
19
|
+
"@revealui/config": "latest",
|
|
20
|
+
"@revealui/db": "latest",
|
|
21
|
+
"@revealui/auth": "latest",
|
|
22
|
+
"@revealui/contracts": "latest",
|
|
23
|
+
"next": "^16.0.0",
|
|
24
|
+
"react": "^19.0.0",
|
|
25
|
+
"react-dom": "^19.0.0",
|
|
26
|
+
"sharp": "^0.34.0",
|
|
27
|
+
"zod": "^4.0.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@biomejs/biome": "^2.0.0",
|
|
31
|
+
"@tailwindcss/postcss": "^4.1.0",
|
|
32
|
+
"tailwindcss": "^4.1.0",
|
|
33
|
+
"typescript": "^6.0.0",
|
|
34
|
+
"vitest": "^4.0.0"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import config from '@revealui/config';
|
|
2
|
+
import { buildConfig, universalPostgresAdapter } from '@revealui/core';
|
|
3
|
+
import sharp from 'sharp';
|
|
4
|
+
|
|
5
|
+
export default buildConfig({
|
|
6
|
+
serverURL: config.reveal.publicServerURL || 'http://localhost:4000',
|
|
7
|
+
secret: config.reveal.secret,
|
|
8
|
+
db: config.database.url
|
|
9
|
+
? universalPostgresAdapter({ connectionString: config.database.url })
|
|
10
|
+
: universalPostgresAdapter({ provider: 'electric' }),
|
|
11
|
+
admin: {
|
|
12
|
+
user: 'users',
|
|
13
|
+
},
|
|
14
|
+
collections: [],
|
|
15
|
+
globals: [],
|
|
16
|
+
plugins: [],
|
|
17
|
+
sharp,
|
|
18
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Metadata } from 'next';
|
|
2
|
+
import './globals.css';
|
|
3
|
+
|
|
4
|
+
export const metadata: Metadata = {
|
|
5
|
+
title: 'RevealUI App',
|
|
6
|
+
description: 'Built with RevealUI',
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
10
|
+
return (
|
|
11
|
+
<html lang="en">
|
|
12
|
+
<body>{children}</body>
|
|
13
|
+
</html>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export default function HomePage() {
|
|
2
|
+
return (
|
|
3
|
+
<main className="mx-auto max-w-xl px-4 py-16">
|
|
4
|
+
<h1 className="text-3xl font-bold tracking-tight text-gray-900">RevealUI</h1>
|
|
5
|
+
<p className="mt-3 text-gray-600">
|
|
6
|
+
Your project is running. Visit{' '}
|
|
7
|
+
<a href="/admin" className="font-medium text-accent hover:text-accent-hover">
|
|
8
|
+
/admin
|
|
9
|
+
</a>{' '}
|
|
10
|
+
to manage content.
|
|
11
|
+
</p>
|
|
12
|
+
<p className="mt-6 text-sm text-gray-500">
|
|
13
|
+
Edit <code className="rounded bg-gray-100 px-1.5 py-0.5 text-xs">src/app/page.tsx</code> to
|
|
14
|
+
customize this page.
|
|
15
|
+
</p>
|
|
16
|
+
</main>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database seed script
|
|
3
|
+
*
|
|
4
|
+
* Run with: pnpm db:seed
|
|
5
|
+
*
|
|
6
|
+
* Customize this file to populate your database with initial data.
|
|
7
|
+
* The examples below show common patterns — uncomment and adapt as needed.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// import { getClient } from '@revealui/db/client';
|
|
11
|
+
// import { users, posts } from '@revealui/db/schema';
|
|
12
|
+
|
|
13
|
+
async function seed(): Promise<void> {
|
|
14
|
+
// const db = getClient();
|
|
15
|
+
|
|
16
|
+
// Example: create an admin user
|
|
17
|
+
// await db.insert(users).values({
|
|
18
|
+
// id: crypto.randomUUID(),
|
|
19
|
+
// email: 'admin@example.com',
|
|
20
|
+
// name: 'Admin',
|
|
21
|
+
// role: 'admin',
|
|
22
|
+
// status: 'active',
|
|
23
|
+
// emailVerified: true,
|
|
24
|
+
// });
|
|
25
|
+
|
|
26
|
+
// Example: create sample content
|
|
27
|
+
// await db.insert(posts).values({
|
|
28
|
+
// id: crypto.randomUUID(),
|
|
29
|
+
// title: 'Welcome to RevealUI',
|
|
30
|
+
// slug: 'welcome',
|
|
31
|
+
// status: 'published',
|
|
32
|
+
// });
|
|
33
|
+
|
|
34
|
+
process.stdout.write('Seed complete.\n');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
seed().catch((error: unknown) => {
|
|
38
|
+
process.stderr.write(`Seed failed: ${error}\n`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"strict": true,
|
|
5
|
+
"strictNullChecks": true,
|
|
6
|
+
"strictFunctionTypes": true,
|
|
7
|
+
"noImplicitReturns": true,
|
|
8
|
+
"noFallthroughCasesInSwitch": true,
|
|
9
|
+
"noUncheckedIndexedAccess": true,
|
|
10
|
+
"noUnusedLocals": true,
|
|
11
|
+
"noUnusedParameters": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"verbatimModuleSyntax": true,
|
|
15
|
+
"skipLibCheck": true,
|
|
16
|
+
"allowJs": true,
|
|
17
|
+
"jsx": "preserve",
|
|
18
|
+
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
|
19
|
+
"module": "ESNext",
|
|
20
|
+
"moduleResolution": "bundler",
|
|
21
|
+
"resolveJsonModule": true,
|
|
22
|
+
"resolvePackageJsonExports": true,
|
|
23
|
+
"target": "ES2022",
|
|
24
|
+
"noEmit": true,
|
|
25
|
+
"incremental": true,
|
|
26
|
+
"plugins": [{ "name": "next" }],
|
|
27
|
+
"baseUrl": ".",
|
|
28
|
+
"paths": {
|
|
29
|
+
"@/*": ["./src/*"]
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"include": ["src", "next-env.d.ts", "next.config.mjs", "revealui.config.ts"],
|
|
33
|
+
"exclude": ["node_modules"]
|
|
34
|
+
}
|