@npio/cli
Version:
A free visual website editor, powered with your own SolidJS components.
602 lines (567 loc) • 17.5 kB
text/typescript
import { confirm, group, intro, outro, spinner } from "@clack/prompts";
import { createId } from "@paralleldrive/cuid2";
import { rgbVal } from "nitropage/internals";
import { hashPassword, useDatabase } from "nitropage/server";
import c from "picocolors";
import type sade from "sade";
export const demoCommands = function (prog: sade.Sade) {
prog
.command("demo")
.describe(
"Cleans up the database, media and fonts and seeds the database with example data",
)
.option(
"--force, -f",
"Don't show a confirmation dialog before deleting everything",
)
.action(async function ({ force }) {
intro("Demo mode preparation");
if (!force) {
const result = await group(
{
ask1: () =>
confirm({
message: `Do you really wanna ${c.bold(
"delete all data",
)} and replace it with example data?`,
initialValue: false,
}),
ask2: () =>
confirm({
message: `Are you 100% sure?`,
initialValue: false,
}),
},
{
onCancel(opts) {
outro("That was close!");
process.exit();
},
},
);
if (!result.ask1 || !result.ask2) {
outro("That was close!");
process.exit();
}
}
const s = spinner();
s.start("Cleaning up all existing data");
const client = useDatabase();
// Delete tables with no dependencies
await client.nitroElement.deleteMany();
await client.nitroElementSlot.deleteMany();
await client.nitroFontFace.deleteMany();
await client.nitroLayoutSlot.deleteMany();
await client.nitroMedia.deleteMany();
await client.nitroPageRevision.deleteMany();
await client.nitroPreset.deleteMany();
await client.nitroSetting.deleteMany();
// Delete all other tables
await client.nitroElementHistory.deleteMany(); // After NitroElement
await client.nitroFont.deleteMany(); // After NitroFontFace
await client.nitroPage.deleteMany(); // After NitroLayoutSlot, NitroPageRevision, NitroElementHistory
await client.nitroProject.deleteMany(); // After NitroPage
await client.nitroUser.deleteMany(); // After NitroProject
s.stop("Cleanup done.");
s.start("Seeding example data");
const hashedPassword = await hashPassword(`1234`);
const user = await client.nitroUser.create({
data: {
email: "demo@example.com",
username: "admin",
password: hashedPassword,
admin: true,
},
});
const project = await client.nitroProject.create({
data: {
title: "Demo",
domain: "",
publicId: createId(),
},
});
const colors = {
white: {
css: "",
rgb: { r: 255, g: 255, b: 255 },
id: "rp33x4o7czcumt74fu529o2l",
},
accent: {
css: "",
rgb: { r: 68, g: 21, b: 112 },
id: "ozkonvsv3eeb839w3quu70sn",
},
accent2: {
css: "",
rgb: { r: 145, g: 71, b: 237 },
id: "w4d4isw49js3waaiwxsdi9fw",
},
accent3: {
css: "",
rgb: { r: 181, g: 144, b: 249 },
id: "fgytns2iopaiwk0w63lze1fw",
},
slate: {
css: "",
rgb: { r: 229, g: 231, b: 235 },
id: "ajwmhd2ql2shvwue35fqz0bv",
},
};
for (const color of Object.keys(colors)) {
let c = color as keyof typeof colors;
colors[c].css = rgbVal(colors[c].rgb)!;
}
await client.nitroSetting.create({
data: {
name: "colors",
value: JSON.stringify({
[colors.white.id]: { light: colors.white.rgb },
[colors.accent.id]: { light: colors.accent.rgb },
[colors.accent2.id]: { light: colors.accent2.rgb },
[colors.accent3.id]: { light: colors.accent3.rgb },
[colors.slate.id]: { light: colors.slate.rgb },
}),
project: {
connect: {
id: project.id,
},
},
},
});
const INTRO_BUTTON_DATA = ({
href: url,
text,
icon,
}: {
href: string;
text: string;
icon: string;
}) => ({
link: { override: true, value: { content: url } },
iconSvg: {
override: true,
value: {
content: icon,
},
},
content: {
override: true,
value: { content: `<span class="block">${text}</span>` },
},
size: { override: true, value: { selected: "lg" } },
textColor: {
override: true,
value: colors.accent,
},
bgColor: {
override: true,
value: {
...colors.white,
alpha: 0.9,
},
},
bgColorHover: {
override: true,
value: colors.white,
},
});
const HEADER_BLUEPRINT = "pynj0gs5qlwztg60j596bkgm";
const FOOTER_BLUEPRINT = "b0ailno63q17afjl6wr83xz1";
const SECTION_BLUEPRINT = "usedfsqfwa4g4aedh2wjkqkp";
const INTRO_BLUEPRINT = "ae8f3r696oky0tpbdlu1accp";
const BUTTON_BLUEPRINT = "m2vqbubuu61s8cu8l9z3cllu";
const CONTAINER_BLUEPRINT = "oyf5ut4svjmudc6kl6hhm69h";
const HEADING_BLUEPRINT = "zk7vp8rahquyz034aec4h2on";
const FEATURE_BLUEPRINT = "vt5a4mjfbbbzn5d6y08pho35";
const NARROW_BLUEPRINT = "ba0jpedvc304eoz55l5mkgpu";
const MARKDOWN_BLUEPRINT = "zcvngz3018ov60rtf8kdgqgs";
const [titleFont, bodyFont, serifFont] =
await client.nitroFont.createManyAndReturn({
data: [
{
publicId: createId(),
family: "Jost",
cdn: true,
projectId: project.id,
},
{
publicId: createId(),
family: "Barlow",
cdn: true,
projectId: project.id,
},
{
publicId: createId(),
family: "Noto Serif",
cdn: true,
projectId: project.id,
},
],
});
await client.nitroSetting.create({
data: {
name: "defaultFont",
value: JSON.stringify(bodyFont.id),
projectId: project.id,
},
});
await client.nitroSetting.create({
data: {
name: "rteTextColors",
value: JSON.stringify({ dyn1: colors.accent2 }),
projectId: project.id,
},
});
await client.nitroSetting.create({
data: {
name: "blueprintDefaults",
value: JSON.stringify({
[HEADER_BLUEPRINT]: {
title: {
value: {
content: "Nitropage Demo",
},
},
titleFont: {
value: {
id: titleFont.id,
publicId: titleFont.publicId,
},
},
titleColor: {
value: colors.white,
},
linkColor: {
value: colors.white,
},
overlayColor: {
value: colors.accent2,
},
links: {
value: {
byId: {
vvvkcdop6jbb9nwb3ifb7rhs: {
url: { content: "/" },
text: { content: '<span class="block">Home</span>' },
},
plohqpgsy6hgqhlrjiz8j0jd: {
url: { content: "https://nitropage.org" },
text: {
content:
'<span class="block">Visit nitropage.org</span>',
},
},
},
ids: ["vvvkcdop6jbb9nwb3ifb7rhs", "plohqpgsy6hgqhlrjiz8j0jd"],
},
},
},
[FOOTER_BLUEPRINT]: {
title: {
value: { content: "Nitropage Demo" },
},
titleFont: {
value: {
id: titleFont.id,
publicId: titleFont.publicId,
},
},
copyrightPrefix: {
value: { content: "Nitropage" },
},
copyrightSuffix: {
value: { content: "" },
},
},
[INTRO_BLUEPRINT]: {
titleFont: {
value: {
id: titleFont.id,
publicId: titleFont.publicId,
},
},
},
[CONTAINER_BLUEPRINT]: {
topPadding: { value: { selected: "lg" } },
bottomPadding: { value: { selected: "lg" } },
},
[HEADING_BLUEPRINT]: {
font: {
value: {
id: titleFont.id,
publicId: titleFont.publicId,
},
},
size: { value: { selected: "lg" }, override: true },
},
[MARKDOWN_BLUEPRINT]: {
headingFont: {
value: {
id: titleFont.id,
publicId: titleFont.publicId,
},
},
},
}),
project: {
connect: {
id: project.id,
},
},
},
});
const page = await client.nitroPage.create({
data: {
project: {
connect: {
id: project.id,
},
},
},
});
const revision = await client.nitroPageRevision.create({
data: {
title: "Nitropage Demo",
urlPath: "/",
publishedPage: {
connect: {
id: page.id,
},
},
page: {
connect: {
id: page.id,
},
},
},
});
const createSlot = async (key: string, revision?: any) => {
return await client.nitroElementSlot.create({
data: {
key,
parentPageRevision: revision
? {
connect: {
id: revision.id,
},
}
: undefined,
},
});
};
const slotPositions = {} as Record<string, number>;
const createElement = async function (
blueprint: string,
data: any,
parentSlot: { id: string },
slot?: { id: string },
) {
slotPositions[parentSlot.id] ??= 0;
const position = slotPositions[parentSlot.id]++;
return await client.nitroElement.create({
data: {
blueprintId: blueprint,
position,
data: JSON.stringify(data),
page: {
connect: {
id: revision.id,
},
},
parentSlot: {
connect: {
id: parentSlot.id,
},
},
slots: slot
? {
connect: {
id: slot.id,
},
}
: undefined,
history: {
create: {
page: {
connect: {
id: page.id,
},
},
},
},
},
});
};
const mainSlot = await createSlot("default", revision);
const introSectionSlot = await createSlot("default");
const introSlot = await createSlot("default");
await createElement(HEADER_BLUEPRINT, {}, mainSlot);
await createElement(
SECTION_BLUEPRINT,
{
backgroundColor: {
value: colors.accent,
override: true,
},
zIndex: { value: { content: 1 }, override: true },
shadow: { value: { selected: "md" }, override: true },
bottomMask: { value: { selected: "wavy3" }, override: true },
},
mainSlot,
introSectionSlot,
);
await createElement(
INTRO_BLUEPRINT,
{
title: {
value: {
content: '<span class="block">Nitropage Demo</span>',
},
override: true,
},
titleColor: {
value: colors.white,
override: true,
},
descriptionColor: {
value: colors.accent3,
override: true,
},
description: {
value: {
content:
'<span class="block">Welcome to the Nitropage demo instance!</span><span class="block">Try it out without hesitation, completely for free.</span>',
},
override: true,
},
},
introSectionSlot,
introSlot,
);
await createElement(
BUTTON_BLUEPRINT,
INTRO_BUTTON_DATA({
text: "Open the Administration",
href: `/admin/project/${project.id}`,
icon: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-login"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M15 8v-2a2 2 0 0 0 -2 -2h-7a2 2 0 0 0 -2 2v12a2 2 0 0 0 2 2h7a2 2 0 0 0 2 -2v-2" /><path d="M21 12h-13l3 -3" /><path d="M11 15l-3 -3" /></svg>',
}),
introSlot,
);
await createElement(
BUTTON_BLUEPRINT,
INTRO_BUTTON_DATA({
text: "Edit this page",
href: `/admin/editor/${page.id}`,
icon: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-edit"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M7 7h-1a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-1" /><path d="M20.385 6.585a2.1 2.1 0 0 0 -2.97 -2.97l-8.415 8.385v3h3l8.385 -8.415z" /><path d="M16 5l3 3" /></svg>',
}),
introSlot,
);
const featureSectionSlot = await createSlot("default");
await createElement(
SECTION_BLUEPRINT,
{
backgroundColor: {
value: colors.slate,
override: true,
},
},
mainSlot,
featureSectionSlot,
);
const featureContainerSlot = await createSlot("default");
await createElement(
CONTAINER_BLUEPRINT,
{
topPadding: { value: { selected: "xl" }, override: true },
},
featureSectionSlot,
featureContainerSlot,
);
await createElement(
HEADING_BLUEPRINT,
{
content: {
value: {
content:
'<span class="block">A <strong class="ql-color-g1"><em>hairy</em></strong> headline</span>',
},
override: true,
},
},
featureContainerSlot,
);
await createElement(
FEATURE_BLUEPRINT,
{
textColor: {
override: true,
value: colors.accent,
},
iconColor: {
value: colors.accent2,
override: true,
},
iconBackgroundColor: {
value: colors.white,
override: true,
},
},
featureContainerSlot,
);
const markdownSectionSlot = await createSlot("default");
await createElement(
SECTION_BLUEPRINT,
{
backgroundColor: {
value: colors.accent3,
override: true,
},
shadow: { value: { selected: "md" }, override: true },
},
mainSlot,
markdownSectionSlot,
);
const markdownContainerSlot = await createSlot("default");
await createElement(
CONTAINER_BLUEPRINT,
{},
markdownSectionSlot,
markdownContainerSlot,
);
const markdownNarrowSlot = await createSlot("default");
await createElement(
NARROW_BLUEPRINT,
{},
markdownContainerSlot,
markdownNarrowSlot,
);
await createElement(
MARKDOWN_BLUEPRINT,
{
content: {
value: {
content: `> There is only one corner of the universe you can be certain of improving, and that's your own self.
>
> — Aldous Huxley`,
},
override: true,
},
font: {
value: {
id: serifFont.id,
publicId: serifFont.publicId,
},
override: true,
},
borderColor: {
value: colors.accent,
override: true,
},
},
markdownNarrowSlot,
);
await createElement(FOOTER_BLUEPRINT, {}, mainSlot);
s.stop("Seeding finished.");
outro("Everything done.");
});
};