mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-19 20:59:22 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c49b34942f | ||
|
|
fcfa8717a5 | ||
|
|
954a265965 | ||
|
|
69845a020a | ||
|
|
22200fd8a7 | ||
|
|
310b099ecf | ||
|
|
1cfaef911c | ||
|
|
b931c5f638 | ||
|
|
7c683668eb | ||
|
|
cab7ac7d58 | ||
|
|
15e69c538a | ||
|
|
31ee938b66 | ||
|
|
e51a8d43d9 | ||
|
|
64cd5b6e4b | ||
|
|
6c9ef34905 | ||
|
|
aa89019236 |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "coolify",
|
"name": "coolify",
|
||||||
"description": "An open-source & self-hostable Heroku / Netlify alternative.",
|
"description": "An open-source & self-hostable Heroku / Netlify alternative.",
|
||||||
"version": "2.0.14",
|
"version": "2.0.17",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "docker compose -f docker-compose-dev.yaml up -d && NODE_ENV=development svelte-kit dev --host 0.0.0.0",
|
"dev": "docker compose -f docker-compose-dev.yaml up -d && NODE_ENV=development svelte-kit dev --host 0.0.0.0",
|
||||||
|
|||||||
19
prisma/migrations/20220219231255_prmr_secrets/migration.sql
Normal file
19
prisma/migrations/20220219231255_prmr_secrets/migration.sql
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_Secret" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"value" TEXT NOT NULL,
|
||||||
|
"isPRMRSecret" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isBuildSecret" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
"applicationId" TEXT NOT NULL,
|
||||||
|
CONSTRAINT "Secret_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
INSERT INTO "new_Secret" ("applicationId", "createdAt", "id", "isBuildSecret", "name", "updatedAt", "value") SELECT "applicationId", "createdAt", "id", "isBuildSecret", "name", "updatedAt", "value" FROM "Secret";
|
||||||
|
DROP TABLE "Secret";
|
||||||
|
ALTER TABLE "new_Secret" RENAME TO "Secret";
|
||||||
|
CREATE UNIQUE INDEX "Secret_name_applicationId_isPRMRSecret_key" ON "Secret"("name", "applicationId", "isPRMRSecret");
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
@@ -109,13 +109,14 @@ model Secret {
|
|||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String
|
name String
|
||||||
value String
|
value String
|
||||||
|
isPRMRSecret Boolean @default(false)
|
||||||
isBuildSecret Boolean @default(false)
|
isBuildSecret Boolean @default(false)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
application Application @relation(fields: [applicationId], references: [id])
|
application Application @relation(fields: [applicationId], references: [id])
|
||||||
applicationId String
|
applicationId String
|
||||||
|
|
||||||
@@unique([name, applicationId])
|
@@unique([name, applicationId, isPRMRSecret])
|
||||||
}
|
}
|
||||||
|
|
||||||
model BuildLog {
|
model BuildLog {
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ export const handle = handleSession(
|
|||||||
let response;
|
let response;
|
||||||
try {
|
try {
|
||||||
if (event.locals.cookies) {
|
if (event.locals.cookies) {
|
||||||
let gitlabToken = event.locals.cookies.gitlabToken || null;
|
|
||||||
let ghToken = event.locals.cookies.ghToken;
|
|
||||||
if (event.locals.cookies['kit.session']) {
|
if (event.locals.cookies['kit.session']) {
|
||||||
const { permission, teamId, userId } = await getUserDetails(event, false);
|
const { permission, teamId, userId } = await getUserDetails(event, false);
|
||||||
const newSession = {
|
const newSession = {
|
||||||
@@ -26,9 +24,7 @@ export const handle = handleSession(
|
|||||||
teamId,
|
teamId,
|
||||||
permission,
|
permission,
|
||||||
isAdmin: permission === 'admin' || permission === 'owner',
|
isAdmin: permission === 'admin' || permission === 'owner',
|
||||||
expires: event.locals.session.data.expires,
|
expires: event.locals.session.data.expires
|
||||||
gitlabToken,
|
|
||||||
ghToken
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (JSON.stringify(event.locals.session.data) !== JSON.stringify(newSession)) {
|
if (JSON.stringify(event.locals.session.data) !== JSON.stringify(newSession)) {
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ export default async function ({
|
|||||||
docker,
|
docker,
|
||||||
buildId,
|
buildId,
|
||||||
baseDirectory,
|
baseDirectory,
|
||||||
secrets
|
secrets,
|
||||||
|
pullmergeRequestId
|
||||||
}) {
|
}) {
|
||||||
try {
|
try {
|
||||||
let file = `${workdir}/Dockerfile`;
|
let file = `${workdir}/Dockerfile`;
|
||||||
@@ -24,7 +25,15 @@ export default async function ({
|
|||||||
if (secrets.length > 0) {
|
if (secrets.length > 0) {
|
||||||
secrets.forEach((secret) => {
|
secrets.forEach((secret) => {
|
||||||
if (secret.isBuildSecret) {
|
if (secret.isBuildSecret) {
|
||||||
Dockerfile.push(`ARG ${secret.name} ${secret.value}`);
|
if (pullmergeRequestId) {
|
||||||
|
if (secret.isPRMRSecret) {
|
||||||
|
Dockerfile.push(`ARG ${secret.name} ${secret.value}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!secret.isPRMRSecret) {
|
||||||
|
Dockerfile.push(`ARG ${secret.name} ${secret.value}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,16 @@ import { buildImage } from '$lib/docker';
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
|
|
||||||
const createDockerfile = async (data, image): Promise<void> => {
|
const createDockerfile = async (data, image): Promise<void> => {
|
||||||
const { workdir, port, installCommand, buildCommand, startCommand, baseDirectory, secrets } =
|
const {
|
||||||
data;
|
workdir,
|
||||||
|
port,
|
||||||
|
installCommand,
|
||||||
|
buildCommand,
|
||||||
|
startCommand,
|
||||||
|
baseDirectory,
|
||||||
|
secrets,
|
||||||
|
pullmergeRequestId
|
||||||
|
} = data;
|
||||||
const Dockerfile: Array<string> = [];
|
const Dockerfile: Array<string> = [];
|
||||||
|
|
||||||
Dockerfile.push(`FROM ${image}`);
|
Dockerfile.push(`FROM ${image}`);
|
||||||
@@ -11,7 +19,15 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
if (secrets.length > 0) {
|
if (secrets.length > 0) {
|
||||||
secrets.forEach((secret) => {
|
secrets.forEach((secret) => {
|
||||||
if (secret.isBuildSecret) {
|
if (secret.isBuildSecret) {
|
||||||
Dockerfile.push(`ARG ${secret.name} ${secret.value}`);
|
if (pullmergeRequestId) {
|
||||||
|
if (secret.isPRMRSecret) {
|
||||||
|
Dockerfile.push(`ARG ${secret.name} ${secret.value}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!secret.isPRMRSecret) {
|
||||||
|
Dockerfile.push(`ARG ${secret.name} ${secret.value}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,16 @@ import { buildImage } from '$lib/docker';
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
|
|
||||||
const createDockerfile = async (data, image): Promise<void> => {
|
const createDockerfile = async (data, image): Promise<void> => {
|
||||||
const { workdir, port, installCommand, buildCommand, startCommand, baseDirectory, secrets } =
|
const {
|
||||||
data;
|
workdir,
|
||||||
|
port,
|
||||||
|
installCommand,
|
||||||
|
buildCommand,
|
||||||
|
startCommand,
|
||||||
|
baseDirectory,
|
||||||
|
secrets,
|
||||||
|
pullmergeRequestId
|
||||||
|
} = data;
|
||||||
const Dockerfile: Array<string> = [];
|
const Dockerfile: Array<string> = [];
|
||||||
|
|
||||||
Dockerfile.push(`FROM ${image}`);
|
Dockerfile.push(`FROM ${image}`);
|
||||||
@@ -11,7 +19,15 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
if (secrets.length > 0) {
|
if (secrets.length > 0) {
|
||||||
secrets.forEach((secret) => {
|
secrets.forEach((secret) => {
|
||||||
if (secret.isBuildSecret) {
|
if (secret.isBuildSecret) {
|
||||||
Dockerfile.push(`ARG ${secret.name} ${secret.value}`);
|
if (pullmergeRequestId) {
|
||||||
|
if (secret.isPRMRSecret) {
|
||||||
|
Dockerfile.push(`ARG ${secret.name} ${secret.value}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!secret.isPRMRSecret) {
|
||||||
|
Dockerfile.push(`ARG ${secret.name} ${secret.value}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,16 @@ import { buildImage } from '$lib/docker';
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
|
|
||||||
const createDockerfile = async (data, image): Promise<void> => {
|
const createDockerfile = async (data, image): Promise<void> => {
|
||||||
const { workdir, port, installCommand, buildCommand, startCommand, baseDirectory, secrets } =
|
const {
|
||||||
data;
|
workdir,
|
||||||
|
port,
|
||||||
|
installCommand,
|
||||||
|
buildCommand,
|
||||||
|
startCommand,
|
||||||
|
baseDirectory,
|
||||||
|
secrets,
|
||||||
|
pullmergeRequestId
|
||||||
|
} = data;
|
||||||
const Dockerfile: Array<string> = [];
|
const Dockerfile: Array<string> = [];
|
||||||
|
|
||||||
Dockerfile.push(`FROM ${image}`);
|
Dockerfile.push(`FROM ${image}`);
|
||||||
@@ -11,7 +19,15 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
if (secrets.length > 0) {
|
if (secrets.length > 0) {
|
||||||
secrets.forEach((secret) => {
|
secrets.forEach((secret) => {
|
||||||
if (secret.isBuildSecret) {
|
if (secret.isBuildSecret) {
|
||||||
Dockerfile.push(`ARG ${secret.name} ${secret.value}`);
|
if (pullmergeRequestId) {
|
||||||
|
if (secret.isPRMRSecret) {
|
||||||
|
Dockerfile.push(`ARG ${secret.name} ${secret.value}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!secret.isPRMRSecret) {
|
||||||
|
Dockerfile.push(`ARG ${secret.name} ${secret.value}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,16 @@ import { buildCacheImageWithNode, buildImage } from '$lib/docker';
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
|
|
||||||
const createDockerfile = async (data, image): Promise<void> => {
|
const createDockerfile = async (data, image): Promise<void> => {
|
||||||
const { applicationId, tag, workdir, buildCommand, baseDirectory, publishDirectory, secrets } =
|
const {
|
||||||
data;
|
applicationId,
|
||||||
|
tag,
|
||||||
|
workdir,
|
||||||
|
buildCommand,
|
||||||
|
baseDirectory,
|
||||||
|
publishDirectory,
|
||||||
|
secrets,
|
||||||
|
pullmergeRequestId
|
||||||
|
} = data;
|
||||||
const Dockerfile: Array<string> = [];
|
const Dockerfile: Array<string> = [];
|
||||||
|
|
||||||
Dockerfile.push(`FROM ${image}`);
|
Dockerfile.push(`FROM ${image}`);
|
||||||
@@ -11,7 +19,15 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
if (secrets.length > 0) {
|
if (secrets.length > 0) {
|
||||||
secrets.forEach((secret) => {
|
secrets.forEach((secret) => {
|
||||||
if (secret.isBuildSecret) {
|
if (secret.isBuildSecret) {
|
||||||
Dockerfile.push(`ARG ${secret.name} ${secret.value}`);
|
if (pullmergeRequestId) {
|
||||||
|
if (secret.isPRMRSecret) {
|
||||||
|
Dockerfile.push(`ARG ${secret.name} ${secret.value}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!secret.isPRMRSecret) {
|
||||||
|
Dockerfile.push(`ARG ${secret.name} ${secret.value}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<script>
|
<script>
|
||||||
import { browser } from '$app/env';
|
import { browser } from '$app/env';
|
||||||
import { toast } from '@zerodevx/svelte-toast';
|
import { toast } from '@zerodevx/svelte-toast';
|
||||||
export let value;
|
|
||||||
let showPassword = false;
|
let showPassword = false;
|
||||||
|
|
||||||
|
export let value;
|
||||||
export let disabled = false;
|
export let disabled = false;
|
||||||
export let isPasswordField = false;
|
export let isPasswordField = false;
|
||||||
export let readonly = false;
|
export let readonly = false;
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
export let name;
|
export let name;
|
||||||
export let placeholder = '';
|
export let placeholder = '';
|
||||||
|
|
||||||
let disabledClass = 'bg-coolback disabled:bg-coolblack select-all';
|
let disabledClass = 'bg-coolback disabled:bg-coolblack';
|
||||||
let actionsShow = false;
|
let actionsShow = false;
|
||||||
let isHttps = browser && window.location.protocol === 'https:';
|
let isHttps = browser && window.location.protocol === 'https:';
|
||||||
|
|
||||||
@@ -29,11 +29,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div class="relative">
|
||||||
class="relative"
|
|
||||||
on:mouseenter={() => showActions(true)}
|
|
||||||
on:mouseleave={() => showActions(false)}
|
|
||||||
>
|
|
||||||
{#if !isPasswordField || showPassword}
|
{#if !isPasswordField || showPassword}
|
||||||
{#if textarea}
|
{#if textarea}
|
||||||
<textarea
|
<textarea
|
||||||
@@ -77,69 +73,67 @@
|
|||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if actionsShow}
|
<div class="absolute top-0 right-0 m-3 cursor-pointer text-warmGray-600 hover:text-white">
|
||||||
<div class="absolute top-0 right-0 m-3 cursor-pointer text-warmGray-600 hover:text-white">
|
<div class="flex space-x-2">
|
||||||
<div class="flex space-x-2">
|
{#if isPasswordField}
|
||||||
{#if isPasswordField}
|
<div on:click={() => (showPassword = !showPassword)}>
|
||||||
<div on:click={() => (showPassword = !showPassword)}>
|
{#if showPassword}
|
||||||
{#if showPassword}
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="h-6 w-6"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
{:else}
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="h-6 w-6"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{#if value && isHttps}
|
|
||||||
<div on:click={copyToClipboard}>
|
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-6 w-6"
|
class="h-6 w-6"
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5"
|
|
||||||
stroke="currentColor"
|
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke-linecap="round"
|
viewBox="0 0 24 24"
|
||||||
stroke-linejoin="round"
|
stroke="currentColor"
|
||||||
>
|
>
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
<path
|
||||||
<rect x="8" y="8" width="12" height="12" rx="2" />
|
stroke-linecap="round"
|
||||||
<path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2" />
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
{:else}
|
||||||
{/if}
|
<svg
|
||||||
</div>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-6 w-6"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if value && isHttps}
|
||||||
|
<div on:click={copyToClipboard}>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-6 w-6"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
fill="none"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
|
<rect x="8" y="8" width="12" height="12" rx="2" />
|
||||||
|
<path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,22 +9,6 @@ export const dateOptions: DateTimeFormatOptions = {
|
|||||||
hour12: false
|
hour12: false
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function getGithubToken({ apiUrl, application, githubToken }): Promise<void> {
|
|
||||||
const response = await fetch(
|
|
||||||
`${apiUrl}/app/installations/${application.gitSource.githubApp.installationId}/access_tokens`,
|
|
||||||
{
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${githubToken}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error('Git Source not configured.');
|
|
||||||
}
|
|
||||||
const data = await response.json();
|
|
||||||
return data.token;
|
|
||||||
}
|
|
||||||
export const staticDeployments = ['react', 'vuejs', 'static', 'svelte', 'gatsby', 'php'];
|
export const staticDeployments = ['react', 'vuejs', 'static', 'svelte', 'gatsby', 'php'];
|
||||||
export const notNodeDeployments = ['php', 'docker', 'rust'];
|
export const notNodeDeployments = ['php', 'docker', 'rust'];
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ export const buildPacks = [
|
|||||||
|
|
||||||
{
|
{
|
||||||
name: 'static',
|
name: 'static',
|
||||||
...defaultBuildAndDeploy,
|
|
||||||
publishDirectory: 'dist',
|
publishDirectory: 'dist',
|
||||||
port: 80,
|
port: 80,
|
||||||
fancyName: 'Static',
|
fancyName: 'Static',
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ export async function isDockerNetworkExists({ network }) {
|
|||||||
return await prisma.destinationDocker.findFirst({ where: { network } });
|
return await prisma.destinationDocker.findFirst({ where: { network } });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function isSecretExists({ id, name }) {
|
export async function isSecretExists({ id, name, isPRMRSecret }) {
|
||||||
return await prisma.secret.findFirst({ where: { name, applicationId: id } });
|
return await prisma.secret.findFirst({ where: { name, applicationId: id, isPRMRSecret } });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function isDomainConfigured({ id, fqdn }) {
|
export async function isDomainConfigured({ id, fqdn }) {
|
||||||
|
|||||||
@@ -1,19 +1,41 @@
|
|||||||
import { encrypt } from '$lib/crypto';
|
import { encrypt, decrypt } from '$lib/crypto';
|
||||||
import { prisma } from './common';
|
import { prisma } from './common';
|
||||||
|
|
||||||
export async function listSecrets({ applicationId }) {
|
export async function listSecrets(applicationId: string) {
|
||||||
return await prisma.secret.findMany({
|
let secrets = await prisma.secret.findMany({
|
||||||
where: { applicationId },
|
where: { applicationId },
|
||||||
orderBy: { createdAt: 'desc' },
|
orderBy: { createdAt: 'desc' }
|
||||||
select: { id: true, createdAt: true, name: true, isBuildSecret: true }
|
});
|
||||||
|
secrets = secrets.map((secret) => {
|
||||||
|
secret.value = decrypt(secret.value);
|
||||||
|
return secret;
|
||||||
|
});
|
||||||
|
|
||||||
|
return secrets;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createSecret({ id, name, value, isBuildSecret, isPRMRSecret }) {
|
||||||
|
value = encrypt(value);
|
||||||
|
return await prisma.secret.create({
|
||||||
|
data: { name, value, isBuildSecret, isPRMRSecret, application: { connect: { id } } }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createSecret({ id, name, value, isBuildSecret }) {
|
export async function updateSecret({ id, name, value, isBuildSecret, isPRMRSecret }) {
|
||||||
value = encrypt(value);
|
value = encrypt(value);
|
||||||
return await prisma.secret.create({
|
const found = await prisma.secret.findFirst({ where: { applicationId: id, name, isPRMRSecret } });
|
||||||
data: { name, value, isBuildSecret, application: { connect: { id } } }
|
console.log(found);
|
||||||
});
|
|
||||||
|
if (found) {
|
||||||
|
return await prisma.secret.updateMany({
|
||||||
|
where: { applicationId: id, name, isPRMRSecret },
|
||||||
|
data: { value, isBuildSecret, isPRMRSecret }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return await prisma.secret.create({
|
||||||
|
data: { name, value, isBuildSecret, isPRMRSecret, application: { connect: { id } } }
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function removeSecret({ id, name }) {
|
export async function removeSecret({ id, name }) {
|
||||||
|
|||||||
@@ -13,15 +13,24 @@ export async function buildCacheImageWithNode(data, imageForBuild) {
|
|||||||
installCommand,
|
installCommand,
|
||||||
buildCommand,
|
buildCommand,
|
||||||
debug,
|
debug,
|
||||||
secrets
|
secrets,
|
||||||
|
pullmergeRequestId
|
||||||
} = data;
|
} = data;
|
||||||
const Dockerfile: Array<string> = [];
|
const Dockerfile: Array<string> = [];
|
||||||
Dockerfile.push(`FROM ${imageForBuild}`);
|
Dockerfile.push(`FROM ${imageForBuild}`);
|
||||||
Dockerfile.push('WORKDIR /usr/src/app');
|
Dockerfile.push('WORKDIR /usr/src/app');
|
||||||
if (secrets.length > 0) {
|
if (secrets.length > 0) {
|
||||||
secrets.forEach((secret) => {
|
secrets.forEach((secret) => {
|
||||||
if (!secret.isBuildSecret) {
|
if (secret.isBuildSecret) {
|
||||||
Dockerfile.push(`ARG ${secret.name} ${secret.value}`);
|
if (pullmergeRequestId) {
|
||||||
|
if (secret.isPRMRSecret) {
|
||||||
|
Dockerfile.push(`ARG ${secret.name} ${secret.value}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!secret.isPRMRSecret) {
|
||||||
|
Dockerfile.push(`ARG ${secret.name} ${secret.value}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ export default async function (job) {
|
|||||||
if (destinationDockerId) {
|
if (destinationDockerId) {
|
||||||
destinationType = 'docker';
|
destinationType = 'docker';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (destinationType === 'docker') {
|
if (destinationType === 'docker') {
|
||||||
const docker = dockerInstance({ destinationDocker });
|
const docker = dockerInstance({ destinationDocker });
|
||||||
const host = getEngine(destinationDocker.engine);
|
const host = getEngine(destinationDocker.engine);
|
||||||
@@ -205,7 +204,15 @@ export default async function (job) {
|
|||||||
const envs = [];
|
const envs = [];
|
||||||
if (secrets.length > 0) {
|
if (secrets.length > 0) {
|
||||||
secrets.forEach((secret) => {
|
secrets.forEach((secret) => {
|
||||||
envs.push(`${secret.name}=${secret.value}`);
|
if (pullmergeRequestId) {
|
||||||
|
if (secret.isPRMRSecret) {
|
||||||
|
envs.push(`${secret.name}=${secret.value}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!secret.isPRMRSecret) {
|
||||||
|
envs.push(`${secret.name}=${secret.value}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await fs.writeFile(`${workdir}/.env`, envs.join('\n'));
|
await fs.writeFile(`${workdir}/.env`, envs.join('\n'));
|
||||||
|
|||||||
6
src/lib/store.ts
Normal file
6
src/lib/store.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { writable } from 'svelte/store';
|
||||||
|
|
||||||
|
export const gitTokens = writable({
|
||||||
|
githubToken: null,
|
||||||
|
gitlabToken: null
|
||||||
|
});
|
||||||
@@ -17,13 +17,20 @@
|
|||||||
const endpoint = `/applications/${params.id}.json`;
|
const endpoint = `/applications/${params.id}.json`;
|
||||||
const res = await fetch(endpoint);
|
const res = await fetch(endpoint);
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const { application, isRunning, appId } = await res.json();
|
let { application, isRunning, appId, githubToken, gitlabToken } = await res.json();
|
||||||
if (!application || Object.entries(application).length === 0) {
|
if (!application || Object.entries(application).length === 0) {
|
||||||
return {
|
return {
|
||||||
status: 302,
|
status: 302,
|
||||||
redirect: '/applications'
|
redirect: '/applications'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (application.gitSource?.githubAppId && !githubToken) {
|
||||||
|
const response = await fetch(`/applications/${params.id}/configuration/githubToken.json`);
|
||||||
|
if (response.ok) {
|
||||||
|
const { token } = await response.json();
|
||||||
|
githubToken = token;
|
||||||
|
}
|
||||||
|
}
|
||||||
const configurationPhase = checkConfiguration(application);
|
const configurationPhase = checkConfiguration(application);
|
||||||
if (
|
if (
|
||||||
configurationPhase &&
|
configurationPhase &&
|
||||||
@@ -38,7 +45,9 @@
|
|||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
application,
|
application,
|
||||||
isRunning
|
isRunning,
|
||||||
|
githubToken,
|
||||||
|
gitlabToken
|
||||||
},
|
},
|
||||||
stuff: {
|
stuff: {
|
||||||
isRunning,
|
isRunning,
|
||||||
@@ -58,12 +67,18 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let application;
|
export let application;
|
||||||
export let isRunning;
|
export let isRunning;
|
||||||
|
export let githubToken;
|
||||||
|
export let gitlabToken;
|
||||||
import { page, session } from '$app/stores';
|
import { page, session } from '$app/stores';
|
||||||
import { errorNotification } from '$lib/form';
|
import { errorNotification } from '$lib/form';
|
||||||
import DeleteIcon from '$lib/components/DeleteIcon.svelte';
|
import DeleteIcon from '$lib/components/DeleteIcon.svelte';
|
||||||
import Loading from '$lib/components/Loading.svelte';
|
import Loading from '$lib/components/Loading.svelte';
|
||||||
import { del, post } from '$lib/api';
|
import { del, post } from '$lib/api';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
import { gitTokens } from '$lib/store';
|
||||||
|
|
||||||
|
if (githubToken) $gitTokens.githubToken = githubToken;
|
||||||
|
if (gitlabToken) $gitTokens.gitlabToken = gitlabToken;
|
||||||
|
|
||||||
let loading = false;
|
let loading = false;
|
||||||
const { id } = $page.params;
|
const { id } = $page.params;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { goto } from '$app/navigation';
|
|
||||||
|
|
||||||
export let application;
|
export let application;
|
||||||
|
|
||||||
import { page, session } from '$app/stores';
|
import { goto } from '$app/navigation';
|
||||||
|
import { page } from '$app/stores';
|
||||||
import { get, post } from '$lib/api';
|
import { get, post } from '$lib/api';
|
||||||
import { errorNotification } from '$lib/form';
|
import { errorNotification } from '$lib/form';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
import { gitTokens } from '$lib/store';
|
||||||
|
|
||||||
const { id } = $page.params;
|
const { id } = $page.params;
|
||||||
const from = $page.url.searchParams.get('from');
|
const from = $page.url.searchParams.get('from');
|
||||||
@@ -29,13 +29,9 @@
|
|||||||
};
|
};
|
||||||
let showSave = false;
|
let showSave = false;
|
||||||
async function loadRepositoriesByPage(page = 0) {
|
async function loadRepositoriesByPage(page = 0) {
|
||||||
try {
|
return await get(`${apiUrl}/installation/repositories?per_page=100&page=${page}`, {
|
||||||
return await get(`${apiUrl}/installation/repositories?per_page=100&page=${page}`, {
|
Authorization: `token ${$gitTokens.githubToken}`
|
||||||
Authorization: `token ${$session.ghToken}`
|
});
|
||||||
});
|
|
||||||
} catch ({ error }) {
|
|
||||||
return errorNotification(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
async function loadRepositories() {
|
async function loadRepositories() {
|
||||||
let page = 1;
|
let page = 1;
|
||||||
@@ -58,7 +54,7 @@
|
|||||||
selected.projectId = repositories.find((repo) => repo.full_name === selected.repository).id;
|
selected.projectId = repositories.find((repo) => repo.full_name === selected.repository).id;
|
||||||
try {
|
try {
|
||||||
branches = await get(`${apiUrl}/repos/${selected.repository}/branches`, {
|
branches = await get(`${apiUrl}/repos/${selected.repository}/branches`, {
|
||||||
Authorization: `token ${$session.ghToken}`
|
Authorization: `token ${$gitTokens.githubToken}`
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
} catch ({ error }) {
|
} catch ({ error }) {
|
||||||
@@ -85,7 +81,47 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
await loadRepositories();
|
try {
|
||||||
|
if (!$gitTokens.githubToken) {
|
||||||
|
const { token } = await get(`/applications/${id}/configuration/githubToken.json`);
|
||||||
|
$gitTokens.githubToken = token;
|
||||||
|
}
|
||||||
|
await loadRepositories();
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (error.message === 'Bad credentials') {
|
||||||
|
const { token } = await get(`/applications/${id}/configuration/githubToken.json`);
|
||||||
|
$gitTokens.githubToken = token;
|
||||||
|
return await loadRepositories();
|
||||||
|
}
|
||||||
|
return errorNotification(error);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
import cuid from 'cuid';
|
import cuid from 'cuid';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { del, get, post, put } from '$lib/api';
|
import { del, get, post, put } from '$lib/api';
|
||||||
|
import { gitTokens } from '$lib/store';
|
||||||
|
|
||||||
const { id } = $page.params;
|
const { id } = $page.params;
|
||||||
const from = $page.url.searchParams.get('from');
|
const from = $page.url.searchParams.get('from');
|
||||||
|
|
||||||
@@ -35,13 +37,13 @@
|
|||||||
branch: undefined
|
branch: undefined
|
||||||
};
|
};
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
if (!$session.gitlabToken) {
|
if (!$gitTokens.gitlabToken) {
|
||||||
getGitlabToken();
|
getGitlabToken();
|
||||||
} else {
|
} else {
|
||||||
loading.base = true;
|
loading.base = true;
|
||||||
try {
|
try {
|
||||||
const user = await get(`${apiUrl}/v4/user`, {
|
const user = await get(`${apiUrl}/v4/user`, {
|
||||||
Authorization: `Bearer ${$session.gitlabToken}`
|
Authorization: `Bearer ${$gitTokens.gitlabToken}`
|
||||||
});
|
});
|
||||||
username = user.username;
|
username = user.username;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -49,7 +51,7 @@
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
groups = await get(`${apiUrl}/v4/groups?per_page=5000`, {
|
groups = await get(`${apiUrl}/v4/groups?per_page=5000`, {
|
||||||
Authorization: `Bearer ${$session.gitlabToken}`
|
Authorization: `Bearer ${$gitTokens.gitlabToken}`
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
errorNotification(error);
|
errorNotification(error);
|
||||||
@@ -87,7 +89,7 @@
|
|||||||
projects = await get(
|
projects = await get(
|
||||||
`${apiUrl}/v4/users/${selected.group.name}/projects?min_access_level=40&page=1&per_page=25&archived=false`,
|
`${apiUrl}/v4/users/${selected.group.name}/projects?min_access_level=40&page=1&per_page=25&archived=false`,
|
||||||
{
|
{
|
||||||
Authorization: `Bearer ${$session.gitlabToken}`
|
Authorization: `Bearer ${$gitTokens.gitlabToken}`
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -101,7 +103,7 @@
|
|||||||
projects = await get(
|
projects = await get(
|
||||||
`${apiUrl}/v4/groups/${selected.group.id}/projects?page=1&per_page=25&archived=false`,
|
`${apiUrl}/v4/groups/${selected.group.id}/projects?page=1&per_page=25&archived=false`,
|
||||||
{
|
{
|
||||||
Authorization: `Bearer ${$session.gitlabToken}`
|
Authorization: `Bearer ${$gitTokens.gitlabToken}`
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -119,7 +121,7 @@
|
|||||||
branches = await get(
|
branches = await get(
|
||||||
`${apiUrl}/v4/projects/${selected.project.id}/repository/branches?per_page=100&page=1`,
|
`${apiUrl}/v4/projects/${selected.project.id}/repository/branches?per_page=100&page=1`,
|
||||||
{
|
{
|
||||||
Authorization: `Bearer ${$session.gitlabToken}`
|
Authorization: `Bearer ${$gitTokens.gitlabToken}`
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -169,7 +171,7 @@
|
|||||||
merge_requests_events: true
|
merge_requests_events: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Authorization: `Bearer ${$session.gitlabToken}`
|
Authorization: `Bearer ${$gitTokens.gitlabToken}`
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -193,7 +195,7 @@
|
|||||||
publicSshKey = publicKey;
|
publicSshKey = publicKey;
|
||||||
}
|
}
|
||||||
const deployKeys = await get(deployKeyUrl, {
|
const deployKeys = await get(deployKeyUrl, {
|
||||||
Authorization: `Bearer ${$session.gitlabToken}`
|
Authorization: `Bearer ${$gitTokens.gitlabToken}`
|
||||||
});
|
});
|
||||||
const deployKeyFound = deployKeys.filter((dk) => dk.title === `${appId}-coolify-deploy-key`);
|
const deployKeyFound = deployKeys.filter((dk) => dk.title === `${appId}-coolify-deploy-key`);
|
||||||
if (deployKeyFound.length > 0) {
|
if (deployKeyFound.length > 0) {
|
||||||
@@ -202,7 +204,7 @@
|
|||||||
`${deployKeyUrl}/${deployKey.id}`,
|
`${deployKeyUrl}/${deployKey.id}`,
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
Authorization: `Bearer ${$session.gitlabToken}`
|
Authorization: `Bearer ${$gitTokens.gitlabToken}`
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -215,7 +217,7 @@
|
|||||||
can_push: false
|
can_push: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Authorization: `Bearer ${$session.gitlabToken}`
|
Authorization: `Bearer ${$gitTokens.gitlabToken}`
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
await post(updateDeployKeyIdUrl, { deployKeyId: id });
|
await post(updateDeployKeyIdUrl, { deployKeyId: id });
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
import { page, session } from '$app/stores';
|
import { page, session } from '$app/stores';
|
||||||
import { get } from '$lib/api';
|
import { get } from '$lib/api';
|
||||||
import { errorNotification } from '$lib/form';
|
import { errorNotification } from '$lib/form';
|
||||||
|
import { gitTokens } from '$lib/store';
|
||||||
|
|
||||||
let scanning = true;
|
let scanning = true;
|
||||||
let foundConfig = null;
|
let foundConfig = null;
|
||||||
@@ -59,7 +60,7 @@
|
|||||||
try {
|
try {
|
||||||
if (type === 'gitlab') {
|
if (type === 'gitlab') {
|
||||||
const files = await get(`${apiUrl}/v4/projects/${projectId}/repository/tree`, {
|
const files = await get(`${apiUrl}/v4/projects/${projectId}/repository/tree`, {
|
||||||
Authorization: `Bearer ${$session.gitlabToken}`
|
Authorization: `Bearer ${$gitTokens.gitlabToken}`
|
||||||
});
|
});
|
||||||
const packageJson = files.find(
|
const packageJson = files.find(
|
||||||
(file) => file.name === 'package.json' && file.type === 'blob'
|
(file) => file.name === 'package.json' && file.type === 'blob'
|
||||||
@@ -78,7 +79,7 @@
|
|||||||
const data = await get(
|
const data = await get(
|
||||||
`${apiUrl}/v4/projects/${projectId}/repository/files/${path}/raw?ref=${branch}`,
|
`${apiUrl}/v4/projects/${projectId}/repository/files/${path}/raw?ref=${branch}`,
|
||||||
{
|
{
|
||||||
Authorization: `Bearer ${$session.gitlabToken}`
|
Authorization: `Bearer ${$gitTokens.gitlabToken}`
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const json = JSON.parse(data) || {};
|
const json = JSON.parse(data) || {};
|
||||||
@@ -94,7 +95,7 @@
|
|||||||
}
|
}
|
||||||
} else if (type === 'github') {
|
} else if (type === 'github') {
|
||||||
const files = await get(`${apiUrl}/repos/${repository}/contents?ref=${branch}`, {
|
const files = await get(`${apiUrl}/repos/${repository}/contents?ref=${branch}`, {
|
||||||
Authorization: `Bearer ${$session.ghToken || ghToken}`,
|
Authorization: `Bearer ${$gitTokens.githubToken}`,
|
||||||
Accept: 'application/vnd.github.v2.json'
|
Accept: 'application/vnd.github.v2.json'
|
||||||
});
|
});
|
||||||
const packageJson = files.find(
|
const packageJson = files.find(
|
||||||
@@ -111,7 +112,7 @@
|
|||||||
foundConfig.buildPack = 'docker';
|
foundConfig.buildPack = 'docker';
|
||||||
} else if (packageJson) {
|
} else if (packageJson) {
|
||||||
const data = await get(`${packageJson.git_url}`, {
|
const data = await get(`${packageJson.git_url}`, {
|
||||||
Authorization: `Bearer ${$session.ghToken}`,
|
Authorization: `Bearer ${$gitTokens.githubToken}`,
|
||||||
Accept: 'application/vnd.github.v2.raw'
|
Accept: 'application/vnd.github.v2.raw'
|
||||||
});
|
});
|
||||||
const json = JSON.parse(data) || {};
|
const json = JSON.parse(data) || {};
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import { getUserDetails } from '$lib/common';
|
||||||
|
import * as db from '$lib/database';
|
||||||
|
import { ErrorHandler } from '$lib/database';
|
||||||
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
|
import jsonwebtoken from 'jsonwebtoken';
|
||||||
|
|
||||||
|
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 });
|
||||||
|
const payload = {
|
||||||
|
iat: Math.round(new Date().getTime() / 1000),
|
||||||
|
exp: Math.round(new Date().getTime() / 1000 + 60),
|
||||||
|
iss: application.gitSource.githubApp.appId
|
||||||
|
};
|
||||||
|
const githubToken = jsonwebtoken.sign(payload, application.gitSource.githubApp.privateKey, {
|
||||||
|
algorithm: 'RS256'
|
||||||
|
});
|
||||||
|
const response = await fetch(
|
||||||
|
`${application.gitSource.apiUrl}/app/installations/${application.gitSource.githubApp.installationId}/access_tokens`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${githubToken}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`${response.status} ${response.statusText}`);
|
||||||
|
}
|
||||||
|
const data = await response.json();
|
||||||
|
return {
|
||||||
|
status: 201,
|
||||||
|
body: { token: data.token },
|
||||||
|
headers: {
|
||||||
|
'Set-Cookie': `githubToken=${data.token}; Path=/; HttpOnly; Max-Age=15778800;`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorHandler(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let application;
|
export let application;
|
||||||
export let appId;
|
export let appId;
|
||||||
|
|
||||||
import GithubRepositories from './_GithubRepositories.svelte';
|
import GithubRepositories from './_GithubRepositories.svelte';
|
||||||
import GitlabRepositories from './_GitlabRepositories.svelte';
|
import GitlabRepositories from './_GitlabRepositories.svelte';
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,54 +1,37 @@
|
|||||||
import { getTeam, getUserDetails } from '$lib/common';
|
import { getUserDetails } from '$lib/common';
|
||||||
import { getGithubToken } from '$lib/components/common';
|
|
||||||
import * as db from '$lib/database';
|
import * as db from '$lib/database';
|
||||||
import { ErrorHandler } from '$lib/database';
|
import { ErrorHandler } from '$lib/database';
|
||||||
import { checkContainer } from '$lib/haproxy';
|
import { checkContainer } from '$lib/haproxy';
|
||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
import jsonwebtoken from 'jsonwebtoken';
|
import jsonwebtoken from 'jsonwebtoken';
|
||||||
|
import { get as getRequest } from '$lib/api';
|
||||||
|
|
||||||
export const get: RequestHandler = async (event) => {
|
export const get: RequestHandler = async (event) => {
|
||||||
const { teamId, status, body } = await getUserDetails(event);
|
const { teamId, status, body } = await getUserDetails(event);
|
||||||
if (status === 401) return { status, body };
|
if (status === 401) return { status, body };
|
||||||
|
|
||||||
const appId = process.env['COOLIFY_APP_ID'];
|
|
||||||
let githubToken = null;
|
|
||||||
let ghToken = null;
|
|
||||||
let isRunning = false;
|
|
||||||
const { id } = event.params;
|
const { id } = event.params;
|
||||||
|
|
||||||
|
const appId = process.env['COOLIFY_APP_ID'];
|
||||||
|
let isRunning = false;
|
||||||
|
let githubToken = event.locals.cookies?.githubToken || null;
|
||||||
|
let gitlabToken = event.locals.cookies?.gitlabToken || null;
|
||||||
try {
|
try {
|
||||||
const application = await db.getApplication({ id, teamId });
|
const application = await db.getApplication({ id, teamId });
|
||||||
const { gitSource } = application;
|
|
||||||
if (gitSource?.type === 'github' && gitSource?.githubApp) {
|
|
||||||
if (!event.locals.session.data.ghToken) {
|
|
||||||
const payload = {
|
|
||||||
iat: Math.round(new Date().getTime() / 1000),
|
|
||||||
exp: Math.round(new Date().getTime() / 1000 + 600),
|
|
||||||
iss: gitSource.githubApp.appId
|
|
||||||
};
|
|
||||||
githubToken = jsonwebtoken.sign(payload, gitSource.githubApp.privateKey, {
|
|
||||||
algorithm: 'RS256'
|
|
||||||
});
|
|
||||||
ghToken = await getGithubToken({ apiUrl: gitSource.apiUrl, application, githubToken });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (application.destinationDockerId) {
|
if (application.destinationDockerId) {
|
||||||
isRunning = await checkContainer(application.destinationDocker.engine, id);
|
isRunning = await checkContainer(application.destinationDocker.engine, id);
|
||||||
}
|
}
|
||||||
const payload = {
|
return {
|
||||||
|
status: 200,
|
||||||
body: {
|
body: {
|
||||||
isRunning,
|
isRunning,
|
||||||
application,
|
application,
|
||||||
appId
|
appId,
|
||||||
|
githubToken,
|
||||||
|
gitlabToken
|
||||||
},
|
},
|
||||||
headers: {}
|
headers: {}
|
||||||
};
|
};
|
||||||
if (ghToken) {
|
|
||||||
payload.headers = {
|
|
||||||
'set-cookie': [`ghToken=${ghToken}; HttpOnly; Path=/; Max-Age=15778800;`]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return payload;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
return ErrorHandler(error);
|
return ErrorHandler(error);
|
||||||
|
|||||||
@@ -197,7 +197,7 @@
|
|||||||
value={application.gitSource.name}
|
value={application.gitSource.name}
|
||||||
id="gitSource"
|
id="gitSource"
|
||||||
disabled
|
disabled
|
||||||
class="cursor-pointer bg-coolgray-200 hover:bg-coolgray-500"
|
class="cursor-pointer hover:bg-coolgray-500"
|
||||||
/></a
|
/></a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
@@ -214,7 +214,7 @@
|
|||||||
value="{application.repository}/{application.branch}"
|
value="{application.repository}/{application.branch}"
|
||||||
id="repository"
|
id="repository"
|
||||||
disabled
|
disabled
|
||||||
class="cursor-pointer bg-coolgray-200 hover:bg-coolgray-500"
|
class="cursor-pointer hover:bg-coolgray-500"
|
||||||
/></a
|
/></a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
@@ -232,7 +232,7 @@
|
|||||||
value={application.buildPack}
|
value={application.buildPack}
|
||||||
id="buildPack"
|
id="buildPack"
|
||||||
disabled
|
disabled
|
||||||
class="cursor-pointer bg-coolgray-200 hover:bg-coolgray-500"
|
class="cursor-pointer hover:bg-coolgray-500"
|
||||||
/></a
|
/></a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ export const get: RequestHandler = async (event) => {
|
|||||||
|
|
||||||
const { id } = event.params;
|
const { id } = event.params;
|
||||||
try {
|
try {
|
||||||
|
const secrets = await db.listSecrets(id);
|
||||||
|
const applicationSecrets = secrets.filter((secret) => !secret.isPRMRSecret);
|
||||||
|
const PRMRSecrets = secrets.filter((secret) => secret.isPRMRSecret);
|
||||||
const destinationDocker = await db.getDestinationByApplicationId({ id, teamId });
|
const destinationDocker = await db.getDestinationByApplicationId({ id, teamId });
|
||||||
const docker = dockerInstance({ destinationDocker });
|
const docker = dockerInstance({ destinationDocker });
|
||||||
const listContainers = await docker.engine.listContainers({
|
const listContainers = await docker.engine.listContainers({
|
||||||
@@ -35,7 +38,13 @@ export const get: RequestHandler = async (event) => {
|
|||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
body: {
|
body: {
|
||||||
containers: jsonContainers
|
containers: jsonContainers,
|
||||||
|
applicationSecrets: applicationSecrets.sort((a, b) => {
|
||||||
|
return ('' + a.name).localeCompare(b.name);
|
||||||
|
}),
|
||||||
|
PRMRSecrets: PRMRSecrets.sort((a, b) => {
|
||||||
|
return ('' + a.name).localeCompare(b.name);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -22,8 +22,19 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let containers;
|
export let containers;
|
||||||
export let application;
|
export let application;
|
||||||
|
export let PRMRSecrets;
|
||||||
|
export let applicationSecrets;
|
||||||
import { getDomain } from '$lib/components/common';
|
import { getDomain } from '$lib/components/common';
|
||||||
|
import Secret from '../secrets/_Secret.svelte';
|
||||||
|
import { get } from '$lib/api';
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
import Explainer from '$lib/components/Explainer.svelte';
|
||||||
|
|
||||||
|
const { id } = $page.params;
|
||||||
|
async function refreshSecrets() {
|
||||||
|
const data = await get(`/applications/${id}/secrets.json`);
|
||||||
|
PRMRSecrets = [...data.secrets];
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex space-x-1 p-6 font-bold">
|
<div class="flex space-x-1 p-6 font-bold">
|
||||||
@@ -32,7 +43,57 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mx-auto max-w-4xl px-6">
|
<div class="mx-auto max-w-6xl rounded-xl px-6 pt-4">
|
||||||
|
<table class="mx-auto">
|
||||||
|
<thead class=" rounded-xl border-b border-coolgray-500">
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
scope="col"
|
||||||
|
class="px-6 py-3 text-left text-xs font-bold uppercase tracking-wider text-white">Name</th
|
||||||
|
>
|
||||||
|
<th
|
||||||
|
scope="col"
|
||||||
|
class="px-6 py-3 text-left text-xs font-bold uppercase tracking-wider text-white"
|
||||||
|
>Value</th
|
||||||
|
>
|
||||||
|
<th
|
||||||
|
scope="col"
|
||||||
|
class="px-6 py-3 text-left text-xs font-bold uppercase tracking-wider text-white"
|
||||||
|
>Need during buildtime?</th
|
||||||
|
>
|
||||||
|
<th
|
||||||
|
scope="col"
|
||||||
|
class="px-6 py-3 text-left text-xs font-bold uppercase tracking-wider text-white"
|
||||||
|
/>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="">
|
||||||
|
{#each applicationSecrets as secret}
|
||||||
|
{#key secret.id}
|
||||||
|
<tr class="h-20 transition duration-100 hover:bg-coolgray-400">
|
||||||
|
<Secret
|
||||||
|
PRMRSecret={PRMRSecrets.find((s) => s.name === secret.name)}
|
||||||
|
isPRMRSecret
|
||||||
|
name={secret.name}
|
||||||
|
value={secret.value}
|
||||||
|
isBuildSecret={secret.isBuildSecret}
|
||||||
|
on:refresh={refreshSecrets}
|
||||||
|
/>
|
||||||
|
</tr>
|
||||||
|
{/key}
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-center py-4 text-center">
|
||||||
|
<Explainer
|
||||||
|
customClass="w-full"
|
||||||
|
text={applicationSecrets.length === 0
|
||||||
|
? "<span class='font-bold text-white text-xl'>Please add secrets to the application first.</span> <br><br>These values overwrite application secrets in PR/MR deployments. Useful for creating <span class='text-green-500 font-bold'>staging</span> environments."
|
||||||
|
: "These values overwrite application secrets in PR/MR deployments. Useful for creating <span class='text-green-500 font-bold'>staging</span> environments."}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="mx-auto max-w-4xl py-10">
|
||||||
<div class="flex flex-wrap justify-center space-x-2">
|
<div class="flex flex-wrap justify-center space-x-2">
|
||||||
{#if containers.length > 0}
|
{#if containers.length > 0}
|
||||||
{#each containers as container}
|
{#each containers as container}
|
||||||
|
|||||||
@@ -3,14 +3,19 @@
|
|||||||
export let value = '';
|
export let value = '';
|
||||||
export let isBuildSecret = false;
|
export let isBuildSecret = false;
|
||||||
export let isNewSecret = false;
|
export let isNewSecret = false;
|
||||||
|
export let isPRMRSecret = false;
|
||||||
|
export let PRMRSecret = {};
|
||||||
|
|
||||||
|
if (isPRMRSecret) value = PRMRSecret.value;
|
||||||
|
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { del, post } from '$lib/api';
|
import { del, post } from '$lib/api';
|
||||||
|
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
||||||
import { errorNotification } from '$lib/form';
|
import { errorNotification } from '$lib/form';
|
||||||
|
import { toast } from '@zerodevx/svelte-toast';
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
let nameEl;
|
|
||||||
let valueEl;
|
|
||||||
const { id } = $page.params;
|
const { id } = $page.params;
|
||||||
async function removeSecret() {
|
async function removeSecret() {
|
||||||
try {
|
try {
|
||||||
@@ -25,24 +30,24 @@
|
|||||||
return errorNotification(error);
|
return errorNotification(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function saveSecret() {
|
async function saveSecret(isNew = false) {
|
||||||
const nameValid = nameEl.checkValidity();
|
if (!name) return errorNotification('Name is required.');
|
||||||
const valueValid = valueEl.checkValidity();
|
if (!value) return errorNotification('Value is required.');
|
||||||
if (!nameValid) {
|
|
||||||
return nameEl.reportValidity();
|
|
||||||
}
|
|
||||||
if (!valueValid) {
|
|
||||||
return valueEl.reportValidity();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await post(`/applications/${id}/secrets.json`, { name, value, isBuildSecret });
|
await post(`/applications/${id}/secrets.json`, {
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
isBuildSecret,
|
||||||
|
isPRMRSecret,
|
||||||
|
isNew
|
||||||
|
});
|
||||||
dispatch('refresh');
|
dispatch('refresh');
|
||||||
if (isNewSecret) {
|
if (isNewSecret) {
|
||||||
name = '';
|
name = '';
|
||||||
value = '';
|
value = '';
|
||||||
isBuildSecret = false;
|
isBuildSecret = false;
|
||||||
}
|
}
|
||||||
|
toast.push('Secret saved.');
|
||||||
} catch ({ error }) {
|
} catch ({ error }) {
|
||||||
return errorNotification(error);
|
return errorNotification(error);
|
||||||
}
|
}
|
||||||
@@ -56,8 +61,7 @@
|
|||||||
|
|
||||||
<td class="whitespace-nowrap px-6 py-2 text-sm font-medium text-white">
|
<td class="whitespace-nowrap px-6 py-2 text-sm font-medium text-white">
|
||||||
<input
|
<input
|
||||||
id="secretName"
|
id={isNewSecret ? 'secretName' : 'secretNameNew'}
|
||||||
bind:this={nameEl}
|
|
||||||
bind:value={name}
|
bind:value={name}
|
||||||
required
|
required
|
||||||
placeholder="EXAMPLE_VARIABLE"
|
placeholder="EXAMPLE_VARIABLE"
|
||||||
@@ -68,16 +72,13 @@
|
|||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td class="whitespace-nowrap px-6 py-2 text-sm font-medium text-white">
|
<td class="whitespace-nowrap px-6 py-2 text-sm font-medium text-white">
|
||||||
<input
|
<CopyPasswordField
|
||||||
id="secretValue"
|
id={isNewSecret ? 'secretValue' : 'secretValueNew'}
|
||||||
|
name={isNewSecret ? 'secretValue' : 'secretValueNew'}
|
||||||
|
isPasswordField={true}
|
||||||
bind:value
|
bind:value
|
||||||
bind:this={valueEl}
|
|
||||||
required
|
required
|
||||||
placeholder="J$#@UIO%HO#$U%H"
|
placeholder="J$#@UIO%HO#$U%H"
|
||||||
class="-mx-2 w-64 border-2 border-transparent"
|
|
||||||
class:bg-transparent={!isNewSecret}
|
|
||||||
class:cursor-not-allowed={!isNewSecret}
|
|
||||||
readonly={!isNewSecret}
|
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td class="whitespace-nowrap px-6 py-2 text-center text-sm font-medium text-white">
|
<td class="whitespace-nowrap px-6 py-2 text-center text-sm font-medium text-white">
|
||||||
@@ -132,11 +133,20 @@
|
|||||||
<td class="whitespace-nowrap px-6 py-2 text-sm font-medium text-white">
|
<td class="whitespace-nowrap px-6 py-2 text-sm font-medium text-white">
|
||||||
{#if isNewSecret}
|
{#if isNewSecret}
|
||||||
<div class="flex items-center justify-center">
|
<div class="flex items-center justify-center">
|
||||||
<button class="w-24 bg-green-600 hover:bg-green-500" on:click={saveSecret}>Add</button>
|
<button class="bg-green-600 hover:bg-green-500" on:click={() => saveSecret(true)}>Add</button>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex justify-center items-end">
|
<div class="flex-col space-y-2">
|
||||||
<button class="w-24 bg-red-600 hover:bg-red-500" on:click={removeSecret}>Remove</button>
|
<div class="flex items-center justify-center">
|
||||||
|
<button class="w-24 bg-green-600 hover:bg-green-500" on:click={() => saveSecret(false)}
|
||||||
|
>Set</button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
{#if !isPRMRSecret}
|
||||||
|
<div class="flex justify-center items-end">
|
||||||
|
<button class="w-24 bg-red-600 hover:bg-red-500" on:click={removeSecret}>Remove</button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -7,8 +7,9 @@ export const get: RequestHandler = async (event) => {
|
|||||||
const { teamId, status, body } = await getUserDetails(event);
|
const { teamId, status, body } = await getUserDetails(event);
|
||||||
if (status === 401) return { status, body };
|
if (status === 401) return { status, body };
|
||||||
|
|
||||||
|
const { id } = event.params;
|
||||||
try {
|
try {
|
||||||
const secrets = await db.listSecrets({ applicationId: event.params.id });
|
const secrets = await (await db.listSecrets(id)).filter((secret) => !secret.isPRMRSecret);
|
||||||
return {
|
return {
|
||||||
status: 200,
|
status: 200,
|
||||||
body: {
|
body: {
|
||||||
@@ -27,16 +28,22 @@ export const post: RequestHandler = async (event) => {
|
|||||||
if (status === 401) return { status, body };
|
if (status === 401) return { status, body };
|
||||||
|
|
||||||
const { id } = event.params;
|
const { id } = event.params;
|
||||||
const { name, value, isBuildSecret } = await event.request.json();
|
const { name, value, isBuildSecret, isPRMRSecret, isNew } = await event.request.json();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const found = await db.isSecretExists({ id, name });
|
if (isNew) {
|
||||||
if (found) {
|
const found = await db.isSecretExists({ id, name, isPRMRSecret });
|
||||||
throw {
|
if (found) {
|
||||||
error: `Secret ${name} already exists.`
|
throw {
|
||||||
};
|
error: `Secret ${name} already exists.`
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
await db.createSecret({ id, name, value, isBuildSecret, isPRMRSecret });
|
||||||
|
return {
|
||||||
|
status: 201
|
||||||
|
};
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
await db.createSecret({ id, name, value, isBuildSecret });
|
await db.updateSecret({ id, name, value, isBuildSecret, isPRMRSecret });
|
||||||
return {
|
return {
|
||||||
status: 201
|
status: 201
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -67,10 +67,10 @@
|
|||||||
<tbody class="">
|
<tbody class="">
|
||||||
{#each secrets as secret}
|
{#each secrets as secret}
|
||||||
{#key secret.id}
|
{#key secret.id}
|
||||||
<tr class="hover:bg-coolgray-200">
|
<tr class="h-20 transition duration-100 hover:bg-coolgray-400">
|
||||||
<Secret
|
<Secret
|
||||||
name={secret.name}
|
name={secret.name}
|
||||||
value={secret.value ? secret.value : 'ENCRYPTED'}
|
value={secret.value}
|
||||||
isBuildSecret={secret.isBuildSecret}
|
isBuildSecret={secret.isBuildSecret}
|
||||||
on:refresh={refreshSecrets}
|
on:refresh={refreshSecrets}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<div class="flex space-x-1 py-5 font-bold">
|
<div class="flex space-x-1 py-5 font-bold">
|
||||||
<div class="title">CouchDB</div>
|
<div class="title">CouchDB</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="px-10">
|
<div class="space-y-2 px-10">
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<label for="defaultDatabase">Default Database</label>
|
<label for="defaultDatabase">Default Database</label>
|
||||||
<CopyPasswordField
|
<CopyPasswordField
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
function generateUrl() {
|
function generateUrl() {
|
||||||
return browser
|
return browser
|
||||||
? `${database.type}://${database.type === 'redis' && ':'}${
|
? `${database.type}://${
|
||||||
databaseDbUser ? databaseDbUser + ':' : ''
|
databaseDbUser ? databaseDbUser + ':' : ''
|
||||||
}${databaseDbUserPassword}@${
|
}${databaseDbUserPassword}@${
|
||||||
isPublic
|
isPublic
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<div class="flex space-x-1 py-5 font-bold">
|
<div class="flex space-x-1 py-5 font-bold">
|
||||||
<div class="title">MongoDB</div>
|
<div class="title">MongoDB</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="px-10">
|
<div class="space-y-2 px-10">
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<label for="rootUser">Root User</label>
|
<label for="rootUser">Root User</label>
|
||||||
<CopyPasswordField
|
<CopyPasswordField
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<div class="flex space-x-1 py-5 font-bold">
|
<div class="flex space-x-1 py-5 font-bold">
|
||||||
<div class="title">MySQL</div>
|
<div class="title">MySQL</div>
|
||||||
</div>
|
</div>
|
||||||
<div class=" px-10">
|
<div class="space-y-2 px-10">
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<label for="defaultDatabase">Default Database</label>
|
<label for="defaultDatabase">Default Database</label>
|
||||||
<CopyPasswordField
|
<CopyPasswordField
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<div class="flex space-x-1 py-5 font-bold">
|
<div class="flex space-x-1 py-5 font-bold">
|
||||||
<div class="title">PostgreSQL</div>
|
<div class="title">PostgreSQL</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="px-10">
|
<div class="space-y-2 px-10">
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<label for="defaultDatabase">Default Database</label>
|
<label for="defaultDatabase">Default Database</label>
|
||||||
<CopyPasswordField
|
<CopyPasswordField
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<div class="flex space-x-1 py-5 font-bold">
|
<div class="flex space-x-1 py-5 font-bold">
|
||||||
<div class="title">Redis</div>
|
<div class="title">Redis</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="px-10">
|
<div class="space-y-2 px-10">
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<label for="dbUserPassword">Password</label>
|
<label for="dbUserPassword">Password</label>
|
||||||
<CopyPasswordField
|
<CopyPasswordField
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<div class="flex space-x-1 py-5 font-bold">
|
<div class="flex space-x-1 py-5 font-bold">
|
||||||
<div class="title">MinIO Server</div>
|
<div class="title">MinIO Server</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
<label for="rootUser">Root User</label>
|
<label for="rootUser">Root User</label>
|
||||||
<input
|
<input
|
||||||
name="rootUser"
|
name="rootUser"
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
readonly
|
readonly
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
<label for="rootUserPassword">Root's Password</label>
|
<label for="rootUserPassword">Root's Password</label>
|
||||||
<CopyPasswordField
|
<CopyPasswordField
|
||||||
id="rootUserPassword"
|
id="rootUserPassword"
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
value={service.minio.rootUserPassword}
|
value={service.minio.rootUserPassword}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
<label for="publicPort">API Port</label>
|
<label for="publicPort">API Port</label>
|
||||||
<input
|
<input
|
||||||
name="publicPort"
|
name="publicPort"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { dev } from '$app/env';
|
||||||
import { getDomain, getUserDetails } from '$lib/common';
|
import { getDomain, getUserDetails } from '$lib/common';
|
||||||
import * as db from '$lib/database';
|
import * as db from '$lib/database';
|
||||||
import { listSettings, ErrorHandler } from '$lib/database';
|
import { listSettings, ErrorHandler } from '$lib/database';
|
||||||
@@ -43,7 +44,13 @@ export const del: RequestHandler = async (event) => {
|
|||||||
if (status === 401) return { status, body };
|
if (status === 401) return { status, body };
|
||||||
|
|
||||||
const { fqdn } = await event.request.json();
|
const { fqdn } = await event.request.json();
|
||||||
const ip = await dns.resolve(event.url.hostname);
|
let ip;
|
||||||
|
console.log(fqdn);
|
||||||
|
try {
|
||||||
|
ip = await dns.resolve(fqdn);
|
||||||
|
} catch (error) {
|
||||||
|
// Do not care.
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const domain = getDomain(fqdn);
|
const domain = getDomain(fqdn);
|
||||||
await db.prisma.setting.update({ where: { fqdn }, data: { fqdn: null } });
|
await db.prisma.setting.update({ where: { fqdn }, data: { fqdn: null } });
|
||||||
@@ -53,7 +60,7 @@ export const del: RequestHandler = async (event) => {
|
|||||||
status: 200,
|
status: 200,
|
||||||
body: {
|
body: {
|
||||||
message: 'Domain removed',
|
message: 'Domain removed',
|
||||||
redirect: `http://${ip[0]}:3000/settings`
|
redirect: ip ? `http://${ip[0]}:3000/settings` : undefined
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -159,7 +159,7 @@
|
|||||||
: browser && 'http://' + window.location.hostname + ':8404'
|
: browser && 'http://' + window.location.hostname + ':8404'
|
||||||
} target="_blank">stats</a> page.`}
|
} target="_blank">stats</a> page.`}
|
||||||
/>
|
/>
|
||||||
<div class="px-10 py-5">
|
<div class="space-y-2 px-10 py-5">
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<label for="proxyUser">User</label>
|
<label for="proxyUser">User</label>
|
||||||
<CopyPasswordField
|
<CopyPasswordField
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import { page, session } from '$app/stores';
|
import { page, session } from '$app/stores';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { post } from '$lib/api';
|
import { post } from '$lib/api';
|
||||||
|
import { browser } from '$app/env';
|
||||||
const { id } = $page.params;
|
const { id } = $page.params;
|
||||||
|
|
||||||
let loading = false;
|
let loading = false;
|
||||||
@@ -121,12 +122,16 @@
|
|||||||
|
|
||||||
<Explainer
|
<Explainer
|
||||||
customClass="w-full"
|
customClass="w-full"
|
||||||
text="<span class='font-bold text-base'>Scopes required:</span>
|
text="<span class='font-bold text-base text-white'>Scopes required:</span>
|
||||||
<br>- api (Access the authenticated user's API)
|
<br>- <span class='text-orange-500 font-bold'>api</span> (Access the authenticated user's API)
|
||||||
<br>- read_repository (Allows read-only access to the repository)
|
<br>- <span class='text-orange-500 font-bold'>read_repository</span> (Allows read-only access to the repository)
|
||||||
<br>- email (Allows read-only access to the user's primary email address using OpenID Connect)
|
<br>- <span class='text-orange-500 font-bold'>email</span> (Allows read-only access to the user's primary email address using OpenID Connect)
|
||||||
<br>
|
<br>
|
||||||
<br>For extra security, you can add Expire access tokens!"
|
<br>For extra security, you can set Expire access tokens!
|
||||||
|
<br><br>Webhook URL: <span class='text-orange-500 font-bold'>{browser
|
||||||
|
? window.location.origin
|
||||||
|
: ''}/webhooks/gitlab</span>
|
||||||
|
<br>But if you will set a custom domain name for Coolify, use that instead."
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
<form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4 pt-10">
|
<form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4 pt-10">
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ export const get: RequestHandler = async (event) => {
|
|||||||
const code = event.url.searchParams.get('code');
|
const code = event.url.searchParams.get('code');
|
||||||
const state = event.url.searchParams.get('state');
|
const state = event.url.searchParams.get('state');
|
||||||
try {
|
try {
|
||||||
|
const { fqdn } = await db.listSettings();
|
||||||
const application = await db.getApplication({ id: state, teamId });
|
const application = await db.getApplication({ id: state, teamId });
|
||||||
const { fqdn } = application;
|
|
||||||
const { appId, appSecret } = application.gitSource.gitlabApp;
|
const { appId, appSecret } = application.gitSource.gitlabApp;
|
||||||
const { htmlUrl } = application.gitSource;
|
const { htmlUrl } = application.gitSource;
|
||||||
|
|
||||||
|
|||||||
@@ -35,10 +35,10 @@ main,
|
|||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
@apply h-12 w-96 select-all rounded border border-transparent bg-transparent bg-coolgray-200 p-2 text-xs tracking-tight text-white placeholder-stone-600 outline-none transition duration-150 hover:bg-coolgray-500 focus:bg-coolgray-500 disabled:bg-transparent md:text-sm;
|
@apply h-12 w-96 rounded border border-transparent bg-transparent bg-coolgray-200 p-2 text-xs tracking-tight text-white placeholder-stone-600 outline-none transition duration-150 hover:bg-coolgray-500 focus:bg-coolgray-500 disabled:border disabled:border-dashed disabled:border-coolgray-300 disabled:bg-transparent md:text-sm;
|
||||||
}
|
}
|
||||||
textarea {
|
textarea {
|
||||||
@apply w-96 select-all rounded border border-transparent bg-transparent bg-coolgray-200 p-2 text-xs tracking-tight text-white placeholder-stone-600 outline-none transition duration-150 hover:bg-coolgray-500 focus:bg-coolgray-500 disabled:bg-transparent md:text-sm;
|
@apply w-96 rounded border border-transparent bg-transparent bg-coolgray-200 p-2 text-xs tracking-tight text-white placeholder-stone-600 outline-none transition duration-150 hover:bg-coolgray-500 focus:bg-coolgray-500 disabled:border disabled:border-dashed disabled:border-coolgray-300 disabled:bg-transparent md:text-sm;
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
|||||||
Reference in New Issue
Block a user