Use slug everywhere instead of app type
This commit is contained in:
parent
c563415795
commit
d563343669
|
@ -21,6 +21,7 @@ import Badge from "@components/ui/Badge";
|
|||
export default function App({
|
||||
name,
|
||||
type,
|
||||
slug,
|
||||
logo,
|
||||
body,
|
||||
categories,
|
||||
|
@ -36,6 +37,7 @@ export default function App({
|
|||
privacy,
|
||||
}: {
|
||||
name: string;
|
||||
slug: string;
|
||||
type: AppType["type"];
|
||||
isGlobal?: AppType["isGlobal"];
|
||||
logo: string;
|
||||
|
@ -60,6 +62,7 @@ export default function App({
|
|||
useGrouping: false,
|
||||
}).format(price);
|
||||
const [installedApp, setInstalledApp] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
async function getInstalledApp(appCredentialType: string) {
|
||||
const queryParam = new URLSearchParams();
|
||||
|
@ -80,8 +83,9 @@ export default function App({
|
|||
}
|
||||
}
|
||||
}
|
||||
getInstalledApp(type);
|
||||
}, [type]);
|
||||
getInstalledApp(slug);
|
||||
}, [slug]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Shell large isPublic>
|
||||
|
|
|
@ -7,8 +7,9 @@ import { getSession } from "@lib/auth";
|
|||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
req.session = await getSession({ req });
|
||||
if (req.method === "GET" && req.session && req.session.user.id && req.query) {
|
||||
const { "app-credential-type": appCredentialType } = req.query;
|
||||
if (!appCredentialType && Array.isArray(appCredentialType)) {
|
||||
const { "app-credential-type": slug } = req.query;
|
||||
|
||||
if (!slug && Array.isArray(slug)) {
|
||||
return res.status(400);
|
||||
}
|
||||
|
||||
|
@ -16,7 +17,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
try {
|
||||
const installedApp = await prisma.credential.findFirst({
|
||||
where: {
|
||||
type: appCredentialType as string,
|
||||
appId: slug as string,
|
||||
userId: userId,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -3,6 +3,27 @@ import { NextApiHandler, NextApiRequest, NextApiResponse } from "next";
|
|||
import { getSession } from "@lib/auth";
|
||||
import { HttpError } from "@lib/core/http/error";
|
||||
|
||||
function getSlugFromLegacy(legacySlug) {
|
||||
const oldTypes = ["video", "other", "calendar", "web3", "payment", "messaging"];
|
||||
|
||||
// There can be two types of legacy slug
|
||||
// - zoom_video
|
||||
// - zoomvideo
|
||||
|
||||
// Transform `zoom_video` to `zoomvideo`;
|
||||
let slug = legacySlug.split("_").join("");
|
||||
|
||||
// Transform zoomvideo to zoom
|
||||
oldTypes.some((type) => {
|
||||
const matcher = new RegExp(`(.+)${type}$`);
|
||||
if (legacySlug.match(matcher)) {
|
||||
slug = legacySlug.replace(matcher, "$1");
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return slug;
|
||||
}
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
// Check that user is authenticated
|
||||
req.session = await getSession({ req });
|
||||
|
@ -14,12 +35,13 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||
}
|
||||
|
||||
const [_appName, apiEndpoint] = args;
|
||||
const appName = _appName.split("_").join(""); // Transform `zoom_video` to `zoomvideo`;
|
||||
|
||||
const appName = getSlugFromLegacy(_appName);
|
||||
try {
|
||||
/* Absolute path didn't work */
|
||||
const handlerMap = (await import("@calcom/app-store/apps.generated")).apiHandlers;
|
||||
const handlers = await handlerMap[appName as keyof typeof handlerMap];
|
||||
const handlerKey = appName as keyof typeof handlerMap;
|
||||
console.log(handlerKey);
|
||||
const handlers = await handlerMap[handlerKey];
|
||||
const handler = handlers[apiEndpoint as keyof typeof handlers] as NextApiHandler;
|
||||
|
||||
if (typeof handler !== "function")
|
||||
|
|
|
@ -49,6 +49,7 @@ const components = {
|
|||
function SingleAppPage({ data, source }: inferSSRProps<typeof getStaticProps>) {
|
||||
return (
|
||||
<App
|
||||
slug={data.slug}
|
||||
name={data.name}
|
||||
isGlobal={data.isGlobal}
|
||||
type={data.type}
|
||||
|
|
|
@ -622,7 +622,7 @@ const loggedInViewerRouter = createProtectedRouter()
|
|||
const apps = getApps(credentials).map(
|
||||
({ credentials: _, credential: _1 /* don't leak to frontend */, ...app }) => ({
|
||||
...app,
|
||||
credentialIds: credentials.filter((c) => c.type === app.type).map((c) => c.id),
|
||||
credentialIds: credentials.filter((c) => c.appId === app.slug).map((c) => c.id),
|
||||
})
|
||||
);
|
||||
// `flatMap()` these work like `.filter()` but infers the types correctly
|
||||
|
|
|
@ -14,16 +14,29 @@ Change name and description
|
|||
|
||||
## TODO
|
||||
|
||||
- Put lowercase and - restriction only on App name
|
||||
- Add space restriction as well for Appname. Maybe look for valid dirname or slug regex
|
||||
- Merge app-store:watch and app-store commands, introduce app-store --watch
|
||||
- Get strong confirmation for deletion of app. Get the name of the app from user that he wants to delete
|
||||
- App Description Missing
|
||||
- Select Box for App Type
|
||||
- Credentials table doesn't get new entries with cli. Figure out how to do it.
|
||||
- App already exists check. Ask user to run edit/regenerate command
|
||||
- Allow deletion of App, cleaning up everything
|
||||
- folder
|
||||
- prisma credentials table
|
||||
- seed.config.json
|
||||
- Using app/config.json -> Allow Editing App Details.
|
||||
- Beta Release
|
||||
- Handle legacy apps which have dirname as something else and type as something else. type is used to do lookups with key
|
||||
- Add comment in config.json that this file shouldn't be modified manually.
|
||||
- Install button not coming
|
||||
- Put lowercase and - restriction only on slug. Keep App Name and others unchanged. Also, use slug instead of appName for dirNames
|
||||
- Add space restriction as well for Appname. Maybe look for valid dirname or slug regex
|
||||
- Get strong confirmation for deletion of app. Get the name of the app from user that he wants to delete
|
||||
- App Description Missing
|
||||
- Select Box for App Type
|
||||
- App types Validations
|
||||
- Credentials table doesn't get new entries with cli. Figure out how to do it.
|
||||
- Using app/config.json -> Allow Editing App Details.
|
||||
- Edit App Type, Description, Title, Publisher Name, Email - Name shouldn't be allowed to change as that is unique.
|
||||
|
||||
- Improvements
|
||||
- Merge app-store:watch and app-store commands, introduce app-store --watch
|
||||
- Allow inputs in non interactive way as well - That would allow easily copy pasting commands.
|
||||
- Maybe get dx to run app-store:watch
|
||||
- App already exists check. Ask user to run edit/regenerate command
|
||||
|
||||
## Roadmap
|
||||
|
||||
- Allow editing and updating app from the cal app itself - including assets uploading when developing locally.
|
||||
- Improvements in shared code across app
|
||||
- Use baseApp/api/add.ts for all apps with configuration of credentials creation and redirection URL.
|
||||
|
|
@ -113,7 +113,7 @@ const CreateApp = ({ noDbUpdate }) => {
|
|||
const fieldName = fields[inputIndex]?.name || "";
|
||||
const fieldValue = appInputData[fieldName] || "";
|
||||
const appName = appInputData["appName"];
|
||||
const appType = appInputData["appType"];
|
||||
const appType = `${appName}_${appInputData["appType"]}`;
|
||||
const appTitle = appInputData["appTitle"];
|
||||
const publisherName = appInputData["publisherName"];
|
||||
const publisherEmail = appInputData["publisherEmail"];
|
||||
|
|
|
@ -8,11 +8,12 @@ import _package from "./package.json";
|
|||
export const metadata = {
|
||||
description: _package.description,
|
||||
category: "other",
|
||||
// FIXME: Currently for an app to be shown as installed, it must have this variable set. Either hardcoded or if it depends on some env variable, that should be checked here
|
||||
installed: true,
|
||||
rating: 0,
|
||||
reviews: 0,
|
||||
trending: true,
|
||||
verified: true,
|
||||
email: "CLI_BASE__PUBLISHER_EMAIL",
|
||||
...config,
|
||||
} as App;
|
||||
|
||||
|
|
|
@ -2,16 +2,23 @@ import type { NextApiRequest, NextApiResponse } from "next";
|
|||
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
import appConfig from "../config.json";
|
||||
|
||||
// TODO: There is a lot of code here that would be used by almost all apps
|
||||
// - Login Validation
|
||||
// - Looking up credential.
|
||||
// - Creating credential would be specific to app, so there can be just createCredential method that app can expose
|
||||
// - Redirection after successful installation can also be configured by app
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (!req.session?.user?.id) {
|
||||
return res.status(401).json({ message: "You must be logged in to do this" });
|
||||
}
|
||||
// TODO: Define appType once and import everywhere
|
||||
const appType = "CLI_BASE__APP_NAME_CLI_BASE__APP_TYPE";
|
||||
const slug = appConfig.slug;
|
||||
try {
|
||||
const alreadyInstalled = await prisma.credential.findFirst({
|
||||
where: {
|
||||
type: appType,
|
||||
appId: slug,
|
||||
userId: req.session.user.id,
|
||||
},
|
||||
});
|
||||
|
@ -20,21 +27,23 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
}
|
||||
const installation = await prisma.credential.create({
|
||||
data: {
|
||||
type: appType,
|
||||
// TODO: Why do we need type in Credential? Why can't we simply use appId
|
||||
type: slug,
|
||||
key: {},
|
||||
userId: req.session.user.id,
|
||||
appId: "CLI_BASE__APP_NAME",
|
||||
appId: slug,
|
||||
},
|
||||
});
|
||||
if (!installation) {
|
||||
throw new Error("Unable to create user credential for CLI_BASE__APP_NAME");
|
||||
throw new Error(`Unable to create user credential for ${slug}`);
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
return res.status(500).json({ message: error.message });
|
||||
}
|
||||
return res.status(500);
|
||||
}
|
||||
|
||||
return res.status(200).json({ url: "/apps/zapier/setup" });
|
||||
return res.status(200).json({ url: "/apps/installed" });
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import type { InstallAppButtonProps } from "@calcom/app-store/types";
|
||||
|
||||
import useAddAppMutation from "../../_utils/useAddAppMutation";
|
||||
import appConfig from "../config.json";
|
||||
|
||||
export default function InstallAppButton(props: InstallAppButtonProps) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
const mutation = useAddAppMutation("CLI_BASE__APP_NAME_CLI_BASE__APP_TYPE");
|
||||
const mutation = useAddAppMutation(appConfig.slug);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -98,6 +98,12 @@ if (isInWatchMode) {
|
|||
debouncedGenerateFiles();
|
||||
}
|
||||
})
|
||||
.on("change", (filePath) => {
|
||||
if (filePath.endsWith("config.json")) {
|
||||
console.log("Config file changed");
|
||||
debouncedGenerateFiles();
|
||||
}
|
||||
})
|
||||
.on("unlinkDir", (dirPath) => {
|
||||
const appName = getAppName(dirPath);
|
||||
if (appName) {
|
||||
|
|
|
@ -8,11 +8,11 @@ import _package from "./package.json";
|
|||
export const metadata = {
|
||||
description: _package.description,
|
||||
category: "other",
|
||||
installed: true,
|
||||
rating: 0,
|
||||
reviews: 0,
|
||||
trending: true,
|
||||
verified: true,
|
||||
email: "CLI_BASE__PUBLISHER_EMAIL",
|
||||
...config,
|
||||
} as App;
|
||||
|
||||
|
|
|
@ -2,16 +2,18 @@ import type { NextApiRequest, NextApiResponse } from "next";
|
|||
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
import appConfig from "../config.json";
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (!req.session?.user?.id) {
|
||||
return res.status(401).json({ message: "You must be logged in to do this" });
|
||||
}
|
||||
// TODO: Define appType once and import everywhere
|
||||
const appType = "CLI_BASE__APP_NAME_CLI_BASE__APP_TYPE";
|
||||
const slug = appConfig.slug;
|
||||
try {
|
||||
const alreadyInstalled = await prisma.credential.findFirst({
|
||||
where: {
|
||||
type: appType,
|
||||
appId: slug,
|
||||
userId: req.session.user.id,
|
||||
},
|
||||
});
|
||||
|
@ -20,17 +22,19 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
}
|
||||
const installation = await prisma.credential.create({
|
||||
data: {
|
||||
type: appType,
|
||||
// TODO: Why do we need type in Credential? Why can't we simply use appId
|
||||
type: slug,
|
||||
key: {},
|
||||
userId: req.session.user.id,
|
||||
appId: "CLI_BASE__APP_NAME",
|
||||
appId: slug,
|
||||
},
|
||||
});
|
||||
if (!installation) {
|
||||
throw new Error("Unable to create user credential for CLI_BASE__APP_NAME");
|
||||
throw new Error(`Unable to create user credential for ${slug}`);
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
return res.status(500).json({ message: error.message });
|
||||
}
|
||||
return res.status(500);
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import type { InstallAppButtonProps } from "@calcom/app-store/types";
|
||||
|
||||
import useAddAppMutation from "../../_utils/useAddAppMutation";
|
||||
import appConfig from "../config.json";
|
||||
|
||||
export default function InstallAppButton(props: InstallAppButtonProps) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
const mutation = useAddAppMutation("CLI_BASE__APP_NAME_CLI_BASE__APP_TYPE");
|
||||
const mutation = useAddAppMutation(appConfig.slug);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "demo",
|
||||
"title": "it's a demo app",
|
||||
"type": "other",
|
||||
"type": "demo_other",
|
||||
"slug": "demo",
|
||||
"imageSrc": "/api/app-store/demo/icon.svg",
|
||||
"logo": "/api/app-store/demo/icon.svg",
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
width="640"
|
||||
height="360"
|
||||
src="https://www.loom.com/embed/f8d2cd9b2ac74f0c916f20c4441bd1da"
|
||||
frameborder="0"
|
||||
webkitallowfullscreen
|
||||
mozallowfullscreen
|
||||
allowfullscreen></iframe>
|
||||
frameBorder="0"
|
||||
webkitallowfullscreen="true"
|
||||
mozallowfullscreen="true"
|
||||
allowFullScreen></iframe>
|
||||
|
||||
Looking to honor May 4th? Search no further. Download this app to make your booking success page resemble a long time ago in a galaxy far far away.
|
||||
|
|
|
@ -4,5 +4,11 @@
|
|||
"dirName": "demo",
|
||||
"categories": ["other"],
|
||||
"type": "demo_other"
|
||||
},
|
||||
{
|
||||
"name": "demovideo",
|
||||
"dirName": "demovideo",
|
||||
"categories": ["video"],
|
||||
"type": "demovideo_video"
|
||||
}
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue
Block a user