* New Version: 3.0.0
This commit is contained in:
Andras Bacsai
2022-07-06 11:02:36 +02:00
committed by GitHub
parent 9137e8bc32
commit 87ba4560ad
491 changed files with 16824 additions and 20459 deletions

View File

@@ -0,0 +1,52 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import { post } from '$lib/api';
import { errorNotification } from '$lib/common';
import { findBuildPack } from '$lib/templates';
import { t } from '$lib/translations';
const { id } = $page.params;
const from = $page.url.searchParams.get('from');
export let buildPack: any;
export let foundConfig: any;
export let scanning: any;
export let packageManager: any;
async function handleSubmit(name: string) {
try {
const tempBuildPack = JSON.parse(
JSON.stringify(findBuildPack(buildPack.name, packageManager))
);
delete tempBuildPack.name;
delete tempBuildPack.fancyName;
delete tempBuildPack.color;
delete tempBuildPack.hoverColor;
if (foundConfig.buildPack !== name) {
await post(`/applications/${id}`, { ...tempBuildPack, buildPack: name });
}
await post(`/applications/${id}/configuration/buildpack`, { 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"
>{$t('application.configuration.buildpack.choose_this_one')}</span
>
{/if}
</button>
</form>

View File

@@ -0,0 +1,201 @@
<script lang="ts">
export let application: any;
//@ts-ignore
import Select from 'svelte-select';
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import { get, post } from '$lib/api';
import { onMount } from 'svelte';
import { appSession } from '$lib/store';
import { t } from '$lib/translations';
import { errorNotification } from '$lib/common';
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: any = [];
let branches: any = [];
let selected = {
projectId: undefined,
repository: undefined,
branch: undefined,
autodeploy: application.settings.autodeploy || true
};
let showSave = false;
async function loadRepositoriesByPage(page = 0) {
return await get(`${apiUrl}/installation/repositories?per_page=100&page=${page}`, {
Authorization: `token ${$appSession.tokens.github}`
});
}
async function loadBranchesByPage(page = 0) {
return await get(`${apiUrl}/repos/${selected.repository}/branches?per_page=100&page=${page}`, {
Authorization: `token ${$appSession.tokens.github}`
});
}
let reposSelectOptions: any;
let branchSelectOptions: any;
async function loadRepositories() {
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;
reposSelectOptions = repositories.map((repo: any) => ({
value: repo.full_name,
label: repo.name
}));
}
async function loadBranches(event: any) {
branches = [];
selected.repository = event.detail.value;
selected.projectId = repositories.find(
(repo: any) => repo.full_name === selected.repository
).id;
let page = 1;
let branchCount = 0;
loading.branches = true;
const loadedBranches = await loadBranchesByPage();
branches = branches.concat(loadedBranches);
branchCount = branches.length;
if (branchCount === 100) {
while (branchCount === 100) {
page = page + 1;
const nextBranches = await loadBranchesByPage(page);
branches = branches.concat(nextBranches);
branchCount = nextBranches.length;
}
}
loading.branches = false;
branchSelectOptions = branches.map((branch: any) => ({
value: branch.name,
label: branch.name
}));
}
async function isBranchAlreadyUsed(event: any) {
selected.branch = event.detail.value;
try {
const data = await get(
`/applications/${id}/configuration/repository?repository=${selected.repository}&branch=${selected.branch}`
);
if (data.used) {
const sure = confirm($t('application.configuration.branch_already_in_use'));
if (sure) {
selected.autodeploy = false;
showSave = true;
return true;
}
showSave = false;
return true;
}
showSave = true;
} catch ({ error }) {
showSave = false;
return errorNotification(error);
}
}
onMount(async () => {
try {
if (!$appSession.tokens.github) {
const { token } = await get(`/applications/${id}/configuration/githubToken`);
$appSession.tokens.github = token;
}
await loadRepositories();
} catch (error: any) {
if (error.message === 'Bad credentials') {
const { token } = await get(`/applications/${id}/configuration/githubToken`);
$appSession.tokens.github = token;
return await loadRepositories();
}
return errorNotification(error);
}
});
async function handleSubmit() {
try {
await post(`/applications/${id}/configuration/repository`, { ...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">{$t('application.configuration.no_repositories_configured')}</div>
<a href={`/sources/${application.gitSource.id}`}
><button>{$t('application.configuration.configure_it_now')}</button></a
>
</div>
{:else}
<form on:submit|preventDefault={handleSubmit} class="flex flex-col justify-center text-center">
<div class="flex-col space-y-3 md:space-y-0 space-x-1">
<div class="flex-row md:flex gap-4">
<div class="custom-select-wrapper">
<Select
placeholder={loading.repositories
? $t('application.configuration.loading_repositories')
: $t('application.configuration.select_a_repository')}
id="repository"
showIndicator={!loading.repositories}
isWaiting={loading.repositories}
on:select={loadBranches}
items={reposSelectOptions}
isDisabled={loading.repositories}
isClearable={false}
/>
</div>
<input class="hidden" bind:value={selected.projectId} name="projectId" />
<div class="custom-select-wrapper">
<Select
placeholder={loading.branches
? $t('application.configuration.loading_branches')
: !selected.repository
? $t('application.configuration.select_a_repository_first')
: $t('application.configuration.select_a_branch')}
isWaiting={loading.branches}
showIndicator={selected.repository && !loading.branches}
id="branches"
on:select={isBranchAlreadyUsed}
items={branchSelectOptions}
isDisabled={loading.branches || !selected.repository}
isClearable={false}
/>
</div>
</div>
</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}>{$t('forms.save')}</button
>
</div>
</form>
{/if}

View File

@@ -0,0 +1,412 @@
<script lang="ts">
export let application: any;
export let appId: any;
export let settings: any;
//@ts-ignore
import cuid from 'cuid';
import { page } from '$app/stores';
import { onMount } from 'svelte';
import { dev } from '$app/env';
import { goto } from '$app/navigation';
import { del, get, post } from '$lib/api';
import { t } from '$lib/translations';
import { errorNotification } from '$lib/common';
import { appSession } from '$lib/store';
import Select from 'svelte-select';
const { id } = $page.params;
const from = $page.url.searchParams.get('from');
let url = settings?.fqdn ? settings.fqdn : window.location.origin;
if (dev) url = `http://localhost:3001`;
const updateDeployKeyIdUrl = `/applications/${id}/configuration/deploykey`;
let loading = {
base: true,
projects: false,
branches: false,
save: false
};
let htmlUrl = application.gitSource.htmlUrl;
let apiUrl = application.gitSource.apiUrl;
let username: any = null;
let groups: any = [];
let projects: any = [];
let branches: any = [];
let showSave = false;
let autodeploy = application.settings.autodeploy || true;
let selected: any = {
group: undefined,
project: undefined,
branch: undefined
};
let search = {
project: '',
branch: ''
};
onMount(async () => {
if (!$appSession.tokens.gitlab) {
await getGitlabToken();
}
loading.base = true;
try {
const user = await get(`${apiUrl}/v4/user`, {
Authorization: `Bearer ${$appSession.tokens.gitlab}`
});
username = user.username;
} catch (error) {
return await getGitlabToken();
}
try {
groups = await get(`${apiUrl}/v4/groups?per_page=5000`, {
Authorization: `Bearer ${$appSession.tokens.gitlab}`
});
} catch (error: any) {
errorNotification(error);
throw new Error(error);
} finally {
loading.base = false;
}
});
function selectGroup(event: any) {
selected.group = event.detail;
selected.project = null;
selected.branch = null;
showSave = false;
loadProjects();
}
async function searchProjects(searchText: any) {
if (!selected.group) {
return;
}
search.project = searchText;
await loadProjects();
return projects;
}
function selectProject(event: any) {
selected.project = event.detail;
selected.branch = null;
showSave = false;
loadBranches();
}
async function getGitlabToken() {
return await new Promise<void>((resolve, reject) => {
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=${url}/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);
$appSession.tokens.gitlab = localStorage.getItem('gitLabToken');
localStorage.removeItem('gitLabToken');
resolve();
}
}, 100);
});
}
async function loadProjects() {
//@ts-ignore
const params: any = new URLSearchParams({
page: 1,
per_page: 25,
archived: false
});
if (search.project) {
params.append('search', search.project);
}
loading.projects = true;
if (username === selected.group.name) {
try {
params.append('min_access_level', 40);
projects = await get(`${apiUrl}/v4/users/${selected.group.name}/projects?${params}`, {
Authorization: `Bearer ${$appSession.tokens.gitlab}`
});
} catch (error) {
return errorNotification(error);
} finally {
loading.projects = false;
}
} else {
try {
projects = await get(`${apiUrl}/v4/groups/${selected.group.id}/projects?${params}`, {
Authorization: `Bearer ${$appSession.tokens.gitlab}`
});
} catch (error) {
return errorNotification(error);
} finally {
loading.projects = false;
}
}
}
async function searchBranches(searchText: any) {
if (!selected.project) {
return;
}
search.branch = searchText;
await loadBranches();
return branches;
}
function selectBranch(event: any) {
selected.branch = event.detail;
isBranchAlreadyUsed();
}
async function loadBranches() {
//@ts-ignore
const params = new URLSearchParams({
page: 1,
per_page: 100
});
if (search.branch) {
params.append('search', search.branch);
}
loading.branches = true;
try {
branches = await get(
`${apiUrl}/v4/projects/${selected.project.id}/repository/branches?${params}`,
{
Authorization: `Bearer ${$appSession.tokens.gitlab}`
}
);
} catch (error) {
return errorNotification(error);
} finally {
loading.branches = false;
}
}
async function isBranchAlreadyUsed() {
try {
const data = await get(
`/applications/${id}/configuration/repository?repository=${selected.project.path_with_namespace}&branch=${selected.branch.name}`
);
if (data.used) {
const sure = confirm($t('application.configuration.branch_already_in_use'));
if (sure) {
autodeploy = false;
showSave = true;
return true;
}
showSave = false;
return true;
}
showSave = true;
} catch ({ error }) {
return errorNotification(error);
}
}
async function checkSSHKey(sshkeyUrl: any) {
try {
return await post(sshkeyUrl, {});
} catch (error) {
return errorNotification(error);
}
}
async function setWebhook(url: any, webhookToken: any) {
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 ${$appSession.tokens.gitlab}`
}
);
} catch (error) {
return errorNotification(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`;
const webhookUrl = `${apiUrl}/v4/projects/${selected.project.id}/hooks`;
const webhookToken = cuid();
try {
if (!privateSshKey || !publicSshKey) {
const data: any = await checkSSHKey(sshkeyUrl);
publicSshKey = data.publicKey;
}
const deployKeys = await get(deployKeyUrl, {
Authorization: `Bearer ${$appSession.tokens.gitlab}`
});
const deployKeyFound = deployKeys.filter(
(dk: any) => dk.title === `${appId}-coolify-deploy-key`
);
if (deployKeyFound.length > 0) {
for (const deployKey of deployKeyFound) {
await del(
`${deployKeyUrl}/${deployKey.id}`,
{},
{
Authorization: `Bearer ${$appSession.tokens.gitlab}`
}
);
}
}
const { id } = await post(
deployKeyUrl,
{
title: `${appId}-coolify-deploy-key`,
key: publicSshKey,
can_push: false
},
{
Authorization: `Bearer ${$appSession.tokens.gitlab}`
}
);
await post(updateDeployKeyIdUrl, { deployKeyId: id });
} catch (error) {
return errorNotification(error);
} finally {
loading.save = false;
}
try {
await setWebhook(webhookUrl, webhookToken);
} catch (error) {
return errorNotification(error);
} finally {
loading.save = false;
}
const url = `/applications/${id}/configuration/repository`;
try {
const repository = selected.project.path_with_namespace;
await post(url, {
repository,
branch: selected.branch.name,
projectId: selected.project.id,
autodeploy,
webhookToken
});
return await goto(from || `/applications/${id}/configuration/buildpack`);
} catch (error) {
return errorNotification(error);
} finally {
loading.save = false;
}
}
async function handleSubmit() {
try {
await post(`/applications/${id}/configuration/repository`, { ...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 ">
<div class="custom-select-wrapper">
<Select
placeholder={loading.base
? $t('application.configuration.loading_groups')
: $t('application.configuration.select_a_group')}
id="group"
showIndicator={!loading.base}
isWaiting={loading.base}
on:select={selectGroup}
on:clear={() => {
showSave = false;
projects = [];
branches = [];
selected.group = null;
selected.project = null;
selected.branch = null;
}}
value={selected.group}
isDisabled={loading.base}
isClearable={false}
items={groups}
labelIdentifier="full_name"
optionIdentifier="id"
/>
</div>
<div class="custom-select-wrapper">
<Select
placeholder={loading.projects
? $t('application.configuration.loading_projects')
: $t('application.configuration.select_a_project')}
noOptionsMessage={$t('application.configuration.no_projects_found')}
id="project"
showIndicator={!loading.projects}
isWaiting={loading.projects}
isDisabled={loading.projects || !selected.group}
on:select={selectProject}
on:clear={() => {
showSave = false;
branches = [];
selected.project = null;
selected.branch = null;
}}
value={selected.project}
isClearable={false}
items={projects}
loadOptions={searchProjects}
labelIdentifier="name"
optionIdentifier="id"
/>
</div>
<div class="custom-select-wrapper">
<Select
placeholder={loading.branches
? $t('application.configuration.loading_branches')
: $t('application.configuration.select_a_branch')}
noOptionsMessage={$t('application.configuration.no_branches_found')}
id="branch"
showIndicator={!loading.branches}
isWaiting={loading.branches}
isDisabled={loading.branches || !selected.project}
on:select={selectBranch}
on:clear={() => {
showSave = false;
selected.branch = null;
}}
value={selected.branch}
isClearable={false}
items={branches}
loadOptions={searchBranches}
labelIdentifier="name"
optionIdentifier="web_url"
/>
</div>
</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 ? $t('forms.saving') : $t('forms.save')}</button
>
</div>
</form>

View File

@@ -0,0 +1,273 @@
<script context="module" lang="ts">
import type { Load } from '@sveltejs/kit';
export const load: Load = async ({ fetch, params, url, stuff }) => {
try {
const { application } = stuff;
if (application?.buildPack && !url.searchParams.get('from')) {
return {
status: 302,
redirect: `/applications/${params.id}`
};
}
const response = await get(`/applications/${params.id}/configuration/buildpack`);
return {
props: {
...response
}
};
} catch (error) {
return {
status: 500,
error: new Error(`Could not load ${url}`)
};
}
};
</script>
<script lang="ts">
import { onMount } from 'svelte';
import { page } from '$app/stores';
import { get } from '$lib/api';
import { appSession } from '$lib/store';
import { browser } from '$app/env';
import { t } from '$lib/translations';
import { buildPacks, findBuildPack, scanningTemplates } from '$lib/templates';
import { errorNotification } from '$lib/common';
import BuildPack from './_BuildPack.svelte';
const { id } = $page.params;
let scanning = true;
let foundConfig: any = null;
let packageManager = 'npm';
export let apiUrl: any;
export let projectId: any;
export let repository: any;
export let branch: any;
export let type: any;
export let application: any;
function checkPackageJSONContents({ key, json }: { key: any; json: any }) {
return json?.dependencies?.hasOwnProperty(key) || json?.devDependencies?.hasOwnProperty(key);
}
function checkTemplates({ json, packageManager }: { json: any; packageManager: any }) {
for (const [key, value] of Object.entries(scanningTemplates)) {
if (checkPackageJSONContents({ key, json })) {
foundConfig = findBuildPack(value.buildPack, packageManager);
break;
}
}
}
async function scanRepository() {
try {
if (type === 'gitlab') {
const files = await get(`${apiUrl}/v4/projects/${projectId}/repository/tree`, {
Authorization: `Bearer ${$appSession.tokens.gitlab}`
});
const packageJson = files.find(
(file: { name: string; type: string }) =>
file.name === 'package.json' && file.type === 'blob'
);
const yarnLock = files.find(
(file: { name: string; type: string }) =>
file.name === 'yarn.lock' && file.type === 'blob'
);
const pnpmLock = files.find(
(file: { name: string; type: string }) =>
file.name === 'pnpm-lock.yaml' && file.type === 'blob'
);
const dockerfile = files.find(
(file: { name: string; type: string }) =>
file.name === 'Dockerfile' && file.type === 'blob'
);
const cargoToml = files.find(
(file: { name: string; type: string }) =>
file.name === 'Cargo.toml' && file.type === 'blob'
);
const requirementsTxt = files.find(
(file: { name: string; type: string }) =>
file.name === 'requirements.txt' && file.type === 'blob'
);
const indexHtml = files.find(
(file: { name: string; type: string }) =>
file.name === 'index.html' && file.type === 'blob'
);
const indexPHP = files.find(
(file: { name: string; type: string }) =>
file.name === 'index.php' && file.type === 'blob'
);
const composerPHP = files.find(
(file: { name: string; type: string }) =>
file.name === 'composer.json' && file.type === 'blob'
);
const laravel = files.find(
(file: { name: string; type: string }) => file.name === 'artisan' && file.type === 'blob'
);
if (yarnLock) packageManager = 'yarn';
if (pnpmLock) packageManager = 'pnpm';
if (dockerfile) {
foundConfig = findBuildPack('docker', packageManager);
} else if (packageJson && !laravel) {
const path = packageJson.path;
const data: any = await get(
`${apiUrl}/v4/projects/${projectId}/repository/files/${path}/raw?ref=${branch}`,
{
Authorization: `Bearer ${$appSession.tokens.gitlab}`
}
);
const json = JSON.parse(data) || {};
checkTemplates({ json, packageManager });
} else if (cargoToml) {
foundConfig = findBuildPack('rust');
} else if (requirementsTxt) {
foundConfig = findBuildPack('python');
} else if (indexHtml) {
foundConfig = findBuildPack('static', packageManager);
} else if ((indexPHP || composerPHP) && !laravel) {
foundConfig = findBuildPack('php');
} else if (laravel) {
foundConfig = findBuildPack('laravel');
} else {
foundConfig = findBuildPack('node', packageManager);
}
} else if (type === 'github') {
const files = await get(`${apiUrl}/repos/${repository}/contents?ref=${branch}`, {
Authorization: `Bearer ${$appSession.tokens.github}`,
Accept: 'application/vnd.github.v2.json'
});
const packageJson = files.find(
(file: { name: string; type: string }) =>
file.name === 'package.json' && file.type === 'file'
);
const yarnLock = files.find(
(file: { name: string; type: string }) =>
file.name === 'yarn.lock' && file.type === 'file'
);
const pnpmLock = files.find(
(file: { name: string; type: string }) =>
file.name === 'pnpm-lock.yaml' && file.type === 'file'
);
const dockerfile = files.find(
(file: { name: string; type: string }) =>
file.name === 'Dockerfile' && file.type === 'file'
);
const cargoToml = files.find(
(file: { name: string; type: string }) =>
file.name === 'Cargo.toml' && file.type === 'file'
);
const requirementsTxt = files.find(
(file: { name: string; type: string }) =>
file.name === 'requirements.txt' && file.type === 'file'
);
const indexHtml = files.find(
(file: { name: string; type: string }) =>
file.name === 'index.html' && file.type === 'file'
);
const indexPHP = files.find(
(file: { name: string; type: string }) =>
file.name === 'index.php' && file.type === 'file'
);
const composerPHP = files.find(
(file: { name: string; type: string }) =>
file.name === 'composer.json' && file.type === 'file'
);
const laravel = files.find(
(file: { name: string; type: string }) => file.name === 'artisan' && file.type === 'file'
);
if (yarnLock) packageManager = 'yarn';
if (pnpmLock) packageManager = 'pnpm';
if (dockerfile) {
foundConfig = findBuildPack('docker', packageManager);
} else if (packageJson && !laravel) {
const data: any = await get(`${packageJson.git_url}`, {
Authorization: `Bearer ${$appSession.tokens.github}`,
Accept: 'application/vnd.github.v2.raw'
});
const json = JSON.parse(data) || {};
checkTemplates({ json, packageManager });
} else if (cargoToml) {
foundConfig = findBuildPack('rust');
} else if (requirementsTxt) {
foundConfig = findBuildPack('python');
} else if (indexHtml) {
foundConfig = findBuildPack('static', packageManager);
} else if ((indexPHP || composerPHP) && !laravel) {
foundConfig = findBuildPack('php');
} else if (laravel) {
foundConfig = findBuildPack('laravel');
} else {
foundConfig = findBuildPack('node', packageManager);
}
}
} catch (error: any) {
scanning = true;
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);
}
}
if (error.message === 'Bad credentials') {
const { token } = await get(`/applications/${id}/configuration/githubToken`);
$appSession.tokens.github = token;
return await scanRepository()
}
return errorNotification(error);
} finally {
if (!foundConfig) foundConfig = findBuildPack('node', packageManager);
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">
{$t('application.configuration.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">
{$t('application.configuration.scanning_repository_suggest_build_pack')}
</div>
</div>
{:else}
<div class="max-w-7xl mx-auto flex flex-wrap justify-center">
{#each buildPacks as buildPack}
<div class="p-2">
<BuildPack {packageManager} {buildPack} {scanning} bind:foundConfig />
</div>
{/each}
</div>
{/if}

View File

@@ -0,0 +1,116 @@
<script context="module" lang="ts">
import type { Load } from '@sveltejs/kit';
export const load: Load = async ({ fetch, params, url, stuff }) => {
try {
const { application } = stuff;
if (application?.destinationDockerId && !url.searchParams.get('from')) {
return {
status: 302,
redirect: `/applications/${params.id}`
};
}
const response = await get(`/destinations`);
return {
props: {
...response
}
};
} catch (error) {
return {
status: 500,
error: new Error(`Could not load ${url}`)
};
}
};
</script>
<script lang="ts">
import { page } from '$app/stores';
import { goto } from '$app/navigation';
import { get, post } from '$lib/api';
import { t } from '$lib/translations';
import { appSession } from '$lib/store';
import { errorNotification } from '$lib/common';
const { id } = $page.params;
const from = $page.url.searchParams.get('from');
export let destinations: any;
const ownDestinations = destinations.filter((destination: any) => {
if (destination.teams[0].id === $appSession.teamId) {
return destination;
}
});
const otherDestinations = destinations.filter((destination: any) => {
if (destination.teams[0].id !== $appSession.teamId) {
return destination;
}
});
async function handleSubmit(destinationId: any) {
try {
await post(`/applications/${id}/configuration/destination`, { 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">
{$t('application.configuration.configure_destination')}
</div>
</div>
<div class="flex flex-col justify-center">
{#if !destinations || ownDestinations.length === 0}
<div class="flex-col">
<div class="pb-2">{$t('application.configuration.no_configurable_destination')}</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-col flex-wrap justify-center px-2 md:flex-row">
{#each ownDestinations 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 otherDestinations.length > 0 && $appSession.teamId === '0'}
<div class="px-6 pb-5 pt-10 text-xl font-bold">Other Destinations</div>
{/if}
<div class="flex flex-col flex-wrap justify-center px-2 md:flex-row">
{#each otherDestinations 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,50 @@
<script context="module" lang="ts">
import type { Load } from '@sveltejs/kit';
export const load: Load = async ({ params, url, stuff }) => {
try {
const { application, appId, settings } = stuff;
if (application?.branch && application?.repository && !url.searchParams.get('from')) {
return {
status: 302,
redirect: `/applications/${params.id}`
};
}
return {
props: {
application,
appId,
settings
}
};
} catch (error) {
return {
status: 500,
error: new Error(`Could not load ${url}`)
};
}
};
</script>
<script lang="ts">
import { t } from '$lib/translations';
export let application: any;
export let appId: string;
export let settings: any;
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">
{$t('application.configuration.select_a_repository_project')}
</div>
</div>
<div class="flex flex-wrap justify-center">
{#if application.gitSource.type === 'github'}
<GithubRepositories {application} />
{:else if application.gitSource.type === 'gitlab'}
<GitlabRepositories {application} {appId} {settings} />
{/if}
</div>

View File

@@ -0,0 +1,148 @@
<script context="module" lang="ts">
import type { Load } from '@sveltejs/kit';
export const load: Load = async ({ fetch, params, url, stuff }) => {
try {
const { application } = stuff;
if (application?.gitSourceId && !url.searchParams.get('from')) {
return {
status: 302,
redirect: `/applications/${params.id}`
};
}
const response = await get(`/sources`);
return {
props: {
...response
}
};
} catch (error) {
return {
status: 500,
error: new Error(`Could not load ${url}`)
};
}
};
</script>
<script lang="ts">
import { page, session } from '$app/stores';
import { goto } from '$app/navigation';
import { get, post } from '$lib/api';
import { t } from '$lib/translations';
import { errorNotification } from '$lib/common';
import { appSession } from '$lib/store';
const { id } = $page.params;
const from = $page.url.searchParams.get('from')
export let sources: any;
const filteredSources = sources.filter(
(source: any) =>
(source.type === 'github' && source.githubAppId && source.githubApp.installationId) ||
(source.type === 'gitlab' && source.gitlabAppId)
);
const ownSources = filteredSources.filter((source: any) => {
if (source.teams[0].id === $appSession.teamId) {
return source;
}
});
const otherSources = filteredSources.filter((source: any) => {
if (source.teams[0].id !== $appSession.teamId) {
return source;
}
});
async function handleSubmit(gitSourceId: string) {
try {
await post(`/applications/${id}/configuration/source`, { gitSourceId });
return await goto(from || `/applications/${id}/configuration/repository`);
} catch (error) {
return errorNotification(error);
}
}
async function newSource() {
const { id } = await post('/sources/new', {});
return await goto(`/sources/${id}`, { replaceState: true });
}
</script>
<div class="flex space-x-1 p-6 font-bold">
<div class="mr-4 text-2xl tracking-tight">
{$t('application.configuration.select_a_git_source')}
</div>
</div>
<div class="flex flex-col justify-center">
{#if !filteredSources || ownSources.length === 0}
<div class="flex-col">
<div class="pb-2 text-center font-bold">{$t('application.configuration.no_configurable_git')}</div>
<div class="flex justify-center">
<a href="/sources/new?from={$page.url.pathname}" 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-col flex-wrap justify-center px-2 md:flex-row">
{#each ownSources 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 otherSources.length > 0 && $appSession.teamId === '0'}
<div class="px-6 pb-5 pt-10 text-xl font-bold">Other Sources</div>
{/if}
<div class="flex flex-col flex-wrap justify-center px-2 md:flex-row">
{#each otherSources 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">
{$t('application.configuration.configuration_missing')}
</div>
{/if}
</button>
</form>
</div>
{/each}
</div>
{/if}
</div>