Improvement/AppStore: Remove seeding from app-store-cli. (#8486)
* Remove seeding from cli * Self review fixes * Fix TS error
This commit is contained in:
parent
607ef71f9f
commit
ff859737ec
|
@ -2,6 +2,7 @@ import fs from "fs";
|
|||
import matter from "gray-matter";
|
||||
import MarkdownIt from "markdown-it";
|
||||
import type { GetStaticPaths, GetStaticPropsContext } from "next";
|
||||
import Link from "next/link";
|
||||
import path from "path";
|
||||
import { z } from "zod";
|
||||
|
||||
|
@ -33,7 +34,26 @@ const sourceSchema = z.object({
|
|||
}),
|
||||
});
|
||||
|
||||
function SingleAppPage({ data, source }: inferSSRProps<typeof getStaticProps>) {
|
||||
function SingleAppPage(props: inferSSRProps<typeof getStaticProps>) {
|
||||
// If it's not production environment, it would be a better idea to inform that the App is disabled.
|
||||
if (props.isAppDisabled) {
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
// TODO: Improve disabled App UI. This is just a placeholder.
|
||||
return (
|
||||
<div className="p-2">
|
||||
This App seems to be disabled. If you are an admin, you can enable this app from{" "}
|
||||
<Link href="/settings/admin/apps" className="cursor-pointer text-blue-500 underline">
|
||||
here
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Disabled App should give 404 any ways in production.
|
||||
return null;
|
||||
}
|
||||
|
||||
const { source, data } = props;
|
||||
return (
|
||||
<App
|
||||
name={data.name}
|
||||
|
@ -80,29 +100,43 @@ export const getStaticPaths: GetStaticPaths<{ slug: string }> = async () => {
|
|||
export const getStaticProps = async (ctx: GetStaticPropsContext) => {
|
||||
if (typeof ctx.params?.slug !== "string") return { notFound: true };
|
||||
|
||||
const app = await prisma.app.findUnique({
|
||||
const appMeta = await getAppWithMetadata({
|
||||
slug: ctx.params?.slug,
|
||||
});
|
||||
|
||||
const appFromDb = await prisma.app.findUnique({
|
||||
where: { slug: ctx.params.slug.toLowerCase() },
|
||||
});
|
||||
|
||||
if (!app) return { notFound: true };
|
||||
const isAppAvailableInFileSystem = appMeta;
|
||||
const isAppDisabled = isAppAvailableInFileSystem && (!appFromDb || !appFromDb.enabled);
|
||||
|
||||
const singleApp = await getAppWithMetadata(app);
|
||||
if (process.env.NODE_ENV !== "production" && isAppDisabled) {
|
||||
return {
|
||||
props: {
|
||||
isAppDisabled: true as const,
|
||||
data: {
|
||||
...appMeta,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (!singleApp) return { notFound: true };
|
||||
if (!appFromDb || !appMeta || isAppDisabled) return { notFound: true };
|
||||
|
||||
const isTemplate = singleApp.isTemplate;
|
||||
const appDirname = path.join(isTemplate ? "templates" : "", app.dirName);
|
||||
const isTemplate = appMeta.isTemplate;
|
||||
const appDirname = path.join(isTemplate ? "templates" : "", appFromDb.dirName);
|
||||
const README_PATH = path.join(process.cwd(), "..", "..", `packages/app-store/${appDirname}/DESCRIPTION.md`);
|
||||
const postFilePath = path.join(README_PATH);
|
||||
let source = "";
|
||||
|
||||
try {
|
||||
source = fs.readFileSync(postFilePath).toString();
|
||||
source = source.replace(/{DESCRIPTION}/g, singleApp.description);
|
||||
source = source.replace(/{DESCRIPTION}/g, appMeta.description);
|
||||
} catch (error) {
|
||||
/* If the app doesn't have a README we fallback to the package description */
|
||||
console.log(`No DESCRIPTION.md provided for: ${appDirname}`);
|
||||
source = singleApp.description;
|
||||
source = appMeta.description;
|
||||
}
|
||||
|
||||
const result = matter(source);
|
||||
|
@ -111,8 +145,8 @@ export const getStaticProps = async (ctx: GetStaticPropsContext) => {
|
|||
data.items = data.items.map((item) => {
|
||||
if (typeof item === "string") {
|
||||
return getAppAssetFullPath(item, {
|
||||
dirName: singleApp.dirName,
|
||||
isTemplate: singleApp.isTemplate,
|
||||
dirName: appMeta.dirName,
|
||||
isTemplate: appMeta.isTemplate,
|
||||
});
|
||||
}
|
||||
return item;
|
||||
|
@ -120,8 +154,9 @@ export const getStaticProps = async (ctx: GetStaticPropsContext) => {
|
|||
}
|
||||
return {
|
||||
props: {
|
||||
isAppDisabled: false as const,
|
||||
source: { content, data },
|
||||
data: singleApp,
|
||||
data: appMeta,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -4,9 +4,9 @@ import SelectInput from "ink-select-input";
|
|||
import TextInput from "ink-text-input";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import { AppMeta } from "@calcom/types/App";
|
||||
import type { AppMeta } from "@calcom/types/App";
|
||||
|
||||
import { getSlugFromAppName, BaseAppFork, Seed, generateAppFiles, getAppDirPath } from "../core";
|
||||
import { getSlugFromAppName, BaseAppFork, generateAppFiles, getAppDirPath } from "../core";
|
||||
import { getApp } from "../utils/getApp";
|
||||
import Templates from "../utils/templates";
|
||||
import Label from "./Label";
|
||||
|
@ -148,8 +148,6 @@ export const AppForm = ({
|
|||
oldSlug: givenSlug,
|
||||
});
|
||||
|
||||
await Seed.update({ slug, category: category, oldSlug: givenSlug, isTemplate });
|
||||
|
||||
await generateAppFiles();
|
||||
|
||||
// FIXME: Even after CLI showing this message, it is stuck doing work before exiting
|
||||
|
@ -242,6 +240,10 @@ export const AppForm = ({
|
|||
<Text color="green">Publisher Email: </Text>
|
||||
<Text>{email}</Text>
|
||||
</Box>
|
||||
<Text bold>
|
||||
Next Step: Enable the app from http://localhost:3000/settings/admin/apps as admin user (Email:
|
||||
admin@example.com, Pass: ADMINadmin2022!)
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
|
|
|
@ -4,7 +4,7 @@ import React, { useEffect, useState } from "react";
|
|||
|
||||
import { ImportantText } from "../components/ImportantText";
|
||||
import { Message } from "../components/Message";
|
||||
import { BaseAppFork, Seed, generateAppFiles } from "../core";
|
||||
import { BaseAppFork, generateAppFiles } from "../core";
|
||||
import { getApp } from "../utils/getApp";
|
||||
|
||||
export default function DeleteForm({ slug, action }: { slug: string; action: "delete" | "delete-template" }) {
|
||||
|
@ -28,7 +28,6 @@ export default function DeleteForm({ slug, action }: { slug: string; action: "de
|
|||
if (state === "DELETION_CONFIRMATION_SUCCESSFUL") {
|
||||
(async () => {
|
||||
await BaseAppFork.delete({ slug, isTemplate });
|
||||
Seed.revert({ slug });
|
||||
await generateAppFiles();
|
||||
// successMsg({ text: `App with slug ${slug} has been deleted`, done: true });
|
||||
setState("DELETION_COMPLETED");
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
import type seedAppStoreConfig from "@calcom/prisma/seed-app-store.config.json";
|
||||
|
||||
import { APP_STORE_PATH, TEMPLATES_PATH } from "./constants";
|
||||
import execSync from "./utils/execSync";
|
||||
|
||||
|
@ -27,10 +25,6 @@ export function getAppDirPath(slug: string, isTemplate: boolean) {
|
|||
return path.join(TEMPLATES_PATH, `${slug}`);
|
||||
}
|
||||
|
||||
function absolutePath(appRelativePath: string) {
|
||||
return path.join(APP_STORE_PATH, appRelativePath);
|
||||
}
|
||||
|
||||
const updatePackageJson = ({
|
||||
slug,
|
||||
appDescription,
|
||||
|
@ -138,58 +132,6 @@ export const BaseAppFork = {
|
|||
},
|
||||
};
|
||||
|
||||
export const Seed = {
|
||||
seedConfigPath: absolutePath("../prisma/seed-app-store.config.json"),
|
||||
update: async function ({
|
||||
slug,
|
||||
category,
|
||||
oldSlug,
|
||||
isTemplate,
|
||||
}: {
|
||||
slug: string;
|
||||
category: string;
|
||||
oldSlug: string;
|
||||
isTemplate: boolean;
|
||||
}) {
|
||||
let configContent = "[]";
|
||||
try {
|
||||
if (fs.statSync(this.seedConfigPath)) {
|
||||
configContent = fs.readFileSync(this.seedConfigPath).toString();
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
let seedConfig: typeof seedAppStoreConfig = JSON.parse(configContent);
|
||||
seedConfig = seedConfig.filter((app) => app.slug !== oldSlug);
|
||||
|
||||
if (!seedConfig.find((app) => app.slug === slug)) {
|
||||
seedConfig.push({
|
||||
dirName: slug,
|
||||
categories: [category],
|
||||
slug: slug,
|
||||
type: `${slug}_${category}`,
|
||||
isTemplate: isTemplate,
|
||||
});
|
||||
}
|
||||
|
||||
// Add the message as a property to first item so that it stays always at the top
|
||||
seedConfig[0]["/*"] =
|
||||
"This file is auto-generated and updated by `yarn app-store create/edit`. Don't edit it manually";
|
||||
|
||||
// Add the message as a property to first item so that it stays always at the top
|
||||
seedConfig[0]["/*"] =
|
||||
"This file is auto-generated and updated by `yarn app-store create/edit`. Don't edit it manually";
|
||||
|
||||
fs.writeFileSync(this.seedConfigPath, JSON.stringify(seedConfig, null, 2));
|
||||
await execSync(`cd ${workspaceDir}/packages/prisma && yarn seed-app-store seed-templates`);
|
||||
},
|
||||
revert: async function ({ slug }: { slug: string }) {
|
||||
let seedConfig: typeof seedAppStoreConfig = JSON.parse(fs.readFileSync(this.seedConfigPath).toString());
|
||||
seedConfig = seedConfig.filter((app) => app.slug !== slug);
|
||||
fs.writeFileSync(this.seedConfigPath, JSON.stringify(seedConfig, null, 2));
|
||||
await execSync(`yarn workspace @calcom/prisma delete-app ${slug}`);
|
||||
},
|
||||
};
|
||||
|
||||
export const generateAppFiles = async () => {
|
||||
await execSync(`yarn ts-node --transpile-only src/build.ts`);
|
||||
};
|
||||
|
|
|
@ -5,8 +5,22 @@ import { userMetadata } from "@calcom/prisma/zod-utils";
|
|||
import type { AppFrontendPayload as App } from "@calcom/types/App";
|
||||
import type { CredentialFrontendPayload as Credential } from "@calcom/types/Credential";
|
||||
|
||||
export async function getAppWithMetadata(app: { dirName: string }) {
|
||||
const appMetadata: App | null = appStoreMetadata[app.dirName as keyof typeof appStoreMetadata] as App;
|
||||
/**
|
||||
* Get App metdata either using dirName or slug
|
||||
*/
|
||||
export async function getAppWithMetadata(app: { dirName: string } | { slug: string }) {
|
||||
let appMetadata: App | null;
|
||||
|
||||
if ("dirName" in app) {
|
||||
appMetadata = appStoreMetadata[app.dirName as keyof typeof appStoreMetadata] as App;
|
||||
} else {
|
||||
const foundEntry = Object.entries(appStoreMetadata).find(([, meta]) => {
|
||||
return meta.slug === app.slug;
|
||||
});
|
||||
if (!foundEntry) return null;
|
||||
appMetadata = foundEntry[1] as App;
|
||||
}
|
||||
|
||||
if (!appMetadata) return null;
|
||||
// Let's not leak api keys to the front end
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"/*": "This file is auto-generated and updated by `yarn app-store create/edit`. Don't edit it manually",
|
||||
"/*": "This file is deprecated now. No new entry should be added to it.",
|
||||
"dirName": "routing-forms",
|
||||
"categories": ["other"],
|
||||
"slug": "routing-forms",
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
/**
|
||||
* @deprecated
|
||||
* This file is deprecated. The only use of this file is to seed the database for E2E tests. Each test should take care of seeding it's own data going forward.
|
||||
*/
|
||||
import type { Prisma } from "@prisma/client";
|
||||
import dotEnv from "dotenv";
|
||||
import fs from "fs";
|
||||
|
|
Loading…
Reference in New Issue
Block a user