mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-25 04:59:32 +00:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1adffe260 | ||
|
|
e725887a55 | ||
|
|
5bf79b75b0 | ||
|
|
6926975e40 | ||
|
|
978a01c968 | ||
|
|
f421f5ee84 | ||
|
|
383831c7b8 | ||
|
|
41329facf7 | ||
|
|
7d3c644148 | ||
|
|
7fab9b5930 | ||
|
|
58763ef84c | ||
|
|
0e6abf172b | ||
|
|
9e681ece41 | ||
|
|
28f87a306d | ||
|
|
23e8833208 | ||
|
|
03962663c2 | ||
|
|
cc2ec55c4d | ||
|
|
ff2c38aa16 | ||
|
|
b5a9a2cea8 | ||
|
|
cd3f661f7e | ||
|
|
41bf6b5b86 | ||
|
|
a4e7c85184 | ||
|
|
19aca9ab35 | ||
|
|
08704c289a | ||
|
|
2224c22c6e | ||
|
|
b281889acd | ||
|
|
cfc50a27b0 |
@@ -1,6 +1,6 @@
|
||||
FROM haproxytech/haproxy-alpine:2.5
|
||||
RUN mkdir -p /usr/local/etc/haproxy/ssl /usr/local/etc/haproxy/maps /usr/local/etc/haproxy/spoe
|
||||
|
||||
COPY haproxy/haproxy.cfg-http.template /usr/local/etc/haproxy/haproxy.cfg
|
||||
COPY haproxy/dataplaneapi.hcl /usr/local/etc/haproxy/dataplaneapi.hcl
|
||||
COPY haproxy/ssl/default.pem /usr/local/etc/haproxy/ssl/default.pem
|
||||
COPY data/haproxy/haproxy.cfg-http.template /usr/local/etc/haproxy/haproxy.cfg
|
||||
COPY data/haproxy/dataplaneapi.hcl /usr/local/etc/haproxy/dataplaneapi.hcl
|
||||
COPY data/haproxy/ssl/default.pem /usr/local/etc/haproxy/ssl/default.pem
|
||||
@@ -1,6 +1,6 @@
|
||||
FROM haproxytech/haproxy-alpine:2.5
|
||||
RUN mkdir -p /usr/local/etc/haproxy/ssl /usr/local/etc/haproxy/maps /usr/local/etc/haproxy/spoe
|
||||
|
||||
COPY haproxy/haproxy.cfg-tcp.template /usr/local/etc/haproxy/haproxy.cfg
|
||||
COPY haproxy/dataplaneapi.hcl /usr/local/etc/haproxy/dataplaneapi.hcl
|
||||
COPY haproxy/ssl/default.pem /usr/local/etc/haproxy/ssl/default.pem
|
||||
COPY data/haproxy/haproxy.cfg-tcp.template /usr/local/etc/haproxy/haproxy.cfg
|
||||
COPY data/haproxy/dataplaneapi.hcl /usr/local/etc/haproxy/dataplaneapi.hcl
|
||||
COPY data/haproxy/ssl/default.pem /usr/local/etc/haproxy/ssl/default.pem
|
||||
@@ -1,6 +1,6 @@
|
||||
FROM haproxytech/haproxy-alpine:2.5
|
||||
RUN mkdir -p /usr/local/etc/haproxy/ssl /usr/local/etc/haproxy/maps /usr/local/etc/haproxy/spoe
|
||||
|
||||
COPY haproxy/haproxy.cfg.template /usr/local/etc/haproxy/haproxy.cfg
|
||||
COPY haproxy/dataplaneapi.hcl /usr/local/etc/haproxy/dataplaneapi.hcl
|
||||
COPY haproxy/ssl/default.pem /usr/local/etc/haproxy/ssl/default.pem
|
||||
COPY data/haproxy/haproxy.cfg.template /usr/local/etc/haproxy/haproxy.cfg
|
||||
COPY data/haproxy/dataplaneapi.hcl /usr/local/etc/haproxy/dataplaneapi.hcl
|
||||
COPY data/haproxy/ssl/default.pem /usr/local/etc/haproxy/ssl/default.pem
|
||||
11
package.json
11
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "coolify",
|
||||
"description": "An open-source & self-hostable Heroku / Netlify alternative.",
|
||||
"version": "2.4.3",
|
||||
"version": "2.4.7",
|
||||
"license": "AGPL-3.0",
|
||||
"scripts": {
|
||||
"dev": "docker-compose -f docker-compose-dev.yaml up -d && cross-env NODE_ENV=development & svelte-kit dev",
|
||||
@@ -18,11 +18,12 @@
|
||||
"db:seed": "prisma db seed",
|
||||
"db:migrate": "COOLIFY_DATABASE_URL=file:../db/migration.db prisma migrate dev --skip-seed --name",
|
||||
"release:production:all": "cross-var docker build --platform linux/amd64,linux/arm64 -t coollabsio/coolify:$npm_package_version -t coollabsio/coolify:latest --push .",
|
||||
"release:production:amd": "cross-var docker build --platform linux/amd64 -t coollabsio/coolify:$npm_package_version -t coollabsio/coolify:latest --push .",
|
||||
"release:staging:all": "cross-var docker build --platform linux/amd64,linux/arm64 -t coollabsio/coolify:$npm_package_version --push .",
|
||||
"release:staging:amd": "cross-var docker build --platform linux/amd64 -t coollabsio/coolify:$npm_package_version --push .",
|
||||
"release:haproxy": "docker build --platform linux/amd64,linux/arm64 -t coollabsio/coolify-haproxy-alpine:latest -t coollabsio/coolify-haproxy-alpine:1.1.0 -f haproxy.Dockerfile --push .",
|
||||
"release:haproxy:tcp": "docker build --platform linux/amd64,linux/arm64 -t coollabsio/coolify-haproxy-tcp-alpine:latest -t coollabsio/coolify-haproxy-tcp-alpine:1.1.0 -f haproxy-tcp.Dockerfile --push .",
|
||||
"release:haproxy:http": "docker build --platform linux/amd64,linux/arm64 -t coollabsio/coolify-haproxy-http-alpine:latest -t coollabsio/coolify-haproxy-http-alpine:1.1.0 -f haproxy-http.Dockerfile --push .",
|
||||
"release:haproxy": "docker build --platform linux/amd64,linux/arm64 -t coollabsio/coolify-haproxy-alpine:latest -t coollabsio/coolify-haproxy-alpine:1.1.0 -f data/haproxy.Dockerfile --push .",
|
||||
"release:haproxy:tcp": "docker build --platform linux/amd64,linux/arm64 -t coollabsio/coolify-haproxy-tcp-alpine:latest -t coollabsio/coolify-haproxy-tcp-alpine:1.1.0 -f data/haproxy-tcp.Dockerfile --push .",
|
||||
"release:haproxy:http": "docker build --platform linux/amd64,linux/arm64 -t coollabsio/coolify-haproxy-http-alpine:latest -t coollabsio/coolify-haproxy-http-alpine:1.1.0 -f data/haproxy-http.Dockerfile --push .",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -63,7 +64,7 @@
|
||||
"@prisma/client": "3.11.1",
|
||||
"@sentry/node": "6.19.6",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"bullmq": "1.79.0",
|
||||
"bullmq": "1.80.0",
|
||||
"compare-versions": "4.1.3",
|
||||
"cookie": "0.4.2",
|
||||
"cuid": "2.1.8",
|
||||
|
||||
16
pnpm-lock.yaml
generated
16
pnpm-lock.yaml
generated
@@ -5,7 +5,7 @@ specifiers:
|
||||
'@prisma/client': 3.11.1
|
||||
'@sentry/node': 6.19.6
|
||||
'@sveltejs/adapter-node': 1.0.0-next.73
|
||||
'@sveltejs/kit': 1.0.0-next.310
|
||||
'@sveltejs/kit': 1.0.0-next.303
|
||||
'@types/js-cookie': 3.0.1
|
||||
'@types/js-yaml': 4.0.5
|
||||
'@types/node': 17.0.23
|
||||
@@ -15,7 +15,7 @@ specifiers:
|
||||
'@zerodevx/svelte-toast': 0.7.1
|
||||
autoprefixer: 10.4.4
|
||||
bcryptjs: ^2.4.3
|
||||
bullmq: 1.79.0
|
||||
bullmq: 1.78.1
|
||||
compare-versions: 4.1.3
|
||||
cookie: 0.4.2
|
||||
cross-env: 7.0.3
|
||||
@@ -60,7 +60,7 @@ dependencies:
|
||||
'@prisma/client': 3.11.1_prisma@3.11.1
|
||||
'@sentry/node': 6.19.6
|
||||
bcryptjs: 2.4.3
|
||||
bullmq: 1.79.0
|
||||
bullmq: 1.78.1
|
||||
compare-versions: 4.1.3
|
||||
cookie: 0.4.2
|
||||
cuid: 2.1.8
|
||||
@@ -82,7 +82,7 @@ dependencies:
|
||||
|
||||
devDependencies:
|
||||
'@sveltejs/adapter-node': 1.0.0-next.73
|
||||
'@sveltejs/kit': 1.0.0-next.310_svelte@3.47.0
|
||||
'@sveltejs/kit': 1.0.0-next.303_svelte@3.47.0
|
||||
'@types/js-cookie': 3.0.1
|
||||
'@types/js-yaml': 4.0.5
|
||||
'@types/node': 17.0.23
|
||||
@@ -374,10 +374,10 @@ packages:
|
||||
tiny-glob: 0.2.9
|
||||
dev: true
|
||||
|
||||
/@sveltejs/kit/1.0.0-next.310_svelte@3.47.0:
|
||||
/@sveltejs/kit/1.0.0-next.303_svelte@3.47.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-pTyMyaoyHS+V5cQZIQMfQXmLkhw1VaRwT9avOSgwDc0QBpnNw2LdzwoPYsUr96ca5B6cfT3SMUNolxErTNHmPQ==
|
||||
integrity: sha512-WdxDc8OiF1WEd/bEza7CBdzA+3qIcCi1GKBj/gieKX9I3N8iDJt/Cg2POrLo9wQoJ47nZcAd1eOhfr7XEX1aIQ==
|
||||
}
|
||||
engines: { node: '>=14.13' }
|
||||
hasBin: true
|
||||
@@ -1669,10 +1669,10 @@ packages:
|
||||
ieee754: 1.2.1
|
||||
dev: false
|
||||
|
||||
/bullmq/1.79.0:
|
||||
/bullmq/1.78.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-rVtNCDpcWdc+U1MinRtvhJv+GBFNkz0Q3Unf20010qIC6Pj+O2kkIUeepBkCmMNz6G9abrhsee2PheGRJ32+sw==
|
||||
integrity: sha512-er45mM8nGhgA83EVCJ4PNxPyDSzakvoxeFGU4vdSgYeB+SbeFQAlJYmAC50Ms7YFPstm1LeinbVZ+oX/BmBzOg==
|
||||
}
|
||||
dependencies:
|
||||
cron-parser: 4.2.1
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
// TODO: Make this functions generic
|
||||
|
||||
async function send({
|
||||
method,
|
||||
path,
|
||||
data = {},
|
||||
headers,
|
||||
timeout = 30000
|
||||
timeout = 120000
|
||||
}): Promise<Record<string, unknown>> {
|
||||
const controller = new AbortController();
|
||||
const id = setTimeout(() => controller.abort(), timeout);
|
||||
@@ -53,7 +51,7 @@ async function send({
|
||||
|
||||
export function get(
|
||||
path: string,
|
||||
headers: Record<string, unknown>
|
||||
headers?: Record<string, unknown>
|
||||
): Promise<Record<string, unknown>> {
|
||||
return send({ method: 'GET', path, headers });
|
||||
}
|
||||
@@ -61,7 +59,7 @@ export function get(
|
||||
export function del(
|
||||
path: string,
|
||||
data: Record<string, unknown>,
|
||||
headers: Record<string, unknown>
|
||||
headers?: Record<string, unknown>
|
||||
): Promise<Record<string, unknown>> {
|
||||
return send({ method: 'DELETE', path, data, headers });
|
||||
}
|
||||
@@ -69,7 +67,7 @@ export function del(
|
||||
export function post(
|
||||
path: string,
|
||||
data: Record<string, unknown>,
|
||||
headers: Record<string, unknown>
|
||||
headers?: Record<string, unknown>
|
||||
): Promise<Record<string, unknown>> {
|
||||
return send({ method: 'POST', path, data, headers });
|
||||
}
|
||||
@@ -77,7 +75,7 @@ export function post(
|
||||
export function put(
|
||||
path: string,
|
||||
data: Record<string, unknown>,
|
||||
headers: Record<string, unknown>
|
||||
headers?: Record<string, unknown>
|
||||
): Promise<Record<string, unknown>> {
|
||||
return send({ method: 'PUT', path, data, headers });
|
||||
}
|
||||
|
||||
@@ -93,11 +93,16 @@ export const getUserDetails = async (
|
||||
}> => {
|
||||
const teamId = getTeam(event);
|
||||
const userId = event?.locals?.session?.data?.userId || null;
|
||||
const { permission = 'read' } = await db.prisma.permission.findFirst({
|
||||
where: { teamId, userId },
|
||||
select: { permission: true },
|
||||
rejectOnNotFound: true
|
||||
});
|
||||
let permission = 'read';
|
||||
if (teamId && userId) {
|
||||
const data = await db.prisma.permission.findFirst({
|
||||
where: { teamId, userId },
|
||||
select: { permission: true },
|
||||
rejectOnNotFound: true
|
||||
});
|
||||
if (data.permission) permission = data.permission;
|
||||
}
|
||||
|
||||
const payload = {
|
||||
teamId,
|
||||
userId,
|
||||
|
||||
@@ -219,7 +219,6 @@ export function generateDatabaseConfiguration(database: Database & { settings: D
|
||||
return {
|
||||
privatePort: 5432,
|
||||
environmentVariables: {
|
||||
POSTGRESQL_POSTGRES_PASSWORD: rootUserPassword,
|
||||
POSTGRESQL_PASSWORD: dbUserPassword,
|
||||
POSTGRESQL_USERNAME: dbUser,
|
||||
POSTGRESQL_DATABASE: defaultDatabase
|
||||
|
||||
@@ -18,18 +18,10 @@ export async function listSources(
|
||||
|
||||
export async function newSource({
|
||||
name,
|
||||
teamId,
|
||||
type,
|
||||
htmlUrl,
|
||||
apiUrl,
|
||||
organization
|
||||
teamId
|
||||
}: {
|
||||
name: string;
|
||||
teamId: string;
|
||||
type: string;
|
||||
htmlUrl: string;
|
||||
apiUrl: string;
|
||||
organization: string;
|
||||
}): Promise<GitSource> {
|
||||
return await prisma.gitSource.create({
|
||||
data: {
|
||||
@@ -74,8 +66,11 @@ export async function getSource({
|
||||
if (body?.gitlabApp?.appSecret) body.gitlabApp.appSecret = decrypt(body.gitlabApp.appSecret);
|
||||
return body;
|
||||
}
|
||||
export async function addGitHubSource({ id, teamId, type, name, htmlUrl, apiUrl }) {
|
||||
await prisma.gitSource.update({ where: { id }, data: { type, name, htmlUrl, apiUrl } });
|
||||
export async function addGitHubSource({ id, teamId, type, name, htmlUrl, apiUrl, organization }) {
|
||||
await prisma.gitSource.update({
|
||||
where: { id },
|
||||
data: { type, name, htmlUrl, apiUrl, organization }
|
||||
});
|
||||
return await prisma.githubApp.create({
|
||||
data: {
|
||||
teams: { connect: { id: teamId } },
|
||||
@@ -123,10 +118,14 @@ export async function configureGitsource({
|
||||
}
|
||||
export async function updateGitsource({
|
||||
id,
|
||||
name
|
||||
name,
|
||||
htmlUrl,
|
||||
apiUrl
|
||||
}: {
|
||||
id: string;
|
||||
name: string;
|
||||
htmlUrl: string;
|
||||
apiUrl: string;
|
||||
}): Promise<GitSource> {
|
||||
return await prisma.gitSource.update({
|
||||
where: { id },
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { dev } from '$app/env';
|
||||
import got, { type Got } from 'got';
|
||||
import * as db from '$lib/database';
|
||||
import mustache from 'mustache';
|
||||
import crypto from 'crypto';
|
||||
import { checkContainer, checkHAProxy } from '.';
|
||||
import { asyncExecShell, getDomain, getEngine } from '$lib/common';
|
||||
import { supportedServiceTypesAndVersions } from '$lib/components/common';
|
||||
@@ -18,10 +20,10 @@ global
|
||||
defaults
|
||||
mode http
|
||||
log global
|
||||
timeout http-request 60s
|
||||
timeout http-request 120s
|
||||
timeout connect 10s
|
||||
timeout client 60s
|
||||
timeout server 60s
|
||||
timeout client 120s
|
||||
timeout server 120s
|
||||
|
||||
userlist haproxy-dataplaneapi
|
||||
user admin insecure-password "\${HAPROXY_PASSWORD}"
|
||||
@@ -213,7 +215,8 @@ export async function configureHAProxy(): Promise<void> {
|
||||
plausibleAnalytics: true,
|
||||
vscodeserver: true,
|
||||
wordpress: true,
|
||||
ghost: true
|
||||
ghost: true,
|
||||
meiliSearch: true
|
||||
}
|
||||
});
|
||||
|
||||
@@ -262,36 +265,20 @@ export async function configureHAProxy(): Promise<void> {
|
||||
redirectValue,
|
||||
redirectTo: isWWW ? domain.replace('www.', '') : 'www.' + domain
|
||||
});
|
||||
for (const service of services) {
|
||||
const { fqdn, id, type, destinationDocker, destinationDockerId, updatedAt } = service;
|
||||
if (destinationDockerId) {
|
||||
const { engine } = destinationDocker;
|
||||
const found = supportedServiceTypesAndVersions.find((a) => a.name === type);
|
||||
if (found) {
|
||||
const port = found.ports.main;
|
||||
const publicPort = service[type]?.publicPort;
|
||||
const isRunning = await checkContainer(engine, id);
|
||||
if (fqdn) {
|
||||
const domain = getDomain(fqdn);
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
const isWWW = fqdn.includes('www.');
|
||||
const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`;
|
||||
if (isRunning) {
|
||||
data.services.push({
|
||||
id,
|
||||
port,
|
||||
publicPort,
|
||||
domain,
|
||||
isRunning,
|
||||
isHttps,
|
||||
redirectValue,
|
||||
redirectTo: isWWW ? domain.replace('www.', '') : 'www.' + domain,
|
||||
updatedAt: updatedAt.getTime()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const output = mustache.render(template, data);
|
||||
const newHash = crypto.createHash('md5').update(output).digest('hex');
|
||||
const { proxyHash, id } = await db.listSettings();
|
||||
if (proxyHash !== newHash) {
|
||||
await db.prisma.setting.update({ where: { id }, data: { proxyHash: newHash } });
|
||||
await haproxy.post(`v2/services/haproxy/configuration/raw`, {
|
||||
searchParams: {
|
||||
skip_version: true
|
||||
},
|
||||
body: output,
|
||||
headers: {
|
||||
'Content-Type': 'text/plain'
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,9 +236,15 @@ export async function stopCoolifyProxy(
|
||||
export async function configureNetworkCoolifyProxy(engine: string): Promise<void> {
|
||||
const host = getEngine(engine);
|
||||
const destinations = await db.prisma.destinationDocker.findMany({ where: { engine } });
|
||||
const { stdout: networks } = await asyncExecShell(
|
||||
`DOCKER_HOST="${host}" docker ps -a --filter name=coolify-haproxy --format '{{json .Networks}}'`
|
||||
);
|
||||
const configuredNetworks = networks.replace(/"/g, '').replace('\n', '').split(',');
|
||||
for (const destination of destinations) {
|
||||
await asyncExecShell(
|
||||
`DOCKER_HOST="${host}" docker network connect ${destination.network} coolify-haproxy`
|
||||
);
|
||||
if (!configuredNetworks.includes(destination.network)) {
|
||||
await asyncExecShell(
|
||||
`DOCKER_HOST="${host}" docker network connect ${destination.network} coolify-haproxy`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import cuid from 'cuid';
|
||||
import fs from 'fs/promises';
|
||||
import getPort, { portNumbers } from 'get-port';
|
||||
import { supportedServiceTypesAndVersions } from '$lib/components/common';
|
||||
import { promises as dns } from 'dns';
|
||||
|
||||
export async function letsEncrypt(domain: string, id?: string, isCoolify = false): Promise<void> {
|
||||
try {
|
||||
@@ -148,7 +149,8 @@ export async function generateSSLCerts(): Promise<void> {
|
||||
plausibleAnalytics: true,
|
||||
vscodeserver: true,
|
||||
wordpress: true,
|
||||
ghost: true
|
||||
ghost: true,
|
||||
meiliSearch: true
|
||||
},
|
||||
orderBy: { createdAt: 'desc' }
|
||||
});
|
||||
@@ -198,6 +200,15 @@ export async function generateSSLCerts(): Promise<void> {
|
||||
file.endsWith('.pem') && certificates.push(file.replace(/\.pem$/, ''));
|
||||
}
|
||||
}
|
||||
const resolver = new dns.Resolver({ timeout: 2000 });
|
||||
resolver.setServers(['8.8.8.8', '1.1.1.1']);
|
||||
let ipv4, ipv6;
|
||||
try {
|
||||
ipv4 = await (await asyncExecShell(`curl -4s https://ifconfig.io`)).stdout;
|
||||
} catch (error) {}
|
||||
try {
|
||||
ipv6 = await (await asyncExecShell(`curl -6s https://ifconfig.io`)).stdout;
|
||||
} catch (error) {}
|
||||
for (const ssl of ssls) {
|
||||
if (!dev) {
|
||||
if (
|
||||
@@ -206,8 +217,27 @@ export async function generateSSLCerts(): Promise<void> {
|
||||
) {
|
||||
console.log(`Certificate for ${ssl.domain} already exists`);
|
||||
} else {
|
||||
console.log('Generating SSL for', ssl.domain);
|
||||
await letsEncrypt(ssl.domain, ssl.id, ssl.isCoolify);
|
||||
// Checking DNS entry before generating certificate
|
||||
if (ipv4 || ipv6) {
|
||||
let domains4 = [];
|
||||
let domains6 = [];
|
||||
try {
|
||||
domains4 = await resolver.resolve4(ssl.domain);
|
||||
} catch (error) {}
|
||||
try {
|
||||
domains6 = await resolver.resolve6(ssl.domain);
|
||||
} catch (error) {}
|
||||
if (domains4.length > 0 || domains6.length > 0) {
|
||||
if (
|
||||
(ipv4 && domains4.includes(ipv4.replace('\n', ''))) ||
|
||||
(ipv6 && domains6.includes(ipv6.replace('\n', '')))
|
||||
) {
|
||||
console.log('Generating SSL for', ssl.domain, '.');
|
||||
return await letsEncrypt(ssl.domain, ssl.id, ssl.isCoolify);
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log('DNS settings is incorrect for', ssl.domain, 'skipping.');
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
@@ -216,7 +246,27 @@ export async function generateSSLCerts(): Promise<void> {
|
||||
) {
|
||||
console.log(`Certificate for ${ssl.domain} already exists`);
|
||||
} else {
|
||||
console.log('Generating SSL for', ssl.domain);
|
||||
// Checking DNS entry before generating certificate
|
||||
if (ipv4 || ipv6) {
|
||||
let domains4 = [];
|
||||
let domains6 = [];
|
||||
try {
|
||||
domains4 = await resolver.resolve4(ssl.domain);
|
||||
} catch (error) {}
|
||||
try {
|
||||
domains6 = await resolver.resolve6(ssl.domain);
|
||||
} catch (error) {}
|
||||
if (domains4.length > 0 || domains6.length > 0) {
|
||||
if (
|
||||
(ipv4 && domains4.includes(ipv4.replace('\n', ''))) ||
|
||||
(ipv6 && domains6.includes(ipv6.replace('\n', '')))
|
||||
) {
|
||||
console.log('Generating SSL for', ssl.domain, '.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log('DNS settings is incorrect for', ssl.domain, 'skipping.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,7 +285,15 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
|
||||
networks: [docker.network],
|
||||
labels,
|
||||
depends_on: [],
|
||||
restart: 'always'
|
||||
restart: 'always',
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
condition: 'on-failure',
|
||||
delay: '5s',
|
||||
max_attempts: 3,
|
||||
window: '120s'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
networks: {
|
||||
|
||||
@@ -14,56 +14,23 @@ export default async function (): Promise<void> {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker rmi -f ${images}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
//console.log(error);
|
||||
}
|
||||
try {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker container prune -f`);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
//console.log(error);
|
||||
}
|
||||
try {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker image prune -f --filter "until=2h"`);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
//console.log(error);
|
||||
}
|
||||
// Cleanup old images older than a day
|
||||
try {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker image prune --filter "until=24h" -a -f`);
|
||||
} catch (error) {
|
||||
//console.log(error);
|
||||
}
|
||||
// Tagging images with labels
|
||||
// try {
|
||||
// const images = [
|
||||
// `coollabsio/${defaultProxyImageTcp}`,
|
||||
// `coollabsio/${defaultProxyImageHttp}`,
|
||||
// 'certbot/certbot:latest',
|
||||
// 'node:16.14.0-alpine',
|
||||
// 'alpine:latest',
|
||||
// 'nginx:stable-alpine',
|
||||
// 'node:lts',
|
||||
// 'php:apache',
|
||||
// 'rust:latest'
|
||||
// ];
|
||||
// for (const image of images) {
|
||||
// try {
|
||||
// await asyncExecShell(`DOCKER_HOST=${host} docker image inspect ${image}`);
|
||||
// } catch (error) {
|
||||
// await asyncExecShell(
|
||||
// `DOCKER_HOST=${host} docker pull ${image} && echo "FROM ${image}" | docker build --label coolify.image="true" -t "${image}" -`
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// } catch (error) {}
|
||||
// if (!dev) {
|
||||
// // Cleanup images that are not managed by coolify
|
||||
// try {
|
||||
// await asyncExecShell(
|
||||
// `DOCKER_HOST=${host} docker image prune --filter 'label!=coolify.image=true' -a -f`
|
||||
// );
|
||||
// } catch (error) {
|
||||
// console.log(error);
|
||||
// }
|
||||
// // Cleanup old images >3 days
|
||||
// try {
|
||||
// await asyncExecShell(`DOCKER_HOST=${host} docker image prune --filter "until=72h" -a -f`);
|
||||
// } catch (error) {
|
||||
// console.log(error);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ import * as Bullmq from 'bullmq';
|
||||
import { default as ProdBullmq, QueueScheduler } from 'bullmq';
|
||||
import { dev } from '$app/env';
|
||||
import { prisma } from '$lib/database';
|
||||
|
||||
import builder from './builder';
|
||||
import logger from './logger';
|
||||
import cleanup from './cleanup';
|
||||
import proxy from './proxy';
|
||||
import ssl from './ssl';
|
||||
@@ -142,5 +144,9 @@ buildWorker.on('failed', async (job: Bullmq.Job, failedReason) => {
|
||||
|
||||
const buildLogQueueName = 'log_queue';
|
||||
const buildLogQueue = new Queue(buildLogQueueName, connectionOptions);
|
||||
const buildLogWorker = new Worker(buildLogQueueName, async (job) => await logger(job), {
|
||||
concurrency: 1,
|
||||
...connectionOptions
|
||||
});
|
||||
|
||||
export { buildQueue, buildLogQueue };
|
||||
|
||||
@@ -23,6 +23,14 @@ export type ComposeFileService = {
|
||||
dockerfile: string;
|
||||
args?: Record<string, unknown>;
|
||||
};
|
||||
deploy?: {
|
||||
restart_policy?: {
|
||||
condition?: string;
|
||||
delay?: string;
|
||||
max_attempts?: number;
|
||||
window?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export type ComposerFileVersion =
|
||||
|
||||
@@ -36,8 +36,15 @@
|
||||
});
|
||||
}
|
||||
|
||||
async function loadBranchesByPage(page = 0) {
|
||||
return await get(`${apiUrl}/repos/${selected.repository}/branches?per_page=100&page=${page}`, {
|
||||
Authorization: `token ${$gitTokens.githubToken}`
|
||||
});
|
||||
}
|
||||
|
||||
let reposSelectOptions;
|
||||
let branchSelectOptions;
|
||||
|
||||
async function loadRepositories() {
|
||||
let page = 1;
|
||||
let reposCount = 0;
|
||||
@@ -58,24 +65,28 @@
|
||||
}));
|
||||
}
|
||||
async function loadBranches(event) {
|
||||
branches = [];
|
||||
selected.repository = event.detail.value;
|
||||
loading.branches = true;
|
||||
selected.branch = undefined;
|
||||
selected.projectId = repositories.find((repo) => repo.full_name === selected.repository).id;
|
||||
try {
|
||||
branches = await get(`${apiUrl}/repos/${selected.repository}/branches`, {
|
||||
Authorization: `token ${$gitTokens.githubToken}`
|
||||
});
|
||||
branchSelectOptions = branches.map((branch) => ({
|
||||
value: branch.name,
|
||||
label: branch.name
|
||||
}));
|
||||
return;
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
loading.branches = false;
|
||||
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) => ({
|
||||
value: branch.name,
|
||||
label: branch.name
|
||||
}));
|
||||
}
|
||||
async function isBranchAlreadyUsed(event) {
|
||||
selected.branch = event.detail.value;
|
||||
@@ -166,30 +177,36 @@
|
||||
{: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 gap-4">
|
||||
<div class="flex-col md:flex gap-4">
|
||||
<div class="custom-select-wrapper">
|
||||
<Select
|
||||
placeholder={loading.repositories
|
||||
? 'Loading repositories ...'
|
||||
? 'Loading repositories...'
|
||||
: 'Please select a repository'}
|
||||
id="repository"
|
||||
showIndicator={true}
|
||||
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
|
||||
? 'Loading branches ...'
|
||||
? 'Loading branches...'
|
||||
: !selected.repository
|
||||
? 'Please select a repository first'
|
||||
: 'Please select a branch'}
|
||||
id="repository"
|
||||
isWaiting={loading.branches}
|
||||
showIndicator={selected.repository}
|
||||
id="branches"
|
||||
on:select={isBranchAlreadyUsed}
|
||||
items={branchSelectOptions}
|
||||
isDisabled={loading.branches || !selected.repository}
|
||||
isClearable={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -202,13 +219,6 @@
|
||||
class:bg-orange-600={showSave}
|
||||
class:hover:bg-orange-500={showSave}>Save</button
|
||||
>
|
||||
<!-- <button class="w-40"
|
||||
><a
|
||||
class="no-underline"
|
||||
href="{apiUrl}/apps/{application.gitSource.githubApp.name}/installations/new"
|
||||
>Modify Repositories</a
|
||||
></button
|
||||
> -->
|
||||
</div>
|
||||
</form>
|
||||
{/if}
|
||||
|
||||
@@ -45,7 +45,15 @@ export const post: RequestHandler = async (event) => {
|
||||
volumes: [volume],
|
||||
ulimits,
|
||||
labels,
|
||||
restart: 'always'
|
||||
restart: 'always',
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
condition: 'on-failure',
|
||||
delay: '5s',
|
||||
max_attempts: 3,
|
||||
window: '120s'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
networks: {
|
||||
|
||||
@@ -8,7 +8,6 @@ import type { RequestHandler } from '@sveltejs/kit';
|
||||
export const get: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
if (status === 401) return { status, body };
|
||||
console.log(teamId);
|
||||
const { id } = event.params;
|
||||
try {
|
||||
const destination = await db.getDestination({ id, teamId });
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
export let account;
|
||||
export let accounts;
|
||||
export let invitations;
|
||||
if (accounts.length === 0) {
|
||||
accounts.push(account);
|
||||
}
|
||||
@@ -74,12 +75,51 @@
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
async function acceptInvitation(id, teamId) {
|
||||
try {
|
||||
await post(`/iam/team/${teamId}/invitation/accept.json`, { id });
|
||||
return window.location.reload();
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
async function revokeInvitation(id, teamId) {
|
||||
try {
|
||||
await post(`/iam/team/${teamId}/invitation/revoke.json`, { id });
|
||||
return window.location.reload();
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex space-x-1 p-6 font-bold">
|
||||
<div class="mr-4 text-2xl tracking-tight">Identity and Access Management</div>
|
||||
</div>
|
||||
|
||||
{#if invitations.length > 0}
|
||||
<div class="mx-auto max-w-4xl px-6 py-4">
|
||||
<div class="title font-bold">Pending invitations</div>
|
||||
<div class="pt-10 text-center">
|
||||
{#each invitations as invitation}
|
||||
<div class="flex justify-center space-x-2">
|
||||
<div>
|
||||
Invited to <span class="font-bold text-pink-600">{invitation.teamName}</span> with
|
||||
<span class="font-bold text-rose-600">{invitation.permission}</span> permission.
|
||||
</div>
|
||||
<button
|
||||
class="hover:bg-green-500"
|
||||
on:click={() => acceptInvitation(invitation.id, invitation.teamId)}>Accept</button
|
||||
>
|
||||
<button
|
||||
class="hover:bg-red-600"
|
||||
on:click={() => revokeInvitation(invitation.id, invitation.teamId)}>Delete</button
|
||||
>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="mx-auto max-w-4xl px-6 py-4">
|
||||
{#if $session.teamId === '0' && accounts.length > 0}
|
||||
<div class="title font-bold">Accounts</div>
|
||||
|
||||
@@ -90,7 +90,15 @@ export const post: RequestHandler = async (event) => {
|
||||
environment: config.ghost.environmentVariables,
|
||||
restart: 'always',
|
||||
labels: makeLabelForServices('ghost'),
|
||||
depends_on: [`${id}-mariadb`]
|
||||
depends_on: [`${id}-mariadb`],
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
condition: 'on-failure',
|
||||
delay: '5s',
|
||||
max_attempts: 3,
|
||||
window: '120s'
|
||||
}
|
||||
}
|
||||
},
|
||||
[`${id}-mariadb`]: {
|
||||
container_name: `${id}-mariadb`,
|
||||
@@ -98,7 +106,15 @@ export const post: RequestHandler = async (event) => {
|
||||
networks: [network],
|
||||
volumes: [config.mariadb.volume],
|
||||
environment: config.mariadb.environmentVariables,
|
||||
restart: 'always'
|
||||
restart: 'always',
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
condition: 'on-failure',
|
||||
delay: '5s',
|
||||
max_attempts: 3,
|
||||
window: '120s'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
networks: {
|
||||
|
||||
@@ -43,7 +43,15 @@ export const post: RequestHandler = async (event) => {
|
||||
environment: config.environmentVariables,
|
||||
restart: 'always',
|
||||
volumes: [config.volume],
|
||||
labels: makeLabelForServices('languagetool')
|
||||
labels: makeLabelForServices('languagetool'),
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
condition: 'on-failure',
|
||||
delay: '5s',
|
||||
max_attempts: 3,
|
||||
window: '120s'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
networks: {
|
||||
|
||||
@@ -48,7 +48,15 @@ export const post: RequestHandler = async (event) => {
|
||||
environment: config.environmentVariables,
|
||||
restart: 'always',
|
||||
volumes: [config.volume],
|
||||
labels: makeLabelForServices('meilisearch')
|
||||
labels: makeLabelForServices('meilisearch'),
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
condition: 'on-failure',
|
||||
delay: '5s',
|
||||
max_attempts: 3,
|
||||
window: '120s'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
networks: {
|
||||
|
||||
@@ -67,7 +67,15 @@ export const post: RequestHandler = async (event) => {
|
||||
networks: [network],
|
||||
volumes: [config.volume],
|
||||
restart: 'always',
|
||||
labels: makeLabelForServices('minio')
|
||||
labels: makeLabelForServices('minio'),
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
condition: 'on-failure',
|
||||
delay: '5s',
|
||||
max_attempts: 3,
|
||||
window: '120s'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
networks: {
|
||||
|
||||
@@ -44,7 +44,15 @@ export const post: RequestHandler = async (event) => {
|
||||
volumes: [config.volume],
|
||||
environment: config.environmentVariables,
|
||||
restart: 'always',
|
||||
labels: makeLabelForServices('n8n')
|
||||
labels: makeLabelForServices('n8n'),
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
condition: 'on-failure',
|
||||
delay: '5s',
|
||||
max_attempts: 3,
|
||||
window: '120s'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
networks: {
|
||||
|
||||
@@ -40,7 +40,15 @@ export const post: RequestHandler = async (event) => {
|
||||
networks: [network],
|
||||
environment: config.environmentVariables,
|
||||
restart: 'always',
|
||||
labels: makeLabelForServices('nocodb')
|
||||
labels: makeLabelForServices('nocodb'),
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
condition: 'on-failure',
|
||||
delay: '5s',
|
||||
max_attempts: 3,
|
||||
window: '120s'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
networks: {
|
||||
|
||||
@@ -133,7 +133,15 @@ COPY ./init-db.sh /docker-entrypoint-initdb.d/init-db.sh`;
|
||||
environment: config.plausibleAnalytics.environmentVariables,
|
||||
restart: 'always',
|
||||
depends_on: [`${id}-postgresql`, `${id}-clickhouse`],
|
||||
labels: makeLabelForServices('plausibleAnalytics')
|
||||
labels: makeLabelForServices('plausibleAnalytics'),
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
condition: 'on-failure',
|
||||
delay: '10s',
|
||||
max_attempts: 5,
|
||||
window: '120s'
|
||||
}
|
||||
}
|
||||
},
|
||||
[`${id}-postgresql`]: {
|
||||
container_name: `${id}-postgresql`,
|
||||
@@ -141,7 +149,15 @@ COPY ./init-db.sh /docker-entrypoint-initdb.d/init-db.sh`;
|
||||
networks: [network],
|
||||
environment: config.postgresql.environmentVariables,
|
||||
volumes: [config.postgresql.volume],
|
||||
restart: 'always'
|
||||
restart: 'always',
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
condition: 'on-failure',
|
||||
delay: '10s',
|
||||
max_attempts: 5,
|
||||
window: '120s'
|
||||
}
|
||||
}
|
||||
},
|
||||
[`${id}-clickhouse`]: {
|
||||
build: workdir,
|
||||
@@ -149,7 +165,15 @@ COPY ./init-db.sh /docker-entrypoint-initdb.d/init-db.sh`;
|
||||
networks: [network],
|
||||
environment: config.clickhouse.environmentVariables,
|
||||
volumes: [config.clickhouse.volume],
|
||||
restart: 'always'
|
||||
restart: 'always',
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
condition: 'on-failure',
|
||||
delay: '10s',
|
||||
max_attempts: 5,
|
||||
window: '120s'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
networks: {
|
||||
|
||||
@@ -42,7 +42,15 @@ export const post: RequestHandler = async (event) => {
|
||||
volumes: [config.volume],
|
||||
environment: config.environmentVariables,
|
||||
restart: 'always',
|
||||
labels: makeLabelForServices('uptimekuma')
|
||||
labels: makeLabelForServices('uptimekuma'),
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
condition: 'on-failure',
|
||||
delay: '5s',
|
||||
max_attempts: 3,
|
||||
window: '120s'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
networks: {
|
||||
|
||||
@@ -43,7 +43,15 @@ export const post: RequestHandler = async (event) => {
|
||||
networks: [network],
|
||||
volumes: [config.volume],
|
||||
restart: 'always',
|
||||
labels: makeLabelForServices('vaultWarden')
|
||||
labels: makeLabelForServices('vaultWarden'),
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
condition: 'on-failure',
|
||||
delay: '5s',
|
||||
max_attempts: 3,
|
||||
window: '120s'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
networks: {
|
||||
|
||||
@@ -52,7 +52,15 @@ export const post: RequestHandler = async (event) => {
|
||||
networks: [network],
|
||||
volumes: [config.volume],
|
||||
restart: 'always',
|
||||
labels: makeLabelForServices('vscodeServer')
|
||||
labels: makeLabelForServices('vscodeServer'),
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
condition: 'on-failure',
|
||||
delay: '5s',
|
||||
max_attempts: 3,
|
||||
window: '120s'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
networks: {
|
||||
|
||||
@@ -77,7 +77,15 @@ export const post: RequestHandler = async (event) => {
|
||||
networks: [network],
|
||||
restart: 'always',
|
||||
depends_on: [`${id}-mysql`],
|
||||
labels: makeLabelForServices('wordpress')
|
||||
labels: makeLabelForServices('wordpress'),
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
condition: 'on-failure',
|
||||
delay: '5s',
|
||||
max_attempts: 3,
|
||||
window: '120s'
|
||||
}
|
||||
}
|
||||
},
|
||||
[`${id}-mysql`]: {
|
||||
container_name: `${id}-mysql`,
|
||||
@@ -85,7 +93,15 @@ export const post: RequestHandler = async (event) => {
|
||||
volumes: [config.mysql.volume],
|
||||
environment: config.mysql.environmentVariables,
|
||||
networks: [network],
|
||||
restart: 'always'
|
||||
restart: 'always',
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
condition: 'on-failure',
|
||||
delay: '5s',
|
||||
max_attempts: 3,
|
||||
window: '120s'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
networks: {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
export let source;
|
||||
import { page, session } from '$app/stores';
|
||||
import { post } from '$lib/api';
|
||||
import Explainer from '$lib/components/Explainer.svelte';
|
||||
import { errorNotification } from '$lib/form';
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
const { id } = $page.params;
|
||||
@@ -51,7 +52,8 @@
|
||||
type: 'github',
|
||||
name: source.name,
|
||||
htmlUrl: source.htmlUrl.replace(/\/$/, ''),
|
||||
apiUrl: source.apiUrl.replace(/\/$/, '')
|
||||
apiUrl: source.apiUrl.replace(/\/$/, ''),
|
||||
organization: source.organization
|
||||
});
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
@@ -97,6 +99,22 @@
|
||||
<label for="apiUrl" class="text-base font-bold text-stone-100">API URL</label>
|
||||
<input name="apiUrl" id="apiUrl" required bind:value={source.apiUrl} />
|
||||
</div>
|
||||
<div class="grid grid-cols-2">
|
||||
<div class="flex flex-col">
|
||||
<label for="organization" class="pt-2 text-base font-bold text-stone-100"
|
||||
>Organization</label
|
||||
>
|
||||
<Explainer
|
||||
text="Fill it if you would like to use an organization's as your Git Source. Otherwise your user will be used."
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
name="organization"
|
||||
id="organization"
|
||||
placeholder="eg: coollabsio"
|
||||
bind:value={source.organization}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{#if source.apiUrl && source.htmlUrl && source.name}
|
||||
<div class="text-center">
|
||||
@@ -135,6 +153,21 @@
|
||||
<label for="apiUrl" class="text-base font-bold text-stone-100">API URL</label>
|
||||
<input name="apiUrl" id="apiUrl" required bind:value={source.apiUrl} />
|
||||
</div>
|
||||
<div class="grid grid-cols-2">
|
||||
<div class="flex flex-col">
|
||||
<label for="organization" class="pt-2 text-base font-bold text-stone-100"
|
||||
>Organization</label
|
||||
>
|
||||
</div>
|
||||
<input
|
||||
readonly
|
||||
disabled
|
||||
name="organization"
|
||||
id="organization"
|
||||
placeholder="eg: coollabsio"
|
||||
bind:value={source.organization}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{:else}
|
||||
|
||||
@@ -9,8 +9,8 @@ export const post: RequestHandler = async (event) => {
|
||||
const { id } = event.params;
|
||||
|
||||
try {
|
||||
let { type, name, htmlUrl, apiUrl } = await event.request.json();
|
||||
await db.addGitHubSource({ id, teamId, type, name, htmlUrl, apiUrl });
|
||||
let { type, name, htmlUrl, apiUrl, organization } = await event.request.json();
|
||||
await db.addGitHubSource({ id, teamId, type, name, htmlUrl, apiUrl, organization });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
export let settings;
|
||||
onMount(() => {
|
||||
const { organization, id, htmlUrl } = source;
|
||||
console.log(source);
|
||||
const { fqdn } = settings;
|
||||
const host = dev
|
||||
? 'http://localhost:3000'
|
||||
|
||||
@@ -50,7 +50,10 @@ textarea {
|
||||
}
|
||||
|
||||
#svelte .listContainer {
|
||||
@apply bg-coolgray-400 text-white scrollbar-w-2 scrollbar-thumb-coollabs scrollbar-track-coolgray-200;
|
||||
@apply bg-coolgray-400 text-white scrollbar-w-2 scrollbar-thumb-green-500 scrollbar-track-coolgray-200;
|
||||
}
|
||||
#svelte .selectedItem {
|
||||
@apply pl-3;
|
||||
}
|
||||
|
||||
#svelte .item.hover {
|
||||
|
||||
Reference in New Issue
Block a user