Just v2
This commit is contained in:
Andras Bacsai
2022-02-10 15:47:44 +01:00
committed by GitHub
parent a64b095c13
commit 460ae85226
403 changed files with 22039 additions and 12465 deletions

View File

@@ -0,0 +1,44 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import { post } from '$lib/api';
import { errorNotification } from '$lib/form';
const { id } = $page.params;
const from = $page.url.searchParams.get('from');
export let buildPack;
export let foundConfig;
export let scanning;
async function handleSubmit(name) {
try {
const tempBuildPack = JSON.parse(JSON.stringify(buildPack));
delete tempBuildPack.name;
delete tempBuildPack.fancyName;
delete tempBuildPack.color;
delete tempBuildPack.hoverColor;
if (foundConfig.buildPack !== name) {
await post(`/applications/${id}.json`, { ...tempBuildPack });
}
await post(`/applications/${id}/configuration/buildpack.json`, { buildPack: name });
return await goto(from || `/applications/${id}`);
} catch ({ error }) {
return errorNotification(error);
}
}
</script>
<form on:submit|preventDefault={() => handleSubmit(buildPack.name)}>
<button
type="submit"
class="box-selection relative flex text-xl font-bold {buildPack.hoverColor} {foundConfig?.name ===
buildPack.name && buildPack.color}"
><span>{buildPack.fancyName}</span>
{#if !scanning && foundConfig?.name === buildPack.name}
<span class="absolute bottom-0 pb-2 text-xs">Choose this one...</span>
{/if}
</button>
</form>

View File

@@ -0,0 +1,170 @@
<script lang="ts">
import { goto } from '$app/navigation';
export let githubToken;
export let application;
import { page } from '$app/stores';
import { get, post } from '$lib/api';
import { getGithubToken } from '$lib/components/common';
import { enhance, errorNotification } from '$lib/form';
import { onMount } from 'svelte';
const { id } = $page.params;
const from = $page.url.searchParams.get('from');
const to = $page.url.searchParams.get('to');
let htmlUrl = application.gitSource.htmlUrl;
let apiUrl = application.gitSource.apiUrl;
let loading = {
repositories: true,
branches: false
};
let repositories = [];
let branches = [];
let selected = {
projectId: undefined,
repository: undefined,
branch: undefined
};
let showSave = false;
let token = null;
async function loadRepositoriesByPage(page = 0) {
try {
return await get(`${apiUrl}/installation/repositories?per_page=100&page=${page}`, {
Authorization: `token ${token}`
});
} catch ({ error }) {
return errorNotification(error);
}
}
async function loadRepositories() {
token = await getGithubToken({ apiUrl, githubToken, application });
let page = 1;
let reposCount = 0;
const loadedRepos = await loadRepositoriesByPage();
repositories = repositories.concat(loadedRepos.repositories);
reposCount = loadedRepos.total_count;
if (reposCount > repositories.length) {
while (reposCount > repositories.length) {
page = page + 1;
const repos = await loadRepositoriesByPage(page);
repositories = repositories.concat(repos.repositories);
}
}
loading.repositories = false;
}
async function loadBranches() {
loading.branches = true;
selected.branch = undefined;
selected.projectId = repositories.find((repo) => repo.full_name === selected.repository).id;
try {
branches = await get(`${apiUrl}/repos/${selected.repository}/branches`, {
Authorization: `token ${token}`
});
return;
} catch ({ error }) {
return errorNotification(error);
} finally {
loading.branches = false;
}
}
async function isBranchAlreadyUsed() {
try {
return await get(
`/applications/${id}/configuration/repository.json?repository=${selected.repository}&branch=${selected.branch}`
);
} catch ({ error }) {
return errorNotification(error);
} finally {
showSave = true;
}
}
onMount(async () => {
await loadRepositories();
});
async function handleSubmit() {
try {
await post(`/applications/${id}/configuration/repository.json`, { ...selected });
if (to) {
return await goto(`${to}?from=${from}`);
}
return await goto(from || `/applications/${id}/configuration/destination`);
} catch ({ error }) {
return errorNotification(error);
}
}
</script>
{#if repositories.length === 0 && loading.repositories === false}
<div class="flex-col text-center">
<div class="pb-4">No repositories configured for your Git Application.</div>
<a href={`/sources/${application.gitSource.id}`}><button>Configure it now</button></a>
</div>
{:else}
<form on:submit|preventDefault={handleSubmit}>
<div>
{#if loading.repositories}
<select name="repository" disabled class="w-96">
<option selected value="">Loading repositories...</option>
</select>
{:else}
<select
name="repository"
class="w-96"
bind:value={selected.repository}
on:change={loadBranches}
>
<option value="" disabled selected>Please select a repository</option>
{#each repositories as repository}
<option value={repository.full_name}>{repository.name}</option>
{/each}
</select>
{/if}
<input class="hidden" bind:value={selected.projectId} name="projectId" />
{#if loading.branches}
<select name="branch" disabled class="w-96">
<option selected value="">Loading branches...</option>
</select>
{:else}
<select
name="branch"
class="w-96"
disabled={!selected.repository}
bind:value={selected.branch}
on:change={isBranchAlreadyUsed}
>
{#if !selected.repository}
<option value="" disabled selected>Select a repository first</option>
{:else}
<option value="" disabled selected>Please select a branch</option>
{/if}
{#each branches as branch}
<option value={branch.name}>{branch.name}</option>
{/each}
</select>
{/if}
</div>
<div class="pt-5 flex-col flex justify-center items-center space-y-4">
<button
class="w-40"
type="submit"
disabled={!showSave}
class:bg-orange-600={showSave}
class:hover:bg-orange-500={showSave}>Save</button
>
<!-- <button class="w-40"
><a
class="no-underline"
href="{apiUrl}/apps/{application.gitSource.githubApp.name}/installations/new"
>Modify Repositories</a
></button
> -->
</div>
</form>
{/if}

View File

@@ -0,0 +1,334 @@
<script lang="ts">
export let application;
export let appId;
import { page, session } from '$app/stores';
import { onMount } from 'svelte';
import { errorNotification } from '$lib/form';
import { dev } from '$app/env';
import cuid from 'cuid';
import { goto } from '$app/navigation';
import { del, get, post, put } from '$lib/api';
const { id } = $page.params;
const from = $page.url.searchParams.get('from');
const updateDeployKeyIdUrl = `/applications/${id}/configuration/deploykey.json`;
let loading = {
base: true,
projects: false,
branches: false,
save: false
};
let htmlUrl = application.gitSource.htmlUrl;
let apiUrl = application.gitSource.apiUrl;
let username = null;
let groups = [];
let projects = [];
let branches = [];
let showSave = false;
let selected = {
group: undefined,
project: undefined,
branch: undefined
};
onMount(async () => {
if (!$session.gitlabToken) {
getGitlabToken();
} else {
loading.base = true;
try {
const user = await get(`${apiUrl}/v4/user`, {
Authorization: `Bearer ${$session.gitlabToken}`
});
username = user.username;
} catch (error) {
return getGitlabToken();
}
try {
groups = await get(`${apiUrl}/v4/groups?per_page=5000`, {
Authorization: `Bearer ${$session.gitlabToken}`
});
} catch (error) {
errorNotification(error);
throw new Error(error);
} finally {
loading.base = false;
}
}
});
function getGitlabToken() {
const left = screen.width / 2 - 1020 / 2;
const top = screen.height / 2 - 618 / 2;
const newWindow = open(
`${htmlUrl}/oauth/authorize?client_id=${application.gitSource.gitlabApp.appId}&redirect_uri=${window.location.origin}/webhooks/gitlab&response_type=code&scope=api+email+read_repository&state=${$page.params.id}`,
'GitLab',
'resizable=1, scrollbars=1, fullscreen=0, height=618, width=1020,top=' +
top +
', left=' +
left +
', toolbar=0, menubar=0, status=0'
);
const timer = setInterval(() => {
if (newWindow?.closed) {
clearInterval(timer);
window.location.reload();
}
}, 100);
}
async function loadProjects() {
loading.projects = true;
if (username === selected.group.name) {
try {
projects = await get(
`${apiUrl}/v4/users/${selected.group.name}/projects?min_access_level=40&page=1&per_page=25&archived=false`,
{
Authorization: `Bearer ${$session.gitlabToken}`
}
);
} catch (error) {
errorNotification(error);
throw new Error(error);
} finally {
loading.projects = false;
}
} else {
try {
projects = await get(
`${apiUrl}/v4/groups/${selected.group.id}/projects?page=1&per_page=25&archived=false`,
{
Authorization: `Bearer ${$session.gitlabToken}`
}
);
} catch (error) {
errorNotification(error);
throw new Error(error);
} finally {
loading.projects = false;
}
}
}
async function loadBranches() {
loading.branches = true;
try {
branches = await get(
`${apiUrl}/v4/projects/${selected.project.id}/repository/branches?per_page=100&page=1`,
{
Authorization: `Bearer ${$session.gitlabToken}`
}
);
} catch (error) {
errorNotification(error);
throw new Error(error);
} finally {
loading.branches = false;
}
}
async function isBranchAlreadyUsed() {
const url = `/applications/${id}/configuration/repository.json?repository=${selected.project.path_with_namespace}&branch=${selected.branch.name}`;
try {
await get(url);
showSave = true;
} catch (error) {
showSave = false;
return errorNotification('Branch already configured');
}
}
// async function saveDeployKey(deployKeyId: number) {
// try {
// await post(updateDeployKeyIdUrl, { deployKeyId });
// } catch (error) {
// errorNotification(error);
// throw new Error(error);
// }
// }
async function checkSSHKey(sshkeyUrl) {
try {
return await post(sshkeyUrl, {});
} catch (error) {
errorNotification(error);
throw new Error(error);
}
}
async function setWebhook(url, webhookToken) {
const host = dev
? 'https://webhook.site/0e5beb2c-4e9b-40e2-a89e-32295e570c21'
: `${window.location.origin}/webhooks/gitlab/events`;
try {
await post(
url,
{
id: selected.project.id,
url: host,
token: webhookToken,
push_events: true,
enable_ssl_verification: true,
merge_requests_events: true
},
{
Authorization: `Bearer ${$session.gitlabToken}`
}
);
} catch (error) {
errorNotification(error);
throw error;
}
}
async function save() {
loading.save = true;
let privateSshKey = application.gitSource.gitlabApp.privateSshKey;
let publicSshKey = application.gitSource.gitlabApp.publicSshKey;
const deployKeyUrl = `${apiUrl}/v4/projects/${selected.project.id}/deploy_keys`;
const sshkeyUrl = `/applications/${id}/configuration/sshkey.json`;
const webhookUrl = `${apiUrl}/v4/projects/${selected.project.id}/hooks`;
const webhookToken = cuid();
try {
if (!privateSshKey || !publicSshKey) {
const { publicKey } = await checkSSHKey(sshkeyUrl);
publicSshKey = publicKey;
}
const deployKeys = await get(deployKeyUrl, {
Authorization: `Bearer ${$session.gitlabToken}`
});
const deployKeyFound = deployKeys.filter((dk) => dk.title === `${appId}-coolify-deploy-key`);
if (deployKeyFound.length > 0) {
for (const deployKey of deployKeyFound) {
console.log(`${deployKeyUrl}/${deployKey.id}`);
await del(
`${deployKeyUrl}/${deployKey.id}`,
{},
{
Authorization: `Bearer ${$session.gitlabToken}`
}
);
}
}
const { id } = await post(
deployKeyUrl,
{
title: `${appId}-coolify-deploy-key`,
key: publicSshKey,
can_push: false
},
{
Authorization: `Bearer ${$session.gitlabToken}`
}
);
await post(updateDeployKeyIdUrl, { deployKeyId: id });
} catch (error) {
console.log(error);
throw new Error(error);
}
try {
await setWebhook(webhookUrl, webhookToken);
} catch (err) {
console.log(err);
if (!dev) throw new Error(err);
}
const url = `/applications/${id}/configuration/repository.json`;
try {
await post(url, {
repository: `${selected.group.full_path}/${selected.project.name}`,
branch: selected.branch.name,
projectId: selected.project.id,
webhookToken
});
return await goto(from || `/applications/${id}/configuration/buildpack`);
} catch ({ error }) {
return errorNotification(error);
}
}
async function handleSubmit() {
try {
await post(`/applications/{id}/configuration/repository.json`, { ...selected });
return await goto(from || `/applications/${id}/configuration/destination`);
} catch ({ error }) {
return errorNotification(error);
}
}
</script>
<form on:submit={handleSubmit}>
<div class="flex flex-col space-y-2 px-4 xl:flex-row xl:space-y-0 xl:space-x-2 ">
{#if loading.base}
<select name="group" disabled class="w-96">
<option selected value="">Loading groups...</option>
</select>
{:else}
<select name="group" class="w-96" bind:value={selected.group} on:change={loadProjects}>
<option value="" disabled selected>Please select a group</option>
{#each groups as group}
<option value={group}>{group.full_name}</option>
{/each}
</select>
{/if}
{#if loading.projects}
<select name="project" disabled class="w-96">
<option selected value="">Loading projects...</option>
</select>
{:else if !loading.projects && projects.length > 0}
<select
name="project"
class="w-96"
bind:value={selected.project}
on:change={loadBranches}
disabled={!selected.group}
>
<option value="" disabled selected>Please select a project</option>
{#each projects as project}
<option value={project}>{project.name}</option>
{/each}
</select>
{:else}
<select name="project" disabled class="w-96">
<option disabled selected value="">No projects found</option>
</select>
{/if}
{#if loading.branches}
<select name="branch" disabled class="w-96">
<option selected value="">Loading branches...</option>
</select>
{:else if !loading.branches && branches.length > 0}
<select
name="branch"
class="w-96"
bind:value={selected.branch}
on:change={isBranchAlreadyUsed}
disabled={!selected.project}
>
<option value="" disabled selected>Please select a branch</option>
{#each branches as branch}
<option value={branch}>{branch.name}</option>
{/each}
</select>
{:else}
<select name="project" disabled class="w-96">
<option disabled selected value="">No branches found</option>
</select>
{/if}
</div>
<div class="flex flex-col items-center justify-center space-y-4 pt-5">
<button
on:click|preventDefault={save}
class="w-40"
type="submit"
disabled={!showSave || loading.save}
class:bg-orange-600={showSave && !loading.save}
class:hover:bg-orange-500={showSave && !loading.save}
>{loading.save ? 'Saving...' : 'Save'}</button
>
</div>
</form>

View File

@@ -0,0 +1,41 @@
import { getUserDetails } from '$lib/common';
import * as db from '$lib/database';
import type { RequestHandler } from '@sveltejs/kit';
import { PrismaErrorHandler } from '$lib/database';
export const get: RequestHandler = async (event) => {
const { teamId, status, body } = await getUserDetails(event);
if (status === 401) return { status, body };
const { id } = event.params;
try {
const application = await db.getApplication({ id, teamId });
return {
status: 200,
body: {
type: application.gitSource.type,
projectId: application.projectId,
repository: application.repository,
branch: application.branch,
apiUrl: application.gitSource.apiUrl
}
};
} catch (error) {
return PrismaErrorHandler(error);
}
};
export const post: RequestHandler = async (event) => {
const { teamId, status, body } = await getUserDetails(event);
if (status === 401) return { status, body };
const { id } = event.params;
const { buildPack } = await event.request.json();
try {
await db.configureBuildPack({ id, buildPack });
return { status: 201 };
} catch (error) {
return PrismaErrorHandler(error);
}
};

View File

@@ -0,0 +1,186 @@
<script context="module" lang="ts">
import type { Load } from '@sveltejs/kit';
export const load: Load = async ({ fetch, params, url, stuff }) => {
const { application, ghToken } = stuff;
if (application?.buildPack && !url.searchParams.get('from')) {
return {
status: 302,
redirect: `/applications/${params.id}`
};
}
const endpoint = `/applications/${params.id}/configuration/buildpack.json`;
const res = await fetch(endpoint);
if (res.ok) {
return {
props: {
...(await res.json()),
application,
ghToken
}
};
}
return {
status: res.status,
error: new Error(`Could not load ${url}`)
};
};
</script>
<script lang="ts">
import { onMount } from 'svelte';
import { buildPacks, scanningTemplates } from '$lib/components/templates';
import BuildPack from './_BuildPack.svelte';
import { page, session } from '$app/stores';
import { get } from '$lib/api';
import { errorNotification } from '$lib/form';
let scanning = true;
let foundConfig = null;
export let apiUrl;
export let projectId;
export let repository;
export let branch;
export let ghToken;
export let type;
export let application;
function checkPackageJSONContents({ key, json }) {
return json?.dependencies?.hasOwnProperty(key) || json?.devDependencies?.hasOwnProperty(key);
}
function checkTemplates({ json }) {
for (const [key, value] of Object.entries(scanningTemplates)) {
if (checkPackageJSONContents({ key, json })) {
return buildPacks.find((bp) => bp.name === value.buildPack);
}
}
}
async function scanRepository() {
try {
if (type === 'gitlab') {
const files = await get(`${apiUrl}/v4/projects/${projectId}/repository/tree`, {
Authorization: `Bearer ${$session.gitlabToken}`
});
const packageJson = files.find(
(file) => file.name === 'package.json' && file.type === 'blob'
);
const dockerfile = files.find((file) => file.name === 'Dockerfile' && file.type === 'blob');
const cargoToml = files.find((file) => file.name === 'Cargo.toml' && file.type === 'blob');
const requirementsTxt = files.find(
(file) => file.name === 'requirements.txt' && file.type === 'blob'
);
const indexHtml = files.find((file) => file.name === 'index.html' && file.type === 'blob');
const indexPHP = files.find((file) => file.name === 'index.php' && file.type === 'blob');
if (dockerfile) {
foundConfig.buildPack = 'docker';
} else if (packageJson) {
const path = packageJson.path;
const data = await get(
`${apiUrl}/v4/projects/${projectId}/repository/files/${path}/raw?ref=${branch}`,
{
Authorization: `Bearer ${$session.gitlabToken}`
}
);
const json = JSON.parse(data) || {};
foundConfig = checkTemplates({ json });
} else if (cargoToml) {
foundConfig = buildPacks.find((bp) => bp.name === 'rust');
} else if (requirementsTxt) {
foundConfig = buildPacks.find((bp) => bp.name === 'python');
} else if (indexHtml) {
foundConfig = buildPacks.find((bp) => bp.name === 'static');
} else if (indexPHP) {
foundConfig = buildPacks.find((bp) => bp.name === 'php');
}
} else if (type === 'github') {
const files = await get(`${apiUrl}/repos/${repository}/contents?ref=${branch}`, {
Authorization: `Bearer ${ghToken}`,
Accept: 'application/vnd.github.v2.json'
});
const packageJson = files.find(
(file) => file.name === 'package.json' && file.type === 'file'
);
const dockerfile = files.find((file) => file.name === 'Dockerfile' && file.type === 'file');
const cargoToml = files.find((file) => file.name === 'Cargo.toml' && file.type === 'file');
const requirementsTxt = files.find(
(file) => file.name === 'requirements.txt' && file.type === 'file'
);
const indexHtml = files.find((file) => file.name === 'index.html' && file.type === 'file');
const indexPHP = files.find((file) => file.name === 'index.php' && file.type === 'file');
if (dockerfile) {
foundConfig.buildPack = 'docker';
} else if (packageJson) {
const data = await get(`${packageJson.git_url}`, {
Authorization: `Bearer ${ghToken}`,
Accept: 'application/vnd.github.v2.raw'
});
const json = JSON.parse(data) || {};
foundConfig = checkTemplates({ json });
} else if (cargoToml) {
foundConfig = buildPacks.find((bp) => bp.name === 'rust');
} else if (requirementsTxt) {
foundConfig = buildPacks.find((bp) => bp.name === 'python');
} else if (indexHtml) {
foundConfig = buildPacks.find((bp) => bp.name === 'static');
} else if (indexPHP) {
foundConfig = buildPacks.find((bp) => bp.name === 'php');
}
}
} catch (error) {
if (
error.error === 'invalid_token' ||
error.error_description ===
'Token is expired. You can either do re-authorization or token refresh.' ||
error.message === '401 Unauthorized'
) {
if (application.gitSource.gitlabAppId) {
let htmlUrl = application.gitSource.htmlUrl;
const left = screen.width / 2 - 1020 / 2;
const top = screen.height / 2 - 618 / 2;
const newWindow = open(
`${htmlUrl}/oauth/authorize?client_id=${application.gitSource.gitlabApp.appId}&redirect_uri=${window.location.origin}/webhooks/gitlab&response_type=code&scope=api+email+read_repository&state=${$page.params.id}`,
'GitLab',
'resizable=1, scrollbars=1, fullscreen=0, height=618, width=1020,top=' +
top +
', left=' +
left +
', toolbar=0, menubar=0, status=0'
);
const timer = setInterval(() => {
if (newWindow?.closed) {
clearInterval(timer);
window.location.reload();
}
}, 100);
}
}
return errorNotification(error);
} finally {
if (!foundConfig) foundConfig = buildPacks.find((bp) => bp.name === 'node');
scanning = false;
}
}
onMount(async () => {
await scanRepository();
});
</script>
<div class="flex space-x-1 p-6 font-bold">
<div class="mr-4 text-2xl tracking-tight">Configure Build Pack</div>
</div>
{#if scanning}
<div class="flex justify-center space-x-1 p-6 font-bold">
<div class="text-xl tracking-tight">Scanning repository to suggest a build pack for you...</div>
</div>
{:else}
<div class="max-w-7xl mx-auto flex flex-wrap justify-center">
{#each buildPacks as buildPack}
<div class="p-2">
<BuildPack {buildPack} {scanning} bind:foundConfig />
</div>
{/each}
</div>
{/if}

View File

@@ -0,0 +1,17 @@
import * as db from '$lib/database';
import { PrismaErrorHandler } from '$lib/database';
import type { RequestHandler } from '@sveltejs/kit';
export const post: RequestHandler = async (event) => {
const { id } = event.params;
let { deployKeyId } = await event.request.json();
deployKeyId = Number(deployKeyId);
try {
await db.updateDeployKey({ id, deployKeyId });
return { status: 201 };
} catch (error) {
return PrismaErrorHandler(error);
}
};

View File

@@ -0,0 +1,19 @@
import { getUserDetails } from '$lib/common';
import * as db from '$lib/database';
import { PrismaErrorHandler } from '$lib/database';
import type { RequestHandler } from '@sveltejs/kit';
export const post: RequestHandler = async (event) => {
const { teamId, status, body } = await getUserDetails(event);
if (status === 401) return { status, body };
const { id } = event.params;
const { destinationId } = await event.request.json();
try {
await db.configureDestinationForApplication({ id, destinationId });
return { status: 201 };
} catch (error) {
return PrismaErrorHandler(error);
}
};

View File

@@ -0,0 +1,91 @@
<script context="module" lang="ts">
import type { Load } from '@sveltejs/kit';
export const load: Load = async ({ fetch, params, url, stuff }) => {
const { application } = stuff;
if (application?.destinationDockerId && !url.searchParams.get('from')) {
return {
status: 302,
redirect: `/applications/${params.id}`
};
}
const endpoint = `/destinations.json`;
const res = await fetch(endpoint);
if (res.ok) {
return {
props: {
...(await res.json())
}
};
}
return {
status: res.status,
error: new Error(`Could not load ${url}`)
};
};
</script>
<script lang="ts">
import type Prisma from '@prisma/client';
import { page } from '$app/stores';
import { errorNotification } from '$lib/form';
import { goto } from '$app/navigation';
import { post } from '$lib/api';
const { id } = $page.params;
const from = $page.url.searchParams.get('from');
export let destinations: Prisma.DestinationDocker[];
async function handleSubmit(destinationId) {
try {
await post(`/applications/${id}/configuration/destination.json`, { destinationId });
return await goto(from || `/applications/${id}/configuration/buildpack`);
} catch ({ error }) {
return errorNotification(error);
}
}
</script>
<div class="flex space-x-1 p-6 font-bold">
<div class="mr-4 text-2xl tracking-tight">Configure Destination</div>
</div>
<div class="flex justify-center">
{#if !destinations || destinations.length === 0}
<div class="flex-col">
<div class="pb-2">No configurable Destination found</div>
<div class="flex justify-center">
<a href="/new/destination" sveltekit:prefetch class="add-icon bg-sky-600 hover:bg-sky-500">
<svg
class="w-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
><path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
/></svg
>
</a>
</div>
</div>
{:else}
<div class="flex flex-wrap justify-center">
{#each destinations as destination}
<div class="p-2">
<form on:submit|preventDefault={() => handleSubmit(destination.id)}>
<button type="submit" class="box-selection hover:bg-sky-700 font-bold">
<div class="font-bold text-xl text-center truncate">{destination.name}</div>
<div class="text-center truncate">{destination.network}</div>
</button>
</form>
</div>
{/each}
</div>
{/if}
</div>

View File

@@ -0,0 +1,47 @@
import { getUserDetails } from '$lib/common';
import * as db from '$lib/database';
import { PrismaErrorHandler } from '$lib/database';
import type { RequestHandler } from '@sveltejs/kit';
export const get: RequestHandler = async (event) => {
const { teamId, status, body } = await getUserDetails(event);
if (status === 401) return { status, body };
const { id } = event.params;
const repository = event.url.searchParams.get('repository')?.toLocaleLowerCase() || undefined;
const branch = event.url.searchParams.get('branch')?.toLocaleLowerCase() || undefined;
try {
const found = await db.isBranchAlreadyUsed({ repository, branch, id });
if (found) {
throw {
error: `Branch ${branch} is already used by another application`
};
}
return {
status: 200
};
} catch (error) {
return PrismaErrorHandler(error);
}
};
export const post: RequestHandler = async (event) => {
const { teamId, status, body } = await getUserDetails(event);
if (status === 401) return { status, body };
const { id } = event.params;
let { repository, branch, projectId, webhookToken } = await event.request.json();
repository = repository.toLowerCase();
branch = branch.toLowerCase();
projectId = Number(projectId);
try {
await db.configureGitRepository({ id, repository, branch, projectId, webhookToken });
return { status: 201 };
} catch (error) {
return PrismaErrorHandler(error);
}
};

View File

@@ -0,0 +1,38 @@
<script context="module" lang="ts">
import type { Load } from '@sveltejs/kit';
export const load: Load = async ({ params, url, stuff }) => {
const { application, githubToken, appId } = stuff;
if (application?.branch && application?.repository && !url.searchParams.get('from')) {
return {
status: 302,
redirect: `/applications/${params.id}`
};
}
return {
props: {
githubToken,
application,
appId
}
};
};
</script>
<script lang="ts">
export let application;
export let githubToken;
export let appId;
import GithubRepositories from './_GithubRepositories.svelte';
import GitlabRepositories from './_GitlabRepositories.svelte';
</script>
<div class="flex space-x-1 p-6 font-bold">
<div class="mr-4 text-2xl tracking-tight">Select a Repository / Project</div>
</div>
<div class="flex flex-wrap justify-center">
{#if application.gitSource.type === 'github'}
<GithubRepositories {application} {githubToken} />
{:else if application.gitSource.type === 'gitlab'}
<GitlabRepositories {application} {appId} />
{/if}
</div>

View File

@@ -0,0 +1,18 @@
import { getUserDetails } from '$lib/common';
import * as db from '$lib/database';
import { PrismaErrorHandler } from '$lib/database';
import type { RequestHandler } from '@sveltejs/kit';
export const post: RequestHandler = async (event) => {
const { teamId, status, body } = await getUserDetails(event);
if (status === 401) return { status, body };
const { id } = event.params;
const { gitSourceId } = await event.request.json();
try {
await db.configureGitsource({ id, gitSourceId });
return { status: 201 };
} catch (error) {
return PrismaErrorHandler(error);
}
};

View File

@@ -0,0 +1,109 @@
<script context="module" lang="ts">
import type { Load } from '@sveltejs/kit';
export const load: Load = async ({ fetch, params, url, stuff }) => {
const { application } = stuff;
if (application?.gitSourceId && !url.searchParams.get('from')) {
return {
status: 302,
redirect: `/applications/${params.id}`
};
}
const endpoint = `/sources.json`;
const res = await fetch(endpoint);
if (res.ok) {
return {
props: {
...(await res.json())
}
};
}
return {
status: res.status,
error: new Error(`Could not load ${url}`)
};
};
</script>
<script lang="ts">
import type Prisma from '@prisma/client';
import { page } from '$app/stores';
import { enhance, errorNotification } from '$lib/form';
import { goto } from '$app/navigation';
import { post } from '$lib/api';
const { id } = $page.params;
const from = $page.url.searchParams.get('from');
export let sources: Prisma.GitSource[] & {
gitlabApp: Prisma.GitlabApp;
githubApp: Prisma.GithubApp;
};
sources = sources.filter(
(source) =>
(source.type === 'github' && source.githubAppId && source.githubApp.installationId) ||
(source.type === 'gitlab' && source.gitlabAppId)
);
async function handleSubmit(gitSourceId) {
try {
await post(`/applications/${id}/configuration/source.json`, { gitSourceId });
return await goto(from || `/applications/${id}/configuration/repository`);
} catch ({ error }) {
return errorNotification(error);
}
}
</script>
<div class="flex space-x-1 p-6 font-bold">
<div class="mr-4 text-2xl tracking-tight">Select a Git Source</div>
</div>
<div class="flex justify-center">
{#if !sources || sources.length === 0}
<div class="flex-col">
<div class="pb-2">No configurable Git Source found</div>
<div class="flex justify-center">
<a href="/new/source" sveltekit:prefetch class="add-icon bg-orange-600 hover:bg-orange-500">
<svg
class="w-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
><path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
/></svg
>
</a>
</div>
</div>
{:else}
<div class="flex flex-wrap justify-center">
{#each sources as source}
<div class="p-2">
<form on:submit|preventDefault={() => handleSubmit(source.id)}>
<button
disabled={source.gitlabApp && !source.gitlabAppId}
type="submit"
class="disabled:opacity-95 bg-coolgray-200 disabled:text-white box-selection hover:bg-orange-700 group"
class:border-red-500={source.gitlabApp && !source.gitlabAppId}
class:border-0={source.gitlabApp && !source.gitlabAppId}
class:border-l-4={source.gitlabApp && !source.gitlabAppId}
>
<div class="font-bold text-xl text-center truncate">{source.name}</div>
{#if source.gitlabApp && !source.gitlabAppId}
<div class="font-bold text-center truncate text-red-500 group-hover:text-white">
Configuration missing
</div>
{/if}
</button>
</form>
</div>
{/each}
</div>
{/if}
</div>

View File

@@ -0,0 +1,20 @@
import * as db from '$lib/database';
import { PrismaErrorHandler } from '$lib/database';
import type { RequestHandler } from '@sveltejs/kit';
export const get: RequestHandler = async (event) => {
const { id } = event.params;
try {
return await db.getSshKey({ id });
} catch (error) {
return PrismaErrorHandler(error);
}
};
export const post: RequestHandler = async (event) => {
const { id } = event.params;
try {
return await db.generateSshKey({ id });
} catch (error) {
return PrismaErrorHandler(error);
}
};