From 6efb02fa328d4d70048cfa343fc2396506a53484 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Fri, 13 Jan 2023 15:21:54 +0100 Subject: [PATCH] wip: trpc --- apps/client/src/lib/common.ts | 9 + .../src/routes/sources/[id]/+layout.svelte | 41 +++ .../client/src/routes/sources/[id]/+layout.ts | 35 ++ .../src/routes/sources/[id]/+page.svelte | 17 + .../sources/[id]/components/Github.svelte | 264 ++++++++++++++ .../sources/[id]/components/Gitlab.svelte | 333 ++++++++++++++++++ .../routes/sources/[id]/components/New.svelte | 62 ++++ .../sources/[id]/components/Source.svelte | 58 +++ apps/server/src/trpc/index.ts | 6 +- apps/server/src/trpc/routers/index.ts | 1 + apps/server/src/trpc/routers/sources/index.ts | 223 ++++++++++++ 11 files changed, 1047 insertions(+), 2 deletions(-) create mode 100644 apps/client/src/routes/sources/[id]/+layout.svelte create mode 100644 apps/client/src/routes/sources/[id]/+layout.ts create mode 100644 apps/client/src/routes/sources/[id]/+page.svelte create mode 100644 apps/client/src/routes/sources/[id]/components/Github.svelte create mode 100644 apps/client/src/routes/sources/[id]/components/Gitlab.svelte create mode 100644 apps/client/src/routes/sources/[id]/components/New.svelte create mode 100644 apps/client/src/routes/sources/[id]/components/Source.svelte create mode 100644 apps/server/src/trpc/routers/sources/index.ts diff --git a/apps/client/src/lib/common.ts b/apps/client/src/lib/common.ts index 8c2361a0c..6fa60b03b 100644 --- a/apps/client/src/lib/common.ts +++ b/apps/client/src/lib/common.ts @@ -3,6 +3,15 @@ import { addToast } from './store'; import Cookies from 'js-cookie'; export const asyncSleep = (delay: number) => new Promise((resolve) => setTimeout(resolve, delay)); +export function dashify(str: string, options?: any): string { + if (typeof str !== 'string') return str; + return str + .trim() + .replace(/\W/g, (m) => (/[À-ž]/.test(m) ? m : '-')) + .replace(/^-+|-+$/g, '') + .replace(/-{2,}/g, (m) => (options && options.condense ? '-' : m)) + .toLowerCase(); +} export function errorNotification(error: any | { message: string }): void { if (error instanceof Error) { console.error(error.message) diff --git a/apps/client/src/routes/sources/[id]/+layout.svelte b/apps/client/src/routes/sources/[id]/+layout.svelte new file mode 100644 index 000000000..d8ddbd31a --- /dev/null +++ b/apps/client/src/routes/sources/[id]/+layout.svelte @@ -0,0 +1,41 @@ + + +{#if id !== 'new' && $appSession.teamId === '0'} + + Delete +{/if} + diff --git a/apps/client/src/routes/sources/[id]/+layout.ts b/apps/client/src/routes/sources/[id]/+layout.ts new file mode 100644 index 000000000..34c9d07e9 --- /dev/null +++ b/apps/client/src/routes/sources/[id]/+layout.ts @@ -0,0 +1,35 @@ +import { error } from '@sveltejs/kit'; +import { trpc } from '$lib/store'; +import type { LayoutLoad } from './$types'; +import { redirect } from '@sveltejs/kit'; + +export const load: LayoutLoad = async ({ params, url }) => { + const { pathname } = new URL(url); + const { id } = params; + try { + const source = await trpc.sources.getSourceById.query({ id }); + if (!source) { + throw redirect(307, '/sources'); + } + + // if ( + // configurationPhase && + // pathname !== `/applications/${params.id}/configuration/${configurationPhase}` + // ) { + // throw redirect(302, `/applications/${params.id}/configuration/${configurationPhase}`); + // } + return { + source + }; + } catch (err) { + if (err instanceof Error) { + throw error(500, { + message: 'An unexpected error occurred, please try again later.' + '

' + err.message + }); + } + + throw error(500, { + message: 'An unexpected error occurred, please try again later.' + }); + } +}; diff --git a/apps/client/src/routes/sources/[id]/+page.svelte b/apps/client/src/routes/sources/[id]/+page.svelte new file mode 100644 index 000000000..f5f9de947 --- /dev/null +++ b/apps/client/src/routes/sources/[id]/+page.svelte @@ -0,0 +1,17 @@ + + +{#if id === 'new'} + +{:else} + +{/if} diff --git a/apps/client/src/routes/sources/[id]/components/Github.svelte b/apps/client/src/routes/sources/[id]/components/Github.svelte new file mode 100644 index 000000000..b18b0a892 --- /dev/null +++ b/apps/client/src/routes/sources/[id]/components/Github.svelte @@ -0,0 +1,264 @@ + + +
+ {#if !source.githubAppId} +
+
+

General

+ {#if !source.githubAppId} +
+ +
+ {/if} +
+
+ + + + + + + + + + + changeSettings('isSystemWide', false)} + title="System Wide Git" + description="System Wide Git are available to all the users in your Coolify instance.

Use with caution, as it can be a security risk." + /> +
+
+ {:else if source.githubApp?.installationId} +
+
+

General

+ {#if $appSession.isAdmin && $appSession.teamId === '0'} +
+ + "Change GitHub App Settings" +
+ {/if} +
+
+ + + + + + + + + + + {#if $appSession.isAdmin} + changeSettings('isSystemWide', true)} + title="System Wide Git Source" + description="System Wide Git Sources are available to all the users in your Coolify instance.

Use with caution, as it can be a security risk." + /> + {/if} +
+
+ {:else} + + {/if} +
diff --git a/apps/client/src/routes/sources/[id]/components/Gitlab.svelte b/apps/client/src/routes/sources/[id]/components/Gitlab.svelte new file mode 100644 index 000000000..79f55c5c4 --- /dev/null +++ b/apps/client/src/routes/sources/[id]/components/Gitlab.svelte @@ -0,0 +1,333 @@ + + +
+
+
+

General

+
+ {#if $appSession.isAdmin} + + {#if source.gitlabAppId} + + {:else} + + {/if} + {/if} +
+
+
+ {#if !source.gitlabAppId} + Documentation and detailed instructions. +
+ + +
+ + {#if applicationType === 'group'} +
+ + +
+ {/if} + {/if} + +
+
+ + +
+
+ {#if source.gitlabApp.groupName} +
+ + +
+ {/if} +
+ + +
+
+ + +
+ {#if selfHosted} +
+ + +
+
+ + +
+ {/if} +
+
+ +
+ +
+ +
+ + +
+
+ + +
+
+
+
diff --git a/apps/client/src/routes/sources/[id]/components/New.svelte b/apps/client/src/routes/sources/[id]/components/New.svelte new file mode 100644 index 000000000..113f6d0e3 --- /dev/null +++ b/apps/client/src/routes/sources/[id]/components/New.svelte @@ -0,0 +1,62 @@ + + +
+
+
+ New Git Source +
+
+
+
+
+
Select a git type
+
+ + +
+
+ {#if source?.type} +
+ {#if source.type === 'github'} + + {:else if source.type === 'gitlab'} + + {/if} +
+ {/if} +
diff --git a/apps/client/src/routes/sources/[id]/components/Source.svelte b/apps/client/src/routes/sources/[id]/components/Source.svelte new file mode 100644 index 000000000..4097720c5 --- /dev/null +++ b/apps/client/src/routes/sources/[id]/components/Source.svelte @@ -0,0 +1,58 @@ + + +
+
+
+ Configuration +
+ {source.name} +
+ {#if source?.type === 'gitlab'} + + + + {:else if source?.type === 'github'} + + + + {/if} +
+
+ {#if source.type === 'github'} + + {:else if source.type === 'gitlab'} + + {/if} +
diff --git a/apps/server/src/trpc/index.ts b/apps/server/src/trpc/index.ts index a5bc3e969..d3944f9f5 100644 --- a/apps/server/src/trpc/index.ts +++ b/apps/server/src/trpc/index.ts @@ -7,7 +7,8 @@ import { dashboardRouter, applicationsRouter, servicesRouter, - databasesRouter + databasesRouter, + sourcesRouter } from './routers'; export const appRouter = router({ @@ -16,7 +17,8 @@ export const appRouter = router({ dashboard: dashboardRouter, applications: applicationsRouter, services: servicesRouter, - databases: databasesRouter + databases: databasesRouter, + sources: sourcesRouter }); export type AppRouter = typeof appRouter; diff --git a/apps/server/src/trpc/routers/index.ts b/apps/server/src/trpc/routers/index.ts index 0e16e9c41..b21ff2dca 100644 --- a/apps/server/src/trpc/routers/index.ts +++ b/apps/server/src/trpc/routers/index.ts @@ -4,3 +4,4 @@ export * from './settings'; export * from './applications'; export * from './services'; export * from './databases'; +export * from './sources'; diff --git a/apps/server/src/trpc/routers/sources/index.ts b/apps/server/src/trpc/routers/sources/index.ts new file mode 100644 index 000000000..05ff96b51 --- /dev/null +++ b/apps/server/src/trpc/routers/sources/index.ts @@ -0,0 +1,223 @@ +import { z } from 'zod'; +import { privateProcedure, router } from '../../trpc'; +import { decrypt, encrypt } from '../../../lib/common'; +import { prisma } from '../../../prisma'; + +import cuid from 'cuid'; + +export const sourcesRouter = router({ + save: privateProcedure + .input( + z.object({ + id: z.string(), + name: z.string(), + htmlUrl: z.string(), + apiUrl: z.string(), + customPort: z.number(), + customUser: z.string(), + isSystemWide: z.boolean().default(false) + }) + ) + .mutation(async ({ input, ctx }) => { + let { id, name, htmlUrl, apiUrl, customPort, customUser, isSystemWide } = input; + if (customPort) customPort = Number(customPort); + await prisma.gitSource.update({ + where: { id }, + data: { name, htmlUrl, apiUrl, customPort, customUser, isSystemWide } + }); + }), + newGitHubApp: privateProcedure + .input( + z.object({ + id: z.string(), + name: z.string(), + htmlUrl: z.string(), + apiUrl: z.string(), + organization: z.string(), + customPort: z.number(), + isSystemWide: z.boolean().default(false) + }) + ) + .mutation(async ({ ctx, input }) => { + const { teamId } = ctx.user; + let { id, name, htmlUrl, apiUrl, organization, customPort, isSystemWide } = input; + + if (customPort) customPort = Number(customPort); + if (id === 'new') { + const newId = cuid(); + await prisma.gitSource.create({ + data: { + id: newId, + name, + htmlUrl, + apiUrl, + organization, + customPort, + isSystemWide, + type: 'github', + teams: { connect: { id: teamId } } + } + }); + return { + id: newId + }; + } + return null; + }), + newGitLabApp: privateProcedure + .input( + z.object({ + id: z.string(), + type: z.string(), + name: z.string(), + htmlUrl: z.string(), + apiUrl: z.string(), + oauthId: z.number(), + appId: z.string(), + appSecret: z.string(), + groupName: z.string().optional().nullable(), + customPort: z.number().optional().nullable(), + customUser: z.string().optional().nullable() + }) + ) + .mutation(async ({ input, ctx }) => { + const { teamId } = ctx.user; + let { + id, + type, + name, + htmlUrl, + apiUrl, + oauthId, + appId, + appSecret, + groupName, + customPort, + customUser + } = input; + + if (oauthId) oauthId = Number(oauthId); + if (customPort) customPort = Number(customPort); + const encryptedAppSecret = encrypt(appSecret); + + if (id === 'new') { + const newId = cuid(); + await prisma.gitSource.create({ + data: { + id: newId, + type, + apiUrl, + htmlUrl, + name, + customPort, + customUser, + teams: { connect: { id: teamId } } + } + }); + await prisma.gitlabApp.create({ + data: { + teams: { connect: { id: teamId } }, + appId, + oauthId, + groupName, + appSecret: encryptedAppSecret, + gitSource: { connect: { id: newId } } + } + }); + return { + status: 201, + id: newId + }; + } else { + await prisma.gitSource.update({ + where: { id }, + data: { type, apiUrl, htmlUrl, name, customPort, customUser } + }); + await prisma.gitlabApp.update({ + where: { id }, + data: { + appId, + oauthId, + groupName, + appSecret: encryptedAppSecret + } + }); + } + }), + delete: privateProcedure + .input( + z.object({ + id: z.string() + }) + ) + .mutation(async ({ input, ctx }) => { + const { id } = input; + const source = await prisma.gitSource.delete({ + where: { id }, + include: { githubApp: true, gitlabApp: true } + }); + if (source.githubAppId) { + await prisma.githubApp.delete({ where: { id: source.githubAppId } }); + } + if (source.gitlabAppId) { + await prisma.gitlabApp.delete({ where: { id: source.gitlabAppId } }); + } + }), + getSourceById: privateProcedure + .input( + z.object({ + id: z.string() + }) + ) + .query(async ({ input, ctx }) => { + const { id } = input; + const { teamId } = ctx.user; + const settings = await prisma.setting.findFirst({}); + + if (id === 'new') { + return { + source: { + name: null, + type: null, + htmlUrl: null, + apiUrl: null, + organization: null, + customPort: 22, + customUser: 'git' + }, + settings + }; + } + + const source = await prisma.gitSource.findFirst({ + where: { + id, + OR: [ + { teams: { some: { id: teamId === '0' ? undefined : teamId } } }, + { isSystemWide: true } + ] + }, + include: { githubApp: true, gitlabApp: true } + }); + if (!source) { + throw { status: 404, message: 'Source not found.' }; + } + + if (source?.githubApp?.clientSecret) + source.githubApp.clientSecret = decrypt(source.githubApp.clientSecret); + if (source?.githubApp?.webhookSecret) + source.githubApp.webhookSecret = decrypt(source.githubApp.webhookSecret); + if (source?.githubApp?.privateKey) + source.githubApp.privateKey = decrypt(source.githubApp.privateKey); + if (source?.gitlabApp?.appSecret) + source.gitlabApp.appSecret = decrypt(source.gitlabApp.appSecret); + + return { + success: true, + data: { + source, + settings + } + }; + }) +});