mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-18 12:33:06 +00:00
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d624f95cb9 | ||
|
|
bedbe46830 | ||
|
|
7846bf1bc3 | ||
|
|
c28bc786ae | ||
|
|
60a53bb812 | ||
|
|
017ba61dc9 | ||
|
|
b5d82dc87b | ||
|
|
866070b9ea | ||
|
|
49ecb15773 | ||
|
|
047df77195 | ||
|
|
0904d1ff9c | ||
|
|
7bf6041d8c | ||
|
|
90597389c9 | ||
|
|
039350d762 | ||
|
|
ce31146a9c | ||
|
|
344dd7db28 | ||
|
|
b735ca2da7 | ||
|
|
cbdd098528 | ||
|
|
d5f9d57be9 | ||
|
|
d5f2d22663 | ||
|
|
9914686ed7 | ||
|
|
ee9acfe556 | ||
|
|
9f9d4297ca | ||
|
|
002637ec5f | ||
|
|
c1238c6594 | ||
|
|
c43b848708 | ||
|
|
e22950cecb | ||
|
|
5a7edcb762 | ||
|
|
9b47de71fc | ||
|
|
8f9462245a | ||
|
|
f0ed51cd22 | ||
|
|
99a7eff6ab | ||
|
|
edfed57df3 | ||
|
|
a70e35cb79 | ||
|
|
3da1b31363 | ||
|
|
59bc2dd8a7 | ||
|
|
7b6e7680a6 | ||
|
|
1c65df282e | ||
|
|
77ae070c98 | ||
|
|
20708f1456 | ||
|
|
9a1a67a4ef | ||
|
|
3a8e5df897 | ||
|
|
88a62be30c | ||
|
|
c478c1b7ad | ||
|
|
18c2b2e38e | ||
|
|
b105e6fbf8 | ||
|
|
da11bae67c | ||
|
|
d344a9bb4f | ||
|
|
222adb212b | ||
|
|
cb01bbe4ac |
@@ -14,7 +14,12 @@
|
||||
// Set *default* container specific settings.json values on container create.
|
||||
"settings": {},
|
||||
// Add the IDs of extensions you want installed when the container is created.
|
||||
"extensions": ["dbaeumer.vscode-eslint", "svelte.svelte-vscode"],
|
||||
"extensions": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"svelte.svelte-vscode",
|
||||
"ardenivanov.svelte-intellisense",
|
||||
"Prisma.prisma"
|
||||
],
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
"forwardPorts": [3000],
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
|
||||
@@ -8,4 +8,5 @@ package
|
||||
!.env.example
|
||||
dist
|
||||
client
|
||||
apps/api/db/*.db
|
||||
apps/api/db/*.db
|
||||
local-serve
|
||||
@@ -1,34 +1,28 @@
|
||||
name: release-coolify
|
||||
name: production-release
|
||||
|
||||
on:
|
||||
release:
|
||||
types: published
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
make-it-coolifyed:
|
||||
making-something-cool:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
-
|
||||
name: Set up QEMU
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
-
|
||||
name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Get current package version
|
||||
uses: martinbeentjes/npm-get-version-action@v1.2.3
|
||||
id: package-version
|
||||
-
|
||||
name: Build and push
|
||||
- name: Get current package version
|
||||
uses: martinbeentjes/npm-get-version-action@v1.2.3
|
||||
id: package-version
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
@@ -37,3 +31,7 @@ jobs:
|
||||
tags: coollabsio/coolify:latest,coollabsio/coolify:${{steps.package-version.outputs.current-version}}
|
||||
cache-from: type=registry,ref=coollabsio/coolify:buildcache
|
||||
cache-to: type=registry,ref=coollabsio/coolify:buildcache,mode=max
|
||||
- uses: sarisia/actions-status-discord@v1
|
||||
if: always()
|
||||
with:
|
||||
webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_CHANNEL }}
|
||||
35
.github/workflows/staging-release.yml
vendored
Normal file
35
.github/workflows/staging-release.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
name: staging-release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- next
|
||||
|
||||
jobs:
|
||||
staging-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
tags: coollabsio/coolify:next
|
||||
cache-from: type=registry,ref=coollabsio/coolify:buildcache-next
|
||||
cache-to: type=registry,ref=coollabsio/coolify:buildcache-next,mode=max
|
||||
- uses: sarisia/actions-status-discord@v1
|
||||
if: always()
|
||||
with:
|
||||
webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_CHANNEL }}
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -8,4 +8,6 @@ package
|
||||
!.env.example
|
||||
dist
|
||||
client
|
||||
apps/api/db/*.db
|
||||
apps/api/db/*.db
|
||||
local-serve
|
||||
apps/api/db/migration.db-journal
|
||||
10
.gitpod.yml
10
.gitpod.yml
@@ -1,9 +1,11 @@
|
||||
# This configuration file was automatically generated by Gitpod.
|
||||
# Please adjust to your needs (see https://www.gitpod.io/docs/config-gitpod-file)
|
||||
# and commit this file to your remote git repository to share the goodness with others.
|
||||
|
||||
image: gitpod/workspace-node:2022-06-20-19-54-55
|
||||
tasks:
|
||||
- init: npm install && npm run build
|
||||
command: npm run start
|
||||
|
||||
- init: pnpm install && pnpm db:push && pnpm db:seed
|
||||
command: pnpm dev
|
||||
|
||||
ports:
|
||||
- port: 3001
|
||||
visibility: public
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
"watch": ["src"],
|
||||
"ignore": ["src/**/*.test.ts"],
|
||||
"ext": "ts,mjs,json,graphql",
|
||||
"exec": "rimraf build && esbuild `find src \\( -name '*.ts' \\) | grep -v client/` --platform=node --outdir=build --format=cjs && node build",
|
||||
"exec": "rimraf build && esbuild `find src \\( -name '*.ts' \\)` --minify=true --platform=node --outdir=build --format=cjs && node build",
|
||||
"legacyWatch": true
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"db:push": "prisma db push && prisma generate",
|
||||
"db:seed": "prisma db seed",
|
||||
"db:studio": "prisma studio",
|
||||
"db:migrate": "COOLIFY_DATABASE_URL=file:../db/migration.db prisma migrate dev --skip-seed --name",
|
||||
"dev": "nodemon",
|
||||
"build": "rimraf build && esbuild `find src \\( -name '*.ts' \\)| grep -v client/` --platform=node --outdir=build --format=cjs",
|
||||
"format": "prettier --write 'src/**/*.{js,ts,json,md}'",
|
||||
@@ -14,11 +15,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@breejs/ts-worker": "2.0.0",
|
||||
"@fastify/autoload": "5.0.0",
|
||||
"@fastify/cookie": "7.0.0",
|
||||
"@fastify/autoload": "5.1.0",
|
||||
"@fastify/cookie": "7.1.0",
|
||||
"@fastify/cors": "8.0.0",
|
||||
"@fastify/env": "4.0.0",
|
||||
"@fastify/jwt": "6.2.0",
|
||||
"@fastify/jwt": "6.3.1",
|
||||
"@fastify/static": "6.4.0",
|
||||
"@iarna/toml": "2.2.5",
|
||||
"@prisma/client": "3.15.2",
|
||||
@@ -31,8 +32,8 @@
|
||||
"dayjs": "1.11.3",
|
||||
"dockerode": "3.3.2",
|
||||
"dotenv-extended": "2.9.0",
|
||||
"fastify": "4.2.0",
|
||||
"fastify-plugin": "3.0.1",
|
||||
"fastify": "4.2.1",
|
||||
"fastify-plugin": "4.0.0",
|
||||
"generate-password": "1.7.0",
|
||||
"get-port": "6.1.2",
|
||||
"got": "12.1.0",
|
||||
@@ -46,11 +47,11 @@
|
||||
"unique-names-generator": "4.7.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "18.0.3",
|
||||
"@types/node": "18.0.4",
|
||||
"@types/node-os-utils": "1.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.30.5",
|
||||
"@typescript-eslint/parser": "5.30.5",
|
||||
"esbuild": "0.14.48",
|
||||
"@typescript-eslint/eslint-plugin": "5.30.6",
|
||||
"@typescript-eslint/parser": "5.30.6",
|
||||
"esbuild": "0.14.49",
|
||||
"eslint": "8.19.0",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
"eslint-plugin-prettier": "4.2.1",
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Application" ADD COLUMN "deploymentType" TEXT;
|
||||
@@ -0,0 +1,24 @@
|
||||
-- RedefineTables
|
||||
PRAGMA foreign_keys=OFF;
|
||||
CREATE TABLE "new_GitSource" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"name" TEXT NOT NULL,
|
||||
"type" TEXT,
|
||||
"apiUrl" TEXT,
|
||||
"htmlUrl" TEXT,
|
||||
"customPort" INTEGER NOT NULL DEFAULT 22,
|
||||
"organization" TEXT,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
"githubAppId" TEXT,
|
||||
"gitlabAppId" TEXT,
|
||||
CONSTRAINT "GitSource_githubAppId_fkey" FOREIGN KEY ("githubAppId") REFERENCES "GithubApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
|
||||
CONSTRAINT "GitSource_gitlabAppId_fkey" FOREIGN KEY ("gitlabAppId") REFERENCES "GitlabApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
INSERT INTO "new_GitSource" ("apiUrl", "createdAt", "githubAppId", "gitlabAppId", "htmlUrl", "id", "name", "organization", "type", "updatedAt") SELECT "apiUrl", "createdAt", "githubAppId", "gitlabAppId", "htmlUrl", "id", "name", "organization", "type", "updatedAt" FROM "GitSource";
|
||||
DROP TABLE "GitSource";
|
||||
ALTER TABLE "new_GitSource" RENAME TO "GitSource";
|
||||
CREATE UNIQUE INDEX "GitSource_githubAppId_key" ON "GitSource"("githubAppId");
|
||||
CREATE UNIQUE INDEX "GitSource_gitlabAppId_key" ON "GitSource"("gitlabAppId");
|
||||
PRAGMA foreign_key_check;
|
||||
PRAGMA foreign_keys=ON;
|
||||
@@ -91,6 +91,7 @@ model Application {
|
||||
startCommand String?
|
||||
baseDirectory String?
|
||||
publishDirectory String?
|
||||
deploymentType String?
|
||||
phpModules String?
|
||||
pythonWSGI String?
|
||||
pythonModule String?
|
||||
@@ -217,6 +218,7 @@ model GitSource {
|
||||
type String?
|
||||
apiUrl String?
|
||||
htmlUrl String?
|
||||
customPort Int @default(22)
|
||||
organization String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
@@ -311,6 +313,7 @@ model Service {
|
||||
umami Umami?
|
||||
hasura Hasura?
|
||||
fider Fider?
|
||||
moodle Moodle?
|
||||
}
|
||||
|
||||
model PlausibleAnalytics {
|
||||
@@ -449,3 +452,20 @@ model Fider {
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model Moodle {
|
||||
id String @id @default(cuid())
|
||||
serviceId String @unique
|
||||
defaultUsername String
|
||||
defaultPassword String
|
||||
defaultEmail String
|
||||
mariadbUser String
|
||||
mariadbPassword String
|
||||
mariadbRootUser String
|
||||
mariadbRootUserPassword String
|
||||
mariadbDatabase String
|
||||
mariadbPublicPort Int?
|
||||
service Service @relation(fields: [serviceId], references: [id])
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
@@ -68,7 +68,8 @@ const schema = {
|
||||
};
|
||||
|
||||
const options = {
|
||||
schema
|
||||
schema,
|
||||
dotenv: true
|
||||
};
|
||||
fastify.register(env, options);
|
||||
if (!isDev) {
|
||||
@@ -76,7 +77,7 @@ if (!isDev) {
|
||||
root: path.join(__dirname, './public'),
|
||||
preCompressed: true
|
||||
});
|
||||
fastify.setNotFoundHandler(function (request, reply) {
|
||||
fastify.setNotFoundHandler(async function (request, reply) {
|
||||
if (request.raw.url && request.raw.url.startsWith('/api')) {
|
||||
return reply.status(404).send({
|
||||
success: false
|
||||
@@ -105,23 +106,30 @@ fastify.listen({ port, host }, async (err: any, address: any) => {
|
||||
await scheduler.start('cleanupStorage');
|
||||
await scheduler.start('checkProxies')
|
||||
|
||||
// Check if no build is running, try to autoupdate.
|
||||
// Check if no build is running
|
||||
|
||||
// Check for update
|
||||
setInterval(async () => {
|
||||
const { isAutoUpdateEnabled } = await prisma.setting.findFirst();
|
||||
if (isAutoUpdateEnabled) {
|
||||
if (scheduler.workers.has('deployApplication')) {
|
||||
scheduler.workers.get('deployApplication').postMessage("status");
|
||||
scheduler.workers.get('deployApplication').postMessage("status:autoUpdater");
|
||||
}
|
||||
}
|
||||
}, 30000 * 10)
|
||||
}, 60000 * 15)
|
||||
|
||||
// Cleanup storage
|
||||
setInterval(async () => {
|
||||
if (scheduler.workers.has('deployApplication')) {
|
||||
scheduler.workers.get('deployApplication').postMessage("status:cleanupStorage");
|
||||
}
|
||||
}, 60000 * 10)
|
||||
|
||||
scheduler.on('worker deleted', async (name) => {
|
||||
if (name === 'autoUpdater') {
|
||||
await scheduler.start('deployApplication');
|
||||
if (name === 'autoUpdater' || name === 'cleanupStorage') {
|
||||
if (!scheduler.workers.has('deployApplication')) await scheduler.start('deployApplication');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
async function initServer() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { parentPort } from 'node:worker_threads';
|
||||
import { asyncExecShell, isDev, prisma, version } from '../lib/common';
|
||||
import { asyncExecShell, cleanupDockerStorage, isDev, prisma, version } from '../lib/common';
|
||||
import { getEngine } from '../lib/docker';
|
||||
|
||||
(async () => {
|
||||
@@ -9,82 +9,52 @@ import { getEngine } from '../lib/docker';
|
||||
for (const engine of engines) {
|
||||
let lowDiskSpace = false;
|
||||
const host = getEngine(engine);
|
||||
// try {
|
||||
// let stdout = null
|
||||
// if (!isDev) {
|
||||
// const output = await asyncExecShell(
|
||||
// `DOCKER_HOST=${host} docker exec coolify sh -c 'df -kPT /'`
|
||||
// );
|
||||
// stdout = output.stdout;
|
||||
// } else {
|
||||
// const output = await asyncExecShell(
|
||||
// `df -kPT /`
|
||||
// );
|
||||
// stdout = output.stdout;
|
||||
// }
|
||||
// let lines = stdout.trim().split('\n');
|
||||
// let header = lines[0];
|
||||
// let regex =
|
||||
// /^Filesystem\s+|Type\s+|1024-blocks|\s+Used|\s+Available|\s+Capacity|\s+Mounted on\s*$/g;
|
||||
// const boundaries = [];
|
||||
// let match;
|
||||
|
||||
// while ((match = regex.exec(header))) {
|
||||
// boundaries.push(match[0].length);
|
||||
// }
|
||||
|
||||
// boundaries[boundaries.length - 1] = -1;
|
||||
// const data = lines.slice(1).map((line) => {
|
||||
// const cl = boundaries.map((boundary) => {
|
||||
// const column = boundary > 0 ? line.slice(0, boundary) : line;
|
||||
// line = line.slice(boundary);
|
||||
// return column.trim();
|
||||
// });
|
||||
// return {
|
||||
// capacity: Number.parseInt(cl[5], 10) / 100
|
||||
// };
|
||||
// });
|
||||
// if (data.length > 0) {
|
||||
// const { capacity } = data[0];
|
||||
// if (capacity > 0.6) {
|
||||
// lowDiskSpace = true;
|
||||
// }
|
||||
// }
|
||||
// } catch (error) {
|
||||
// console.log(error);
|
||||
// }
|
||||
if (!isDev) {
|
||||
// Cleanup old coolify images
|
||||
try {
|
||||
let { stdout: images } = await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker images coollabsio/coolify --filter before="coollabsio/coolify:${version}" -q | xargs `
|
||||
try {
|
||||
let stdout = null
|
||||
if (!isDev) {
|
||||
const output = await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker exec coolify sh -c 'df -kPT /'`
|
||||
);
|
||||
images = images.trim();
|
||||
if (images) {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker rmi -f ${images}`);
|
||||
stdout = output.stdout;
|
||||
} else {
|
||||
const output = await asyncExecShell(
|
||||
`df -kPT /`
|
||||
);
|
||||
stdout = output.stdout;
|
||||
}
|
||||
let lines = stdout.trim().split('\n');
|
||||
let header = lines[0];
|
||||
let regex =
|
||||
/^Filesystem\s+|Type\s+|1024-blocks|\s+Used|\s+Available|\s+Capacity|\s+Mounted on\s*$/g;
|
||||
const boundaries = [];
|
||||
let match;
|
||||
|
||||
while ((match = regex.exec(header))) {
|
||||
boundaries.push(match[0].length);
|
||||
}
|
||||
|
||||
boundaries[boundaries.length - 1] = -1;
|
||||
const data = lines.slice(1).map((line) => {
|
||||
const cl = boundaries.map((boundary) => {
|
||||
const column = boundary > 0 ? line.slice(0, boundary) : line;
|
||||
line = line.slice(boundary);
|
||||
return column.trim();
|
||||
});
|
||||
return {
|
||||
capacity: Number.parseInt(cl[5], 10) / 100
|
||||
};
|
||||
});
|
||||
if (data.length > 0) {
|
||||
const { capacity } = data[0];
|
||||
if (capacity > 0.8) {
|
||||
lowDiskSpace = true;
|
||||
}
|
||||
} catch (error) {
|
||||
//console.log(error);
|
||||
}
|
||||
try {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker container prune -f`);
|
||||
} catch (error) {
|
||||
//console.log(error);
|
||||
}
|
||||
try {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker image prune -f`);
|
||||
} catch (error) {
|
||||
//console.log(error);
|
||||
}
|
||||
try {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker image prune -a -f`);
|
||||
} catch (error) {
|
||||
//console.log(error);
|
||||
}
|
||||
} else {
|
||||
console.log(`[DEV MODE] Low disk space: ${lowDiskSpace}`);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
await cleanupDockerStorage(host, lowDiskSpace, false)
|
||||
}
|
||||
await prisma.$disconnect();
|
||||
await prisma.$disconnect();
|
||||
} else process.exit(0);
|
||||
})();
|
||||
|
||||
@@ -4,7 +4,7 @@ import fs from 'fs/promises';
|
||||
import yaml from 'js-yaml';
|
||||
|
||||
import { copyBaseConfigurationFiles, makeLabelForStandaloneApplication, saveBuildLog, setDefaultConfiguration } from '../lib/buildPacks/common';
|
||||
import { asyncExecShell, createDirectories, decrypt, getDomain, prisma } from '../lib/common';
|
||||
import { asyncExecShell, createDirectories, decrypt, getDomain, prisma } from '../lib/common';
|
||||
import { dockerInstance, getEngine } from '../lib/docker';
|
||||
import * as importers from '../lib/importers';
|
||||
import * as buildpacks from '../lib/buildPacks';
|
||||
@@ -21,8 +21,12 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
parentPort.postMessage('cancelled');
|
||||
return;
|
||||
}
|
||||
if (message === 'status') {
|
||||
parentPort.postMessage({ size: queue.size, pending: queue.pending });
|
||||
if (message === 'status:autoUpdater') {
|
||||
parentPort.postMessage({ size: queue.size, pending: queue.pending, caller: 'autoUpdater' });
|
||||
return;
|
||||
}
|
||||
if (message === 'status:cleanupStorage') {
|
||||
parentPort.postMessage({ size: queue.size, pending: queue.pending, caller: 'cleanupStorage' });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -51,7 +55,8 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
denoOptions,
|
||||
exposePort,
|
||||
baseImage,
|
||||
baseBuildImage
|
||||
baseBuildImage,
|
||||
deploymentType,
|
||||
} = message
|
||||
let {
|
||||
branch,
|
||||
@@ -122,6 +127,7 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
repodir,
|
||||
githubAppId: gitSource.githubApp?.id,
|
||||
gitlabAppId: gitSource.gitlabApp?.id,
|
||||
customPort: gitSource.customPort,
|
||||
repository,
|
||||
branch,
|
||||
buildId,
|
||||
@@ -183,7 +189,7 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
let imageFound = false;
|
||||
try {
|
||||
await image.inspect();
|
||||
imageFound = false;
|
||||
imageFound = true;
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
@@ -221,14 +227,15 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
denoMainFile,
|
||||
denoOptions,
|
||||
baseImage,
|
||||
baseBuildImage
|
||||
baseBuildImage,
|
||||
deploymentType
|
||||
});
|
||||
else {
|
||||
await saveBuildLog({ line: `Build pack ${buildPack} not found`, buildId, applicationId });
|
||||
throw new Error(`Build pack ${buildPack} not found.`);
|
||||
}
|
||||
} else {
|
||||
await saveBuildLog({ line: 'Nothing changed.', buildId, applicationId });
|
||||
await saveBuildLog({ line: 'Build image already available - no rebuild required.', buildId, applicationId });
|
||||
}
|
||||
try {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker stop -t 0 ${imageId}`);
|
||||
|
||||
@@ -17,7 +17,7 @@ const nodeBased = [
|
||||
'nextjs'
|
||||
];
|
||||
|
||||
export function setDefaultBaseImage(buildPack: string | null) {
|
||||
export function setDefaultBaseImage(buildPack: string | null, deploymentType: string | null = null) {
|
||||
const nodeVersions = [
|
||||
{
|
||||
value: 'node:lts',
|
||||
@@ -259,10 +259,17 @@ export function setDefaultBaseImage(buildPack: string | null) {
|
||||
baseBuildImages: []
|
||||
};
|
||||
if (nodeBased.includes(buildPack)) {
|
||||
payload.baseImage = 'node:lts';
|
||||
payload.baseImages = nodeVersions;
|
||||
payload.baseBuildImage = 'node:lts';
|
||||
payload.baseBuildImages = nodeVersions;
|
||||
if (deploymentType === 'static') {
|
||||
payload.baseImage = 'webdevops/nginx:alpine';
|
||||
payload.baseImages = staticVersions;
|
||||
payload.baseBuildImage = 'node:lts';
|
||||
payload.baseBuildImages = nodeVersions;
|
||||
} else {
|
||||
payload.baseImage = 'node:lts';
|
||||
payload.baseImages = nodeVersions;
|
||||
payload.baseBuildImage = 'node:lts';
|
||||
payload.baseBuildImages = nodeVersions;
|
||||
}
|
||||
}
|
||||
if (staticApps.includes(buildPack)) {
|
||||
payload.baseImage = 'webdevops/nginx:alpine';
|
||||
@@ -431,7 +438,7 @@ export async function copyBaseConfigurationFiles(
|
||||
buildId,
|
||||
applicationId
|
||||
});
|
||||
} else if (staticApps.includes(buildPack) && baseImage.includes('nginx')) {
|
||||
} else if (baseImage?.includes('nginx')) {
|
||||
await fs.writeFile(
|
||||
`${workdir}/nginx.conf`,
|
||||
`user nginx;
|
||||
|
||||
@@ -42,9 +42,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
Dockerfile.push(`COPY .${baseDirectory || ''}/deps.ts /app`);
|
||||
Dockerfile.push(`RUN deno cache deps.ts`);
|
||||
}
|
||||
Dockerfile.push(`COPY ${denoMainFile} /app`);
|
||||
Dockerfile.push(`RUN deno cache ${denoMainFile}`);
|
||||
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
||||
Dockerfile.push(`RUN deno cache ${denoMainFile}`);
|
||||
Dockerfile.push(`ENV NO_COLOR true`);
|
||||
Dockerfile.push(`EXPOSE ${port}`);
|
||||
Dockerfile.push(`CMD deno run ${denoOptions ? denoOptions.split(' ') : ''} ${denoMainFile}`);
|
||||
|
||||
@@ -9,7 +9,7 @@ const createDockerfile = async (data, imageforBuild): Promise<void> => {
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`);
|
||||
if (baseImage.includes('nginx')) {
|
||||
if (baseImage?.includes('nginx')) {
|
||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||
}
|
||||
Dockerfile.push(`EXPOSE ${port}`);
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
import { promises as fs } from 'fs';
|
||||
import { buildImage, checkPnpm } from './common';
|
||||
import { buildCacheImageWithNode, buildImage, checkPnpm } from './common';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
const {
|
||||
applicationId,
|
||||
buildId,
|
||||
tag,
|
||||
workdir,
|
||||
publishDirectory,
|
||||
port,
|
||||
installCommand,
|
||||
buildCommand,
|
||||
startCommand,
|
||||
baseDirectory,
|
||||
secrets,
|
||||
pullmergeRequestId
|
||||
pullmergeRequestId,
|
||||
deploymentType,
|
||||
baseImage
|
||||
} = data;
|
||||
const Dockerfile: Array<string> = [];
|
||||
const isPnpm = checkPnpm(installCommand, buildCommand, startCommand);
|
||||
@@ -36,22 +41,34 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
if (isPnpm) {
|
||||
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7');
|
||||
}
|
||||
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
||||
Dockerfile.push(`RUN ${installCommand}`);
|
||||
|
||||
if (buildCommand) {
|
||||
if (deploymentType === 'node') {
|
||||
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
||||
Dockerfile.push(`RUN ${installCommand}`);
|
||||
Dockerfile.push(`RUN ${buildCommand}`);
|
||||
Dockerfile.push(`EXPOSE ${port}`);
|
||||
Dockerfile.push(`CMD ${startCommand}`);
|
||||
} else if (deploymentType === 'static') {
|
||||
if (baseImage?.includes('nginx')) {
|
||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||
}
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`);
|
||||
Dockerfile.push(`EXPOSE 80`);
|
||||
}
|
||||
Dockerfile.push(`EXPOSE ${port}`);
|
||||
Dockerfile.push(`CMD ${startCommand}`);
|
||||
|
||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||
};
|
||||
|
||||
export default async function (data) {
|
||||
try {
|
||||
const { baseImage, baseBuildImage } = data;
|
||||
await createDockerfile(data, baseImage);
|
||||
await buildImage(data);
|
||||
const { baseImage, baseBuildImage, deploymentType, buildCommand } = data;
|
||||
if (deploymentType === 'node') {
|
||||
await createDockerfile(data, baseImage);
|
||||
await buildImage(data);
|
||||
} else if (deploymentType === 'static') {
|
||||
if (buildCommand) await buildCacheImageWithNode(data, baseBuildImage);
|
||||
await createDockerfile(data, baseImage);
|
||||
await buildImage(data);
|
||||
}
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { promises as fs } from 'fs';
|
||||
import { buildImage, checkPnpm } from './common';
|
||||
import { buildCacheImageWithNode, buildImage, checkPnpm } from './common';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
const {
|
||||
applicationId,
|
||||
buildId,
|
||||
tag,
|
||||
workdir,
|
||||
publishDirectory,
|
||||
port,
|
||||
installCommand,
|
||||
buildCommand,
|
||||
@@ -11,7 +15,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
baseDirectory,
|
||||
secrets,
|
||||
pullmergeRequestId,
|
||||
buildId
|
||||
deploymentType,
|
||||
baseImage
|
||||
} = data;
|
||||
const Dockerfile: Array<string> = [];
|
||||
const isPnpm = checkPnpm(installCommand, buildCommand, startCommand);
|
||||
@@ -36,21 +41,34 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
if (isPnpm) {
|
||||
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7');
|
||||
}
|
||||
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
||||
Dockerfile.push(`RUN ${installCommand}`);
|
||||
if (buildCommand) {
|
||||
if (deploymentType === 'node') {
|
||||
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
||||
Dockerfile.push(`RUN ${installCommand}`);
|
||||
Dockerfile.push(`RUN ${buildCommand}`);
|
||||
Dockerfile.push(`EXPOSE ${port}`);
|
||||
Dockerfile.push(`CMD ${startCommand}`);
|
||||
} else if (deploymentType === 'static') {
|
||||
if (baseImage?.includes('nginx')) {
|
||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||
}
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`);
|
||||
Dockerfile.push(`EXPOSE 80`);
|
||||
}
|
||||
Dockerfile.push(`EXPOSE ${port}`);
|
||||
Dockerfile.push(`CMD ${startCommand}`);
|
||||
|
||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||
};
|
||||
|
||||
export default async function (data) {
|
||||
try {
|
||||
const { baseImage, baseBuildImage } = data;
|
||||
await createDockerfile(data, baseImage);
|
||||
await buildImage(data);
|
||||
const { baseImage, baseBuildImage, deploymentType, buildCommand } = data;
|
||||
if (deploymentType === 'node') {
|
||||
await createDockerfile(data, baseImage);
|
||||
await buildImage(data);
|
||||
} else if (deploymentType === 'static') {
|
||||
if (buildCommand) await buildCacheImageWithNode(data, baseBuildImage);
|
||||
await createDockerfile(data, baseImage);
|
||||
await buildImage(data);
|
||||
}
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`);
|
||||
if (baseImage.includes('nginx')) {
|
||||
if (baseImage?.includes('nginx')) {
|
||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||
}
|
||||
Dockerfile.push(`EXPOSE ${port}`);
|
||||
|
||||
@@ -40,7 +40,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
} else {
|
||||
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
||||
}
|
||||
if (baseImage.includes('nginx')) {
|
||||
if (baseImage?.includes('nginx')) {
|
||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||
}
|
||||
Dockerfile.push(`EXPOSE ${port}`);
|
||||
|
||||
@@ -9,7 +9,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`);
|
||||
if (baseImage.includes('nginx')) {
|
||||
if (baseImage?.includes('nginx')) {
|
||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||
}
|
||||
Dockerfile.push(`EXPOSE ${port}`);
|
||||
|
||||
@@ -9,7 +9,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`);
|
||||
if (baseImage.includes('nginx')) {
|
||||
if (baseImage?.includes('nginx')) {
|
||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||
}
|
||||
Dockerfile.push(`EXPOSE ${port}`);
|
||||
|
||||
@@ -15,6 +15,9 @@ import { checkContainer, getEngine, removeContainer } from './docker';
|
||||
import { day } from './dayjs';
|
||||
import * as serviceFields from './serviceFields'
|
||||
|
||||
export const version = '3.1.3';
|
||||
export const isDev = process.env.NODE_ENV === 'development';
|
||||
|
||||
const algorithm = 'aes-256-ctr';
|
||||
const customConfig: Config = {
|
||||
dictionaries: [adjectives, colors, animals],
|
||||
@@ -22,20 +25,34 @@ const customConfig: Config = {
|
||||
separator: ' ',
|
||||
length: 3
|
||||
};
|
||||
export const isDev = process.env.NODE_ENV === 'development';
|
||||
export const version = '3.0.3';
|
||||
|
||||
export const defaultProxyImage = `coolify-haproxy-alpine:latest`;
|
||||
export const defaultProxyImageTcp = `coolify-haproxy-tcp-alpine:latest`;
|
||||
export const defaultProxyImageHttp = `coolify-haproxy-http-alpine:latest`;
|
||||
export const defaultTraefikImage = `traefik:v2.6`;
|
||||
export function getAPIUrl() {
|
||||
if (process.env.GITPOD_WORKSPACE_URL) {
|
||||
const { href } = new URL(process.env.GITPOD_WORKSPACE_URL)
|
||||
const newURL = href.replace('https://', 'https://3001-').replace(/\/$/, '')
|
||||
return newURL
|
||||
}
|
||||
return isDev ? 'http://localhost:3001' : 'http://localhost:3000';
|
||||
}
|
||||
export function getUIUrl() {
|
||||
if (process.env.GITPOD_WORKSPACE_URL) {
|
||||
const { href } = new URL(process.env.GITPOD_WORKSPACE_URL)
|
||||
const newURL = href.replace('https://', 'https://3000-').replace(/\/$/, '')
|
||||
return newURL
|
||||
}
|
||||
return 'http://localhost:3000';
|
||||
}
|
||||
|
||||
const mainTraefikEndpoint = isDev
|
||||
? 'http://host.docker.internal:3001/webhooks/traefik/main.json'
|
||||
? `${getAPIUrl()}/webhooks/traefik/main.json`
|
||||
: 'http://coolify:3000/webhooks/traefik/main.json';
|
||||
|
||||
const otherTraefikEndpoint = isDev
|
||||
? 'http://host.docker.internal:3001/webhooks/traefik/other.json'
|
||||
? `${getAPIUrl()}/webhooks/traefik/other.json`
|
||||
: 'http://coolify:3000/webhooks/traefik/other.json';
|
||||
|
||||
|
||||
@@ -51,7 +68,7 @@ export const include: any = {
|
||||
meiliSearch: true,
|
||||
umami: true,
|
||||
hasura: true,
|
||||
fider: true
|
||||
fider: true,
|
||||
};
|
||||
|
||||
export const uniqueName = (): string => uniqueNamesGenerator(customConfig);
|
||||
@@ -94,6 +111,168 @@ export const encrypt = (text: string) => {
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const supportedServiceTypesAndVersions = [
|
||||
{
|
||||
name: 'plausibleanalytics',
|
||||
fancyName: 'Plausible Analytics',
|
||||
baseImage: 'plausible/analytics',
|
||||
images: ['bitnami/postgresql:13.2.0', 'yandex/clickhouse-server:21.3.2.5'],
|
||||
versions: ['latest', 'stable'],
|
||||
recommendedVersion: 'stable',
|
||||
ports: {
|
||||
main: 8000
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'nocodb',
|
||||
fancyName: 'NocoDB',
|
||||
baseImage: 'nocodb/nocodb',
|
||||
versions: ['latest'],
|
||||
recommendedVersion: 'latest',
|
||||
ports: {
|
||||
main: 8080
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'minio',
|
||||
fancyName: 'MinIO',
|
||||
baseImage: 'minio/minio',
|
||||
versions: ['latest'],
|
||||
recommendedVersion: 'latest',
|
||||
ports: {
|
||||
main: 9001
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'vscodeserver',
|
||||
fancyName: 'VSCode Server',
|
||||
baseImage: 'codercom/code-server',
|
||||
versions: ['latest'],
|
||||
recommendedVersion: 'latest',
|
||||
ports: {
|
||||
main: 8080
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'wordpress',
|
||||
fancyName: 'Wordpress',
|
||||
baseImage: 'wordpress',
|
||||
images: ['bitnami/mysql:5.7'],
|
||||
versions: ['latest', 'php8.1', 'php8.0', 'php7.4', 'php7.3'],
|
||||
recommendedVersion: 'latest',
|
||||
ports: {
|
||||
main: 80
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'vaultwarden',
|
||||
fancyName: 'Vaultwarden',
|
||||
baseImage: 'vaultwarden/server',
|
||||
versions: ['latest'],
|
||||
recommendedVersion: 'latest',
|
||||
ports: {
|
||||
main: 80
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'languagetool',
|
||||
fancyName: 'LanguageTool',
|
||||
baseImage: 'silviof/docker-languagetool',
|
||||
versions: ['latest'],
|
||||
recommendedVersion: 'latest',
|
||||
ports: {
|
||||
main: 8010
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'n8n',
|
||||
fancyName: 'n8n',
|
||||
baseImage: 'n8nio/n8n',
|
||||
versions: ['latest'],
|
||||
recommendedVersion: 'latest',
|
||||
ports: {
|
||||
main: 5678
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'uptimekuma',
|
||||
fancyName: 'Uptime Kuma',
|
||||
baseImage: 'louislam/uptime-kuma',
|
||||
versions: ['latest'],
|
||||
recommendedVersion: 'latest',
|
||||
ports: {
|
||||
main: 3001
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'ghost',
|
||||
fancyName: 'Ghost',
|
||||
baseImage: 'bitnami/ghost',
|
||||
images: ['bitnami/mariadb'],
|
||||
versions: ['latest'],
|
||||
recommendedVersion: 'latest',
|
||||
ports: {
|
||||
main: 2368
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'meilisearch',
|
||||
fancyName: 'Meilisearch',
|
||||
baseImage: 'getmeili/meilisearch',
|
||||
images: [],
|
||||
versions: ['latest'],
|
||||
recommendedVersion: 'latest',
|
||||
ports: {
|
||||
main: 7700
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'umami',
|
||||
fancyName: 'Umami',
|
||||
baseImage: 'ghcr.io/mikecao/umami',
|
||||
images: ['postgres:12-alpine'],
|
||||
versions: ['postgresql-latest'],
|
||||
recommendedVersion: 'postgresql-latest',
|
||||
ports: {
|
||||
main: 3000
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'hasura',
|
||||
fancyName: 'Hasura',
|
||||
baseImage: 'hasura/graphql-engine',
|
||||
images: ['postgres:12-alpine'],
|
||||
versions: ['latest', 'v2.5.1'],
|
||||
recommendedVersion: 'v2.5.1',
|
||||
ports: {
|
||||
main: 8080
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'fider',
|
||||
fancyName: 'Fider',
|
||||
baseImage: 'getfider/fider',
|
||||
images: ['postgres:12-alpine'],
|
||||
versions: ['stable'],
|
||||
recommendedVersion: 'stable',
|
||||
ports: {
|
||||
main: 3000
|
||||
}
|
||||
},
|
||||
// {
|
||||
// name: 'moodle',
|
||||
// fancyName: 'Moodle',
|
||||
// baseImage: 'bitnami/moodle',
|
||||
// images: [],
|
||||
// versions: ['latest', 'v4.0.2'],
|
||||
// recommendedVersion: 'latest',
|
||||
// ports: {
|
||||
// main: 8080
|
||||
// }
|
||||
// }
|
||||
];
|
||||
|
||||
export async function checkDoubleBranch(branch: string, projectId: number): Promise<boolean> {
|
||||
const applications = await prisma.application.findMany({ where: { branch, projectId } });
|
||||
return applications.length > 1;
|
||||
@@ -290,167 +469,6 @@ export const supportedDatabaseTypesAndVersions = [
|
||||
},
|
||||
{ name: 'couchdb', fancyName: 'CouchDB', baseImage: 'bitnami/couchdb', versions: ['3.2.1'] }
|
||||
];
|
||||
export const supportedServiceTypesAndVersions = [
|
||||
{
|
||||
name: 'plausibleanalytics',
|
||||
fancyName: 'Plausible Analytics',
|
||||
baseImage: 'plausible/analytics',
|
||||
images: ['bitnami/postgresql:13.2.0', 'yandex/clickhouse-server:21.3.2.5'],
|
||||
versions: ['latest', 'stable'],
|
||||
recommendedVersion: 'stable',
|
||||
ports: {
|
||||
main: 8000
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'nocodb',
|
||||
fancyName: 'NocoDB',
|
||||
baseImage: 'nocodb/nocodb',
|
||||
versions: ['latest'],
|
||||
recommendedVersion: 'latest',
|
||||
ports: {
|
||||
main: 8080
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'minio',
|
||||
fancyName: 'MinIO',
|
||||
baseImage: 'minio/minio',
|
||||
versions: ['latest'],
|
||||
recommendedVersion: 'latest',
|
||||
ports: {
|
||||
main: 9001
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'vscodeserver',
|
||||
fancyName: 'VSCode Server',
|
||||
baseImage: 'codercom/code-server',
|
||||
versions: ['latest'],
|
||||
recommendedVersion: 'latest',
|
||||
ports: {
|
||||
main: 8080
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'wordpress',
|
||||
fancyName: 'Wordpress',
|
||||
baseImage: 'wordpress',
|
||||
images: ['bitnami/mysql:5.7'],
|
||||
versions: ['latest', 'php8.1', 'php8.0', 'php7.4', 'php7.3'],
|
||||
recommendedVersion: 'latest',
|
||||
ports: {
|
||||
main: 80
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'vaultwarden',
|
||||
fancyName: 'Vaultwarden',
|
||||
baseImage: 'vaultwarden/server',
|
||||
versions: ['latest'],
|
||||
recommendedVersion: 'latest',
|
||||
ports: {
|
||||
main: 80
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'languagetool',
|
||||
fancyName: 'LanguageTool',
|
||||
baseImage: 'silviof/docker-languagetool',
|
||||
versions: ['latest'],
|
||||
recommendedVersion: 'latest',
|
||||
ports: {
|
||||
main: 8010
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'n8n',
|
||||
fancyName: 'n8n',
|
||||
baseImage: 'n8nio/n8n',
|
||||
versions: ['latest'],
|
||||
recommendedVersion: 'latest',
|
||||
ports: {
|
||||
main: 5678
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'uptimekuma',
|
||||
fancyName: 'Uptime Kuma',
|
||||
baseImage: 'louislam/uptime-kuma',
|
||||
versions: ['latest'],
|
||||
recommendedVersion: 'latest',
|
||||
ports: {
|
||||
main: 3001
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'ghost',
|
||||
fancyName: 'Ghost',
|
||||
baseImage: 'bitnami/ghost',
|
||||
images: ['bitnami/mariadb'],
|
||||
versions: ['latest'],
|
||||
recommendedVersion: 'latest',
|
||||
ports: {
|
||||
main: 2368
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'meilisearch',
|
||||
fancyName: 'Meilisearch',
|
||||
baseImage: 'getmeili/meilisearch',
|
||||
images: [],
|
||||
versions: ['latest'],
|
||||
recommendedVersion: 'latest',
|
||||
ports: {
|
||||
main: 7700
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'umami',
|
||||
fancyName: 'Umami',
|
||||
baseImage: 'ghcr.io/mikecao/umami',
|
||||
images: ['postgres:12-alpine'],
|
||||
versions: ['postgresql-latest'],
|
||||
recommendedVersion: 'postgresql-latest',
|
||||
ports: {
|
||||
main: 3000
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'hasura',
|
||||
fancyName: 'Hasura',
|
||||
baseImage: 'hasura/graphql-engine',
|
||||
images: ['postgres:12-alpine'],
|
||||
versions: ['latest', 'v2.8.3'],
|
||||
recommendedVersion: 'v2.8.3',
|
||||
ports: {
|
||||
main: 8080
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'fider',
|
||||
fancyName: 'Fider',
|
||||
baseImage: 'getfider/fider',
|
||||
images: ['postgres:12-alpine'],
|
||||
versions: ['stable'],
|
||||
recommendedVersion: 'stable',
|
||||
ports: {
|
||||
main: 3000
|
||||
}
|
||||
// },
|
||||
// {
|
||||
// name: 'appwrite',
|
||||
// fancyName: 'AppWrite',
|
||||
// baseImage: 'appwrite/appwrite',
|
||||
// images: ['appwrite/influxdb', 'appwrite/telegraf', 'mariadb:10.7', 'redis:6.0-alpine3.12'],
|
||||
// versions: ['latest', '0.13.0'],
|
||||
// recommendedVersion: '0.13.0',
|
||||
// ports: {
|
||||
// main: 3000
|
||||
// }
|
||||
// }
|
||||
}
|
||||
];
|
||||
|
||||
export async function startTraefikProxy(engine: string): Promise<void> {
|
||||
const host = getEngine(engine);
|
||||
@@ -807,7 +825,7 @@ export type ComposeFileService = {
|
||||
context: string;
|
||||
dockerfile: string;
|
||||
args?: Record<string, unknown>;
|
||||
};
|
||||
} | string;
|
||||
deploy?: {
|
||||
restart_policy?: {
|
||||
condition?: string;
|
||||
@@ -1329,6 +1347,40 @@ export async function configureServiceType({
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (type === 'moodle') {
|
||||
const defaultUsername = cuid();
|
||||
const defaultPassword = encrypt(generatePassword());
|
||||
const defaultEmail = `${cuid()}@example.com`;
|
||||
const mariadbUser = cuid();
|
||||
const mariadbPassword = encrypt(generatePassword());
|
||||
const mariadbDatabase = 'moodle_db';
|
||||
const mariadbRootUser = cuid();
|
||||
const mariadbRootUserPassword = encrypt(generatePassword());
|
||||
await prisma.service.update({
|
||||
where: { id },
|
||||
data: {
|
||||
type,
|
||||
moodle: {
|
||||
create: {
|
||||
defaultUsername,
|
||||
defaultPassword,
|
||||
defaultEmail,
|
||||
mariadbUser,
|
||||
mariadbPassword,
|
||||
mariadbDatabase,
|
||||
mariadbRootUser,
|
||||
mariadbRootUserPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await prisma.service.update({
|
||||
where: { id },
|
||||
data: {
|
||||
type
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1477,4 +1529,49 @@ async function cleanupDB(buildId: string) {
|
||||
if (data?.status === 'queued' || data?.status === 'running') {
|
||||
await prisma.build.update({ where: { id: buildId }, data: { status: 'failed' } });
|
||||
}
|
||||
}
|
||||
|
||||
export function convertTolOldVolumeNames(type) {
|
||||
if (type === 'nocodb') {
|
||||
return 'nc'
|
||||
}
|
||||
}
|
||||
// export async function getAvailableServices(): Promise<any> {
|
||||
// const { data } = await axios.get(`https://gist.githubusercontent.com/andrasbacsai/4aac36d8d6214dbfc34fa78110554a50/raw/5b27e6c37d78aaeedc1148d797112c827a2f43cf/availableServices.json`)
|
||||
// return data
|
||||
// }
|
||||
export async function cleanupDockerStorage(host, lowDiskSpace, force) {
|
||||
// Cleanup old coolify images
|
||||
try {
|
||||
let { stdout: images } = await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker images coollabsio/coolify --filter before="coollabsio/coolify:${version}" -q | xargs `
|
||||
);
|
||||
images = images.trim();
|
||||
if (images) {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker rmi -f ${images}`);
|
||||
}
|
||||
} catch (error) {
|
||||
//console.log(error);
|
||||
}
|
||||
if (lowDiskSpace || force) {
|
||||
if (isDev) {
|
||||
if (!force) console.log(`[DEV MODE] Low disk space: ${lowDiskSpace}`);
|
||||
return
|
||||
}
|
||||
try {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker container prune -f`);
|
||||
} catch (error) {
|
||||
//console.log(error);
|
||||
}
|
||||
try {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker image prune -f`);
|
||||
} catch (error) {
|
||||
//console.log(error);
|
||||
}
|
||||
try {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker image prune -a -f`);
|
||||
} catch (error) {
|
||||
//console.log(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,8 @@ export default async function ({
|
||||
apiUrl,
|
||||
htmlUrl,
|
||||
branch,
|
||||
buildId
|
||||
buildId,
|
||||
customPort
|
||||
}: {
|
||||
applicationId: string;
|
||||
workdir: string;
|
||||
@@ -21,6 +22,7 @@ export default async function ({
|
||||
htmlUrl: string;
|
||||
branch: string;
|
||||
buildId: string;
|
||||
customPort: number;
|
||||
}): Promise<string> {
|
||||
const { default: got } = await import('got')
|
||||
const url = htmlUrl.replace('https://', '').replace('http://', '');
|
||||
@@ -54,7 +56,7 @@ export default async function ({
|
||||
applicationId
|
||||
});
|
||||
await asyncExecShell(
|
||||
`git clone -q -b ${branch} https://x-access-token:${token}@${url}/${repository}.git ${workdir}/ && cd ${workdir} && git submodule update --init --recursive && git lfs pull && cd .. `
|
||||
`git clone -q -b ${branch} https://x-access-token:${token}@${url}/${repository}.git --config core.sshCommand="ssh -p ${customPort}" ${workdir}/ && cd ${workdir} && git submodule update --init --recursive && git lfs pull && cd .. `
|
||||
);
|
||||
const { stdout: commit } = await asyncExecShell(`cd ${workdir}/ && git rev-parse HEAD`);
|
||||
return commit.replace('\n', '');
|
||||
|
||||
@@ -9,7 +9,8 @@ export default async function ({
|
||||
repository,
|
||||
branch,
|
||||
buildId,
|
||||
privateSshKey
|
||||
privateSshKey,
|
||||
customPort
|
||||
}: {
|
||||
applicationId: string;
|
||||
workdir: string;
|
||||
@@ -19,6 +20,7 @@ export default async function ({
|
||||
buildId: string;
|
||||
repodir: string;
|
||||
privateSshKey: string;
|
||||
customPort: number;
|
||||
}): Promise<string> {
|
||||
const url = htmlUrl.replace('https://', '').replace('http://', '').replace(/\/$/, '');
|
||||
await saveBuildLog({ line: 'GitLab importer started.', buildId, applicationId });
|
||||
@@ -32,7 +34,7 @@ export default async function ({
|
||||
});
|
||||
|
||||
await asyncExecShell(
|
||||
`git clone -q -b ${branch} git@${url}:${repository}.git --config core.sshCommand="ssh -q -i ${repodir}id.rsa -o StrictHostKeyChecking=no" ${workdir}/ && cd ${workdir}/ && git submodule update --init --recursive && git lfs pull && cd .. `
|
||||
`git clone -q -b ${branch} git@${url}:${repository}.git --config core.sshCommand="ssh -p ${customPort} -q -i ${repodir}id.rsa -o StrictHostKeyChecking=no" ${workdir}/ && cd ${workdir}/ && git submodule update --init --recursive && git lfs pull && cd .. `
|
||||
);
|
||||
const { stdout: commit } = await asyncExecShell(`cd ${workdir}/ && git rev-parse HEAD`);
|
||||
return commit.replace('\n', '');
|
||||
|
||||
@@ -11,11 +11,20 @@ const options: any = {
|
||||
logger: false,
|
||||
workerMessageHandler: async ({ name, message }) => {
|
||||
if (name === 'deployApplication') {
|
||||
if (message.pending === 0) {
|
||||
if (!scheduler.workers.has('autoUpdater')) {
|
||||
await scheduler.stop('deployApplication');
|
||||
await scheduler.run('autoUpdater')
|
||||
if (message.pending === 0 && message.size === 0) {
|
||||
if (message.caller === 'autoUpdater') {
|
||||
if (!scheduler.workers.has('autoUpdater')) {
|
||||
await scheduler.stop('deployApplication');
|
||||
await scheduler.run('autoUpdater')
|
||||
}
|
||||
}
|
||||
if (message.caller === 'cleanupStorage') {
|
||||
if (!scheduler.workers.has('cleanupStorage')) {
|
||||
await scheduler.stop('deployApplication');
|
||||
await scheduler.run('cleanupStorage')
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -25,7 +34,6 @@ const options: any = {
|
||||
},
|
||||
{
|
||||
name: 'cleanupStorage',
|
||||
interval: '10m'
|
||||
},
|
||||
{
|
||||
name: 'checkProxies',
|
||||
|
||||
@@ -15,7 +15,7 @@ export const plausibleAnalytics = [{
|
||||
isNumber: false,
|
||||
isBoolean: false,
|
||||
isEncrypted: false
|
||||
},{
|
||||
}, {
|
||||
name: 'username',
|
||||
isEditable: true,
|
||||
isLowerCase: false,
|
||||
@@ -86,7 +86,7 @@ export const minio = [{
|
||||
isNumber: false,
|
||||
isBoolean: false,
|
||||
isEncrypted: false
|
||||
},{
|
||||
}, {
|
||||
name: 'rootUser',
|
||||
isEditable: false,
|
||||
isLowerCase: false,
|
||||
@@ -173,6 +173,14 @@ export const wordpress = [{
|
||||
isNumber: false,
|
||||
isBoolean: false,
|
||||
isEncrypted: false
|
||||
},
|
||||
{
|
||||
name: 'ftpPassword',
|
||||
isEditable: false,
|
||||
isLowerCase: false,
|
||||
isNumber: false,
|
||||
isBoolean: false,
|
||||
isEncrypted: true
|
||||
}]
|
||||
export const ghost = [{
|
||||
name: 'defaultEmail',
|
||||
@@ -317,7 +325,7 @@ export const fider = [{
|
||||
isNumber: false,
|
||||
isBoolean: false,
|
||||
isEncrypted: true
|
||||
},{
|
||||
}, {
|
||||
name: 'postgreslUser',
|
||||
isEditable: false,
|
||||
isLowerCase: false,
|
||||
@@ -404,4 +412,68 @@ export const fider = [{
|
||||
isNumber: false,
|
||||
isBoolean: false,
|
||||
isEncrypted: false
|
||||
}]
|
||||
export const moodle = [{
|
||||
name: 'defaultEmail',
|
||||
isEditable: true,
|
||||
isLowerCase: true,
|
||||
isNumber: false,
|
||||
isBoolean: false,
|
||||
isEncrypted: false
|
||||
},
|
||||
{
|
||||
name: 'defaultUsername',
|
||||
isEditable: true,
|
||||
isLowerCase: false,
|
||||
isNumber: false,
|
||||
isBoolean: false,
|
||||
isEncrypted: false
|
||||
},
|
||||
{
|
||||
name: 'defaultPassword',
|
||||
isEditable: false,
|
||||
isLowerCase: false,
|
||||
isNumber: false,
|
||||
isBoolean: false,
|
||||
isEncrypted: true
|
||||
},
|
||||
{
|
||||
name: 'mariadbUser',
|
||||
isEditable: false,
|
||||
isLowerCase: false,
|
||||
isNumber: false,
|
||||
isBoolean: false,
|
||||
isEncrypted: false
|
||||
},
|
||||
{
|
||||
name: 'mariadbPassword',
|
||||
isEditable: false,
|
||||
isLowerCase: false,
|
||||
isNumber: false,
|
||||
isBoolean: false,
|
||||
isEncrypted: true
|
||||
},
|
||||
{
|
||||
name: 'mariadbRootUser',
|
||||
isEditable: false,
|
||||
isLowerCase: false,
|
||||
isNumber: false,
|
||||
isBoolean: false,
|
||||
isEncrypted: false
|
||||
},
|
||||
{
|
||||
name: 'mariadbRootUserPassword',
|
||||
isEditable: false,
|
||||
isLowerCase: false,
|
||||
isNumber: false,
|
||||
isBoolean: false,
|
||||
isEncrypted: true
|
||||
},
|
||||
{
|
||||
name: 'mariadbDatabase',
|
||||
isEditable: true,
|
||||
isLowerCase: false,
|
||||
isNumber: false,
|
||||
isBoolean: false,
|
||||
isEncrypted: false
|
||||
}]
|
||||
@@ -2,18 +2,16 @@ import cuid from 'cuid';
|
||||
import crypto from 'node:crypto'
|
||||
import jsonwebtoken from 'jsonwebtoken';
|
||||
import axios from 'axios';
|
||||
import { day } from '../../../../lib/dayjs';
|
||||
|
||||
|
||||
import type { FastifyRequest } from 'fastify';
|
||||
import { FastifyReply } from 'fastify';
|
||||
|
||||
import { CheckDNS, DeleteApplication, DeployApplication, GetApplication, SaveApplication, SaveApplicationSettings } from '.';
|
||||
import { day } from '../../../../lib/dayjs';
|
||||
import { setDefaultBaseImage, setDefaultConfiguration } from '../../../../lib/buildPacks/common';
|
||||
import { asyncExecShell, checkDomainsIsValidInDNS, checkDoubleBranch, decrypt, encrypt, errorHandler, generateSshKeyPair, getContainerUsage, getDomain, isDev, isDomainConfigured, prisma, stopBuild, uniqueName } from '../../../../lib/common';
|
||||
import { checkContainer, dockerInstance, getEngine, isContainerExited, removeContainer } from '../../../../lib/docker';
|
||||
import { scheduler } from '../../../../lib/scheduler';
|
||||
|
||||
import type { FastifyRequest } from 'fastify';
|
||||
import type { GetImages, CancelDeployment, CheckDNS, CheckRepository, DeleteApplication, DeleteSecret, DeleteStorage, GetApplicationLogs, GetBuildIdLogs, GetBuildLogs, SaveApplication, SaveApplicationSettings, SaveApplicationSource, SaveDeployKey, SaveDestination, SaveSecret, SaveStorage, DeployApplication } from './types';
|
||||
import { OnlyId } from '../../../../types';
|
||||
|
||||
export async function listApplications(request: FastifyRequest) {
|
||||
try {
|
||||
@@ -31,14 +29,48 @@ export async function listApplications(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function getApplication(request: FastifyRequest<GetApplication>) {
|
||||
export async function getImages(request: FastifyRequest<GetImages>) {
|
||||
try {
|
||||
const { buildPack, deploymentType } = request.body
|
||||
let publishDirectory = undefined;
|
||||
let port = undefined
|
||||
const { baseImage, baseBuildImage, baseBuildImages, baseImages, } = setDefaultBaseImage(
|
||||
buildPack, deploymentType
|
||||
);
|
||||
if (buildPack === 'nextjs') {
|
||||
if (deploymentType === 'static') {
|
||||
publishDirectory = 'out'
|
||||
port = '80'
|
||||
} else {
|
||||
publishDirectory = ''
|
||||
port = '3000'
|
||||
}
|
||||
}
|
||||
if (buildPack === 'nuxtjs') {
|
||||
if (deploymentType === 'static') {
|
||||
publishDirectory = 'dist'
|
||||
port = '80'
|
||||
} else {
|
||||
publishDirectory = ''
|
||||
port = '3000'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return { baseImage, baseBuildImage, baseBuildImages, baseImages, publishDirectory, port }
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
|
||||
export async function getApplication(request: FastifyRequest<OnlyId>) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const { teamId } = request.user
|
||||
const appId = process.env['COOLIFY_APP_ID'];
|
||||
let isRunning = false;
|
||||
let isExited = false;
|
||||
const application = await getApplicationFromDB(id, teamId);
|
||||
const application: any = await getApplicationFromDB(id, teamId);
|
||||
if (application?.destinationDockerId && application.destinationDocker?.engine) {
|
||||
isRunning = await checkContainer(application.destinationDocker.engine, id);
|
||||
isExited = await isContainerExited(application.destinationDocker.engine, id);
|
||||
@@ -184,7 +216,8 @@ export async function saveApplication(request: FastifyRequest<SaveApplication>,
|
||||
denoMainFile,
|
||||
denoOptions,
|
||||
baseImage,
|
||||
baseBuildImage
|
||||
baseBuildImage,
|
||||
deploymentType
|
||||
} = request.body
|
||||
|
||||
if (port) port = Number(port);
|
||||
@@ -215,6 +248,7 @@ export async function saveApplication(request: FastifyRequest<SaveApplication>,
|
||||
denoOptions,
|
||||
baseImage,
|
||||
baseBuildImage,
|
||||
deploymentType,
|
||||
...defaultConfiguration
|
||||
}
|
||||
});
|
||||
@@ -231,7 +265,8 @@ export async function saveApplicationSettings(request: FastifyRequest<SaveApplic
|
||||
const { debug, previews, dualCerts, autodeploy, branch, projectId } = request.body
|
||||
const isDouble = await checkDoubleBranch(branch, projectId);
|
||||
if (isDouble && autodeploy) {
|
||||
throw { status: 500, message: 'Application not configured.' }
|
||||
await prisma.applicationSettings.updateMany({ where: { application: { branch, projectId } }, data: { autodeploy: false } })
|
||||
throw { status: 500, message: 'Cannot activate automatic deployments until only one application is defined for this repository / branch.' }
|
||||
}
|
||||
await prisma.application.update({
|
||||
where: { id },
|
||||
@@ -244,11 +279,11 @@ export async function saveApplicationSettings(request: FastifyRequest<SaveApplic
|
||||
}
|
||||
}
|
||||
|
||||
export async function stopApplication(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function stopApplication(request: FastifyRequest<OnlyId>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const { teamId } = request.user
|
||||
const application = await getApplicationFromDB(id, teamId);
|
||||
const application: any = await getApplicationFromDB(id, teamId);
|
||||
if (application?.destinationDockerId && application.destinationDocker?.engine) {
|
||||
const { engine } = application.destinationDocker;
|
||||
const found = await checkContainer(engine, id);
|
||||
@@ -336,8 +371,9 @@ export async function getUsage(request) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const teamId = request.user?.teamId;
|
||||
const application = await getApplicationFromDB(id, teamId);
|
||||
let usage = {};
|
||||
|
||||
const application: any = await getApplicationFromDB(id, teamId);
|
||||
if (application.destinationDockerId) {
|
||||
[usage] = await Promise.all([getContainerUsage(application.destinationDocker.engine, id)]);
|
||||
}
|
||||
@@ -352,7 +388,6 @@ export async function deployApplication(request: FastifyRequest<DeployApplicatio
|
||||
try {
|
||||
const { id } = request.params
|
||||
const teamId = request.user?.teamId;
|
||||
|
||||
const { pullmergeRequestId = null, branch } = request.body
|
||||
const buildId = cuid();
|
||||
const application = await getApplicationFromDB(id, teamId);
|
||||
@@ -413,7 +448,7 @@ export async function deployApplication(request: FastifyRequest<DeployApplicatio
|
||||
}
|
||||
|
||||
|
||||
export async function saveApplicationSource(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function saveApplicationSource(request: FastifyRequest<SaveApplicationSource>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const { gitSourceId } = request.body
|
||||
@@ -427,11 +462,11 @@ export async function saveApplicationSource(request: FastifyRequest, reply: Fast
|
||||
}
|
||||
}
|
||||
|
||||
export async function getGitHubToken(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function getGitHubToken(request: FastifyRequest<OnlyId>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const { teamId } = request.user
|
||||
const application = await getApplicationFromDB(id, teamId);
|
||||
const application: any = await getApplicationFromDB(id, teamId);
|
||||
const payload = {
|
||||
iat: Math.round(new Date().getTime() / 1000),
|
||||
exp: Math.round(new Date().getTime() / 1000 + 60),
|
||||
@@ -453,7 +488,7 @@ export async function getGitHubToken(request: FastifyRequest, reply: FastifyRepl
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkRepository(request: FastifyRequest) {
|
||||
export async function checkRepository(request: FastifyRequest<CheckRepository>) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const { repository, branch } = request.query
|
||||
@@ -490,13 +525,17 @@ export async function saveRepository(request, reply) {
|
||||
data: { repository, branch, projectId, settings: { update: { autodeploy } } }
|
||||
});
|
||||
}
|
||||
const isDouble = await checkDoubleBranch(branch, projectId);
|
||||
if (isDouble) {
|
||||
await prisma.applicationSettings.updateMany({ where: { application: { branch, projectId } }, data: { autodeploy: false } })
|
||||
}
|
||||
return reply.code(201).send()
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveDestination(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function saveDestination(request: FastifyRequest<SaveDestination>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const { destinationId } = request.body
|
||||
@@ -514,7 +553,7 @@ export async function getBuildPack(request) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const teamId = request.user?.teamId;
|
||||
const application = await getApplicationFromDB(id, teamId);
|
||||
const application: any = await getApplicationFromDB(id, teamId);
|
||||
return {
|
||||
type: application.gitSource.type,
|
||||
projectId: application.projectId,
|
||||
@@ -538,7 +577,7 @@ export async function saveBuildPack(request, reply) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function getSecrets(request: FastifyRequest) {
|
||||
export async function getSecrets(request: FastifyRequest<OnlyId>) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
let secrets = await prisma.secret.findMany({
|
||||
@@ -560,7 +599,7 @@ export async function getSecrets(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveSecret(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function saveSecret(request: FastifyRequest<SaveSecret>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
let { name, value, isBuildSecret, isPRMRSecret, isNew } = request.body
|
||||
@@ -595,7 +634,7 @@ export async function saveSecret(request: FastifyRequest, reply: FastifyReply) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function deleteSecret(request: FastifyRequest) {
|
||||
export async function deleteSecret(request: FastifyRequest<DeleteSecret>) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const { name } = request.body
|
||||
@@ -606,7 +645,7 @@ export async function deleteSecret(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function getStorages(request: FastifyRequest) {
|
||||
export async function getStorages(request: FastifyRequest<OnlyId>) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const persistentStorages = await prisma.applicationPersistentStorage.findMany({ where: { applicationId: id } });
|
||||
@@ -618,7 +657,7 @@ export async function getStorages(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveStorage(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function saveStorage(request: FastifyRequest<SaveStorage>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const { path, newStorage, storageId } = request.body
|
||||
@@ -639,7 +678,7 @@ export async function saveStorage(request: FastifyRequest, reply: FastifyReply)
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteStorage(request: FastifyRequest) {
|
||||
export async function deleteStorage(request: FastifyRequest<DeleteStorage>) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const { path } = request.body
|
||||
@@ -650,7 +689,7 @@ export async function deleteStorage(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function getPreviews(request: FastifyRequest) {
|
||||
export async function getPreviews(request: FastifyRequest<OnlyId>) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const { teamId } = request.user
|
||||
@@ -698,7 +737,7 @@ export async function getPreviews(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function getApplicationLogs(request: FastifyRequest) {
|
||||
export async function getApplicationLogs(request: FastifyRequest<GetApplicationLogs>) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
let { since = 0 } = request.query
|
||||
@@ -742,7 +781,7 @@ export async function getApplicationLogs(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function getBuildLogs(request: FastifyRequest) {
|
||||
export async function getBuildLogs(request: FastifyRequest<GetBuildLogs>) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
let { buildId, skip = 0 } = request.query
|
||||
@@ -779,9 +818,9 @@ export async function getBuildLogs(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function getBuildIdLogs(request: FastifyRequest) {
|
||||
export async function getBuildIdLogs(request: FastifyRequest<GetBuildIdLogs>) {
|
||||
try {
|
||||
const { id, buildId } = request.params
|
||||
const { buildId } = request.params
|
||||
let { sequence = 0 } = request.query
|
||||
if (typeof sequence !== 'number') {
|
||||
sequence = Number(sequence)
|
||||
@@ -800,7 +839,7 @@ export async function getBuildIdLogs(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function getGitLabSSHKey(request: FastifyRequest) {
|
||||
export async function getGitLabSSHKey(request: FastifyRequest<OnlyId>) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const application = await prisma.application.findUnique({
|
||||
@@ -813,7 +852,7 @@ export async function getGitLabSSHKey(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveGitLabSSHKey(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function saveGitLabSSHKey(request: FastifyRequest<OnlyId>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const application = await prisma.application.findUnique({
|
||||
@@ -835,7 +874,7 @@ export async function saveGitLabSSHKey(request: FastifyRequest, reply: FastifyRe
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveDeployKey(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function saveDeployKey(request: FastifyRequest<SaveDeployKey>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
let { deployKeyId } = request.body;
|
||||
@@ -855,9 +894,8 @@ export async function saveDeployKey(request: FastifyRequest, reply: FastifyReply
|
||||
}
|
||||
}
|
||||
|
||||
export async function cancelDeployment(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function cancelDeployment(request: FastifyRequest<CancelDeployment>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const { buildId, applicationId } = request.body;
|
||||
if (!buildId) {
|
||||
throw { status: 500, message: 'buildId is required' }
|
||||
|
||||
@@ -1,89 +1,60 @@
|
||||
import { FastifyPluginAsync } from 'fastify';
|
||||
import { cancelDeployment, checkDNS, checkRepository, deleteApplication, deleteSecret, deleteStorage, deployApplication, getApplication, getApplicationLogs, getBuildIdLogs, getBuildLogs, getBuildPack, getGitHubToken, getGitLabSSHKey, getPreviews, getSecrets, getStorages, getUsage, listApplications, newApplication, saveApplication, saveApplicationSettings, saveApplicationSource, saveBuildPack, saveDeployKey, saveDestination, saveGitLabSSHKey, saveRepository, saveSecret, saveStorage, stopApplication } from './handlers';
|
||||
import { OnlyId } from '../../../../types';
|
||||
import { cancelDeployment, checkDNS, checkRepository, deleteApplication, deleteSecret, deleteStorage, deployApplication, getApplication, getApplicationLogs, getBuildIdLogs, getBuildLogs, getBuildPack, getGitHubToken, getGitLabSSHKey, getImages, getPreviews, getSecrets, getStorages, getUsage, listApplications, newApplication, saveApplication, saveApplicationSettings, saveApplicationSource, saveBuildPack, saveDeployKey, saveDestination, saveGitLabSSHKey, saveRepository, saveSecret, saveStorage, stopApplication } from './handlers';
|
||||
|
||||
export interface GetApplication {
|
||||
Params: { id: string; }
|
||||
}
|
||||
import type { CancelDeployment, CheckDNS, CheckRepository, DeleteApplication, DeleteSecret, DeleteStorage, DeployApplication, GetApplicationLogs, GetBuildIdLogs, GetBuildLogs, GetImages, SaveApplication, SaveApplicationSettings, SaveApplicationSource, SaveDeployKey, SaveDestination, SaveSecret, SaveStorage } from './types';
|
||||
|
||||
export interface SaveApplication {
|
||||
Params: { id: string; },
|
||||
Body: any
|
||||
}
|
||||
|
||||
export interface SaveApplicationSettings {
|
||||
Params: { id: string; };
|
||||
Querystring: { domain: string; };
|
||||
Body: { debug: boolean; previews: boolean; dualCerts: boolean; autodeploy: boolean; branch: string; projectId: number; };
|
||||
}
|
||||
|
||||
export interface DeleteApplication {
|
||||
Params: { id: string; };
|
||||
Querystring: { domain: string; };
|
||||
}
|
||||
|
||||
export interface CheckDNS {
|
||||
Params: { id: string; };
|
||||
Querystring: { domain: string; };
|
||||
}
|
||||
|
||||
export interface DeployApplication {
|
||||
Params: { id: string },
|
||||
Querystring: { domain: string }
|
||||
Body: { pullmergeRequestId: string | null, branch: string }
|
||||
}
|
||||
|
||||
const root: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
|
||||
fastify.addHook('onRequest', async (request, reply) => {
|
||||
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||
fastify.addHook('onRequest', async (request) => {
|
||||
return await request.jwtVerify()
|
||||
})
|
||||
fastify.get('/', async (request) => await listApplications(request));
|
||||
fastify.post<GetImages>('/images', async (request) => await getImages(request));
|
||||
|
||||
fastify.post('/new', async (request, reply) => await newApplication(request, reply));
|
||||
|
||||
fastify.get<GetApplication>('/:id', async (request) => await getApplication(request));
|
||||
fastify.get<OnlyId>('/:id', async (request) => await getApplication(request));
|
||||
fastify.post<SaveApplication>('/:id', async (request, reply) => await saveApplication(request, reply));
|
||||
fastify.delete<DeleteApplication>('/:id', async (request, reply) => await deleteApplication(request, reply));
|
||||
|
||||
fastify.post('/:id/stop', async (request, reply) => await stopApplication(request, reply));
|
||||
fastify.post<OnlyId>('/:id/stop', async (request, reply) => await stopApplication(request, reply));
|
||||
|
||||
fastify.post<SaveApplicationSettings>('/:id/settings', async (request, reply) => await saveApplicationSettings(request, reply));
|
||||
fastify.post<SaveApplicationSettings>('/:id/check', async (request) => await checkDNS(request));
|
||||
fastify.post<CheckDNS>('/:id/check', async (request) => await checkDNS(request));
|
||||
|
||||
fastify.get('/:id/secrets', async (request) => await getSecrets(request));
|
||||
fastify.post('/:id/secrets', async (request, reply) => await saveSecret(request, reply));
|
||||
fastify.delete('/:id/secrets', async (request) => await deleteSecret(request));
|
||||
fastify.get<OnlyId>('/:id/secrets', async (request) => await getSecrets(request));
|
||||
fastify.post<SaveSecret>('/:id/secrets', async (request, reply) => await saveSecret(request, reply));
|
||||
fastify.delete<DeleteSecret>('/:id/secrets', async (request) => await deleteSecret(request));
|
||||
|
||||
fastify.get('/:id/storages', async (request) => await getStorages(request));
|
||||
fastify.post('/:id/storages', async (request, reply) => await saveStorage(request, reply));
|
||||
fastify.delete('/:id/storages', async (request) => await deleteStorage(request));
|
||||
fastify.get<OnlyId>('/:id/storages', async (request) => await getStorages(request));
|
||||
fastify.post<SaveStorage>('/:id/storages', async (request, reply) => await saveStorage(request, reply));
|
||||
fastify.delete<DeleteStorage>('/:id/storages', async (request) => await deleteStorage(request));
|
||||
|
||||
fastify.get('/:id/previews', async (request) => await getPreviews(request));
|
||||
fastify.get<OnlyId>('/:id/previews', async (request) => await getPreviews(request));
|
||||
|
||||
fastify.get('/:id/logs', async (request) => await getApplicationLogs(request));
|
||||
fastify.get('/:id/logs/build', async (request) => await getBuildLogs(request));
|
||||
fastify.get('/:id/logs/build/:buildId', async (request) => await getBuildIdLogs(request));
|
||||
fastify.get<GetApplicationLogs>('/:id/logs', async (request) => await getApplicationLogs(request));
|
||||
fastify.get<GetBuildLogs>('/:id/logs/build', async (request) => await getBuildLogs(request));
|
||||
fastify.get<GetBuildIdLogs>('/:id/logs/build/:buildId', async (request) => await getBuildIdLogs(request));
|
||||
|
||||
fastify.get<DeployApplication>('/:id/usage', async (request) => await getUsage(request))
|
||||
fastify.get('/:id/usage', async (request) => await getUsage(request))
|
||||
|
||||
fastify.post<DeployApplication>('/:id/deploy', async (request) => await deployApplication(request))
|
||||
fastify.post('/:id/cancel', async (request, reply) => await cancelDeployment(request, reply));
|
||||
|
||||
fastify.post('/:id/configuration/source', async (request, reply) => await saveApplicationSource(request, reply));
|
||||
fastify.post<CancelDeployment>('/:id/cancel', async (request, reply) => await cancelDeployment(request, reply));
|
||||
|
||||
fastify.get('/:id/configuration/repository', async (request) => await checkRepository(request));
|
||||
fastify.post<SaveApplicationSource>('/:id/configuration/source', async (request, reply) => await saveApplicationSource(request, reply));
|
||||
|
||||
fastify.get<CheckRepository>('/:id/configuration/repository', async (request) => await checkRepository(request));
|
||||
fastify.post('/:id/configuration/repository', async (request, reply) => await saveRepository(request, reply));
|
||||
fastify.post('/:id/configuration/destination', async (request, reply) => await saveDestination(request, reply));
|
||||
fastify.post<SaveDestination>('/:id/configuration/destination', async (request, reply) => await saveDestination(request, reply));
|
||||
fastify.get('/:id/configuration/buildpack', async (request) => await getBuildPack(request));
|
||||
fastify.post('/:id/configuration/buildpack', async (request, reply) => await saveBuildPack(request, reply));
|
||||
|
||||
fastify.get('/:id/configuration/sshkey', async (request) => await getGitLabSSHKey(request));
|
||||
fastify.post('/:id/configuration/sshkey', async (request, reply) => await saveGitLabSSHKey(request, reply));
|
||||
fastify.get<OnlyId>('/:id/configuration/sshkey', async (request) => await getGitLabSSHKey(request));
|
||||
fastify.post<OnlyId>('/:id/configuration/sshkey', async (request, reply) => await saveGitLabSSHKey(request, reply));
|
||||
|
||||
fastify.post('/:id/configuration/deploykey', async (request, reply) => await saveDeployKey(request, reply));
|
||||
fastify.post<SaveDeployKey>('/:id/configuration/deploykey', async (request, reply) => await saveDeployKey(request, reply));
|
||||
|
||||
|
||||
|
||||
fastify.get('/:id/configuration/githubToken', async (request, reply) => await getGitHubToken(request, reply));
|
||||
fastify.get<OnlyId>('/:id/configuration/githubToken', async (request, reply) => await getGitHubToken(request, reply));
|
||||
};
|
||||
|
||||
export default root;
|
||||
|
||||
117
apps/api/src/routes/api/v1/applications/types.ts
Normal file
117
apps/api/src/routes/api/v1/applications/types.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import type { OnlyId } from "../../../../types";
|
||||
|
||||
export interface SaveApplication extends OnlyId {
|
||||
Body: {
|
||||
name: string,
|
||||
buildPack: string,
|
||||
fqdn: string,
|
||||
port: number,
|
||||
exposePort: number,
|
||||
installCommand: string,
|
||||
buildCommand: string,
|
||||
startCommand: string,
|
||||
baseDirectory: string,
|
||||
publishDirectory: string,
|
||||
pythonWSGI: string,
|
||||
pythonModule: string,
|
||||
pythonVariable: string,
|
||||
dockerFileLocation: string,
|
||||
denoMainFile: string,
|
||||
denoOptions: string,
|
||||
baseImage: string,
|
||||
baseBuildImage: string,
|
||||
deploymentType: string
|
||||
}
|
||||
}
|
||||
export interface SaveApplicationSettings extends OnlyId {
|
||||
Querystring: { domain: string; };
|
||||
Body: { debug: boolean; previews: boolean; dualCerts: boolean; autodeploy: boolean; branch: string; projectId: number; };
|
||||
}
|
||||
export interface DeleteApplication extends OnlyId {
|
||||
Querystring: { domain: string; };
|
||||
}
|
||||
export interface CheckDNS extends OnlyId {
|
||||
Querystring: { domain: string; };
|
||||
Body: {
|
||||
exposePort: number,
|
||||
fqdn: string,
|
||||
forceSave: boolean,
|
||||
dualCerts: boolean
|
||||
}
|
||||
}
|
||||
export interface DeployApplication {
|
||||
Querystring: { domain: string }
|
||||
Body: { pullmergeRequestId: string | null, branch: string }
|
||||
}
|
||||
export interface GetImages {
|
||||
Body: { buildPack: string, deploymentType: string }
|
||||
}
|
||||
export interface SaveApplicationSource extends OnlyId {
|
||||
Body: { gitSourceId: string }
|
||||
}
|
||||
export interface CheckRepository extends OnlyId {
|
||||
Querystring: { repository: string, branch: string }
|
||||
}
|
||||
export interface SaveDestination extends OnlyId {
|
||||
Body: { destinationId: string }
|
||||
}
|
||||
export interface SaveSecret extends OnlyId {
|
||||
Body: {
|
||||
name: string,
|
||||
value: string,
|
||||
isBuildSecret: boolean,
|
||||
isPRMRSecret: boolean,
|
||||
isNew: boolean
|
||||
}
|
||||
}
|
||||
export interface DeleteSecret extends OnlyId {
|
||||
Body: { name: string }
|
||||
}
|
||||
export interface SaveStorage extends OnlyId {
|
||||
Body: {
|
||||
path: string,
|
||||
newStorage: boolean,
|
||||
storageId: string
|
||||
}
|
||||
}
|
||||
export interface DeleteStorage extends OnlyId {
|
||||
Body: {
|
||||
path: string,
|
||||
}
|
||||
}
|
||||
export interface GetApplicationLogs extends OnlyId {
|
||||
Querystring: {
|
||||
since: number,
|
||||
}
|
||||
}
|
||||
export interface GetBuildLogs extends OnlyId {
|
||||
Querystring: {
|
||||
buildId: string
|
||||
skip: number,
|
||||
}
|
||||
}
|
||||
export interface GetBuildIdLogs {
|
||||
Params: {
|
||||
buildId: string
|
||||
},
|
||||
Querystring: {
|
||||
sequence: number
|
||||
}
|
||||
}
|
||||
export interface SaveDeployKey extends OnlyId {
|
||||
Body: {
|
||||
deployKeyId: number
|
||||
}
|
||||
}
|
||||
export interface CancelDeployment {
|
||||
Body: {
|
||||
buildId: string,
|
||||
applicationId: string
|
||||
}
|
||||
}
|
||||
export interface DeployApplication extends OnlyId {
|
||||
Body: {
|
||||
pullmergeRequestId: string | null,
|
||||
branch: string
|
||||
}
|
||||
}
|
||||
19
apps/api/src/routes/api/v1/base/index.ts
Normal file
19
apps/api/src/routes/api/v1/base/index.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { FastifyPluginAsync } from 'fastify';
|
||||
import { errorHandler, version } from '../../../../lib/common';
|
||||
|
||||
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||
fastify.get('/', async () => {
|
||||
try {
|
||||
return {
|
||||
version,
|
||||
whiteLabeled: process.env.COOLIFY_WHITE_LABELED === 'true',
|
||||
whiteLabeledIcon: process.env.COOLIFY_WHITE_LABELED_ICON,
|
||||
}
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
export default root;
|
||||
@@ -6,10 +6,11 @@ import fs from 'fs/promises';
|
||||
import { asyncExecShell, ComposeFile, createDirectories, decrypt, encrypt, errorHandler, generateDatabaseConfiguration, generatePassword, getContainerUsage, getDatabaseImage, getDatabaseVersions, getFreePort, listSettings, makeLabelForStandaloneDatabase, prisma, startTcpProxy, startTraefikTCPProxy, stopDatabaseContainer, stopTcpHttpProxy, supportedDatabaseTypesAndVersions, uniqueName, updatePasswordInDb } from '../../../../lib/common';
|
||||
import { dockerInstance, getEngine } from '../../../../lib/docker';
|
||||
import { day } from '../../../../lib/dayjs';
|
||||
import { GetDatabaseLogs, OnlyId, SaveDatabase, SaveDatabaseDestination, SaveDatabaseSettings, SaveVersion } from '../../../../types';
|
||||
import { SaveDatabaseType } from './types';
|
||||
|
||||
export async function listDatabases(request: FastifyRequest) {
|
||||
try {
|
||||
const userId = request.user.userId;
|
||||
const teamId = request.user.teamId;
|
||||
let databases = []
|
||||
if (teamId === '0') {
|
||||
@@ -55,7 +56,7 @@ export async function newDatabase(request: FastifyRequest, reply: FastifyReply)
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function getDatabase(request: FastifyRequest) {
|
||||
export async function getDatabase(request: FastifyRequest<OnlyId>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -107,7 +108,7 @@ export async function getDatabaseTypes(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function saveDatabaseType(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function saveDatabaseType(request: FastifyRequest<SaveDatabaseType>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const { type } = request.body;
|
||||
@@ -120,7 +121,7 @@ export async function saveDatabaseType(request: FastifyRequest, reply: FastifyRe
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function getVersions(request: FastifyRequest) {
|
||||
export async function getVersions(request: FastifyRequest<OnlyId>) {
|
||||
try {
|
||||
const teamId = request.user.teamId;
|
||||
const { id } = request.params;
|
||||
@@ -135,7 +136,7 @@ export async function getVersions(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function saveVersion(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function saveVersion(request: FastifyRequest<SaveVersion>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const { version } = request.body;
|
||||
@@ -144,7 +145,6 @@ export async function saveVersion(request: FastifyRequest, reply: FastifyReply)
|
||||
where: { id },
|
||||
data: {
|
||||
version,
|
||||
|
||||
}
|
||||
});
|
||||
return reply.code(201).send({})
|
||||
@@ -152,7 +152,7 @@ export async function saveVersion(request: FastifyRequest, reply: FastifyReply)
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function saveDatabaseDestination(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function saveDatabaseDestination(request: FastifyRequest<SaveDatabaseDestination>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const { destinationId } = request.body;
|
||||
@@ -181,7 +181,7 @@ export async function saveDatabaseDestination(request: FastifyRequest, reply: Fa
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function getDatabaseUsage(request: FastifyRequest) {
|
||||
export async function getDatabaseUsage(request: FastifyRequest<OnlyId>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -203,7 +203,7 @@ export async function getDatabaseUsage(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function startDatabase(request: FastifyRequest) {
|
||||
export async function startDatabase(request: FastifyRequest<OnlyId>) {
|
||||
try {
|
||||
const teamId = request.user.teamId;
|
||||
const { id } = request.params;
|
||||
@@ -226,7 +226,6 @@ export async function startDatabase(request: FastifyRequest) {
|
||||
|
||||
const network = destinationDockerId && destinationDocker.network;
|
||||
const host = getEngine(destinationDocker.engine);
|
||||
const engine = destinationDocker.engine;
|
||||
const volumeName = volume.split(':')[0];
|
||||
const labels = await makeLabelForStandaloneDatabase({ id, image, volume });
|
||||
|
||||
@@ -285,7 +284,7 @@ export async function startDatabase(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function stopDatabase(request: FastifyRequest) {
|
||||
export async function stopDatabase(request: FastifyRequest<OnlyId>) {
|
||||
try {
|
||||
const teamId = request.user.teamId;
|
||||
const { id } = request.params;
|
||||
@@ -310,7 +309,7 @@ export async function stopDatabase(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function getDatabaseLogs(request: FastifyRequest) {
|
||||
export async function getDatabaseLogs(request: FastifyRequest<GetDatabaseLogs>) {
|
||||
try {
|
||||
const teamId = request.user.teamId;
|
||||
const { id } = request.params;
|
||||
@@ -361,7 +360,7 @@ export async function getDatabaseLogs(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function deleteDatabase(request: FastifyRequest) {
|
||||
export async function deleteDatabase(request: FastifyRequest<OnlyId>) {
|
||||
try {
|
||||
const teamId = request.user.teamId;
|
||||
const { id } = request.params;
|
||||
@@ -382,7 +381,7 @@ export async function deleteDatabase(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function saveDatabase(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function saveDatabase(request: FastifyRequest<SaveDatabase>, reply: FastifyReply) {
|
||||
try {
|
||||
const teamId = request.user.teamId;
|
||||
const { id } = request.params;
|
||||
@@ -428,7 +427,7 @@ export async function saveDatabase(request: FastifyRequest, reply: FastifyReply)
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function saveDatabaseSettings(request: FastifyRequest) {
|
||||
export async function saveDatabaseSettings(request: FastifyRequest<SaveDatabaseSettings>) {
|
||||
try {
|
||||
const teamId = request.user.teamId;
|
||||
const { id } = request.params;
|
||||
|
||||
@@ -1,32 +1,35 @@
|
||||
import { FastifyPluginAsync } from 'fastify';
|
||||
import { deleteDatabase, getDatabase, getDatabaseLogs, getDatabaseTypes, getDatabaseUsage, getVersions, listDatabases, newDatabase, saveDatabase, saveDatabaseDestination, saveDatabaseSettings, saveDatabaseType, saveVersion, startDatabase, stopDatabase } from './handlers';
|
||||
|
||||
const root: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
|
||||
fastify.addHook('onRequest', async (request, reply) => {
|
||||
import type { GetDatabaseLogs, OnlyId, SaveDatabase, SaveDatabaseDestination, SaveDatabaseSettings, SaveVersion } from '../../../../types';
|
||||
import type { SaveDatabaseType } from './types';
|
||||
|
||||
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||
fastify.addHook('onRequest', async (request) => {
|
||||
return await request.jwtVerify()
|
||||
})
|
||||
fastify.get('/', async (request) => await listDatabases(request));
|
||||
fastify.post('/new', async (request, reply) => await newDatabase(request, reply));
|
||||
|
||||
fastify.get('/:id', async (request) => await getDatabase(request));
|
||||
fastify.post('/:id', async (request, reply) => await saveDatabase(request, reply));
|
||||
fastify.delete('/:id', async (request) => await deleteDatabase(request));
|
||||
fastify.get<OnlyId>('/:id', async (request) => await getDatabase(request));
|
||||
fastify.post<SaveDatabase>('/:id', async (request, reply) => await saveDatabase(request, reply));
|
||||
fastify.delete<OnlyId>('/:id', async (request) => await deleteDatabase(request));
|
||||
|
||||
fastify.post('/:id/settings', async (request) => await saveDatabaseSettings(request));
|
||||
fastify.post<SaveDatabaseSettings>('/:id/settings', async (request) => await saveDatabaseSettings(request));
|
||||
|
||||
fastify.get('/:id/configuration/type', async (request) => await getDatabaseTypes(request));
|
||||
fastify.post('/:id/configuration/type', async (request, reply) => await saveDatabaseType(request, reply));
|
||||
fastify.post<SaveDatabaseType>('/:id/configuration/type', async (request, reply) => await saveDatabaseType(request, reply));
|
||||
|
||||
fastify.get('/:id/configuration/version', async (request) => await getVersions(request));
|
||||
fastify.post('/:id/configuration/version', async (request, reply) => await saveVersion(request, reply));
|
||||
fastify.get<OnlyId>('/:id/configuration/version', async (request) => await getVersions(request));
|
||||
fastify.post<SaveVersion>('/:id/configuration/version', async (request, reply) => await saveVersion(request, reply));
|
||||
|
||||
fastify.post('/:id/configuration/destination', async (request, reply) => await saveDatabaseDestination(request, reply));
|
||||
fastify.post<SaveDatabaseDestination>('/:id/configuration/destination', async (request, reply) => await saveDatabaseDestination(request, reply));
|
||||
|
||||
fastify.get('/:id/usage', async (request) => await getDatabaseUsage(request));
|
||||
fastify.get('/:id/logs', async (request) => await getDatabaseLogs(request));
|
||||
fastify.get<OnlyId>('/:id/usage', async (request) => await getDatabaseUsage(request));
|
||||
fastify.get<GetDatabaseLogs>('/:id/logs', async (request) => await getDatabaseLogs(request));
|
||||
|
||||
fastify.post('/:id/start', async (request) => await startDatabase(request));
|
||||
fastify.post('/:id/stop', async (request) => await stopDatabase(request));
|
||||
fastify.post<OnlyId>('/:id/start', async (request) => await startDatabase(request));
|
||||
fastify.post<OnlyId>('/:id/stop', async (request) => await stopDatabase(request));
|
||||
};
|
||||
|
||||
export default root;
|
||||
|
||||
5
apps/api/src/routes/api/v1/databases/types.ts
Normal file
5
apps/api/src/routes/api/v1/databases/types.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import type { OnlyId } from "../../../../types";
|
||||
|
||||
export interface SaveDatabaseType extends OnlyId {
|
||||
Body: { type: string }
|
||||
}
|
||||
@@ -3,6 +3,9 @@ import { FastifyReply } from 'fastify';
|
||||
import { asyncExecShell, errorHandler, listSettings, prisma, startCoolifyProxy, startTraefikProxy, stopTraefikProxy } from '../../../../lib/common';
|
||||
import { checkContainer, dockerInstance, getEngine } from '../../../../lib/docker';
|
||||
|
||||
import type { OnlyId } from '../../../../types';
|
||||
import type { CheckDestination, NewDestination, Proxy, SaveDestinationSettings } from './types';
|
||||
|
||||
export async function listDestinations(request: FastifyRequest) {
|
||||
try {
|
||||
const teamId = request.user.teamId;
|
||||
@@ -22,7 +25,7 @@ export async function listDestinations(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function checkDestination(request: FastifyRequest) {
|
||||
export async function checkDestination(request: FastifyRequest<CheckDestination>) {
|
||||
try {
|
||||
const { network } = request.body;
|
||||
const found = await prisma.destinationDocker.findFirst({ where: { network } });
|
||||
@@ -36,7 +39,7 @@ export async function checkDestination(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function getDestination(request: FastifyRequest) {
|
||||
export async function getDestination(request: FastifyRequest<OnlyId>) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const teamId = request.user?.teamId;
|
||||
@@ -74,7 +77,7 @@ export async function getDestination(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function newDestination(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function newDestination(request: FastifyRequest<NewDestination>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
let { name, network, engine, isCoolifyProxyUsed } = request.body
|
||||
@@ -119,7 +122,7 @@ export async function newDestination(request: FastifyRequest, reply: FastifyRepl
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function deleteDestination(request: FastifyRequest) {
|
||||
export async function deleteDestination(request: FastifyRequest<OnlyId>) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const destination = await prisma.destinationDocker.delete({ where: { id } });
|
||||
@@ -143,7 +146,7 @@ export async function deleteDestination(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function saveDestinationSettings(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function saveDestinationSettings(request: FastifyRequest<SaveDestinationSettings>) {
|
||||
try {
|
||||
const { engine, isCoolifyProxyUsed } = request.body;
|
||||
await prisma.destinationDocker.updateMany({
|
||||
@@ -159,7 +162,7 @@ export async function saveDestinationSettings(request: FastifyRequest, reply: Fa
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function startProxy(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function startProxy(request: FastifyRequest<Proxy>) {
|
||||
const { engine } = request.body;
|
||||
try {
|
||||
await startTraefikProxy(engine);
|
||||
@@ -169,19 +172,16 @@ export async function startProxy(request: FastifyRequest, reply: FastifyReply) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function stopProxy(request: FastifyRequest, reply: FastifyReply) {
|
||||
const settings = await prisma.setting.findFirst({});
|
||||
export async function stopProxy(request: FastifyRequest<Proxy>) {
|
||||
const { engine } = request.body;
|
||||
try {
|
||||
await stopTraefikProxy(engine);
|
||||
|
||||
return {}
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function restartProxy(request: FastifyRequest, reply: FastifyReply) {
|
||||
const settings = await prisma.setting.findFirst({});
|
||||
export async function restartProxy(request: FastifyRequest<Proxy>) {
|
||||
const { engine } = request.body;
|
||||
try {
|
||||
await stopTraefikProxy(engine);
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import { FastifyPluginAsync } from 'fastify';
|
||||
import { checkDestination, deleteDestination, getDestination, listDestinations, newDestination, restartProxy, saveDestinationSettings, startProxy, stopProxy } from './handlers';
|
||||
|
||||
const root: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
|
||||
fastify.addHook('onRequest', async (request, reply) => {
|
||||
import type { OnlyId } from '../../../../types';
|
||||
import type { CheckDestination, NewDestination, Proxy, SaveDestinationSettings } from './types';
|
||||
|
||||
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||
fastify.addHook('onRequest', async (request) => {
|
||||
return await request.jwtVerify()
|
||||
})
|
||||
fastify.get('/', async (request) => await listDestinations(request));
|
||||
fastify.post('/check', async (request) => await checkDestination(request));
|
||||
|
||||
fastify.get('/:id', async (request) => await getDestination(request));
|
||||
fastify.post('/:id', async (request, reply) => await newDestination(request, reply));
|
||||
fastify.delete('/:id', async (request) => await deleteDestination(request));
|
||||
|
||||
fastify.post('/:id/settings', async (request, reply) => await saveDestinationSettings(request, reply));
|
||||
fastify.post('/:id/start', async (request, reply) => await startProxy(request, reply));
|
||||
fastify.post('/:id/stop', async (request, reply) => await stopProxy(request, reply));
|
||||
fastify.post('/:id/restart', async (request, reply) => await restartProxy(request, reply));
|
||||
|
||||
fastify.post<CheckDestination>('/check', async (request) => await checkDestination(request));
|
||||
|
||||
fastify.get<OnlyId>('/:id', async (request) => await getDestination(request));
|
||||
fastify.post<NewDestination>('/:id', async (request, reply) => await newDestination(request, reply));
|
||||
fastify.delete<OnlyId>('/:id', async (request) => await deleteDestination(request));
|
||||
|
||||
fastify.post<SaveDestinationSettings>('/:id/settings', async (request, reply) => await saveDestinationSettings(request));
|
||||
fastify.post<Proxy>('/:id/start', async (request, reply) => await startProxy(request));
|
||||
fastify.post<Proxy>('/:id/stop', async (request, reply) => await stopProxy(request));
|
||||
fastify.post<Proxy>('/:id/restart', async (request, reply) => await restartProxy(request));
|
||||
};
|
||||
|
||||
export default root;
|
||||
|
||||
26
apps/api/src/routes/api/v1/destinations/types.ts
Normal file
26
apps/api/src/routes/api/v1/destinations/types.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { OnlyId } from "../../../../types"
|
||||
|
||||
export interface CheckDestination {
|
||||
Body: {
|
||||
network: string
|
||||
}
|
||||
}
|
||||
export interface NewDestination extends OnlyId {
|
||||
Body: {
|
||||
name: string
|
||||
network: string
|
||||
engine: string
|
||||
isCoolifyProxyUsed: boolean
|
||||
}
|
||||
}
|
||||
export interface SaveDestinationSettings extends OnlyId {
|
||||
Body: {
|
||||
engine: string
|
||||
isCoolifyProxyUsed: boolean
|
||||
}
|
||||
}
|
||||
export interface Proxy extends OnlyId {
|
||||
Body: {
|
||||
engine: string
|
||||
}
|
||||
}
|
||||
@@ -4,31 +4,39 @@ import axios from 'axios';
|
||||
import compare from 'compare-versions';
|
||||
import cuid from 'cuid';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import { asyncExecShell, asyncSleep, errorHandler, isDev, prisma, uniqueName, version } from '../../../lib/common';
|
||||
import { asyncExecShell, asyncSleep, cleanupDockerStorage, errorHandler, isDev, prisma, uniqueName, version } from '../../../lib/common';
|
||||
|
||||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import type { Login, Update } from '.';
|
||||
|
||||
import type { GetCurrentUser } from './types';
|
||||
|
||||
export async function hashPassword(password: string): Promise<string> {
|
||||
const saltRounds = 15;
|
||||
return bcrypt.hash(password, saltRounds);
|
||||
}
|
||||
|
||||
|
||||
export async function cleanupManually() {
|
||||
try {
|
||||
await cleanupDockerStorage('unix:///var/run/docker.sock', true, true)
|
||||
return {}
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function checkUpdate(request: FastifyRequest) {
|
||||
try {
|
||||
const isStaging = request.hostname === 'staging.coolify.io'
|
||||
const currentVersion = version;
|
||||
const { data: versions } = await axios.get(
|
||||
`https://get.coollabs.io/versions.json?appId=${process.env['COOLIFY_APP_ID']}&version=${currentVersion}`
|
||||
);
|
||||
const latestVersion =
|
||||
request.hostname === 'staging.coolify.io'
|
||||
isStaging
|
||||
? versions['coolify'].next.version
|
||||
: versions['coolify'].main.version;
|
||||
const isUpdateAvailable = compare(latestVersion, currentVersion);
|
||||
return {
|
||||
isUpdateAvailable: isUpdateAvailable === 1,
|
||||
isUpdateAvailable: isStaging ? true : isUpdateAvailable === 1,
|
||||
latestVersion
|
||||
};
|
||||
} catch ({ status, message }) {
|
||||
@@ -150,10 +158,18 @@ export async function login(request: FastifyRequest<Login>, reply: FastifyReply)
|
||||
if (userFound.password === 'RESETME') {
|
||||
const hashedPassword = await hashPassword(password);
|
||||
if (userFound.updatedAt < new Date(Date.now() - 1000 * 60 * 10)) {
|
||||
await prisma.user.update({
|
||||
where: { email: userFound.email },
|
||||
data: { password: 'RESETTIMEOUT' }
|
||||
});
|
||||
if (userFound.id === '0') {
|
||||
await prisma.user.update({
|
||||
where: { email: userFound.email },
|
||||
data: { password: 'RESETME' }
|
||||
});
|
||||
} else {
|
||||
await prisma.user.update({
|
||||
where: { email: userFound.email },
|
||||
data: { password: 'RESETTIMEOUT' }
|
||||
});
|
||||
}
|
||||
|
||||
throw {
|
||||
status: 500,
|
||||
message: 'Password reset link has expired. Please request a new one.'
|
||||
@@ -238,9 +254,9 @@ export async function login(request: FastifyRequest<Login>, reply: FastifyReply)
|
||||
}
|
||||
}
|
||||
|
||||
export async function getCurrentUser(request: FastifyRequest, fastify) {
|
||||
export async function getCurrentUser(request: FastifyRequest<GetCurrentUser>, fastify) {
|
||||
let token = null
|
||||
|
||||
const { teamId } = request.query
|
||||
try {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: request.user.userId }
|
||||
@@ -251,18 +267,19 @@ export async function getCurrentUser(request: FastifyRequest, fastify) {
|
||||
} catch (error) {
|
||||
throw { status: 401, message: error };
|
||||
}
|
||||
if (request.query.teamId) {
|
||||
if (teamId) {
|
||||
try {
|
||||
const user = await prisma.user.findFirst({
|
||||
where: { id: request.user.userId, teams: { some: { id: request.query.teamId } } },
|
||||
where: { id: request.user.userId, teams: { some: { id: teamId } } },
|
||||
include: { teams: true, permission: true }
|
||||
})
|
||||
if (user) {
|
||||
const permission = user.permission.find(p => p.teamId === teamId).permission
|
||||
const payload = {
|
||||
...request.user,
|
||||
teamId: request.query.teamId,
|
||||
permission: user.permission.find(p => p.teamId === request.query.teamId).permission || null,
|
||||
isAdmin: user.permission.find(p => p.teamId === request.query.teamId).permission === 'owner'
|
||||
teamId,
|
||||
permission: permission || null,
|
||||
isAdmin: permission === 'owner' || permission === 'admin'
|
||||
|
||||
}
|
||||
token = fastify.jwt.sign(payload)
|
||||
|
||||
@@ -3,6 +3,10 @@ import type { FastifyRequest } from 'fastify';
|
||||
import { FastifyReply } from 'fastify';
|
||||
import { decrypt, errorHandler, prisma, uniqueName } from '../../../../lib/common';
|
||||
import { day } from '../../../../lib/dayjs';
|
||||
|
||||
import type { OnlyId } from '../../../../types';
|
||||
import type { BodyId, InviteToTeam, SaveTeam, SetPermission } from './types';
|
||||
|
||||
export async function listTeams(request: FastifyRequest) {
|
||||
try {
|
||||
const userId = request.user.userId;
|
||||
@@ -36,7 +40,7 @@ export async function listTeams(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function deleteTeam(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function deleteTeam(request: FastifyRequest<OnlyId>, reply: FastifyReply) {
|
||||
try {
|
||||
const userId = request.user.userId;
|
||||
const { id } = request.params;
|
||||
@@ -136,7 +140,7 @@ export async function newTeam(request: FastifyRequest, reply: FastifyReply) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function getTeam(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function getTeam(request: FastifyRequest<OnlyId>, reply: FastifyReply) {
|
||||
try {
|
||||
const userId = request.user.userId;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -163,7 +167,7 @@ export async function getTeam(request: FastifyRequest, reply: FastifyReply) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function saveTeam(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function saveTeam(request: FastifyRequest<SaveTeam>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const { name } = request.body;
|
||||
@@ -263,7 +267,7 @@ export async function saveTeam(request: FastifyRequest, reply: FastifyReply) {
|
||||
// }
|
||||
// }
|
||||
|
||||
export async function inviteToTeam(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function inviteToTeam(request: FastifyRequest<InviteToTeam>, reply: FastifyReply) {
|
||||
try {
|
||||
const userId = request.user.userId;
|
||||
const { email, permission, teamId, teamName } = request.body;
|
||||
@@ -306,7 +310,7 @@ export async function inviteToTeam(request: FastifyRequest, reply: FastifyReply)
|
||||
}
|
||||
}
|
||||
|
||||
export async function acceptInvitation(request: FastifyRequest) {
|
||||
export async function acceptInvitation(request: FastifyRequest<BodyId>) {
|
||||
try {
|
||||
const userId = request.user.userId;
|
||||
const { id } = request.body;
|
||||
@@ -331,7 +335,7 @@ export async function acceptInvitation(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function revokeInvitation(request: FastifyRequest) {
|
||||
export async function revokeInvitation(request: FastifyRequest<BodyId>) {
|
||||
try {
|
||||
const { id } = request.body
|
||||
await prisma.teamInvitation.delete({ where: { id } });
|
||||
@@ -341,15 +345,15 @@ export async function revokeInvitation(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function removeUser(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function removeUser(request: FastifyRequest<BodyId>, reply: FastifyReply) {
|
||||
try {
|
||||
const { uid } = request.body;
|
||||
const user = await prisma.user.findUnique({ where: { id: uid }, include: { teams: true, permission: true } });
|
||||
const { id } = request.body;
|
||||
const user = await prisma.user.findUnique({ where: { id }, include: { teams: true, permission: true } });
|
||||
if (user) {
|
||||
const permissions = user.permission;
|
||||
if (permissions.length > 0) {
|
||||
for (const permission of permissions) {
|
||||
await prisma.permission.deleteMany({ where: { id: permission.id, userId: uid } });
|
||||
await prisma.permission.deleteMany({ where: { id: permission.id, userId: id } });
|
||||
}
|
||||
}
|
||||
const teams = user.teams;
|
||||
@@ -357,7 +361,7 @@ export async function removeUser(request: FastifyRequest, reply: FastifyReply) {
|
||||
for (const team of teams) {
|
||||
const newTeam = await prisma.team.update({
|
||||
where: { id: team.id },
|
||||
data: { users: { disconnect: { id: uid } } },
|
||||
data: { users: { disconnect: { id } } },
|
||||
include: { applications: true, database: true, gitHubApps: true, gitLabApps: true, gitSources: true, destinationDocker: true, service: true, users: true }
|
||||
});
|
||||
if (newTeam.users.length === 0) {
|
||||
@@ -422,14 +426,14 @@ export async function removeUser(request: FastifyRequest, reply: FastifyReply) {
|
||||
}
|
||||
}
|
||||
}
|
||||
await prisma.user.delete({ where: { id: uid } });
|
||||
await prisma.user.delete({ where: { id } });
|
||||
return reply.code(201).send()
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
|
||||
export async function setPermission(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function setPermission(request: FastifyRequest<SetPermission>, reply: FastifyReply) {
|
||||
try {
|
||||
const { userId, newPermission, permissionId } = request.body;
|
||||
await prisma.permission.updateMany({
|
||||
@@ -442,10 +446,10 @@ export async function setPermission(request: FastifyRequest, reply: FastifyReply
|
||||
}
|
||||
}
|
||||
|
||||
export async function changePassword(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function changePassword(request: FastifyRequest<BodyId>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.body;
|
||||
await prisma.user.update({ where: { id: undefined }, data: { password: 'RESETME' } });
|
||||
await prisma.user.update({ where: { id }, data: { password: 'RESETME' } });
|
||||
return reply.code(201).send()
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message })
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
import { FastifyPluginAsync } from 'fastify';
|
||||
import { acceptInvitation, changePassword, deleteTeam, getTeam, inviteToTeam, listTeams, newTeam, removeUser, revokeInvitation, saveTeam, setPermission } from './handlers';
|
||||
|
||||
import type { OnlyId } from '../../../../types';
|
||||
import type { BodyId, InviteToTeam, SaveTeam, SetPermission } from './types';
|
||||
|
||||
const root: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
|
||||
fastify.addHook('onRequest', async (request, reply) => {
|
||||
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||
fastify.addHook('onRequest', async (request) => {
|
||||
return await request.jwtVerify()
|
||||
})
|
||||
fastify.get('/', async (request) => await listTeams(request));
|
||||
fastify.post('/new', async (request, reply) => await newTeam(request, reply));
|
||||
|
||||
fastify.get('/team/:id', async (request, reply) => await getTeam(request, reply));
|
||||
fastify.post('/team/:id', async (request, reply) => await saveTeam(request, reply));
|
||||
fastify.delete('/team/:id', async (request, reply) => await deleteTeam(request, reply));
|
||||
fastify.get<OnlyId>('/team/:id', async (request, reply) => await getTeam(request, reply));
|
||||
fastify.post<SaveTeam>('/team/:id', async (request, reply) => await saveTeam(request, reply));
|
||||
fastify.delete<OnlyId>('/team/:id', async (request, reply) => await deleteTeam(request, reply));
|
||||
|
||||
fastify.post('/team/:id/invitation/invite', async (request, reply) => await inviteToTeam(request, reply))
|
||||
fastify.post('/team/:id/invitation/accept', async (request) => await acceptInvitation(request));
|
||||
fastify.post('/team/:id/invitation/revoke', async (request) => await revokeInvitation(request));
|
||||
fastify.post<InviteToTeam>('/team/:id/invitation/invite', async (request, reply) => await inviteToTeam(request, reply))
|
||||
fastify.post<BodyId>('/team/:id/invitation/accept', async (request) => await acceptInvitation(request));
|
||||
fastify.post<BodyId>('/team/:id/invitation/revoke', async (request) => await revokeInvitation(request));
|
||||
|
||||
fastify.post<SetPermission>('/team/:id/permission', async (request, reply) => await setPermission(request, reply));
|
||||
|
||||
fastify.post('/team/:id/permission', async (request, reply) => await setPermission(request, reply));
|
||||
|
||||
fastify.delete('/user/remove', async (request, reply) => await removeUser(request, reply));
|
||||
fastify.post('/user/password', async (request, reply) => await changePassword(request, reply));
|
||||
fastify.delete<BodyId>('/user/remove', async (request, reply) => await removeUser(request, reply));
|
||||
fastify.post<BodyId>('/user/password', async (request, reply) => await changePassword(request, reply));
|
||||
// fastify.delete('/user', async (request, reply) => await deleteUser(request, reply));
|
||||
|
||||
};
|
||||
|
||||
27
apps/api/src/routes/api/v1/iam/types.ts
Normal file
27
apps/api/src/routes/api/v1/iam/types.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { OnlyId } from "../../../../types"
|
||||
|
||||
export interface SaveTeam extends OnlyId {
|
||||
Body: {
|
||||
name: string
|
||||
}
|
||||
}
|
||||
export interface InviteToTeam {
|
||||
Body: {
|
||||
email: string,
|
||||
permission: string,
|
||||
teamId: string,
|
||||
teamName: string
|
||||
}
|
||||
}
|
||||
export interface BodyId {
|
||||
Body: {
|
||||
id: string
|
||||
}
|
||||
}
|
||||
export interface SetPermission {
|
||||
Body: {
|
||||
userId: string,
|
||||
newPermission: string,
|
||||
permissionId: string
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { FastifyPluginAsync } from 'fastify';
|
||||
import { scheduler } from '../../../lib/scheduler';
|
||||
import { checkUpdate, login, showDashboard, update, showUsage, getCurrentUser } from './handlers';
|
||||
import { checkUpdate, login, showDashboard, update, showUsage, getCurrentUser, cleanupManually } from './handlers';
|
||||
import { GetCurrentUser } from './types';
|
||||
|
||||
export interface Update {
|
||||
Body: { latestVersion: string }
|
||||
@@ -9,7 +9,7 @@ export interface Login {
|
||||
Body: { email: string, password: string, isLogin: boolean }
|
||||
}
|
||||
|
||||
const root: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
|
||||
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||
fastify.get('/', async function (_request, reply) {
|
||||
return reply.redirect(302, '/');
|
||||
});
|
||||
@@ -19,7 +19,7 @@ const root: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
|
||||
return { token, payload }
|
||||
});
|
||||
|
||||
fastify.get('/user', {
|
||||
fastify.get<GetCurrentUser>('/user', {
|
||||
onRequest: [fastify.authenticate]
|
||||
}, async (request) => await getCurrentUser(request, fastify));
|
||||
|
||||
@@ -46,6 +46,10 @@ const root: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
|
||||
fastify.get('/usage', {
|
||||
onRequest: [fastify.authenticate]
|
||||
}, async () => await showUsage());
|
||||
|
||||
fastify.post('/internal/cleanup', {
|
||||
onRequest: [fastify.authenticate]
|
||||
}, async () => await cleanupManually());
|
||||
};
|
||||
|
||||
export default root;
|
||||
|
||||
@@ -2,13 +2,148 @@ import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import fs from 'fs/promises';
|
||||
import yaml from 'js-yaml';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import { prisma, uniqueName, asyncExecShell, getServiceImage, getServiceImages, configureServiceType, getServiceFromDB, getContainerUsage, removeService, isDomainConfigured, saveUpdateableFields, fixType, decrypt, encrypt, getServiceMainPort, createDirectories, ComposeFile, makeLabelForServices, getFreePort, getDomain, errorHandler, supportedServiceTypesAndVersions } from '../../../../lib/common';
|
||||
import { prisma, uniqueName, asyncExecShell, getServiceImage, getServiceImages, configureServiceType, getServiceFromDB, getContainerUsage, removeService, isDomainConfigured, saveUpdateableFields, fixType, decrypt, encrypt, getServiceMainPort, createDirectories, ComposeFile, makeLabelForServices, getFreePort, getDomain, errorHandler, generatePassword, isDev, stopTcpHttpProxy, supportedServiceTypesAndVersions } from '../../../../lib/common';
|
||||
import { day } from '../../../../lib/dayjs';
|
||||
import { checkContainer, dockerInstance, getEngine, removeContainer } from '../../../../lib/docker';
|
||||
import cuid from 'cuid';
|
||||
|
||||
import type { OnlyId } from '../../../../types';
|
||||
import type { ActivateWordpressFtp, CheckService, DeleteServiceSecret, DeleteServiceStorage, GetServiceLogs, SaveService, SaveServiceDestination, SaveServiceSecret, SaveServiceSettings, SaveServiceStorage, SaveServiceType, SaveServiceVersion, ServiceStartStop, SetWordpressSettings } from './types';
|
||||
|
||||
// async function startServiceNew(request: FastifyRequest<OnlyId>) {
|
||||
// try {
|
||||
// const { id } = request.params;
|
||||
// const teamId = request.user.teamId;
|
||||
// const service = await getServiceFromDB({ id, teamId });
|
||||
// const { type, version, destinationDockerId, destinationDocker, serviceSecret, exposePort } =
|
||||
// service;
|
||||
// const network = destinationDockerId && destinationDocker.network;
|
||||
// const host = getEngine(destinationDocker.engine);
|
||||
// const port = getServiceMainPort(type);
|
||||
|
||||
// const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||
// const image = getServiceImage(type);
|
||||
// const config = (await getAvailableServices()).find((name) => name.name === type).compose
|
||||
// const environmentVariables = {}
|
||||
// if (serviceSecret.length > 0) {
|
||||
// serviceSecret.forEach((secret) => {
|
||||
// environmentVariables[secret.name] = secret.value;
|
||||
// });
|
||||
// }
|
||||
// config.newVolumes = {}
|
||||
// for (const service of Object.entries(config.services)) {
|
||||
// const name = service[0]
|
||||
// const details: any = service[1]
|
||||
// config.services[`${id}-${name}`] = JSON.parse(JSON.stringify(details))
|
||||
// config.services[`${id}-${name}`].container_name = `${id}-${name}`
|
||||
// config.services[`${id}-${name}`].restart = "always"
|
||||
// config.services[`${id}-${name}`].networks = [network]
|
||||
// config.services[`${id}-${name}`].labels = makeLabelForServices(type)
|
||||
// if (name === config.name) {
|
||||
// config.services[`${id}-${name}`].image = `${details.image.split(':')[0]}:${version}`
|
||||
// config.services[`${id}-${name}`].ports = (exposePort ? [`${exposePort}:${port}`] : [])
|
||||
// config.services[`${id}-${name}`].environment = environmentVariables
|
||||
// }
|
||||
// config.services[`${id}-${name}`].deploy = {
|
||||
// restart_policy: {
|
||||
// condition: 'on-failure',
|
||||
// delay: '5s',
|
||||
// max_attempts: 3,
|
||||
// window: '120s'
|
||||
// }
|
||||
// }
|
||||
// if (config.services[`${id}-${name}`]?.volumes?.length > 0) {
|
||||
// config.services[`${id}-${name}`].volumes.forEach((volume, index) => {
|
||||
// let oldVolumeName = volume.split(':')[0]
|
||||
// const path = volume.split(':')[1]
|
||||
// // if (config?.volumes[oldVolumeName]) delete config?.volumes[oldVolumeName]
|
||||
// const newName = convertTolOldVolumeNames(type)
|
||||
// if (newName) oldVolumeName = newName
|
||||
|
||||
// const volumeName = `${id}-${oldVolumeName}`
|
||||
// config.newVolumes[volumeName] = {
|
||||
// name: volumeName
|
||||
// }
|
||||
// config.services[`${id}-${name}`].volumes[index] = `${volumeName}:${path}`
|
||||
// })
|
||||
// config.services[`${id}-${config.name}`] = {
|
||||
// ...config.services[`${id}-${config.name}`],
|
||||
// environment: environmentVariables
|
||||
// }
|
||||
// }
|
||||
// config.networks = {
|
||||
// [network]: {
|
||||
// external: true
|
||||
// }
|
||||
// }
|
||||
|
||||
// config.volumes = config.newVolumes
|
||||
|
||||
// // config.services[`${id}-${name}`]?.volumes?.length > 0 && config.services[`${id}-${name}`].volumes.forEach((volume, index) => {
|
||||
// // let oldVolumeName = volume.split(':')[0]
|
||||
// // const path = volume.split(':')[1]
|
||||
// // oldVolumeName = convertTolOldVolumeNames(type)
|
||||
// // const volumeName = `${id}-${oldVolumeName}`
|
||||
// // config.volumes[volumeName] = {
|
||||
// // name: volumeName
|
||||
// // }
|
||||
// // config.services[`${id}-${name}`].volumes[index] = `${volumeName}:${path}`
|
||||
// // })
|
||||
// // config.services[`${id}-${config.name}`] = {
|
||||
// // ...config.services[`${id}-${config.name}`],
|
||||
// // environment: environmentVariables
|
||||
// // }
|
||||
// delete config.services[name]
|
||||
|
||||
// }
|
||||
// console.log(config.services)
|
||||
// console.log(config.volumes)
|
||||
|
||||
// // config.services[id] = JSON.parse(JSON.stringify(config.services[type]))
|
||||
// // config.services[id].container_name = id
|
||||
// // config.services[id].image = `${image}:${version}`
|
||||
// // config.services[id].ports = (exposePort ? [`${exposePort}:${port}`] : []),
|
||||
// // config.services[id].restart = "always"
|
||||
// // config.services[id].networks = [network]
|
||||
// // config.services[id].labels = makeLabelForServices(type)
|
||||
// // config.services[id].deploy = {
|
||||
// // restart_policy: {
|
||||
// // condition: 'on-failure',
|
||||
// // delay: '5s',
|
||||
// // max_attempts: 3,
|
||||
// // window: '120s'
|
||||
// // }
|
||||
// // }
|
||||
// // config.networks = {
|
||||
// // [network]: {
|
||||
// // external: true
|
||||
// // }
|
||||
// // }
|
||||
// // config.volumes = {}
|
||||
// // config.services[id].volumes.forEach((volume, index) => {
|
||||
// // let oldVolumeName = volume.split(':')[0]
|
||||
// // const path = volume.split(':')[1]
|
||||
// // oldVolumeName = convertTolOldVolumeNames(type)
|
||||
// // const volumeName = `${id}-${oldVolumeName}`
|
||||
// // config.volumes[volumeName] = {
|
||||
// // name: volumeName
|
||||
// // }
|
||||
// // config.services[id].volumes[index] = `${volumeName}:${path}`
|
||||
// // })
|
||||
// // delete config.services[type]
|
||||
// // config.services[id].environment = environmentVariables
|
||||
// const composeFileDestination = `${workdir}/docker-compose.yaml`;
|
||||
// // await fs.writeFile(composeFileDestination, yaml.dump(config));
|
||||
// // await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} pull`);
|
||||
// // await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
|
||||
// return {}
|
||||
// } catch ({ status, message }) {
|
||||
// return errorHandler({ status, message })
|
||||
// }
|
||||
// }
|
||||
|
||||
export async function listServices(request: FastifyRequest) {
|
||||
try {
|
||||
const userId = request.user.userId;
|
||||
const teamId = request.user.teamId;
|
||||
let services = []
|
||||
if (teamId === '0') {
|
||||
@@ -37,12 +172,12 @@ export async function newService(request: FastifyRequest, reply: FastifyReply) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function getService(request: FastifyRequest) {
|
||||
export async function getService(request: FastifyRequest<OnlyId>) {
|
||||
try {
|
||||
const teamId = request.user.teamId;
|
||||
const { id } = request.params;
|
||||
const service = await getServiceFromDB({ id, teamId });
|
||||
|
||||
|
||||
if (!service) {
|
||||
throw { status: 404, message: 'Service not found.' }
|
||||
}
|
||||
@@ -90,9 +225,8 @@ export async function getServiceType(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function saveServiceType(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function saveServiceType(request: FastifyRequest<SaveServiceType>, reply: FastifyReply) {
|
||||
try {
|
||||
const teamId = request.user.teamId;
|
||||
const { id } = request.params;
|
||||
const { type } = request.body;
|
||||
await configureServiceType({ id, type });
|
||||
@@ -101,7 +235,7 @@ export async function saveServiceType(request: FastifyRequest, reply: FastifyRep
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function getServiceVersions(request: FastifyRequest) {
|
||||
export async function getServiceVersions(request: FastifyRequest<OnlyId>) {
|
||||
try {
|
||||
const teamId = request.user.teamId;
|
||||
const { id } = request.params;
|
||||
@@ -114,9 +248,8 @@ export async function getServiceVersions(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function saveServiceVersion(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function saveServiceVersion(request: FastifyRequest<SaveServiceVersion>, reply: FastifyReply) {
|
||||
try {
|
||||
const teamId = request.user.teamId;
|
||||
const { id } = request.params;
|
||||
const { version } = request.body;
|
||||
await prisma.service.update({
|
||||
@@ -128,9 +261,8 @@ export async function saveServiceVersion(request: FastifyRequest, reply: Fastify
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function saveServiceDestination(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function saveServiceDestination(request: FastifyRequest<SaveServiceDestination>, reply: FastifyReply) {
|
||||
try {
|
||||
const teamId = request.user.teamId;
|
||||
const { id } = request.params;
|
||||
const { destinationId } = request.body;
|
||||
await prisma.service.update({
|
||||
@@ -142,7 +274,7 @@ export async function saveServiceDestination(request: FastifyRequest, reply: Fas
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function getServiceUsage(request: FastifyRequest) {
|
||||
export async function getServiceUsage(request: FastifyRequest<OnlyId>) {
|
||||
try {
|
||||
const teamId = request.user.teamId;
|
||||
const { id } = request.params;
|
||||
@@ -160,9 +292,8 @@ export async function getServiceUsage(request: FastifyRequest) {
|
||||
}
|
||||
|
||||
}
|
||||
export async function getServiceLogs(request: FastifyRequest) {
|
||||
export async function getServiceLogs(request: FastifyRequest<GetServiceLogs>) {
|
||||
try {
|
||||
const teamId = request.user.teamId;
|
||||
const { id } = request.params;
|
||||
let { since = 0 } = request.query
|
||||
if (since !== 0) {
|
||||
@@ -211,7 +342,7 @@ export async function getServiceLogs(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function deleteService(request: FastifyRequest) {
|
||||
export async function deleteService(request: FastifyRequest<OnlyId>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
await removeService({ id });
|
||||
@@ -220,7 +351,7 @@ export async function deleteService(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function saveServiceSettings(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function saveServiceSettings(request: FastifyRequest<SaveServiceSettings>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const { dualCerts } = request.body;
|
||||
@@ -233,22 +364,24 @@ export async function saveServiceSettings(request: FastifyRequest, reply: Fastif
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function checkService(request: FastifyRequest) {
|
||||
export async function checkService(request: FastifyRequest<CheckService>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
let { fqdn, exposePort, otherFqdns } = request.body;
|
||||
|
||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||
if (otherFqdns && otherFqdns.length > 0) otherFqdns = otherFqdns.map((f) => f.toLowerCase());
|
||||
if (exposePort) exposePort = Number(exposePort);
|
||||
|
||||
let found = await isDomainConfigured({ id, fqdn });
|
||||
if (found) {
|
||||
throw `Domain already configured.`
|
||||
throw { status: 500, message: `Domain ${getDomain(fqdn).replace('www.', '')} is already in use!` }
|
||||
}
|
||||
if (otherFqdns && otherFqdns.length > 0) {
|
||||
for (const ofqdn of otherFqdns) {
|
||||
found = await isDomainConfigured({ id, fqdn: ofqdn, checkOwn: true });
|
||||
if (found) {
|
||||
throw "Domain already configured."
|
||||
throw { status: 500, message: `Domain ${getDomain(ofqdn).replace('www.', '')} is already in use!` }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -257,12 +390,12 @@ export async function checkService(request: FastifyRequest) {
|
||||
exposePort = Number(exposePort);
|
||||
|
||||
if (exposePort < 1024 || exposePort > 65535) {
|
||||
throw `Exposed Port needs to be between 1024 and 65535.`
|
||||
throw { status: 500, message: `Exposed Port needs to be between 1024 and 65535.` }
|
||||
}
|
||||
|
||||
const publicPort = await getPort({ port: exposePort });
|
||||
if (publicPort !== exposePort) {
|
||||
throw `Port ${exposePort} is already in use.`
|
||||
throw { status: 500, message: `Port ${exposePort} is already in use.` }
|
||||
}
|
||||
}
|
||||
return {}
|
||||
@@ -270,7 +403,7 @@ export async function checkService(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function saveService(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function saveService(request: FastifyRequest<SaveService>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
let { name, fqdn, exposePort, type } = request.body;
|
||||
@@ -298,7 +431,7 @@ export async function saveService(request: FastifyRequest, reply: FastifyReply)
|
||||
}
|
||||
}
|
||||
|
||||
export async function getServiceSecrets(request: FastifyRequest) {
|
||||
export async function getServiceSecrets(request: FastifyRequest<OnlyId>) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
let secrets = await prisma.serviceSecret.findMany({
|
||||
@@ -318,10 +451,10 @@ export async function getServiceSecrets(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveServiceSecret(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function saveServiceSecret(request: FastifyRequest<SaveServiceSecret>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
let { name, value, isBuildSecret, isPRMRSecret, isNew } = request.body
|
||||
let { name, value, isNew } = request.body
|
||||
|
||||
if (isNew) {
|
||||
const found = await prisma.serviceSecret.findFirst({ where: { name, serviceId: id } });
|
||||
@@ -353,7 +486,7 @@ export async function saveServiceSecret(request: FastifyRequest, reply: FastifyR
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function deleteServiceSecret(request: FastifyRequest) {
|
||||
export async function deleteServiceSecret(request: FastifyRequest<DeleteServiceSecret>) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const { name } = request.body
|
||||
@@ -364,7 +497,7 @@ export async function deleteServiceSecret(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function getServiceStorages(request: FastifyRequest) {
|
||||
export async function getServiceStorages(request: FastifyRequest<OnlyId>) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const persistentStorages = await prisma.servicePersistentStorage.findMany({
|
||||
@@ -378,7 +511,7 @@ export async function getServiceStorages(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveServiceStorage(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function saveServiceStorage(request: FastifyRequest<SaveServiceStorage>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const { path, newStorage, storageId } = request.body
|
||||
@@ -399,7 +532,7 @@ export async function saveServiceStorage(request: FastifyRequest, reply: Fastify
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteServiceStorage(request: FastifyRequest) {
|
||||
export async function deleteServiceStorage(request: FastifyRequest<DeleteServiceStorage>) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const { path } = request.body
|
||||
@@ -410,7 +543,7 @@ export async function deleteServiceStorage(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function startService(request: FastifyRequest,) {
|
||||
export async function startService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { type } = request.params
|
||||
if (type === 'plausibleanalytics') {
|
||||
@@ -455,12 +588,15 @@ export async function startService(request: FastifyRequest,) {
|
||||
if (type === 'fider') {
|
||||
return await startFiderService(request)
|
||||
}
|
||||
if (type === 'moodle') {
|
||||
return await startMoodleService(request)
|
||||
}
|
||||
throw `Service type ${type} not supported.`
|
||||
} catch (error) {
|
||||
throw { status: 500, message: error?.message || error }
|
||||
}
|
||||
}
|
||||
export async function stopService(request: FastifyRequest) {
|
||||
export async function stopService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { type } = request.params
|
||||
if (type === 'plausibleanalytics') {
|
||||
@@ -505,12 +641,15 @@ export async function stopService(request: FastifyRequest) {
|
||||
if (type === 'fider') {
|
||||
return await stopFiderService(request)
|
||||
}
|
||||
if (type === 'moodle') {
|
||||
return await stopMoodleService(request)
|
||||
}
|
||||
throw `Service type ${type} not supported.`
|
||||
} catch (error) {
|
||||
throw { status: 500, message: error?.message || error }
|
||||
}
|
||||
}
|
||||
export async function setSettingsService(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function setSettingsService(request: FastifyRequest<ServiceStartStop & SetWordpressSettings>, reply: FastifyReply) {
|
||||
try {
|
||||
const { type } = request.params
|
||||
if (type === 'wordpress') {
|
||||
@@ -521,7 +660,7 @@ export async function setSettingsService(request: FastifyRequest, reply: Fastify
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
async function setWordpressSettings(request: FastifyRequest, reply: FastifyReply) {
|
||||
async function setWordpressSettings(request: FastifyRequest<ServiceStartStop & SetWordpressSettings>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const { ownMysql } = request.body
|
||||
@@ -535,7 +674,7 @@ async function setWordpressSettings(request: FastifyRequest, reply: FastifyReply
|
||||
}
|
||||
}
|
||||
|
||||
async function startPlausibleAnalyticsService(request: FastifyRequest) {
|
||||
async function startPlausibleAnalyticsService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const teamId = request.user.teamId;
|
||||
@@ -729,7 +868,7 @@ COPY ./init-db.sh /docker-entrypoint-initdb.d/init-db.sh`;
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
async function stopPlausibleAnalyticsService(request: FastifyRequest) {
|
||||
async function stopPlausibleAnalyticsService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -758,7 +897,7 @@ async function stopPlausibleAnalyticsService(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
async function startNocodbService(request: FastifyRequest) {
|
||||
async function startNocodbService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -824,7 +963,7 @@ async function startNocodbService(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
async function stopNocodbService(request: FastifyRequest) {
|
||||
async function stopNocodbService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -843,7 +982,7 @@ async function stopNocodbService(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
async function startMinioService(request: FastifyRequest) {
|
||||
async function startMinioService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -927,7 +1066,7 @@ async function startMinioService(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
async function stopMinioService(request: FastifyRequest) {
|
||||
async function stopMinioService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -947,7 +1086,7 @@ async function stopMinioService(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
async function startVscodeService(request: FastifyRequest) {
|
||||
async function startVscodeService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -1052,7 +1191,7 @@ async function startVscodeService(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
async function stopVscodeService(request: FastifyRequest) {
|
||||
async function stopVscodeService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -1071,7 +1210,7 @@ async function stopVscodeService(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
async function startWordpressService(request: FastifyRequest) {
|
||||
async function startWordpressService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -1079,7 +1218,6 @@ async function startWordpressService(request: FastifyRequest) {
|
||||
const {
|
||||
type,
|
||||
version,
|
||||
fqdn,
|
||||
destinationDockerId,
|
||||
serviceSecret,
|
||||
destinationDocker,
|
||||
@@ -1197,7 +1335,7 @@ async function startWordpressService(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
async function stopWordpressService(request: FastifyRequest) {
|
||||
async function stopWordpressService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -1246,7 +1384,7 @@ async function stopWordpressService(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
async function startVaultwardenService(request: FastifyRequest) {
|
||||
async function startVaultwardenService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -1313,7 +1451,7 @@ async function startVaultwardenService(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
async function stopVaultwardenService(request: FastifyRequest) {
|
||||
async function stopVaultwardenService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -1337,7 +1475,7 @@ async function stopVaultwardenService(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
async function startLanguageToolService(request: FastifyRequest) {
|
||||
async function startLanguageToolService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -1405,7 +1543,7 @@ async function startLanguageToolService(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
async function stopLanguageToolService(request: FastifyRequest) {
|
||||
async function stopLanguageToolService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -1429,7 +1567,7 @@ async function stopLanguageToolService(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
async function startN8nService(request: FastifyRequest) {
|
||||
async function startN8nService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -1497,7 +1635,7 @@ async function startN8nService(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
async function stopN8nService(request: FastifyRequest) {
|
||||
async function stopN8nService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -1521,7 +1659,7 @@ async function stopN8nService(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
async function startUptimekumaService(request: FastifyRequest) {
|
||||
async function startUptimekumaService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -1588,7 +1726,7 @@ async function startUptimekumaService(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
async function stopUptimekumaService(request: FastifyRequest) {
|
||||
async function stopUptimekumaService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -1612,7 +1750,7 @@ async function stopUptimekumaService(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
async function startGhostService(request: FastifyRequest) {
|
||||
async function startGhostService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -1641,6 +1779,7 @@ async function startGhostService(request: FastifyRequest) {
|
||||
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||
const image = getServiceImage(type);
|
||||
const domain = getDomain(fqdn);
|
||||
const port = getServiceMainPort('ghost');
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
const config = {
|
||||
ghost: {
|
||||
@@ -1739,7 +1878,7 @@ async function startGhostService(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
async function stopGhostService(request: FastifyRequest) {
|
||||
async function stopGhostService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -1767,7 +1906,7 @@ async function stopGhostService(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
async function startMeilisearchService(request: FastifyRequest) {
|
||||
async function startMeilisearchService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -1840,7 +1979,7 @@ async function startMeilisearchService(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
async function stopMeilisearchService(request: FastifyRequest) {
|
||||
async function stopMeilisearchService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -1864,7 +2003,7 @@ async function stopMeilisearchService(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
async function startUmamiService(request: FastifyRequest) {
|
||||
async function startUmamiService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -2059,7 +2198,7 @@ async function startUmamiService(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
async function stopUmamiService(request: FastifyRequest) {
|
||||
async function stopUmamiService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -2091,7 +2230,7 @@ async function stopUmamiService(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
async function startHasuraService(request: FastifyRequest) {
|
||||
async function startHasuraService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -2195,7 +2334,7 @@ async function startHasuraService(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
async function stopHasuraService(request: FastifyRequest) {
|
||||
async function stopHasuraService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -2227,7 +2366,7 @@ async function stopHasuraService(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
async function startFiderService(request: FastifyRequest) {
|
||||
async function startFiderService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -2358,7 +2497,7 @@ async function startFiderService(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
async function stopFiderService(request: FastifyRequest) {
|
||||
async function stopFiderService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
@@ -2390,7 +2529,171 @@ async function stopFiderService(request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function activatePlausibleUsers(request: FastifyRequest, reply: FastifyReply) {
|
||||
async function startMoodleService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
const service = await getServiceFromDB({ id, teamId });
|
||||
const {
|
||||
type,
|
||||
version,
|
||||
fqdn,
|
||||
destinationDockerId,
|
||||
destinationDocker,
|
||||
serviceSecret,
|
||||
exposePort,
|
||||
moodle: {
|
||||
defaultUsername,
|
||||
defaultPassword,
|
||||
defaultEmail,
|
||||
mariadbRootUser,
|
||||
mariadbRootUserPassword,
|
||||
mariadbDatabase,
|
||||
mariadbPassword,
|
||||
mariadbUser
|
||||
}
|
||||
} = service;
|
||||
const network = destinationDockerId && destinationDocker.network;
|
||||
const host = getEngine(destinationDocker.engine);
|
||||
const port = getServiceMainPort('moodle');
|
||||
|
||||
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||
const image = getServiceImage(type);
|
||||
const domain = getDomain(fqdn);
|
||||
const config = {
|
||||
moodle: {
|
||||
image: `${image}:${version}`,
|
||||
volume: `${id}-data:/bitnami/moodle`,
|
||||
environmentVariables: {
|
||||
MOODLE_USERNAME: defaultUsername,
|
||||
MOODLE_PASSWORD: defaultPassword,
|
||||
MOODLE_EMAIL: defaultEmail,
|
||||
MOODLE_DATABASE_HOST: `${id}-mariadb`,
|
||||
MOODLE_DATABASE_USER: mariadbUser,
|
||||
MOODLE_DATABASE_PASSWORD: mariadbPassword,
|
||||
MOODLE_DATABASE_NAME: mariadbDatabase,
|
||||
MOODLE_REVERSEPROXY: 'yes'
|
||||
}
|
||||
},
|
||||
mariadb: {
|
||||
image: 'bitnami/mariadb:latest',
|
||||
volume: `${id}-mariadb-data:/bitnami/mariadb`,
|
||||
environmentVariables: {
|
||||
MARIADB_USER: mariadbUser,
|
||||
MARIADB_PASSWORD: mariadbPassword,
|
||||
MARIADB_DATABASE: mariadbDatabase,
|
||||
MARIADB_ROOT_USER: mariadbRootUser,
|
||||
MARIADB_ROOT_PASSWORD: mariadbRootUserPassword
|
||||
}
|
||||
}
|
||||
};
|
||||
if (serviceSecret.length > 0) {
|
||||
serviceSecret.forEach((secret) => {
|
||||
config.moodle.environmentVariables[secret.name] = secret.value;
|
||||
});
|
||||
}
|
||||
|
||||
const composeFile: ComposeFile = {
|
||||
version: '3.8',
|
||||
services: {
|
||||
[id]: {
|
||||
container_name: id,
|
||||
image: config.moodle.image,
|
||||
environment: config.moodle.environmentVariables,
|
||||
networks: [network],
|
||||
volumes: [],
|
||||
restart: 'always',
|
||||
labels: makeLabelForServices('moodle'),
|
||||
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
condition: 'on-failure',
|
||||
delay: '5s',
|
||||
max_attempts: 3,
|
||||
window: '120s'
|
||||
}
|
||||
},
|
||||
depends_on: [`${id}-mariadb`]
|
||||
},
|
||||
[`${id}-mariadb`]: {
|
||||
container_name: `${id}-mariadb`,
|
||||
image: config.mariadb.image,
|
||||
environment: config.mariadb.environmentVariables,
|
||||
networks: [network],
|
||||
volumes: [],
|
||||
restart: 'always',
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
condition: 'on-failure',
|
||||
delay: '5s',
|
||||
max_attempts: 3,
|
||||
window: '120s'
|
||||
}
|
||||
},
|
||||
depends_on: []
|
||||
}
|
||||
|
||||
},
|
||||
networks: {
|
||||
[network]: {
|
||||
external: true
|
||||
}
|
||||
},
|
||||
volumes: {
|
||||
[config.moodle.volume.split(':')[0]]: {
|
||||
name: config.moodle.volume.split(':')[0]
|
||||
},
|
||||
[config.mariadb.volume.split(':')[0]]: {
|
||||
name: config.mariadb.volume.split(':')[0]
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
const composeFileDestination = `${workdir}/docker-compose.yaml`;
|
||||
await fs.writeFile(composeFileDestination, yaml.dump(composeFile));
|
||||
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} pull`);
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
|
||||
|
||||
return {}
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
async function stopMoodleService(request: FastifyRequest<ServiceStartStop>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
const service = await getServiceFromDB({ id, teamId });
|
||||
const { destinationDockerId, destinationDocker, fqdn } = service;
|
||||
if (destinationDockerId) {
|
||||
const engine = destinationDocker.engine;
|
||||
|
||||
try {
|
||||
const found = await checkContainer(engine, id);
|
||||
if (found) {
|
||||
await removeContainer({ id, engine });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
try {
|
||||
const found = await checkContainer(engine, `${id}-mariadb`);
|
||||
if (found) {
|
||||
await removeContainer({ id: `${id}-mariadb`, engine });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
return {}
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export async function activatePlausibleUsers(request: FastifyRequest<OnlyId>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const teamId = request.user.teamId;
|
||||
@@ -2414,4 +2717,166 @@ export async function activatePlausibleUsers(request: FastifyRequest, reply: Fas
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
}
|
||||
export async function activateWordpressFtp(request: FastifyRequest<ActivateWordpressFtp>, reply: FastifyReply) {
|
||||
const { id } = request.params
|
||||
const { ftpEnabled } = request.body;
|
||||
|
||||
const publicPort = await getFreePort();
|
||||
let ftpUser = cuid();
|
||||
let ftpPassword = generatePassword();
|
||||
|
||||
const hostkeyDir = isDev ? '/tmp/hostkeys' : '/app/ssl/hostkeys';
|
||||
try {
|
||||
const data = await prisma.wordpress.update({
|
||||
where: { serviceId: id },
|
||||
data: { ftpEnabled },
|
||||
include: { service: { include: { destinationDocker: true } } }
|
||||
});
|
||||
const {
|
||||
service: { destinationDockerId, destinationDocker },
|
||||
ftpPublicPort,
|
||||
ftpUser: user,
|
||||
ftpPassword: savedPassword,
|
||||
ftpHostKey,
|
||||
ftpHostKeyPrivate
|
||||
} = data;
|
||||
const { network, engine } = destinationDocker;
|
||||
const host = getEngine(engine);
|
||||
if (ftpEnabled) {
|
||||
if (user) ftpUser = user;
|
||||
if (savedPassword) ftpPassword = decrypt(savedPassword);
|
||||
|
||||
const { stdout: password } = await asyncExecShell(
|
||||
`echo ${ftpPassword} | openssl passwd -1 -stdin`
|
||||
);
|
||||
if (destinationDockerId) {
|
||||
try {
|
||||
await fs.stat(hostkeyDir);
|
||||
} catch (error) {
|
||||
await asyncExecShell(`mkdir -p ${hostkeyDir}`);
|
||||
}
|
||||
if (!ftpHostKey) {
|
||||
await asyncExecShell(
|
||||
`ssh-keygen -t ed25519 -f ssh_host_ed25519_key -N "" -q -f ${hostkeyDir}/${id}.ed25519`
|
||||
);
|
||||
const { stdout: ftpHostKey } = await asyncExecShell(`cat ${hostkeyDir}/${id}.ed25519`);
|
||||
await prisma.wordpress.update({
|
||||
where: { serviceId: id },
|
||||
data: { ftpHostKey: encrypt(ftpHostKey) }
|
||||
});
|
||||
} else {
|
||||
await asyncExecShell(`echo "${decrypt(ftpHostKey)}" > ${hostkeyDir}/${id}.ed25519`);
|
||||
}
|
||||
if (!ftpHostKeyPrivate) {
|
||||
await asyncExecShell(`ssh-keygen -t rsa -b 4096 -N "" -f ${hostkeyDir}/${id}.rsa`);
|
||||
const { stdout: ftpHostKeyPrivate } = await asyncExecShell(`cat ${hostkeyDir}/${id}.rsa`);
|
||||
await prisma.wordpress.update({
|
||||
where: { serviceId: id },
|
||||
data: { ftpHostKeyPrivate: encrypt(ftpHostKeyPrivate) }
|
||||
});
|
||||
} else {
|
||||
await asyncExecShell(`echo "${decrypt(ftpHostKeyPrivate)}" > ${hostkeyDir}/${id}.rsa`);
|
||||
}
|
||||
|
||||
await prisma.wordpress.update({
|
||||
where: { serviceId: id },
|
||||
data: {
|
||||
ftpPublicPort: publicPort,
|
||||
ftpUser: user ? undefined : ftpUser,
|
||||
ftpPassword: savedPassword ? undefined : encrypt(ftpPassword)
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
const isRunning = await checkContainer(engine, `${id}-ftp`);
|
||||
if (isRunning) {
|
||||
await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker stop -t 0 ${id}-ftp && docker rm ${id}-ftp`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
//
|
||||
}
|
||||
const volumes = [
|
||||
`${id}-wordpress-data:/home/${ftpUser}/wordpress`,
|
||||
`${isDev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
|
||||
}/${id}.ed25519:/etc/ssh/ssh_host_ed25519_key`,
|
||||
`${isDev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
|
||||
}/${id}.rsa:/etc/ssh/ssh_host_rsa_key`,
|
||||
`${isDev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
|
||||
}/${id}.sh:/etc/sftp.d/chmod.sh`
|
||||
];
|
||||
|
||||
const compose: ComposeFile = {
|
||||
version: '3.8',
|
||||
services: {
|
||||
[`${id}-ftp`]: {
|
||||
image: `atmoz/sftp:alpine`,
|
||||
command: `'${ftpUser}:${password.replace('\n', '').replace(/\$/g, '$$$')}:e:33'`,
|
||||
extra_hosts: ['host.docker.internal:host-gateway'],
|
||||
container_name: `${id}-ftp`,
|
||||
volumes,
|
||||
networks: [network],
|
||||
depends_on: [],
|
||||
restart: 'always'
|
||||
}
|
||||
},
|
||||
networks: {
|
||||
[network]: {
|
||||
external: true
|
||||
}
|
||||
},
|
||||
volumes: {
|
||||
[`${id}-wordpress-data`]: {
|
||||
external: true,
|
||||
name: `${id}-wordpress-data`
|
||||
}
|
||||
}
|
||||
};
|
||||
await fs.writeFile(
|
||||
`${hostkeyDir}/${id}.sh`,
|
||||
`#!/bin/bash\nchmod 600 /etc/ssh/ssh_host_ed25519_key /etc/ssh/ssh_host_rsa_key\nuserdel -f xfs\nchown -R 33:33 /home/${ftpUser}/wordpress/`
|
||||
);
|
||||
await asyncExecShell(`chmod +x ${hostkeyDir}/${id}.sh`);
|
||||
await fs.writeFile(`${hostkeyDir}/${id}-docker-compose.yml`, yaml.dump(compose));
|
||||
await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker compose -f ${hostkeyDir}/${id}-docker-compose.yml up -d`
|
||||
);
|
||||
}
|
||||
return reply.code(201).send({
|
||||
publicPort,
|
||||
ftpUser,
|
||||
ftpPassword
|
||||
})
|
||||
} else {
|
||||
await prisma.wordpress.update({
|
||||
where: { serviceId: id },
|
||||
data: { ftpPublicPort: null }
|
||||
});
|
||||
try {
|
||||
await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker stop -t 0 ${id}-ftp && docker rm ${id}-ftp`
|
||||
);
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
await stopTcpHttpProxy(id, destinationDocker, ftpPublicPort);
|
||||
return {
|
||||
};
|
||||
}
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message })
|
||||
} finally {
|
||||
try {
|
||||
await asyncExecShell(
|
||||
`rm -fr ${hostkeyDir}/${id}-docker-compose.yml ${hostkeyDir}/${id}.ed25519 ${hostkeyDir}/${id}.ed25519.pub ${hostkeyDir}/${id}.rsa ${hostkeyDir}/${id}.rsa.pub ${hostkeyDir}/${id}.sh`
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { FastifyPluginAsync } from 'fastify';
|
||||
import {
|
||||
activatePlausibleUsers,
|
||||
activateWordpressFtp,
|
||||
checkService,
|
||||
deleteService,
|
||||
deleteServiceSecret,
|
||||
@@ -26,45 +27,49 @@ import {
|
||||
stopService
|
||||
} from './handlers';
|
||||
|
||||
const root: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
|
||||
fastify.addHook('onRequest', async (request, reply) => {
|
||||
import type { OnlyId } from '../../../../types';
|
||||
import type { ActivateWordpressFtp, CheckService, DeleteServiceSecret, DeleteServiceStorage, GetServiceLogs, SaveService, SaveServiceDestination, SaveServiceSecret, SaveServiceSettings, SaveServiceStorage, SaveServiceType, SaveServiceVersion, ServiceStartStop, SetWordpressSettings } from './types';
|
||||
|
||||
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||
fastify.addHook('onRequest', async (request) => {
|
||||
return await request.jwtVerify()
|
||||
})
|
||||
fastify.get('/', async (request) => await listServices(request));
|
||||
fastify.post('/new', async (request, reply) => await newService(request, reply));
|
||||
|
||||
fastify.get('/:id', async (request) => await getService(request));
|
||||
fastify.post('/:id', async (request, reply) => await saveService(request, reply));
|
||||
fastify.delete('/:id', async (request) => await deleteService(request));
|
||||
fastify.get<OnlyId>('/:id', async (request) => await getService(request));
|
||||
fastify.post<SaveService>('/:id', async (request, reply) => await saveService(request, reply));
|
||||
fastify.delete<OnlyId>('/:id', async (request) => await deleteService(request));
|
||||
|
||||
fastify.post('/:id/check', async (request) => await checkService(request));
|
||||
fastify.post<CheckService>('/:id/check', async (request) => await checkService(request));
|
||||
|
||||
fastify.post('/:id/settings', async (request, reply) => await saveServiceSettings(request, reply));
|
||||
fastify.post<SaveServiceSettings>('/:id/settings', async (request, reply) => await saveServiceSettings(request, reply));
|
||||
|
||||
fastify.get('/:id/secrets', async (request) => await getServiceSecrets(request));
|
||||
fastify.post('/:id/secrets', async (request, reply) => await saveServiceSecret(request, reply));
|
||||
fastify.delete('/:id/secrets', async (request) => await deleteServiceSecret(request));
|
||||
fastify.get<OnlyId>('/:id/secrets', async (request) => await getServiceSecrets(request));
|
||||
fastify.post<SaveServiceSecret>('/:id/secrets', async (request, reply) => await saveServiceSecret(request, reply));
|
||||
fastify.delete<DeleteServiceSecret>('/:id/secrets', async (request) => await deleteServiceSecret(request));
|
||||
|
||||
fastify.get('/:id/storages', async (request) => await getServiceStorages(request));
|
||||
fastify.post('/:id/storages', async (request, reply) => await saveServiceStorage(request, reply));
|
||||
fastify.delete('/:id/storages', async (request) => await deleteServiceStorage(request));
|
||||
fastify.get<OnlyId>('/:id/storages', async (request) => await getServiceStorages(request));
|
||||
fastify.post<SaveServiceStorage>('/:id/storages', async (request, reply) => await saveServiceStorage(request, reply));
|
||||
fastify.delete<DeleteServiceStorage>('/:id/storages', async (request) => await deleteServiceStorage(request));
|
||||
|
||||
fastify.get('/:id/configuration/type', async (request) => await getServiceType(request));
|
||||
fastify.post('/:id/configuration/type', async (request, reply) => await saveServiceType(request, reply));
|
||||
fastify.post<SaveServiceType>('/:id/configuration/type', async (request, reply) => await saveServiceType(request, reply));
|
||||
|
||||
fastify.get('/:id/configuration/version', async (request) => await getServiceVersions(request));
|
||||
fastify.post('/:id/configuration/version', async (request, reply) => await saveServiceVersion(request, reply));
|
||||
fastify.get<OnlyId>('/:id/configuration/version', async (request) => await getServiceVersions(request));
|
||||
fastify.post<SaveServiceVersion>('/:id/configuration/version', async (request, reply) => await saveServiceVersion(request, reply));
|
||||
|
||||
fastify.post('/:id/configuration/destination', async (request, reply) => await saveServiceDestination(request, reply));
|
||||
fastify.post<SaveServiceDestination>('/:id/configuration/destination', async (request, reply) => await saveServiceDestination(request, reply));
|
||||
|
||||
fastify.get('/:id/usage', async (request) => await getServiceUsage(request));
|
||||
fastify.get('/:id/logs', async (request) => await getServiceLogs(request));
|
||||
fastify.get<OnlyId>('/:id/usage', async (request) => await getServiceUsage(request));
|
||||
fastify.get<GetServiceLogs>('/:id/logs', async (request) => await getServiceLogs(request));
|
||||
|
||||
fastify.post('/:id/:type/start', async (request) => await startService(request));
|
||||
fastify.post('/:id/:type/stop', async (request) => await stopService(request));
|
||||
fastify.post('/:id/:type/settings', async (request, reply) => await setSettingsService(request, reply));
|
||||
fastify.post<ServiceStartStop>('/:id/:type/start', async (request) => await startService(request));
|
||||
fastify.post<ServiceStartStop>('/:id/:type/stop', async (request) => await stopService(request));
|
||||
fastify.post<ServiceStartStop & SetWordpressSettings>('/:id/:type/settings', async (request, reply) => await setSettingsService(request, reply));
|
||||
|
||||
fastify.post('/:id/plausibleanalytics/activate', async (request, reply) => await activatePlausibleUsers(request, reply));
|
||||
fastify.post<OnlyId>('/:id/plausibleanalytics/activate', async (request, reply) => await activatePlausibleUsers(request, reply));
|
||||
fastify.post<ActivateWordpressFtp>('/:id/wordpress/ftp', async (request, reply) => await activateWordpressFtp(request, reply));
|
||||
};
|
||||
|
||||
export default root;
|
||||
|
||||
87
apps/api/src/routes/api/v1/services/types.ts
Normal file
87
apps/api/src/routes/api/v1/services/types.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { OnlyId } from "../../../../types";
|
||||
|
||||
export interface SaveServiceType extends OnlyId {
|
||||
Body: {
|
||||
type: string
|
||||
}
|
||||
}
|
||||
export interface SaveServiceVersion extends OnlyId {
|
||||
Body: {
|
||||
version: string
|
||||
}
|
||||
}
|
||||
export interface SaveServiceDestination extends OnlyId {
|
||||
Body: {
|
||||
destinationId: string
|
||||
}
|
||||
}
|
||||
export interface GetServiceLogs extends OnlyId {
|
||||
Querystring: {
|
||||
since: number
|
||||
}
|
||||
}
|
||||
export interface SaveServiceSettings extends OnlyId {
|
||||
Body: {
|
||||
dualCerts: boolean
|
||||
}
|
||||
}
|
||||
export interface CheckService extends OnlyId {
|
||||
Body: {
|
||||
fqdn: string,
|
||||
exposePort: number,
|
||||
otherFqdns: Array<string>
|
||||
}
|
||||
}
|
||||
export interface SaveService extends OnlyId {
|
||||
Body: {
|
||||
name: string,
|
||||
fqdn: string,
|
||||
exposePort: number,
|
||||
type: string
|
||||
}
|
||||
}
|
||||
export interface SaveServiceSecret extends OnlyId {
|
||||
Body: {
|
||||
name: string,
|
||||
value: string,
|
||||
isNew: string,
|
||||
}
|
||||
}
|
||||
export interface DeleteServiceSecret extends OnlyId {
|
||||
Body: {
|
||||
name: string,
|
||||
}
|
||||
}
|
||||
export interface SaveServiceStorage extends OnlyId {
|
||||
Body: {
|
||||
path: string,
|
||||
newStorage: string,
|
||||
storageId: string,
|
||||
}
|
||||
}
|
||||
|
||||
export interface DeleteServiceStorage extends OnlyId {
|
||||
Body: {
|
||||
path: string,
|
||||
}
|
||||
}
|
||||
export interface ServiceStartStop {
|
||||
Params: {
|
||||
id?: string,
|
||||
type: string,
|
||||
}
|
||||
}
|
||||
export interface SetWordpressSettings extends OnlyId {
|
||||
Body: {
|
||||
ownMysql: boolean
|
||||
}
|
||||
}
|
||||
export interface ActivateWordpressFtp extends OnlyId {
|
||||
Body: {
|
||||
ftpEnabled: boolean
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { promises as dns } from 'dns';
|
||||
|
||||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { checkDomainsIsValidInDNS, errorHandler, getDomain, isDNSValid, isDomainConfigured, listSettings, prisma } from '../../../../lib/common';
|
||||
import { CheckDNS, CheckDomain, DeleteDomain, SaveSettings } from './types';
|
||||
|
||||
|
||||
export async function listAllSettings(request: FastifyRequest) {
|
||||
@@ -14,7 +15,7 @@ export async function listAllSettings(request: FastifyRequest) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function saveSettings(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function saveSettings(request: FastifyRequest<SaveSettings>, reply: FastifyReply) {
|
||||
try {
|
||||
const {
|
||||
fqdn,
|
||||
@@ -41,7 +42,7 @@ export async function saveSettings(request: FastifyRequest, reply: FastifyReply)
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function deleteDomain(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function deleteDomain(request: FastifyRequest<DeleteDomain>, reply: FastifyReply) {
|
||||
try {
|
||||
const { fqdn } = request.body
|
||||
let ip;
|
||||
@@ -57,7 +58,7 @@ export async function deleteDomain(request: FastifyRequest, reply: FastifyReply)
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkDomain(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function checkDomain(request: FastifyRequest<CheckDomain>) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
let { fqdn, forceSave, dualCerts, isDNSCheckEnabled } = request.body
|
||||
@@ -74,9 +75,9 @@ export async function checkDomain(request: FastifyRequest, reply: FastifyReply)
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function checkDNS(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function checkDNS(request: FastifyRequest<CheckDNS>) {
|
||||
try {
|
||||
const { id, domain } = request.params;
|
||||
const { domain } = request.params;
|
||||
await isDNSValid(request.hostname, domain);
|
||||
return {}
|
||||
} catch ({ status, message }) {
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import { FastifyPluginAsync } from 'fastify';
|
||||
import { checkDNS, checkDomain, deleteDomain, listAllSettings, saveSettings } from './handlers';
|
||||
import { CheckDNS, CheckDomain, DeleteDomain, SaveSettings } from './types';
|
||||
|
||||
|
||||
const root: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
|
||||
fastify.addHook('onRequest', async (request, reply) => {
|
||||
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||
fastify.addHook('onRequest', async (request) => {
|
||||
return await request.jwtVerify()
|
||||
})
|
||||
fastify.get('/', async (request) => await listAllSettings(request));
|
||||
fastify.post('/', async (request, reply) => await saveSettings(request, reply));
|
||||
fastify.delete('/', async (request, reply) => await deleteDomain(request, reply));
|
||||
fastify.post<SaveSettings>('/', async (request, reply) => await saveSettings(request, reply));
|
||||
fastify.delete<DeleteDomain>('/', async (request, reply) => await deleteDomain(request, reply));
|
||||
|
||||
fastify.get('/check', async (request, reply) => await checkDNS(request, reply));
|
||||
fastify.post('/check', async (request, reply) => await checkDomain(request, reply));
|
||||
fastify.get<CheckDNS>('/check', async (request) => await checkDNS(request));
|
||||
fastify.post<CheckDomain>('/check', async (request) => await checkDomain(request));
|
||||
};
|
||||
|
||||
export default root;
|
||||
|
||||
31
apps/api/src/routes/api/v1/settings/types.ts
Normal file
31
apps/api/src/routes/api/v1/settings/types.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { OnlyId } from "../../../../types"
|
||||
|
||||
export interface SaveSettings {
|
||||
Body: {
|
||||
fqdn: string,
|
||||
isRegistrationEnabled: boolean,
|
||||
dualCerts: boolean,
|
||||
minPort: number,
|
||||
maxPort: number,
|
||||
isAutoUpdateEnabled: boolean,
|
||||
isDNSCheckEnabled: boolean
|
||||
}
|
||||
}
|
||||
export interface DeleteDomain {
|
||||
Body: {
|
||||
fqdn: string
|
||||
}
|
||||
}
|
||||
export interface CheckDomain extends OnlyId {
|
||||
Body: {
|
||||
fqdn: string,
|
||||
forceSave: boolean,
|
||||
dualCerts: boolean,
|
||||
isDNSCheckEnabled: boolean,
|
||||
}
|
||||
}
|
||||
export interface CheckDNS {
|
||||
Params: {
|
||||
domain: string,
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@ import cuid from 'cuid';
|
||||
import type { FastifyRequest } from 'fastify';
|
||||
import { FastifyReply } from 'fastify';
|
||||
import { decrypt, encrypt, errorHandler, prisma } from '../../../../lib/common';
|
||||
import { OnlyId } from '../../../../types';
|
||||
import { CheckGitLabOAuthId, SaveGitHubSource, SaveGitLabSource } from './types';
|
||||
|
||||
export async function listSources(request: FastifyRequest) {
|
||||
try {
|
||||
@@ -20,17 +22,18 @@ export async function listSources(request: FastifyRequest) {
|
||||
export async function saveSource(request, reply) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const { name, htmlUrl, apiUrl } = request.body
|
||||
let { name, htmlUrl, apiUrl, customPort } = request.body
|
||||
if (customPort) customPort = Number(customPort)
|
||||
await prisma.gitSource.update({
|
||||
where: { id },
|
||||
data: { name, htmlUrl, apiUrl }
|
||||
data: { name, htmlUrl, apiUrl, customPort }
|
||||
});
|
||||
return reply.code(201).send()
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function getSource(request: FastifyRequest) {
|
||||
export async function getSource(request: FastifyRequest<OnlyId>) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const { teamId } = request.user
|
||||
@@ -45,7 +48,8 @@ export async function getSource(request: FastifyRequest) {
|
||||
type: null,
|
||||
htmlUrl: null,
|
||||
apiUrl: null,
|
||||
organization: null
|
||||
organization: null,
|
||||
customPort: 22,
|
||||
},
|
||||
settings
|
||||
}
|
||||
@@ -58,7 +62,7 @@ export async function getSource(request: FastifyRequest) {
|
||||
if (!source) {
|
||||
throw { status: 404, message: 'Source not found.' }
|
||||
}
|
||||
|
||||
|
||||
if (source?.githubApp?.clientSecret)
|
||||
source.githubApp.clientSecret = decrypt(source.githubApp.clientSecret);
|
||||
if (source?.githubApp?.webhookSecret)
|
||||
@@ -95,11 +99,14 @@ export async function deleteSource(request) {
|
||||
}
|
||||
|
||||
}
|
||||
export async function saveGitHubSource(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function saveGitHubSource(request: FastifyRequest<SaveGitHubSource>) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const { name, type, htmlUrl, apiUrl, organization } = request.body
|
||||
const { teamId } = request.user
|
||||
|
||||
const { id } = request.params
|
||||
let { name, htmlUrl, apiUrl, organization, customPort } = request.body
|
||||
|
||||
if (customPort) customPort = Number(customPort)
|
||||
if (id === 'new') {
|
||||
const newId = cuid()
|
||||
await prisma.gitSource.create({
|
||||
@@ -109,6 +116,7 @@ export async function saveGitHubSource(request: FastifyRequest, reply: FastifyRe
|
||||
htmlUrl,
|
||||
apiUrl,
|
||||
organization,
|
||||
customPort,
|
||||
type: 'github',
|
||||
teams: { connect: { id: teamId } }
|
||||
}
|
||||
@@ -122,19 +130,20 @@ export async function saveGitHubSource(request: FastifyRequest, reply: FastifyRe
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function saveGitLabSource(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function saveGitLabSource(request: FastifyRequest<SaveGitLabSource>) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
const { teamId } = request.user
|
||||
let { type, name, htmlUrl, apiUrl, oauthId, appId, appSecret, groupName } =
|
||||
let { type, name, htmlUrl, apiUrl, oauthId, appId, appSecret, groupName, customPort } =
|
||||
request.body
|
||||
|
||||
oauthId = Number(oauthId);
|
||||
if (oauthId) oauthId = Number(oauthId);
|
||||
if (customPort) customPort = Number(customPort)
|
||||
const encryptedAppSecret = encrypt(appSecret);
|
||||
|
||||
if (id === 'new') {
|
||||
const newId = cuid()
|
||||
await prisma.gitSource.create({ data: { id: newId, type, apiUrl, htmlUrl, name, teams: { connect: { id: teamId } } } });
|
||||
await prisma.gitSource.create({ data: { id: newId, type, apiUrl, htmlUrl, name, customPort, teams: { connect: { id: teamId } } } });
|
||||
await prisma.gitlabApp.create({
|
||||
data: {
|
||||
teams: { connect: { id: teamId } },
|
||||
@@ -150,7 +159,7 @@ export async function saveGitLabSource(request: FastifyRequest, reply: FastifyRe
|
||||
id: newId
|
||||
}
|
||||
} else {
|
||||
await prisma.gitSource.update({ where: { id }, data: { type, apiUrl, htmlUrl, name } });
|
||||
await prisma.gitSource.update({ where: { id }, data: { type, apiUrl, htmlUrl, name, customPort } });
|
||||
await prisma.gitlabApp.update({
|
||||
where: { id },
|
||||
data: {
|
||||
@@ -168,7 +177,7 @@ export async function saveGitLabSource(request: FastifyRequest, reply: FastifyRe
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkGitLabOAuthID(request: FastifyRequest) {
|
||||
export async function checkGitLabOAuthID(request: FastifyRequest<CheckGitLabOAuthId>) {
|
||||
try {
|
||||
const { oauthId } = request.body
|
||||
const found = await prisma.gitlabApp.findFirst({ where: { oauthId: Number(oauthId) } });
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import { FastifyPluginAsync } from 'fastify';
|
||||
import { checkGitLabOAuthID, deleteSource, getSource, listSources, saveGitHubSource, saveGitLabSource, saveSource } from './handlers';
|
||||
|
||||
import type { OnlyId } from '../../../../types';
|
||||
import type { CheckGitLabOAuthId, SaveGitHubSource, SaveGitLabSource } from './types';
|
||||
|
||||
const root: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
|
||||
fastify.addHook('onRequest', async (request, reply) => {
|
||||
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||
fastify.addHook('onRequest', async (request) => {
|
||||
return await request.jwtVerify()
|
||||
})
|
||||
fastify.get('/', async (request) => await listSources(request));
|
||||
|
||||
fastify.get('/:id', async (request) => await getSource(request));
|
||||
fastify.get<OnlyId>('/:id', async (request) => await getSource(request));
|
||||
fastify.post('/:id', async (request, reply) => await saveSource(request, reply));
|
||||
fastify.delete('/:id', async (request) => await deleteSource(request));
|
||||
|
||||
fastify.post('/:id/check', async (request) => await checkGitLabOAuthID(request));
|
||||
fastify.post('/:id/github', async (request, reply) => await saveGitHubSource(request, reply));
|
||||
fastify.post('/:id/gitlab', async (request, reply) => await saveGitLabSource(request, reply));
|
||||
|
||||
fastify.post<CheckGitLabOAuthId>('/:id/check', async (request) => await checkGitLabOAuthID(request));
|
||||
fastify.post<SaveGitHubSource>('/:id/github', async (request) => await saveGitHubSource(request));
|
||||
fastify.post<SaveGitLabSource>('/:id/gitlab', async (request) => await saveGitLabSource(request));
|
||||
};
|
||||
|
||||
export default root;
|
||||
|
||||
29
apps/api/src/routes/api/v1/sources/types.ts
Normal file
29
apps/api/src/routes/api/v1/sources/types.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { OnlyId } from "../../../../types";
|
||||
|
||||
export interface SaveGitHubSource extends OnlyId {
|
||||
Body: {
|
||||
name: string,
|
||||
htmlUrl: string,
|
||||
apiUrl: string,
|
||||
organization: string,
|
||||
customPort: number,
|
||||
}
|
||||
}
|
||||
export interface SaveGitLabSource extends OnlyId {
|
||||
Body: {
|
||||
type: string,
|
||||
name: string,
|
||||
htmlUrl: string,
|
||||
apiUrl: string,
|
||||
oauthId: number,
|
||||
appId: string,
|
||||
appSecret: string,
|
||||
groupName: string,
|
||||
customPort: number,
|
||||
}
|
||||
}
|
||||
export interface CheckGitLabOAuthId extends OnlyId {
|
||||
Body: {
|
||||
oauthId: number,
|
||||
}
|
||||
}
|
||||
3
apps/api/src/routes/api/v1/types.ts
Normal file
3
apps/api/src/routes/api/v1/types.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface GetCurrentUser {
|
||||
Querystring: { teamId: string }
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
import axios from "axios";
|
||||
import cuid from "cuid";
|
||||
import crypto from "crypto";
|
||||
import type { FastifyReply, FastifyRequest } from "fastify";
|
||||
import { encrypt, errorHandler, isDev, prisma } from "../../../lib/common";
|
||||
import { encrypt, errorHandler, getUIUrl, isDev, prisma } from "../../../lib/common";
|
||||
import { checkContainer, removeContainer } from "../../../lib/docker";
|
||||
import { scheduler } from "../../../lib/scheduler";
|
||||
import { getApplicationFromDB, getApplicationFromDBWebhook } from "../../api/v1/applications/handlers";
|
||||
import { getApplicationFromDBWebhook } from "../../api/v1/applications/handlers";
|
||||
|
||||
export async function installGithub(request: FastifyRequest, reply: FastifyReply): Promise<any> {
|
||||
import type { FastifyReply, FastifyRequest } from "fastify";
|
||||
import type { GitHubEvents, InstallGithub } from "./types";
|
||||
|
||||
export async function installGithub(request: FastifyRequest<InstallGithub>, reply: FastifyReply): Promise<any> {
|
||||
try {
|
||||
const { gitSourceId, installation_id } = request.query;
|
||||
const source = await prisma.gitSource.findUnique({
|
||||
@@ -19,7 +21,7 @@ export async function installGithub(request: FastifyRequest, reply: FastifyReply
|
||||
data: { installationId: Number(installation_id) }
|
||||
});
|
||||
if (isDev) {
|
||||
return reply.redirect(`http://localhost:3000/sources/${gitSourceId}`)
|
||||
return reply.redirect(`${getUIUrl()}/sources/${gitSourceId}`)
|
||||
} else {
|
||||
return reply.redirect(`/sources/${gitSourceId}`)
|
||||
}
|
||||
@@ -55,7 +57,7 @@ export async function configureGitHubApp(request, reply) {
|
||||
}
|
||||
});
|
||||
if (isDev) {
|
||||
return reply.redirect(`http://localhost:3000/sources/${state}`)
|
||||
return reply.redirect(`${getUIUrl()}/sources/${state}`)
|
||||
} else {
|
||||
return reply.redirect(`/sources/${state}`)
|
||||
}
|
||||
@@ -63,7 +65,7 @@ export async function configureGitHubApp(request, reply) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function gitHubEvents(request: FastifyRequest, reply: FastifyReply): Promise<any> {
|
||||
export async function gitHubEvents(request: FastifyRequest<GitHubEvents>): Promise<any> {
|
||||
try {
|
||||
const buildId = cuid();
|
||||
const allowedGithubEvents = ['push', 'pull_request'];
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { FastifyPluginAsync } from 'fastify';
|
||||
import { configureGitHubApp, gitHubEvents, installGithub } from './handlers';
|
||||
|
||||
const root: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
|
||||
import type { GitHubEvents, InstallGithub } from './types';
|
||||
|
||||
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||
fastify.get('/', async (request, reply) => configureGitHubApp(request, reply));
|
||||
fastify.get('/install', async (request, reply) => installGithub(request, reply));
|
||||
fastify.post('/events', async (request, reply) => gitHubEvents(request, reply));
|
||||
fastify.get<InstallGithub>('/install', async (request, reply) => installGithub(request, reply));
|
||||
fastify.post<GitHubEvents>('/events', async (request, reply) => gitHubEvents(request));
|
||||
};
|
||||
|
||||
export default root;
|
||||
|
||||
20
apps/api/src/routes/webhooks/github/types.ts
Normal file
20
apps/api/src/routes/webhooks/github/types.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
export interface InstallGithub {
|
||||
Querystring: {
|
||||
gitSourceId: string,
|
||||
installation_id: string
|
||||
}
|
||||
}
|
||||
export interface GitHubEvents {
|
||||
Body: {
|
||||
number: string,
|
||||
action: string,
|
||||
repository: string,
|
||||
ref: string,
|
||||
pull_request: {
|
||||
head: {
|
||||
ref: string,
|
||||
repo: string
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,21 +2,23 @@ import axios from "axios";
|
||||
import cuid from "cuid";
|
||||
import crypto from "crypto";
|
||||
import type { FastifyReply, FastifyRequest } from "fastify";
|
||||
import { encrypt, errorHandler, isDev, listSettings, prisma } from "../../../lib/common";
|
||||
import { errorHandler, getAPIUrl, isDev, listSettings, prisma } from "../../../lib/common";
|
||||
import { checkContainer, removeContainer } from "../../../lib/docker";
|
||||
import { scheduler } from "../../../lib/scheduler";
|
||||
import { getApplicationFromDB, getApplicationFromDBWebhook } from "../../api/v1/applications/handlers";
|
||||
|
||||
export async function configureGitLabApp(request: FastifyRequest, reply: FastifyReply) {
|
||||
import type { ConfigureGitLabApp, GitLabEvents } from "./types";
|
||||
|
||||
export async function configureGitLabApp(request: FastifyRequest<ConfigureGitLabApp>, reply: FastifyReply) {
|
||||
try {
|
||||
const { code, state } = request.query;
|
||||
const { fqdn } = await listSettings();
|
||||
const { gitSource: { gitlabApp: { appId, appSecret }, htmlUrl } } = await getApplicationFromDB(state, undefined);
|
||||
const { gitSource: { gitlabApp: { appId, appSecret }, htmlUrl } }: any = await getApplicationFromDB(state, undefined);
|
||||
|
||||
let domain = `http://${request.hostname}`;
|
||||
if (fqdn) domain = fqdn;
|
||||
if (isDev) {
|
||||
domain = `http://localhost:3001`;
|
||||
domain = getAPIUrl();
|
||||
}
|
||||
const params = new URLSearchParams({
|
||||
client_id: appId,
|
||||
@@ -28,7 +30,7 @@ export async function configureGitLabApp(request: FastifyRequest, reply: Fastify
|
||||
});
|
||||
const { data } = await axios.post(`${htmlUrl}/oauth/token`, params)
|
||||
if (isDev) {
|
||||
return reply.redirect(`http://localhost:3000/webhooks/success?token=${data.access_token}`)
|
||||
return reply.redirect(`${getAPIUrl()}/webhooks/success?token=${data.access_token}`)
|
||||
}
|
||||
return reply.redirect(`/webhooks/success?token=${data.access_token}`)
|
||||
} catch ({ status, message, ...other }) {
|
||||
@@ -36,12 +38,13 @@ export async function configureGitLabApp(request: FastifyRequest, reply: Fastify
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function gitLabEvents(request: FastifyRequest, reply: FastifyReply) {
|
||||
export async function gitLabEvents(request: FastifyRequest<GitLabEvents>) {
|
||||
const { object_kind: objectKind, ref, project_id } = request.body
|
||||
try {
|
||||
const buildId = cuid();
|
||||
|
||||
const allowedActions = ['opened', 'reopen', 'close', 'open', 'update'];
|
||||
const { object_kind: objectKind, ref, project_id } = request.body
|
||||
|
||||
const webhookToken = request.headers['x-gitlab-token'];
|
||||
if (!webhookToken) {
|
||||
throw { status: 500, message: 'Invalid webhookToken.' }
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { FastifyPluginAsync } from 'fastify';
|
||||
import { configureGitLabApp, gitLabEvents } from './handlers';
|
||||
|
||||
const root: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
|
||||
fastify.get('/', async (request, reply) => configureGitLabApp(request, reply));
|
||||
fastify.post('/events', async (request, reply) => gitLabEvents(request, reply));
|
||||
import type { ConfigureGitLabApp, GitLabEvents } from './types';
|
||||
|
||||
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||
fastify.get<ConfigureGitLabApp>('/', async (request, reply) => configureGitLabApp(request, reply));
|
||||
fastify.post<GitLabEvents>('/events', async (request) => gitLabEvents(request));
|
||||
};
|
||||
|
||||
export default root;
|
||||
|
||||
24
apps/api/src/routes/webhooks/gitlab/types.ts
Normal file
24
apps/api/src/routes/webhooks/gitlab/types.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
export interface ConfigureGitLabApp {
|
||||
Querystring: {
|
||||
code: string,
|
||||
state: string
|
||||
}
|
||||
}
|
||||
export interface GitLabEvents {
|
||||
Body: {
|
||||
object_attributes: {
|
||||
work_in_progress: string
|
||||
isDraft: string
|
||||
action: string
|
||||
source_branch: string
|
||||
target_branch: string
|
||||
iid: string
|
||||
},
|
||||
project: {
|
||||
id: string
|
||||
},
|
||||
object_kind: string,
|
||||
ref: string,
|
||||
project_id: string
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { FastifyRequest } from "fastify";
|
||||
import { asyncExecShell, errorHandler, getDomain, isDev, listServicesWithIncludes, prisma, supportedServiceTypesAndVersions } from "../../../lib/common";
|
||||
import { getEngine } from "../../../lib/docker";
|
||||
import { TraefikOtherConfiguration } from "./types";
|
||||
|
||||
function configureMiddleware(
|
||||
{ id, container, port, domain, nakedDomain, isHttps, isWWW, isDualCerts, scriptName, type },
|
||||
@@ -362,7 +363,7 @@ export async function traefikConfiguration(request, reply) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function traefikOtherConfiguration(request: FastifyRequest, reply) {
|
||||
export async function traefikOtherConfiguration(request: FastifyRequest<TraefikOtherConfiguration>) {
|
||||
try {
|
||||
const { id } = request.query
|
||||
if (id) {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { FastifyPluginAsync } from 'fastify';
|
||||
import { traefikConfiguration, traefikOtherConfiguration } from './handlers';
|
||||
import { TraefikOtherConfiguration } from './types';
|
||||
|
||||
const root: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
|
||||
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||
fastify.get('/main.json', async (request, reply) => traefikConfiguration(request, reply));
|
||||
fastify.get('/other.json', async (request, reply) => traefikOtherConfiguration(request, reply));
|
||||
fastify.get<TraefikOtherConfiguration>('/other.json', async (request, reply) => traefikOtherConfiguration(request));
|
||||
};
|
||||
|
||||
export default root;
|
||||
|
||||
9
apps/api/src/routes/webhooks/traefik/types.ts
Normal file
9
apps/api/src/routes/webhooks/traefik/types.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export interface TraefikOtherConfiguration {
|
||||
Querystring: {
|
||||
id: string,
|
||||
privatePort: number,
|
||||
publicPort: number,
|
||||
type: string,
|
||||
address: string
|
||||
}
|
||||
}
|
||||
38
apps/api/src/types.ts
Normal file
38
apps/api/src/types.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
export interface OnlyId {
|
||||
Params: { id: string },
|
||||
}
|
||||
export interface SaveVersion extends OnlyId {
|
||||
Body: {
|
||||
version: string
|
||||
}
|
||||
}
|
||||
export interface SaveDatabaseDestination extends OnlyId {
|
||||
Body: {
|
||||
destinationId: string
|
||||
}
|
||||
}
|
||||
export interface GetDatabaseLogs extends OnlyId {
|
||||
Querystring: {
|
||||
since: number
|
||||
}
|
||||
}
|
||||
export interface SaveDatabase extends OnlyId {
|
||||
Body: {
|
||||
name: string,
|
||||
defaultDatabase: string,
|
||||
dbUser: string,
|
||||
dbUserPassword: string,
|
||||
rootUser: string,
|
||||
rootUserPassword: string,
|
||||
version: string,
|
||||
isRunning: boolean
|
||||
}
|
||||
}
|
||||
export interface SaveDatabaseSettings extends OnlyId {
|
||||
Body: {
|
||||
isPublic: boolean,
|
||||
appendOnly: boolean
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,12 +15,11 @@
|
||||
"format": "prettier --write --plugin-search-dir=. ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.23.1",
|
||||
"@sveltejs/adapter-auto": "1.0.0-next.53",
|
||||
"@sveltejs/kit": "1.0.0-next.359",
|
||||
"@playwright/test": "1.23.3",
|
||||
"@sveltejs/kit": "1.0.0-next.375",
|
||||
"@types/js-cookie": "3.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "5.30.5",
|
||||
"@typescript-eslint/parser": "5.30.5",
|
||||
"@typescript-eslint/eslint-plugin": "5.30.6",
|
||||
"@typescript-eslint/parser": "5.30.6",
|
||||
"autoprefixer": "10.4.7",
|
||||
"eslint": "8.19.0",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
@@ -28,18 +27,18 @@
|
||||
"postcss": "8.4.14",
|
||||
"prettier": "2.7.1",
|
||||
"prettier-plugin-svelte": "2.7.0",
|
||||
"svelte": "3.48.0",
|
||||
"svelte": "3.49.0",
|
||||
"svelte-check": "2.8.0",
|
||||
"svelte-preprocess": "4.10.7",
|
||||
"tailwindcss": "3.1.4",
|
||||
"tailwindcss": "3.1.6",
|
||||
"tailwindcss-scrollbar": "0.1.0",
|
||||
"tslib": "2.4.0",
|
||||
"typescript": "4.7.4",
|
||||
"vite": "^2.9.13"
|
||||
"vite": "^3.0.0"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@sveltejs/adapter-static": "1.0.0-next.34",
|
||||
"@sveltejs/adapter-static": "1.0.0-next.36",
|
||||
"@zerodevx/svelte-toast": "0.7.2",
|
||||
"cuid": "2.1.8",
|
||||
"js-cookie": "3.0.1",
|
||||
|
||||
3
apps/ui/src/app.d.ts
vendored
3
apps/ui/src/app.d.ts
vendored
@@ -19,3 +19,6 @@ declare namespace App {
|
||||
privatePort: string;
|
||||
}
|
||||
}
|
||||
|
||||
declare const GITPOD_WORKSPACE_URL: string
|
||||
|
||||
@@ -1,10 +1,28 @@
|
||||
import { browser, dev } from '$app/env';
|
||||
import { dev } from '$app/env';
|
||||
import Cookies from 'js-cookie';
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
|
||||
export function getAPIUrl() {
|
||||
if (GITPOD_WORKSPACE_URL) {
|
||||
const { href } = new URL(GITPOD_WORKSPACE_URL)
|
||||
const newURL = href.replace('https://', 'https://3001-').replace(/\/$/, '')
|
||||
return newURL
|
||||
}
|
||||
return dev ? 'http://localhost:3001' : 'http://localhost:3000';
|
||||
}
|
||||
export function getWebhookUrl(type: string) {
|
||||
console.log(GITPOD_WORKSPACE_URL)
|
||||
if (GITPOD_WORKSPACE_URL) {
|
||||
const { href } = new URL(GITPOD_WORKSPACE_URL)
|
||||
const newURL = href.replace('https://', 'https://3001-').replace(/\/$/, '')
|
||||
if (type === 'github') {
|
||||
return `${newURL}/webhooks/github/events`
|
||||
}
|
||||
if (type === 'gitlab') {
|
||||
return `${newURL}/webhooks/gitlab/events`
|
||||
}
|
||||
}
|
||||
return `https://webhook.site/0e5beb2c-4e9b-40e2-a89e-32295e570c21/events`;
|
||||
}
|
||||
async function send({
|
||||
method,
|
||||
path,
|
||||
@@ -52,7 +70,7 @@ async function send({
|
||||
}
|
||||
|
||||
if (dev && !path.startsWith('https://')) {
|
||||
path = `http://localhost:3001${path}`;
|
||||
path = `${getAPIUrl()}${path}`;
|
||||
}
|
||||
|
||||
const response = await fetch(`${path}`, opts);
|
||||
@@ -74,7 +92,7 @@ async function send({
|
||||
return {};
|
||||
}
|
||||
if (!response.ok) {
|
||||
if (response.status === 401 && !path.startsWith('https://api.github')) {
|
||||
if (response.status === 401 && !path.startsWith('https://api.github') && !path.includes('/v4/user')) {
|
||||
Cookies.remove('token');
|
||||
}
|
||||
|
||||
|
||||
@@ -1,260 +1,4 @@
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
export const asyncSleep = (delay: number) =>
|
||||
new Promise((resolve) => setTimeout(resolve, delay));
|
||||
|
||||
export function errorNotification(error: any): void {
|
||||
if (error.message) {
|
||||
toast.push(error.message);
|
||||
} else {
|
||||
toast.push('Ooops, something is not okay, are you okay?');
|
||||
}
|
||||
console.error(JSON.stringify(error));
|
||||
}
|
||||
|
||||
export function getDomain(domain: string) {
|
||||
return domain?.replace('https://', '').replace('http://', '');
|
||||
}
|
||||
export function dashify(str: string, options?: any): string {
|
||||
if (typeof str !== 'string') return str;
|
||||
return str
|
||||
.trim()
|
||||
.replace(/\W/g, (m) => (/[À-ž]/.test(m) ? m : '-'))
|
||||
.replace(/^-+|-+$/g, '')
|
||||
.replace(/-{2,}/g, (m) => (options && options.condense ? '-' : m))
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
export const dateOptions: any = {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: '2-digit',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
second: 'numeric',
|
||||
hour12: false
|
||||
};
|
||||
|
||||
export const staticDeployments = [
|
||||
'react',
|
||||
'vuejs',
|
||||
'static',
|
||||
'svelte',
|
||||
'gatsby',
|
||||
'php',
|
||||
'astro',
|
||||
'eleventy'
|
||||
];
|
||||
export const notNodeDeployments = ['php', 'docker', 'rust', 'python', 'deno', 'laravel'];
|
||||
|
||||
|
||||
export function generateRemoteEngine(destination: any) {
|
||||
return `ssh://${destination.user}@${destination.ipAddress}:${destination.port}`;
|
||||
}
|
||||
|
||||
export function changeQueryParams(buildId: string) {
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
queryParams.set('buildId', buildId);
|
||||
// @ts-ignore
|
||||
return history.pushState(null, null, '?' + queryParams.toString());
|
||||
}
|
||||
|
||||
// export const supportedDatabaseTypesAndVersions = [
|
||||
// {
|
||||
// name: 'mongodb',
|
||||
// fancyName: 'MongoDB',
|
||||
// baseImage: 'bitnami/mongodb',
|
||||
// versions: ['5.0', '4.4', '4.2']
|
||||
// },
|
||||
// { name: 'mysql', fancyName: 'MySQL', baseImage: 'bitnami/mysql', versions: ['8.0', '5.7'] },
|
||||
// {
|
||||
// name: 'mariadb',
|
||||
// fancyName: 'MariaDB',
|
||||
// baseImage: 'bitnami/mariadb',
|
||||
// versions: ['10.7', '10.6', '10.5', '10.4', '10.3', '10.2']
|
||||
// },
|
||||
// {
|
||||
// name: 'postgresql',
|
||||
// fancyName: 'PostgreSQL',
|
||||
// baseImage: 'bitnami/postgresql',
|
||||
// versions: ['14.2.0', '13.6.0', '12.10.0 ', '11.15.0', '10.20.0']
|
||||
// },
|
||||
// {
|
||||
// name: 'redis',
|
||||
// fancyName: 'Redis',
|
||||
// baseImage: 'bitnami/redis',
|
||||
// versions: ['6.2', '6.0', '5.0']
|
||||
// },
|
||||
// { name: 'couchdb', fancyName: 'CouchDB', baseImage: 'bitnami/couchdb', versions: ['3.2.1'] }
|
||||
// ];
|
||||
// export const supportedServiceTypesAndVersions = [
|
||||
// {
|
||||
// name: 'plausibleanalytics',
|
||||
// fancyName: 'Plausible Analytics',
|
||||
// baseImage: 'plausible/analytics',
|
||||
// images: ['bitnami/postgresql:13.2.0', 'yandex/clickhouse-server:21.3.2.5'],
|
||||
// versions: ['latest', 'stable'],
|
||||
// recommendedVersion: 'stable',
|
||||
// ports: {
|
||||
// main: 8000
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// name: 'nocodb',
|
||||
// fancyName: 'NocoDB',
|
||||
// baseImage: 'nocodb/nocodb',
|
||||
// versions: ['latest'],
|
||||
// recommendedVersion: 'latest',
|
||||
// ports: {
|
||||
// main: 8080
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// name: 'minio',
|
||||
// fancyName: 'MinIO',
|
||||
// baseImage: 'minio/minio',
|
||||
// versions: ['latest'],
|
||||
// recommendedVersion: 'latest',
|
||||
// ports: {
|
||||
// main: 9001
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// name: 'vscodeserver',
|
||||
// fancyName: 'VSCode Server',
|
||||
// baseImage: 'codercom/code-server',
|
||||
// versions: ['latest'],
|
||||
// recommendedVersion: 'latest',
|
||||
// ports: {
|
||||
// main: 8080
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// name: 'wordpress',
|
||||
// fancyName: 'Wordpress',
|
||||
// baseImage: 'wordpress',
|
||||
// images: ['bitnami/mysql:5.7'],
|
||||
// versions: ['latest', 'php8.1', 'php8.0', 'php7.4', 'php7.3'],
|
||||
// recommendedVersion: 'latest',
|
||||
// ports: {
|
||||
// main: 80
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// name: 'vaultwarden',
|
||||
// fancyName: 'Vaultwarden',
|
||||
// baseImage: 'vaultwarden/server',
|
||||
// versions: ['latest'],
|
||||
// recommendedVersion: 'latest',
|
||||
// ports: {
|
||||
// main: 80
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// name: 'languagetool',
|
||||
// fancyName: 'LanguageTool',
|
||||
// baseImage: 'silviof/docker-languagetool',
|
||||
// versions: ['latest'],
|
||||
// recommendedVersion: 'latest',
|
||||
// ports: {
|
||||
// main: 8010
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// name: 'n8n',
|
||||
// fancyName: 'n8n',
|
||||
// baseImage: 'n8nio/n8n',
|
||||
// versions: ['latest'],
|
||||
// recommendedVersion: 'latest',
|
||||
// ports: {
|
||||
// main: 5678
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// name: 'uptimekuma',
|
||||
// fancyName: 'Uptime Kuma',
|
||||
// baseImage: 'louislam/uptime-kuma',
|
||||
// versions: ['latest'],
|
||||
// recommendedVersion: 'latest',
|
||||
// ports: {
|
||||
// main: 3001
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// name: 'ghost',
|
||||
// fancyName: 'Ghost',
|
||||
// baseImage: 'bitnami/ghost',
|
||||
// images: ['bitnami/mariadb'],
|
||||
// versions: ['latest'],
|
||||
// recommendedVersion: 'latest',
|
||||
// ports: {
|
||||
// main: 2368
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// name: 'meilisearch',
|
||||
// fancyName: 'Meilisearch',
|
||||
// baseImage: 'getmeili/meilisearch',
|
||||
// images: [],
|
||||
// versions: ['latest'],
|
||||
// recommendedVersion: 'latest',
|
||||
// ports: {
|
||||
// main: 7700
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// name: 'umami',
|
||||
// fancyName: 'Umami',
|
||||
// baseImage: 'ghcr.io/mikecao/umami',
|
||||
// images: ['postgres:12-alpine'],
|
||||
// versions: ['postgresql-latest'],
|
||||
// recommendedVersion: 'postgresql-latest',
|
||||
// ports: {
|
||||
// main: 3000
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// name: 'hasura',
|
||||
// fancyName: 'Hasura',
|
||||
// baseImage: 'hasura/graphql-engine',
|
||||
// images: ['postgres:12-alpine'],
|
||||
// versions: ['latest', 'v2.5.1'],
|
||||
// recommendedVersion: 'v2.5.1',
|
||||
// ports: {
|
||||
// main: 8080
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// name: 'fider',
|
||||
// fancyName: 'Fider',
|
||||
// baseImage: 'getfider/fider',
|
||||
// images: ['postgres:12-alpine'],
|
||||
// versions: ['stable'],
|
||||
// recommendedVersion: 'stable',
|
||||
// ports: {
|
||||
// main: 3000
|
||||
// }
|
||||
// // },
|
||||
// // {
|
||||
// // name: 'appwrite',
|
||||
// // fancyName: 'AppWrite',
|
||||
// // baseImage: 'appwrite/appwrite',
|
||||
// // images: ['appwrite/influxdb', 'appwrite/telegraf', 'mariadb:10.7', 'redis:6.0-alpine3.12'],
|
||||
// // versions: ['latest', '0.13.0'],
|
||||
// // recommendedVersion: '0.13.0',
|
||||
// // ports: {
|
||||
// // main: 3000
|
||||
// // }
|
||||
// // }
|
||||
// }
|
||||
// ];
|
||||
|
||||
export const getServiceMainPort = (service: string) => {
|
||||
const serviceType = supportedServiceTypesAndVersions.find((s) => s.name === service);
|
||||
if (serviceType) {
|
||||
return serviceType.ports.main;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const supportedServiceTypesAndVersions = [
|
||||
{
|
||||
@@ -403,9 +147,122 @@ export const supportedServiceTypesAndVersions = [
|
||||
ports: {
|
||||
main: 3000
|
||||
}
|
||||
}
|
||||
},
|
||||
// {
|
||||
// name: 'moodle',
|
||||
// fancyName: 'Moodle',
|
||||
// baseImage: 'bitnami/moodle',
|
||||
// images: [],
|
||||
// versions: ['latest', 'v4.0.2'],
|
||||
// recommendedVersion: 'latest',
|
||||
// ports: {
|
||||
// main: 8080
|
||||
// }
|
||||
// }
|
||||
];
|
||||
|
||||
export const asyncSleep = (delay: number) =>
|
||||
new Promise((resolve) => setTimeout(resolve, delay));
|
||||
|
||||
export function errorNotification(error: any): void {
|
||||
if (error.message) {
|
||||
if (error.message === 'Cannot read properties of undefined (reading \'postMessage\')') {
|
||||
toast.push('Currently there is background process in progress. Please try again later.');
|
||||
return;
|
||||
}
|
||||
toast.push(error.message);
|
||||
} else {
|
||||
toast.push('Ooops, something is not okay, are you okay?');
|
||||
}
|
||||
console.error(JSON.stringify(error));
|
||||
}
|
||||
|
||||
export function getDomain(domain: string) {
|
||||
return domain?.replace('https://', '').replace('http://', '');
|
||||
}
|
||||
export function dashify(str: string, options?: any): string {
|
||||
if (typeof str !== 'string') return str;
|
||||
return str
|
||||
.trim()
|
||||
.replace(/\W/g, (m) => (/[À-ž]/.test(m) ? m : '-'))
|
||||
.replace(/^-+|-+$/g, '')
|
||||
.replace(/-{2,}/g, (m) => (options && options.condense ? '-' : m))
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
export const dateOptions: any = {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: '2-digit',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
second: 'numeric',
|
||||
hour12: false
|
||||
};
|
||||
|
||||
export const staticDeployments = [
|
||||
'react',
|
||||
'vuejs',
|
||||
'static',
|
||||
'svelte',
|
||||
'gatsby',
|
||||
'php',
|
||||
'astro',
|
||||
'eleventy'
|
||||
];
|
||||
export const notNodeDeployments = ['php', 'docker', 'rust', 'python', 'deno', 'laravel'];
|
||||
|
||||
|
||||
export function generateRemoteEngine(destination: any) {
|
||||
return `ssh://${destination.user}@${destination.ipAddress}:${destination.port}`;
|
||||
}
|
||||
|
||||
export function changeQueryParams(buildId: string) {
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
queryParams.set('buildId', buildId);
|
||||
// @ts-ignore
|
||||
return history.pushState(null, null, '?' + queryParams.toString());
|
||||
}
|
||||
|
||||
export const supportedDatabaseTypesAndVersions = [
|
||||
{
|
||||
name: 'mongodb',
|
||||
fancyName: 'MongoDB',
|
||||
baseImage: 'bitnami/mongodb',
|
||||
versions: ['5.0', '4.4', '4.2']
|
||||
},
|
||||
{ name: 'mysql', fancyName: 'MySQL', baseImage: 'bitnami/mysql', versions: ['8.0', '5.7'] },
|
||||
{
|
||||
name: 'mariadb',
|
||||
fancyName: 'MariaDB',
|
||||
baseImage: 'bitnami/mariadb',
|
||||
versions: ['10.7', '10.6', '10.5', '10.4', '10.3', '10.2']
|
||||
},
|
||||
{
|
||||
name: 'postgresql',
|
||||
fancyName: 'PostgreSQL',
|
||||
baseImage: 'bitnami/postgresql',
|
||||
versions: ['14.2.0', '13.6.0', '12.10.0 ', '11.15.0', '10.20.0']
|
||||
},
|
||||
{
|
||||
name: 'redis',
|
||||
fancyName: 'Redis',
|
||||
baseImage: 'bitnami/redis',
|
||||
versions: ['6.2', '6.0', '5.0']
|
||||
},
|
||||
{ name: 'couchdb', fancyName: 'CouchDB', baseImage: 'bitnami/couchdb', versions: ['3.2.1'] }
|
||||
];
|
||||
|
||||
export const getServiceMainPort = (service: string) => {
|
||||
const serviceType = supportedServiceTypesAndVersions.find((s) => s.name === service);
|
||||
if (serviceType) {
|
||||
return serviceType.ports.main;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
|
||||
export function handlerNotFoundLoad(error: any, url: URL) {
|
||||
if (error?.status === 404) {
|
||||
return {
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
};
|
||||
import { appSession } from '$lib/store';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { get } from '$lib/api';
|
||||
import { get, post } from '$lib/api';
|
||||
import { errorNotification } from '$lib/common';
|
||||
import Trend from './Trend.svelte';
|
||||
async function getStatus() {
|
||||
@@ -59,6 +59,9 @@
|
||||
cpu: 'stable',
|
||||
disk: 'stable'
|
||||
};
|
||||
async function manuallyCleanupStorage() {
|
||||
return await post('/internal/cleanup', {});
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if $appSession.teamId === '0'}
|
||||
@@ -129,6 +132,9 @@
|
||||
<dd class="mt-1 text-3xl font-semibold text-white">
|
||||
{usage?.disk.usedGb}<span class="text-sm">GB</span>
|
||||
</dd>
|
||||
<button on:click={manuallyCleanupStorage} class="bg-coollabs hover:bg-coollabs-100"
|
||||
>Cleanup Storage</button
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left"
|
||||
@@ -143,5 +149,6 @@
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
|
||||
<div class="px-6 pt-20 text-2xl font-bold">Resources</div>
|
||||
{/if}
|
||||
|
||||
8
apps/ui/src/lib/components/svg/services/Moodle.svelte
Normal file
8
apps/ui/src/lib/components/svg/services/Moodle.svelte
Normal file
@@ -0,0 +1,8 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = false;
|
||||
</script>
|
||||
<img
|
||||
alt="moodle logo"
|
||||
class={isAbsolute ? 'w-9 absolute top-0 left-0 -m-3 -mt-5' : 'w-8 mx-auto'}
|
||||
src="/moodle.png"
|
||||
/>
|
||||
17
apps/ui/src/lib/components/svg/services/index.ts
Normal file
17
apps/ui/src/lib/components/svg/services/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
//@ts-nocheck
|
||||
export { default as PlausibleAnalytics } from './PlausibleAnalytics.svelte';
|
||||
export { default as NocoDb } from './NocoDB.svelte';
|
||||
export { default as MinIo } from './MinIO.svelte';
|
||||
export { default as VsCodeServer } from './VSCodeServer.svelte';
|
||||
export { default as Wordpress } from './Wordpress.svelte';
|
||||
export { default as VaultWarden } from './VaultWarden.svelte';
|
||||
export { default as LanguageTool } from './LanguageTool.svelte';
|
||||
export { default as N8n } from './N8n.svelte';
|
||||
export { default as UptimeKuma } from './UptimeKuma.svelte';
|
||||
export { default as Ghost } from './Ghost.svelte';
|
||||
export { default as MeiliSearch } from './MeiliSearch.svelte';
|
||||
export { default as Umami } from './Umami.svelte';
|
||||
export { default as Hasura } from './Hasura.svelte';
|
||||
export { default as Fider } from './Fider.svelte';
|
||||
export { default as Moodle } from './Moodle.svelte';
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
"domain_already_in_use": "Domain {{domain}} is already used.",
|
||||
"dns_not_set_error": "DNS not set correctly or propogated for {{domain}}.<br><br>Please check your DNS settings.",
|
||||
"domain_required": "Domain is required.",
|
||||
"settings_saved": "Settings saved.",
|
||||
"settings_saved": "Configuration saved.",
|
||||
"dns_not_set_partial_error": "DNS not set",
|
||||
"domain_not_valid": "Could not resolve domain or it's not pointing to the server IP address.<br><br>Please check your DNS configuration and try again.",
|
||||
"git_source": "Git Source",
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { browser } from '$app/env';
|
||||
import { writable, readable, type Writable, type Readable } from 'svelte/store';
|
||||
// import { version as currentVersion } from '../../package.json';
|
||||
|
||||
interface AppSession {
|
||||
version: string
|
||||
version: string | null,
|
||||
userId: string | null,
|
||||
teamId: string | null,
|
||||
permission: string,
|
||||
@@ -18,7 +17,7 @@ interface AppSession {
|
||||
}
|
||||
export const loginEmail: Writable<string | undefined> = writable()
|
||||
export const appSession: Writable<AppSession> = writable({
|
||||
version: '3.0.3',
|
||||
version: null,
|
||||
userId: null,
|
||||
teamId: null,
|
||||
permission: 'read',
|
||||
@@ -45,7 +44,7 @@ export const status: Writable<any> = writable({
|
||||
initialLoading: true,
|
||||
loading: false,
|
||||
isRunning: false
|
||||
},
|
||||
},
|
||||
database: {
|
||||
initialLoading: true,
|
||||
loading: false,
|
||||
@@ -58,3 +57,17 @@ export const features = readable({
|
||||
beta: window.localStorage.getItem('beta') === 'true',
|
||||
latestVersion: window.localStorage.getItem('latestVersion')
|
||||
});
|
||||
|
||||
export const location: Writable<null | string> = writable(null)
|
||||
export const setLocation = (resource: any) => {
|
||||
console.log(GITPOD_WORKSPACE_URL)
|
||||
if (GITPOD_WORKSPACE_URL && resource.exposePort) {
|
||||
const { href } = new URL(GITPOD_WORKSPACE_URL);
|
||||
const newURL = href
|
||||
.replace('https://', `https://${resource.exposePort}-`)
|
||||
.replace(/\/$/, '');
|
||||
location.set(newURL)
|
||||
} else {
|
||||
location.set(resource.fqdn)
|
||||
}
|
||||
}
|
||||
@@ -70,7 +70,8 @@ export function findBuildPack(pack: string, packageManager = 'npm') {
|
||||
...metaData,
|
||||
...defaultBuildAndDeploy(packageManager),
|
||||
publishDirectory: null,
|
||||
port: 3000
|
||||
port: 3000,
|
||||
deploymentType: 'node'
|
||||
};
|
||||
}
|
||||
if (pack === 'gatsby') {
|
||||
@@ -94,7 +95,8 @@ export function findBuildPack(pack: string, packageManager = 'npm') {
|
||||
...metaData,
|
||||
...defaultBuildAndDeploy(packageManager),
|
||||
publishDirectory: null,
|
||||
port: 3000
|
||||
port: 3000,
|
||||
deploymentType: 'node'
|
||||
};
|
||||
}
|
||||
if (pack === 'preact') {
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
<script context="module" lang="ts">
|
||||
import type { Load } from '@sveltejs/kit';
|
||||
export const load: Load = async ({ url }) => {
|
||||
const baseSettings = await get('/base');
|
||||
try {
|
||||
if (Cookies.get('token')) {
|
||||
const response = await get(`/user`);
|
||||
return {
|
||||
props: {
|
||||
...response
|
||||
...response,
|
||||
baseSettings
|
||||
},
|
||||
stuff: {
|
||||
...response
|
||||
@@ -16,10 +18,17 @@
|
||||
if (url.pathname !== '/login' && url.pathname !== '/register') {
|
||||
return {
|
||||
status: 302,
|
||||
redirect: '/login'
|
||||
redirect: '/login',
|
||||
props: {
|
||||
baseSettings
|
||||
}
|
||||
};
|
||||
}
|
||||
return {};
|
||||
return {
|
||||
props: {
|
||||
baseSettings
|
||||
}
|
||||
};
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error?.code?.startsWith('FAST_JWT') || error.status === 401) {
|
||||
@@ -27,25 +36,39 @@
|
||||
if (url.pathname !== '/login') {
|
||||
return {
|
||||
status: 302,
|
||||
redirect: '/login'
|
||||
redirect: '/login',
|
||||
props: {
|
||||
baseSettings
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
if (url.pathname !== '/login') {
|
||||
return {
|
||||
status: 302,
|
||||
redirect: '/login'
|
||||
redirect: '/login',
|
||||
props: {
|
||||
baseSettings
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: 500,
|
||||
error: new Error(error)
|
||||
error: new Error(error),
|
||||
props: {
|
||||
baseSettings
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export let baseSettings: any;
|
||||
$appSession.version = baseSettings.version;
|
||||
$appSession.whiteLabeled = baseSettings.whiteLabeled;
|
||||
$appSession.whiteLabeledDetails.icon = baseSettings.whiteLabeledIcon;
|
||||
|
||||
export let settings: any;
|
||||
export let userId: string;
|
||||
export let teamId: string;
|
||||
@@ -54,7 +77,6 @@
|
||||
import '../tailwind.css';
|
||||
import Cookies from 'js-cookie';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
import { SvelteToast } from '@zerodevx/svelte-toast';
|
||||
import { navigating, page } from '$app/stores';
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
}
|
||||
if (newStorage) toast.push($t('application.storage.storage_saved'));
|
||||
else toast.push($t('application.storage.storage_updated'));
|
||||
} catch ({ error }) {
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@
|
||||
await del(`/applications/${id}/storages`, { path: storage.path });
|
||||
dispatch('refresh');
|
||||
toast.push($t('application.storage.storage_deleted'));
|
||||
} catch ({ error }) {
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,12 +62,13 @@
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { t } from '$lib/translations';
|
||||
import { appSession, disabledButton, status } from '$lib/store';
|
||||
import { appSession, disabledButton, status, location, setLocation } from '$lib/store';
|
||||
import { errorNotification, handlerNotFoundLoad } from '$lib/common';
|
||||
import Loading from '$lib/components/Loading.svelte';
|
||||
|
||||
let loading = false;
|
||||
let statusInterval: any;
|
||||
|
||||
$disabledButton =
|
||||
!$appSession.isAdmin ||
|
||||
!application.fqdn ||
|
||||
@@ -75,6 +76,7 @@
|
||||
!application.repository ||
|
||||
!application.destinationDocker ||
|
||||
!application.buildPack;
|
||||
|
||||
const { id } = $page.params;
|
||||
|
||||
async function handleDeploySubmit() {
|
||||
@@ -110,7 +112,7 @@
|
||||
loading = true;
|
||||
await post(`/applications/${id}/stop`, {});
|
||||
return window.location.reload();
|
||||
} catch ({ error }) {
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
@@ -118,6 +120,7 @@
|
||||
if ($status.application.loading) return;
|
||||
$status.application.loading = true;
|
||||
const data = await get(`/applications/${id}`);
|
||||
isQueueActive = data.isQueueActive;
|
||||
$status.application.isRunning = data.isRunning;
|
||||
$status.application.isExited = data.isExited;
|
||||
$status.application.loading = false;
|
||||
@@ -125,9 +128,12 @@
|
||||
}
|
||||
onDestroy(() => {
|
||||
$status.application.initialLoading = true;
|
||||
$location = null;
|
||||
clearInterval(statusInterval);
|
||||
});
|
||||
onMount(async () => {
|
||||
setLocation(application);
|
||||
|
||||
$status.application.isRunning = false;
|
||||
$status.application.isExited = false;
|
||||
$status.application.loading = false;
|
||||
@@ -146,6 +152,30 @@
|
||||
{#if loading}
|
||||
<Loading fullscreen cover />
|
||||
{:else}
|
||||
{#if $location}
|
||||
<a
|
||||
href={$location}
|
||||
target="_blank"
|
||||
class="icons tooltip-bottom flex items-center bg-transparent text-sm"
|
||||
><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" />
|
||||
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
|
||||
<line x1="10" y1="14" x2="20" y2="4" />
|
||||
<polyline points="15 4 20 4 20 9" />
|
||||
</svg></a
|
||||
>
|
||||
{/if}
|
||||
|
||||
<div class="border border-coolgray-500 h-8" />
|
||||
{#if $status.application.isExited}
|
||||
<a
|
||||
href={!$disabledButton ? `/applications/${id}/logs` : null}
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
return true;
|
||||
}
|
||||
showSave = true;
|
||||
} catch ({ error }) {
|
||||
} catch (error) {
|
||||
showSave = false;
|
||||
return errorNotification(error);
|
||||
}
|
||||
@@ -138,7 +138,7 @@
|
||||
return await goto(`${to}?from=${from}`);
|
||||
}
|
||||
return await goto(from || `/applications/${id}/configuration/destination`);
|
||||
} catch ({ error }) {
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { dev } from '$app/env';
|
||||
import { goto } from '$app/navigation';
|
||||
import { del, get, post } from '$lib/api';
|
||||
import { del, get, getAPIUrl, getWebhookUrl, post } from '$lib/api';
|
||||
import { t } from '$lib/translations';
|
||||
import { errorNotification } from '$lib/common';
|
||||
import { appSession } from '$lib/store';
|
||||
@@ -18,7 +18,7 @@
|
||||
const from = $page.url.searchParams.get('from');
|
||||
|
||||
let url = settings?.fqdn ? settings.fqdn : window.location.origin;
|
||||
if (dev) url = `http://localhost:3001`;
|
||||
if (dev) url = getAPIUrl();
|
||||
|
||||
const updateDeployKeyIdUrl = `/applications/${id}/configuration/deploykey`;
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
branches: false,
|
||||
save: false
|
||||
};
|
||||
let tryAgain = false;
|
||||
|
||||
let htmlUrl = application.gitSource.htmlUrl;
|
||||
let apiUrl = application.gitSource.apiUrl;
|
||||
@@ -44,10 +45,7 @@
|
||||
project: undefined,
|
||||
branch: undefined
|
||||
};
|
||||
let search = {
|
||||
project: '',
|
||||
branch: ''
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
if (!$appSession.tokens.gitlab) {
|
||||
await getGitlabToken();
|
||||
@@ -58,18 +56,10 @@
|
||||
Authorization: `Bearer ${$appSession.tokens.gitlab}`
|
||||
});
|
||||
username = user.username;
|
||||
await loadGroups();
|
||||
} catch (error) {
|
||||
return await getGitlabToken();
|
||||
}
|
||||
try {
|
||||
groups = await get(`${apiUrl}/v4/groups?per_page=5000`, {
|
||||
Authorization: `Bearer ${$appSession.tokens.gitlab}`
|
||||
});
|
||||
} catch (error: any) {
|
||||
errorNotification(error);
|
||||
throw new Error(error);
|
||||
} finally {
|
||||
loading.base = false;
|
||||
tryAgain = true;
|
||||
}
|
||||
});
|
||||
function selectGroup(event: any) {
|
||||
@@ -77,16 +67,11 @@
|
||||
selected.project = null;
|
||||
selected.branch = null;
|
||||
showSave = false;
|
||||
|
||||
// Clear out projects
|
||||
projects = [];
|
||||
loadProjects();
|
||||
}
|
||||
async function searchProjects(searchText: any) {
|
||||
if (!selected.group) {
|
||||
return;
|
||||
}
|
||||
search.project = searchText;
|
||||
await loadProjects();
|
||||
return projects;
|
||||
}
|
||||
function selectProject(event: any) {
|
||||
selected.project = event.detail;
|
||||
selected.branch = null;
|
||||
@@ -116,24 +101,50 @@
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
|
||||
async function loadProjects() {
|
||||
async function loadGroups(page: number = 1) {
|
||||
let perPage = 100;
|
||||
//@ts-ignore
|
||||
const params: any = new URLSearchParams({
|
||||
page: 1,
|
||||
per_page: 25,
|
||||
page,
|
||||
per_page: perPage
|
||||
});
|
||||
loading.base = true;
|
||||
try {
|
||||
const newGroups = await get(`${apiUrl}/v4/groups?${params}`, {
|
||||
Authorization: `Bearer ${$appSession.tokens.gitlab}`
|
||||
});
|
||||
groups = groups.concat(newGroups);
|
||||
if (newGroups.length === perPage) {
|
||||
await loadGroups(page + 1);
|
||||
}
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
loading.base = false;
|
||||
}
|
||||
}
|
||||
async function loadProjects(page: number = 1) {
|
||||
let perPage = 100;
|
||||
//@ts-ignore
|
||||
const params: any = new URLSearchParams({
|
||||
page,
|
||||
per_page: perPage,
|
||||
archived: false
|
||||
});
|
||||
if (search.project) {
|
||||
params.append('search', search.project);
|
||||
}
|
||||
loading.projects = true;
|
||||
if (username === selected.group.name) {
|
||||
try {
|
||||
params.append('min_access_level', 40);
|
||||
projects = await get(`${apiUrl}/v4/users/${selected.group.name}/projects?${params}`, {
|
||||
Authorization: `Bearer ${$appSession.tokens.gitlab}`
|
||||
});
|
||||
const newProjects = await get(
|
||||
`${apiUrl}/v4/users/${selected.group.name}/projects?${params}`,
|
||||
{
|
||||
Authorization: `Bearer ${$appSession.tokens.gitlab}`
|
||||
}
|
||||
);
|
||||
projects = projects.concat(newProjects);
|
||||
if (newProjects.length === perPage) {
|
||||
await loadProjects(page + 1);
|
||||
}
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
@@ -141,9 +152,16 @@
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
projects = await get(`${apiUrl}/v4/groups/${selected.group.id}/projects?${params}`, {
|
||||
Authorization: `Bearer ${$appSession.tokens.gitlab}`
|
||||
});
|
||||
const newProjects = await get(
|
||||
`${apiUrl}/v4/groups/${selected.group.id}/projects?${params}`,
|
||||
{
|
||||
Authorization: `Bearer ${$appSession.tokens.gitlab}`
|
||||
}
|
||||
);
|
||||
projects = projects.concat(newProjects);
|
||||
if (newProjects.length === perPage) {
|
||||
await loadProjects(page + 1);
|
||||
}
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
@@ -151,35 +169,29 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
async function searchBranches(searchText: any) {
|
||||
if (!selected.project) {
|
||||
return;
|
||||
}
|
||||
search.branch = searchText;
|
||||
await loadBranches();
|
||||
return branches;
|
||||
}
|
||||
function selectBranch(event: any) {
|
||||
selected.branch = event.detail;
|
||||
isBranchAlreadyUsed();
|
||||
}
|
||||
async function loadBranches() {
|
||||
async function loadBranches(page: number = 1) {
|
||||
let perPage = 100;
|
||||
//@ts-ignore
|
||||
const params = new URLSearchParams({
|
||||
page: 1,
|
||||
per_page: 100
|
||||
page,
|
||||
per_page: perPage
|
||||
});
|
||||
if (search.branch) {
|
||||
params.append('search', search.branch);
|
||||
}
|
||||
loading.branches = true;
|
||||
try {
|
||||
branches = await get(
|
||||
const newBranches = await get(
|
||||
`${apiUrl}/v4/projects/${selected.project.id}/repository/branches?${params}`,
|
||||
{
|
||||
Authorization: `Bearer ${$appSession.tokens.gitlab}`
|
||||
}
|
||||
);
|
||||
branches = branches.concat(newBranches);
|
||||
if (newBranches.length === perPage) {
|
||||
await loadBranches(page + 1);
|
||||
}
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
@@ -203,7 +215,7 @@
|
||||
return true;
|
||||
}
|
||||
showSave = true;
|
||||
} catch ({ error }) {
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
@@ -216,7 +228,7 @@
|
||||
}
|
||||
async function setWebhook(url: any, webhookToken: any) {
|
||||
const host = dev
|
||||
? 'https://webhook.site/0e5beb2c-4e9b-40e2-a89e-32295e570c21'
|
||||
? getWebhookUrl('gitlab')
|
||||
: `${window.location.origin}/webhooks/gitlab/events`;
|
||||
try {
|
||||
await post(
|
||||
@@ -369,9 +381,9 @@
|
||||
value={selected.project}
|
||||
isClearable={false}
|
||||
items={projects}
|
||||
loadOptions={searchProjects}
|
||||
labelIdentifier="name"
|
||||
optionIdentifier="id"
|
||||
isSearchable={true}
|
||||
/>
|
||||
</div>
|
||||
<div class="custom-select-wrapper">
|
||||
@@ -392,7 +404,7 @@
|
||||
value={selected.branch}
|
||||
isClearable={false}
|
||||
items={branches}
|
||||
loadOptions={searchBranches}
|
||||
isSearchable={true}
|
||||
labelIdentifier="name"
|
||||
optionIdentifier="web_url"
|
||||
/>
|
||||
@@ -408,5 +420,17 @@
|
||||
class:hover:bg-orange-500={showSave && !loading.save}
|
||||
>{loading.save ? $t('forms.saving') : $t('forms.save')}</button
|
||||
>
|
||||
{#if tryAgain}
|
||||
<div>
|
||||
An error occured during authenticating with GitLab. Please check your GitLab Source
|
||||
configuration <a href={`/sources/${application.gitSource.id}`}>here.</a>
|
||||
</div>
|
||||
<button
|
||||
class="w-40 bg-green-600"
|
||||
on:click|stopPropagation|preventDefault={() => window.location.reload()}
|
||||
>
|
||||
Try again
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
async function scanRepository() {
|
||||
async function scanRepository(): Promise<void> {
|
||||
try {
|
||||
if (type === 'gitlab') {
|
||||
const files = await get(`${apiUrl}/v4/projects/${projectId}/repository/tree`, {
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
try {
|
||||
await post(`/applications/${id}/configuration/destination`, { destinationId });
|
||||
return await goto(from || `/applications/${id}/configuration/buildpack`);
|
||||
} catch ({ error }) {
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
import { appSession } from '$lib/store';
|
||||
|
||||
const { id } = $page.params;
|
||||
const from = $page.url.searchParams.get('from')
|
||||
const from = $page.url.searchParams.get('from');
|
||||
|
||||
export let sources: any;
|
||||
const filteredSources = sources.filter(
|
||||
@@ -74,9 +74,14 @@
|
||||
<div class="flex flex-col justify-center">
|
||||
{#if !filteredSources || ownSources.length === 0}
|
||||
<div class="flex-col">
|
||||
<div class="pb-2 text-center font-bold">{$t('application.configuration.no_configurable_git')}</div>
|
||||
<div class="pb-2 text-center font-bold">
|
||||
{$t('application.configuration.no_configurable_git')}
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
<a href="/sources/new?from={$page.url.pathname}" class="add-icon bg-orange-600 hover:bg-orange-500">
|
||||
<a
|
||||
href="/sources/new?from={$page.url.pathname}"
|
||||
class="add-icon bg-orange-600 hover:bg-orange-500"
|
||||
>
|
||||
<svg
|
||||
class="w-6"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -94,9 +99,46 @@
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-col flex-wrap justify-center px-2 md:flex-row">
|
||||
<div class="flex flex-col flex-wrap justify-center px-2 md:flex-row ">
|
||||
{#each ownSources as source}
|
||||
<div class="p-2">
|
||||
<div class="p-2 relative">
|
||||
<div class="absolute -m-4">
|
||||
{#if source?.type === 'gitlab'}
|
||||
<svg viewBox="0 0 128 128" class="w-8">
|
||||
<path
|
||||
fill="#FC6D26"
|
||||
d="M126.615 72.31l-7.034-21.647L105.64 7.76c-.716-2.206-3.84-2.206-4.556 0l-13.94 42.903H40.856L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664 1.385 72.31a4.792 4.792 0 001.74 5.358L64 121.894l60.874-44.227a4.793 4.793 0 001.74-5.357"
|
||||
/><path fill="#E24329" d="M64 121.894l23.144-71.23H40.856L64 121.893z" /><path
|
||||
fill="#FC6D26"
|
||||
d="M64 121.894l-23.144-71.23H8.42L64 121.893z"
|
||||
/><path
|
||||
fill="#FCA326"
|
||||
d="M8.42 50.663L1.384 72.31a4.79 4.79 0 001.74 5.357L64 121.894 8.42 50.664z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M8.42 50.663h32.436L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664z"
|
||||
/><path fill="#FC6D26" d="M64 121.894l23.144-71.23h32.437L64 121.893z" /><path
|
||||
fill="#FCA326"
|
||||
d="M119.58 50.663l7.035 21.647a4.79 4.79 0 01-1.74 5.357L64 121.894l55.58-71.23z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M119.58 50.663H87.145l13.94-42.902c.717-2.206 3.84-2.206 4.557 0l13.94 42.903z"
|
||||
/>
|
||||
</svg>
|
||||
{:else if source?.type === 'github'}
|
||||
<svg viewBox="0 0 128 128" class="w-8">
|
||||
<g fill="#ffffff"
|
||||
><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
|
||||
/><path
|
||||
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm2.446 2.729c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zM31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm3.261 3.361c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm4.5 1.951c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm4.943.361c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm4.598-.782c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
|
||||
/></g
|
||||
>
|
||||
</svg>
|
||||
{/if}
|
||||
</div>
|
||||
<form on:submit|preventDefault={() => handleSubmit(source.id)}>
|
||||
<button
|
||||
disabled={source.gitlabApp && !source.gitlabAppId}
|
||||
|
||||
@@ -35,12 +35,14 @@
|
||||
import { get, post } from '$lib/api';
|
||||
import cuid from 'cuid';
|
||||
import { browser } from '$app/env';
|
||||
import { appSession, disabledButton, status } from '$lib/store';
|
||||
import { appSession, disabledButton, setLocation, status } from '$lib/store';
|
||||
import { t } from '$lib/translations';
|
||||
import { errorNotification, getDomain, notNodeDeployments, staticDeployments } from '$lib/common';
|
||||
import Setting from './_Setting.svelte';
|
||||
const { id } = $page.params;
|
||||
|
||||
$: isDisabled = !$appSession.isAdmin || $status.application.isRunning;
|
||||
|
||||
let domainEl: HTMLInputElement;
|
||||
|
||||
let loading = false;
|
||||
@@ -63,7 +65,6 @@
|
||||
let isNonWWWDomainOK = false;
|
||||
let isWWWDomainOK = false;
|
||||
|
||||
$: isDisabled = !$appSession.isAdmin || $status.application.isRunning;
|
||||
let wsgis = [
|
||||
{
|
||||
value: 'None',
|
||||
@@ -103,8 +104,18 @@
|
||||
usageInterval = setInterval(async () => {
|
||||
await getUsage();
|
||||
}, 1000);
|
||||
await getBaseBuildImages();
|
||||
});
|
||||
|
||||
async function getBaseBuildImages() {
|
||||
const data = await post(`/applications/images`, {
|
||||
buildPack: application.buildPack,
|
||||
deploymentType: application.deploymentType
|
||||
});
|
||||
application = {
|
||||
...application,
|
||||
...data
|
||||
};
|
||||
}
|
||||
async function changeSettings(name: any) {
|
||||
if (name === 'debug') {
|
||||
debug = !debug;
|
||||
@@ -145,10 +156,12 @@
|
||||
}
|
||||
}
|
||||
async function handleSubmit() {
|
||||
if (loading) return;
|
||||
if (loading || !application.fqdn) return;
|
||||
loading = true;
|
||||
try {
|
||||
nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
|
||||
if (application.deploymentType)
|
||||
application.deploymentType = application.deploymentType.toLowerCase();
|
||||
await post(`/applications/${id}/check`, {
|
||||
fqdn: application.fqdn,
|
||||
forceSave,
|
||||
@@ -156,6 +169,7 @@
|
||||
exposePort: application.exposePort
|
||||
});
|
||||
await post(`/applications/${id}`, { ...application });
|
||||
setLocation(application)
|
||||
$disabledButton = false;
|
||||
forceSave = false;
|
||||
return toast.push('Configurations saved.');
|
||||
@@ -192,6 +206,11 @@
|
||||
application.baseBuildImage = event.detail.value;
|
||||
await handleSubmit();
|
||||
}
|
||||
async function selectDeploymentType(event: any) {
|
||||
application.deploymentType = event.detail.value;
|
||||
await getBaseBuildImages();
|
||||
await handleSubmit();
|
||||
}
|
||||
|
||||
async function isDNSValid(domain: any, isWWW: any) {
|
||||
try {
|
||||
@@ -199,7 +218,7 @@
|
||||
toast.push('DNS configuration is valid.');
|
||||
isWWW ? (isWWWDomainOK = true) : (isNonWWWDomainOK = true);
|
||||
return true;
|
||||
} catch ({ error }) {
|
||||
} catch (error) {
|
||||
errorNotification(error);
|
||||
isWWW ? (isWWWDomainOK = false) : (isNonWWWDomainOK = false);
|
||||
return false;
|
||||
@@ -212,72 +231,51 @@
|
||||
<div class="md:max-w-64 truncate text-base tracking-tight md:text-2xl lg:block">
|
||||
Configuration
|
||||
</div>
|
||||
<span class="text-xs">{application.name} </span>
|
||||
<span class="text-xs">{application.name}</span>
|
||||
</div>
|
||||
|
||||
{#if application.fqdn}
|
||||
{#if application.gitSource?.htmlUrl && application.repository && application.branch}
|
||||
<a
|
||||
href={application.fqdn}
|
||||
href="{application.gitSource.htmlUrl}/{application.repository}/tree/{application.branch}"
|
||||
target="_blank"
|
||||
class="icons tooltip-bottom flex items-center bg-transparent text-sm"
|
||||
><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" />
|
||||
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
|
||||
<line x1="10" y1="14" x2="20" y2="4" />
|
||||
<polyline points="15 4 20 4 20 9" />
|
||||
</svg></a
|
||||
class="w-10"
|
||||
>
|
||||
{/if}
|
||||
<a
|
||||
href="{application.gitSource.htmlUrl}/{application.repository}/tree/{application.branch}"
|
||||
target="_blank"
|
||||
class="w-10"
|
||||
>
|
||||
{#if application.gitSource?.type === 'gitlab'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
<path
|
||||
fill="#FC6D26"
|
||||
d="M126.615 72.31l-7.034-21.647L105.64 7.76c-.716-2.206-3.84-2.206-4.556 0l-13.94 42.903H40.856L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664 1.385 72.31a4.792 4.792 0 001.74 5.358L64 121.894l60.874-44.227a4.793 4.793 0 001.74-5.357"
|
||||
/><path fill="#E24329" d="M64 121.894l23.144-71.23H40.856L64 121.893z" /><path
|
||||
fill="#FC6D26"
|
||||
d="M64 121.894l-23.144-71.23H8.42L64 121.893z"
|
||||
/><path
|
||||
fill="#FCA326"
|
||||
d="M8.42 50.663L1.384 72.31a4.79 4.79 0 001.74 5.357L64 121.894 8.42 50.664z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M8.42 50.663h32.436L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664z"
|
||||
/><path fill="#FC6D26" d="M64 121.894l23.144-71.23h32.437L64 121.893z" /><path
|
||||
fill="#FCA326"
|
||||
d="M119.58 50.663l7.035 21.647a4.79 4.79 0 01-1.74 5.357L64 121.894l55.58-71.23z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M119.58 50.663H87.145l13.94-42.902c.717-2.206 3.84-2.206 4.557 0l13.94 42.903z"
|
||||
/>
|
||||
</svg>
|
||||
{:else if application.gitSource?.type === 'github'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
<g fill="#ffffff"
|
||||
><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
|
||||
{#if application.gitSource?.type === 'gitlab'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
<path
|
||||
fill="#FC6D26"
|
||||
d="M126.615 72.31l-7.034-21.647L105.64 7.76c-.716-2.206-3.84-2.206-4.556 0l-13.94 42.903H40.856L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664 1.385 72.31a4.792 4.792 0 001.74 5.358L64 121.894l60.874-44.227a4.793 4.793 0 001.74-5.357"
|
||||
/><path fill="#E24329" d="M64 121.894l23.144-71.23H40.856L64 121.893z" /><path
|
||||
fill="#FC6D26"
|
||||
d="M64 121.894l-23.144-71.23H8.42L64 121.893z"
|
||||
/><path
|
||||
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm2.446 2.729c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zM31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm3.261 3.361c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm4.5 1.951c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm4.943.361c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm4.598-.782c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
|
||||
/></g
|
||||
>
|
||||
</svg>
|
||||
{/if}
|
||||
</a>
|
||||
fill="#FCA326"
|
||||
d="M8.42 50.663L1.384 72.31a4.79 4.79 0 001.74 5.357L64 121.894 8.42 50.664z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M8.42 50.663h32.436L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664z"
|
||||
/><path fill="#FC6D26" d="M64 121.894l23.144-71.23h32.437L64 121.893z" /><path
|
||||
fill="#FCA326"
|
||||
d="M119.58 50.663l7.035 21.647a4.79 4.79 0 01-1.74 5.357L64 121.894l55.58-71.23z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M119.58 50.663H87.145l13.94-42.902c.717-2.206 3.84-2.206 4.557 0l13.94 42.903z"
|
||||
/>
|
||||
</svg>
|
||||
{:else if application.gitSource?.type === 'github'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
<g fill="#ffffff"
|
||||
><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
|
||||
/><path
|
||||
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm2.446 2.729c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zM31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm3.261 3.361c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm4.5 1.951c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm4.943.361c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm4.598-.782c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
|
||||
/></g
|
||||
>
|
||||
</svg>
|
||||
{/if}
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="mx-auto max-w-4xl px-6 py-4">
|
||||
@@ -332,7 +330,6 @@
|
||||
<div class="mt-2 grid grid-cols-2 items-center">
|
||||
<label for="name" class="text-base font-bold text-stone-100">{$t('forms.name')}</label>
|
||||
<input
|
||||
readonly={!isDisabled}
|
||||
name="name"
|
||||
id="name"
|
||||
bind:value={application.name}
|
||||
@@ -404,26 +401,6 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{#if application.buildPack !== 'docker'}
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="baseImage" class="text-base font-bold text-stone-100"
|
||||
>{$t('application.base_image')}</label
|
||||
>
|
||||
<div class="custom-select-wrapper">
|
||||
<Select
|
||||
{isDisabled}
|
||||
containerClasses={isDisabled && containerClass()}
|
||||
id="baseImages"
|
||||
showIndicator={!$status.application.isRunning}
|
||||
items={application.baseImages}
|
||||
on:select={selectBaseImage}
|
||||
value={application.baseImage}
|
||||
isClearable={false}
|
||||
/>
|
||||
</div>
|
||||
<Explainer text={$t('application.base_image_explainer')} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if application.buildCommand || application.buildPack === 'rust' || application.buildPack === 'laravel'}
|
||||
<div class="grid grid-cols-2 items-center pb-8">
|
||||
<label for="baseBuildImage" class="text-base font-bold text-stone-100"
|
||||
@@ -449,6 +426,48 @@
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{#if application.buildPack !== 'docker'}
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="baseImage" class="text-base font-bold text-stone-100"
|
||||
>{$t('application.base_image')}</label
|
||||
>
|
||||
<div class="custom-select-wrapper">
|
||||
<Select
|
||||
{isDisabled}
|
||||
containerClasses={isDisabled && containerClass()}
|
||||
id="baseImages"
|
||||
showIndicator={!$status.application.isRunning}
|
||||
items={application.baseImages}
|
||||
on:select={selectBaseImage}
|
||||
value={application.baseImage}
|
||||
isClearable={false}
|
||||
/>
|
||||
</div>
|
||||
<Explainer text={$t('application.base_image_explainer')} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if application.buildPack !== 'docker' && (application.buildPack === 'nextjs' || application.buildPack === 'nuxtjs')}
|
||||
<div class="grid grid-cols-2 items-center pb-8">
|
||||
<label for="deploymentType" class="text-base font-bold text-stone-100"
|
||||
>Deployment Type</label
|
||||
>
|
||||
<div class="custom-select-wrapper">
|
||||
<Select
|
||||
{isDisabled}
|
||||
containerClasses={isDisabled && containerClass()}
|
||||
id="deploymentTypes"
|
||||
showIndicator={!$status.application.isRunning}
|
||||
items={['static', 'node']}
|
||||
on:select={selectDeploymentType}
|
||||
value={application.deploymentType}
|
||||
isClearable={false}
|
||||
/>
|
||||
</div>
|
||||
<Explainer
|
||||
text="Defines how to deploy your application. <br><br><span class='text-green-500 font-bold'>Static</span> is for static websites, <span class='text-green-500 font-bold'>node</span> is for server-side applications."
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex space-x-1 py-5 font-bold">
|
||||
<div class="title">{$t('application.application')}</div>
|
||||
@@ -473,6 +492,7 @@
|
||||
bind:this={domainEl}
|
||||
name="fqdn"
|
||||
id="fqdn"
|
||||
required
|
||||
bind:value={application.fqdn}
|
||||
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
|
||||
placeholder="eg: https://coollabs.io"
|
||||
@@ -516,7 +536,7 @@
|
||||
<div class="grid grid-cols-2 items-center pb-8">
|
||||
<Setting
|
||||
dataTooltip={$t('forms.must_be_stopped_to_modify')}
|
||||
disabled={$status.application.isRunning}
|
||||
disabled={isDisabled}
|
||||
isCenter={false}
|
||||
bind:setting={dualCerts}
|
||||
title={$t('application.ssl_www_and_non_www')}
|
||||
@@ -535,6 +555,7 @@
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="pythonModule" class="text-base font-bold text-stone-100">Module</label>
|
||||
<input
|
||||
disabled={isDisabled}
|
||||
readonly={!$appSession.isAdmin}
|
||||
name="pythonModule"
|
||||
id="pythonModule"
|
||||
@@ -547,6 +568,7 @@
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="pythonVariable" class="text-base font-bold text-stone-100">Variable</label>
|
||||
<input
|
||||
disabled={isDisabled}
|
||||
readonly={!$appSession.isAdmin}
|
||||
name="pythonVariable"
|
||||
id="pythonVariable"
|
||||
@@ -560,6 +582,7 @@
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="pythonVariable" class="text-base font-bold text-stone-100">Variable</label>
|
||||
<input
|
||||
disabled={isDisabled}
|
||||
readonly={!$appSession.isAdmin}
|
||||
name="pythonVariable"
|
||||
id="pythonVariable"
|
||||
@@ -574,6 +597,7 @@
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="port" class="text-base font-bold text-stone-100">{$t('forms.port')}</label>
|
||||
<input
|
||||
disabled={isDisabled}
|
||||
readonly={!$appSession.isAdmin}
|
||||
name="port"
|
||||
id="port"
|
||||
@@ -582,28 +606,27 @@
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{#if application.buildPack !== 'docker'}
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="exposePort" class="text-base font-bold text-stone-100">Exposed Port</label>
|
||||
<input
|
||||
readonly={!$appSession.isAdmin && !$status.application.isRunning}
|
||||
disabled={isDisabled}
|
||||
name="exposePort"
|
||||
id="exposePort"
|
||||
bind:value={application.exposePort}
|
||||
placeholder="12345"
|
||||
/>
|
||||
<Explainer
|
||||
text={'You can expose your application to a port on the host system.<br><br>Useful if you would like to use your own reverse proxy or tunnel and also in development mode. Otherwise leave empty.'}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="exposePort" class="text-base font-bold text-stone-100">Exposed Port</label>
|
||||
<input
|
||||
readonly={!$appSession.isAdmin && !$status.application.isRunning}
|
||||
disabled={isDisabled}
|
||||
name="exposePort"
|
||||
id="exposePort"
|
||||
bind:value={application.exposePort}
|
||||
placeholder="12345"
|
||||
/>
|
||||
<Explainer
|
||||
text={'You can expose your application to a port on the host system.<br><br>Useful if you would like to use your own reverse proxy or tunnel and also in development mode. Otherwise leave empty.'}
|
||||
/>
|
||||
</div>
|
||||
{#if !notNodeDeployments.includes(application.buildPack)}
|
||||
<div class="grid grid-cols-2 items-center pt-4">
|
||||
<label for="installCommand" class="text-base font-bold text-stone-100"
|
||||
>{$t('application.install_command')}</label
|
||||
>
|
||||
<input
|
||||
disabled={isDisabled}
|
||||
readonly={!$appSession.isAdmin}
|
||||
name="installCommand"
|
||||
id="installCommand"
|
||||
@@ -616,6 +639,7 @@
|
||||
>{$t('application.build_command')}</label
|
||||
>
|
||||
<input
|
||||
disabled={isDisabled}
|
||||
readonly={!$appSession.isAdmin}
|
||||
name="buildCommand"
|
||||
id="buildCommand"
|
||||
@@ -628,6 +652,7 @@
|
||||
>{$t('application.start_command')}</label
|
||||
>
|
||||
<input
|
||||
disabled={isDisabled}
|
||||
readonly={!$appSession.isAdmin}
|
||||
name="startCommand"
|
||||
id="startCommand"
|
||||
@@ -642,6 +667,7 @@
|
||||
>Dockerfile Location</label
|
||||
>
|
||||
<input
|
||||
disabled={isDisabled}
|
||||
readonly={!$appSession.isAdmin}
|
||||
name="dockerFileLocation"
|
||||
id="dockerFileLocation"
|
||||
@@ -649,7 +675,7 @@
|
||||
placeholder="default: /Dockerfile"
|
||||
/>
|
||||
<Explainer
|
||||
text="Does not rely on Base Directory. <br>Should be absolute path, like <span class='text-green-500 font-bold'>/data/Dockerfile</span> or <span class='text-green-500 font-bold'>/Dockerfile.</span>"
|
||||
text="Should be absolute path, like <span class='text-green-500 font-bold'>/data/Dockerfile</span> or <span class='text-green-500 font-bold'>/Dockerfile.</span>"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -657,6 +683,7 @@
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="denoMainFile" class="text-base font-bold text-stone-100">Main File</label>
|
||||
<input
|
||||
disabled={isDisabled}
|
||||
readonly={!$appSession.isAdmin}
|
||||
name="denoMainFile"
|
||||
id="denoMainFile"
|
||||
@@ -667,6 +694,7 @@
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="denoOptions" class="text-base font-bold text-stone-100">Arguments</label>
|
||||
<input
|
||||
disabled={isDisabled}
|
||||
readonly={!$appSession.isAdmin}
|
||||
name="denoOptions"
|
||||
id="denoOptions"
|
||||
@@ -687,6 +715,7 @@
|
||||
<Explainer text={$t('application.directory_to_use_explainer')} />
|
||||
</div>
|
||||
<input
|
||||
disabled={isDisabled}
|
||||
readonly={!$appSession.isAdmin}
|
||||
name="baseDirectory"
|
||||
id="baseDirectory"
|
||||
@@ -705,9 +734,11 @@
|
||||
</div>
|
||||
|
||||
<input
|
||||
disabled={isDisabled}
|
||||
readonly={!$appSession.isAdmin}
|
||||
name="publishDirectory"
|
||||
id="publishDirectory"
|
||||
required={application.deploymentType === 'static'}
|
||||
bind:value={application.publishDirectory}
|
||||
placeholder=" {$t('forms.default')}: /"
|
||||
/>
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
const data = await get(`/applications/${id}/logs/build?skip=${skip}`);
|
||||
builds = builds.concat(data.builds);
|
||||
return;
|
||||
} catch ({ error }) {
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
} else {
|
||||
@@ -88,70 +88,49 @@
|
||||
</div>
|
||||
<span class="text-xs">{application.name} </span>
|
||||
</div>
|
||||
|
||||
{#if application.fqdn}
|
||||
{#if application.gitSource?.htmlUrl && application.repository && application.branch}
|
||||
<a
|
||||
href={application.fqdn}
|
||||
href="{application.gitSource.htmlUrl}/{application.repository}/tree/{application.branch}"
|
||||
target="_blank"
|
||||
class="icons tooltip-bottom flex items-center bg-transparent text-sm"
|
||||
><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" />
|
||||
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
|
||||
<line x1="10" y1="14" x2="20" y2="4" />
|
||||
<polyline points="15 4 20 4 20 9" />
|
||||
</svg></a
|
||||
class="w-10"
|
||||
>
|
||||
{/if}
|
||||
<a
|
||||
href="{application.gitSource.htmlUrl}/{application.repository}/tree/{application.branch}"
|
||||
target="_blank"
|
||||
class="w-10"
|
||||
>
|
||||
{#if application.gitSource?.type === 'gitlab'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
<path
|
||||
fill="#FC6D26"
|
||||
d="M126.615 72.31l-7.034-21.647L105.64 7.76c-.716-2.206-3.84-2.206-4.556 0l-13.94 42.903H40.856L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664 1.385 72.31a4.792 4.792 0 001.74 5.358L64 121.894l60.874-44.227a4.793 4.793 0 001.74-5.357"
|
||||
/><path fill="#E24329" d="M64 121.894l23.144-71.23H40.856L64 121.893z" /><path
|
||||
fill="#FC6D26"
|
||||
d="M64 121.894l-23.144-71.23H8.42L64 121.893z"
|
||||
/><path
|
||||
fill="#FCA326"
|
||||
d="M8.42 50.663L1.384 72.31a4.79 4.79 0 001.74 5.357L64 121.894 8.42 50.664z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M8.42 50.663h32.436L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664z"
|
||||
/><path fill="#FC6D26" d="M64 121.894l23.144-71.23h32.437L64 121.893z" /><path
|
||||
fill="#FCA326"
|
||||
d="M119.58 50.663l7.035 21.647a4.79 4.79 0 01-1.74 5.357L64 121.894l55.58-71.23z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M119.58 50.663H87.145l13.94-42.902c.717-2.206 3.84-2.206 4.557 0l13.94 42.903z"
|
||||
/>
|
||||
</svg>
|
||||
{:else if application.gitSource?.type === 'github'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
<g fill="#ffffff"
|
||||
><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
|
||||
{#if application.gitSource?.type === 'gitlab'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
<path
|
||||
fill="#FC6D26"
|
||||
d="M126.615 72.31l-7.034-21.647L105.64 7.76c-.716-2.206-3.84-2.206-4.556 0l-13.94 42.903H40.856L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664 1.385 72.31a4.792 4.792 0 001.74 5.358L64 121.894l60.874-44.227a4.793 4.793 0 001.74-5.357"
|
||||
/><path fill="#E24329" d="M64 121.894l23.144-71.23H40.856L64 121.893z" /><path
|
||||
fill="#FC6D26"
|
||||
d="M64 121.894l-23.144-71.23H8.42L64 121.893z"
|
||||
/><path
|
||||
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm2.446 2.729c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zM31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm3.261 3.361c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm4.5 1.951c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm4.943.361c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm4.598-.782c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
|
||||
/></g
|
||||
>
|
||||
</svg>
|
||||
{/if}
|
||||
</a>
|
||||
fill="#FCA326"
|
||||
d="M8.42 50.663L1.384 72.31a4.79 4.79 0 001.74 5.357L64 121.894 8.42 50.664z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M8.42 50.663h32.436L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664z"
|
||||
/><path fill="#FC6D26" d="M64 121.894l23.144-71.23h32.437L64 121.893z" /><path
|
||||
fill="#FCA326"
|
||||
d="M119.58 50.663l7.035 21.647a4.79 4.79 0 01-1.74 5.357L64 121.894l55.58-71.23z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M119.58 50.663H87.145l13.94-42.902c.717-2.206 3.84-2.206 4.557 0l13.94 42.903z"
|
||||
/>
|
||||
</svg>
|
||||
{:else if application.gitSource?.type === 'github'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
<g fill="#ffffff"
|
||||
><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
|
||||
/><path
|
||||
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm2.446 2.729c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zM31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm3.261 3.361c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm4.5 1.951c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm4.943.361c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm4.598-.782c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
|
||||
/></g
|
||||
>
|
||||
</svg>
|
||||
{/if}
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="block flex-row justify-start space-x-2 px-5 pt-6 sm:px-10 md:flex">
|
||||
<div class="mb-4 min-w-[16rem] space-y-2 md:mb-0 ">
|
||||
|
||||
@@ -104,70 +104,49 @@
|
||||
</div>
|
||||
<span class="text-xs">{application.name}</span>
|
||||
</div>
|
||||
|
||||
{#if application.fqdn}
|
||||
{#if application.gitSource?.htmlUrl && application.repository && application.branch}
|
||||
<a
|
||||
href={application.fqdn}
|
||||
href="{application.gitSource.htmlUrl}/{application.repository}/tree/{application.branch}"
|
||||
target="_blank"
|
||||
class="icons tooltip-bottom flex items-center bg-transparent text-sm"
|
||||
><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" />
|
||||
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
|
||||
<line x1="10" y1="14" x2="20" y2="4" />
|
||||
<polyline points="15 4 20 4 20 9" />
|
||||
</svg></a
|
||||
class="w-10"
|
||||
>
|
||||
{/if}
|
||||
<a
|
||||
href="{application.gitSource.htmlUrl}/{application.repository}/tree/{application.branch}"
|
||||
target="_blank"
|
||||
class="w-10"
|
||||
>
|
||||
{#if application.gitSource?.type === 'gitlab'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
<path
|
||||
fill="#FC6D26"
|
||||
d="M126.615 72.31l-7.034-21.647L105.64 7.76c-.716-2.206-3.84-2.206-4.556 0l-13.94 42.903H40.856L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664 1.385 72.31a4.792 4.792 0 001.74 5.358L64 121.894l60.874-44.227a4.793 4.793 0 001.74-5.357"
|
||||
/><path fill="#E24329" d="M64 121.894l23.144-71.23H40.856L64 121.893z" /><path
|
||||
fill="#FC6D26"
|
||||
d="M64 121.894l-23.144-71.23H8.42L64 121.893z"
|
||||
/><path
|
||||
fill="#FCA326"
|
||||
d="M8.42 50.663L1.384 72.31a4.79 4.79 0 001.74 5.357L64 121.894 8.42 50.664z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M8.42 50.663h32.436L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664z"
|
||||
/><path fill="#FC6D26" d="M64 121.894l23.144-71.23h32.437L64 121.893z" /><path
|
||||
fill="#FCA326"
|
||||
d="M119.58 50.663l7.035 21.647a4.79 4.79 0 01-1.74 5.357L64 121.894l55.58-71.23z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M119.58 50.663H87.145l13.94-42.902c.717-2.206 3.84-2.206 4.557 0l13.94 42.903z"
|
||||
/>
|
||||
</svg>
|
||||
{:else if application.gitSource?.type === 'github'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
<g fill="#ffffff"
|
||||
><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
|
||||
{#if application.gitSource?.type === 'gitlab'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
<path
|
||||
fill="#FC6D26"
|
||||
d="M126.615 72.31l-7.034-21.647L105.64 7.76c-.716-2.206-3.84-2.206-4.556 0l-13.94 42.903H40.856L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664 1.385 72.31a4.792 4.792 0 001.74 5.358L64 121.894l60.874-44.227a4.793 4.793 0 001.74-5.357"
|
||||
/><path fill="#E24329" d="M64 121.894l23.144-71.23H40.856L64 121.893z" /><path
|
||||
fill="#FC6D26"
|
||||
d="M64 121.894l-23.144-71.23H8.42L64 121.893z"
|
||||
/><path
|
||||
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm2.446 2.729c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zM31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm3.261 3.361c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm4.5 1.951c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm4.943.361c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm4.598-.782c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
|
||||
/></g
|
||||
>
|
||||
</svg>
|
||||
{/if}
|
||||
</a>
|
||||
fill="#FCA326"
|
||||
d="M8.42 50.663L1.384 72.31a4.79 4.79 0 001.74 5.357L64 121.894 8.42 50.664z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M8.42 50.663h32.436L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664z"
|
||||
/><path fill="#FC6D26" d="M64 121.894l23.144-71.23h32.437L64 121.893z" /><path
|
||||
fill="#FCA326"
|
||||
d="M119.58 50.663l7.035 21.647a4.79 4.79 0 01-1.74 5.357L64 121.894l55.58-71.23z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M119.58 50.663H87.145l13.94-42.902c.717-2.206 3.84-2.206 4.557 0l13.94 42.903z"
|
||||
/>
|
||||
</svg>
|
||||
{:else if application.gitSource?.type === 'github'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
<g fill="#ffffff"
|
||||
><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
|
||||
/><path
|
||||
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm2.446 2.729c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zM31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm3.261 3.361c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm4.5 1.951c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm4.943.361c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm4.598-.782c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
|
||||
/></g
|
||||
>
|
||||
</svg>
|
||||
{/if}
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex flex-row justify-center space-x-2 px-10 pt-6">
|
||||
{#if logs.length === 0}
|
||||
|
||||
@@ -64,70 +64,49 @@
|
||||
</div>
|
||||
<span class="text-xs">{application.name} </span>
|
||||
</div>
|
||||
|
||||
{#if application.fqdn}
|
||||
{#if application.gitSource?.htmlUrl && application.repository && application.branch}
|
||||
<a
|
||||
href={application.fqdn}
|
||||
href="{application.gitSource.htmlUrl}/{application.repository}/tree/{application.branch}"
|
||||
target="_blank"
|
||||
class="icons tooltip-bottom flex items-center bg-transparent text-sm"
|
||||
><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" />
|
||||
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
|
||||
<line x1="10" y1="14" x2="20" y2="4" />
|
||||
<polyline points="15 4 20 4 20 9" />
|
||||
</svg></a
|
||||
class="w-10"
|
||||
>
|
||||
{/if}
|
||||
<a
|
||||
href="{application.gitSource.htmlUrl}/{application.repository}/tree/{application.branch}"
|
||||
target="_blank"
|
||||
class="w-10"
|
||||
>
|
||||
{#if application.gitSource?.type === 'gitlab'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
<path
|
||||
fill="#FC6D26"
|
||||
d="M126.615 72.31l-7.034-21.647L105.64 7.76c-.716-2.206-3.84-2.206-4.556 0l-13.94 42.903H40.856L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664 1.385 72.31a4.792 4.792 0 001.74 5.358L64 121.894l60.874-44.227a4.793 4.793 0 001.74-5.357"
|
||||
/><path fill="#E24329" d="M64 121.894l23.144-71.23H40.856L64 121.893z" /><path
|
||||
fill="#FC6D26"
|
||||
d="M64 121.894l-23.144-71.23H8.42L64 121.893z"
|
||||
/><path
|
||||
fill="#FCA326"
|
||||
d="M8.42 50.663L1.384 72.31a4.79 4.79 0 001.74 5.357L64 121.894 8.42 50.664z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M8.42 50.663h32.436L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664z"
|
||||
/><path fill="#FC6D26" d="M64 121.894l23.144-71.23h32.437L64 121.893z" /><path
|
||||
fill="#FCA326"
|
||||
d="M119.58 50.663l7.035 21.647a4.79 4.79 0 01-1.74 5.357L64 121.894l55.58-71.23z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M119.58 50.663H87.145l13.94-42.902c.717-2.206 3.84-2.206 4.557 0l13.94 42.903z"
|
||||
/>
|
||||
</svg>
|
||||
{:else if application.gitSource?.type === 'github'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
<g fill="#ffffff"
|
||||
><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
|
||||
{#if application.gitSource?.type === 'gitlab'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
<path
|
||||
fill="#FC6D26"
|
||||
d="M126.615 72.31l-7.034-21.647L105.64 7.76c-.716-2.206-3.84-2.206-4.556 0l-13.94 42.903H40.856L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664 1.385 72.31a4.792 4.792 0 001.74 5.358L64 121.894l60.874-44.227a4.793 4.793 0 001.74-5.357"
|
||||
/><path fill="#E24329" d="M64 121.894l23.144-71.23H40.856L64 121.893z" /><path
|
||||
fill="#FC6D26"
|
||||
d="M64 121.894l-23.144-71.23H8.42L64 121.893z"
|
||||
/><path
|
||||
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm2.446 2.729c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zM31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm3.261 3.361c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm4.5 1.951c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm4.943.361c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm4.598-.782c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
|
||||
/></g
|
||||
>
|
||||
</svg>
|
||||
{/if}
|
||||
</a>
|
||||
fill="#FCA326"
|
||||
d="M8.42 50.663L1.384 72.31a4.79 4.79 0 001.74 5.357L64 121.894 8.42 50.664z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M8.42 50.663h32.436L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664z"
|
||||
/><path fill="#FC6D26" d="M64 121.894l23.144-71.23h32.437L64 121.893z" /><path
|
||||
fill="#FCA326"
|
||||
d="M119.58 50.663l7.035 21.647a4.79 4.79 0 01-1.74 5.357L64 121.894l55.58-71.23z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M119.58 50.663H87.145l13.94-42.902c.717-2.206 3.84-2.206 4.557 0l13.94 42.903z"
|
||||
/>
|
||||
</svg>
|
||||
{:else if application.gitSource?.type === 'github'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
<g fill="#ffffff"
|
||||
><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
|
||||
/><path
|
||||
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm2.446 2.729c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zM31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm3.261 3.361c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm4.5 1.951c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm4.943.361c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm4.598-.782c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
|
||||
/></g
|
||||
>
|
||||
</svg>
|
||||
{/if}
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="mx-auto max-w-6xl px-6 pt-4">
|
||||
<div class="flex justify-center py-4 text-center">
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
const response = await get(`/applications/${params.id}/secrets`);
|
||||
return {
|
||||
props: {
|
||||
application: stuff.application,
|
||||
application: stuff.application,
|
||||
...response
|
||||
}
|
||||
};
|
||||
@@ -70,70 +70,49 @@
|
||||
</div>
|
||||
<span class="text-xs">{application.name} </span>
|
||||
</div>
|
||||
|
||||
{#if application.fqdn}
|
||||
{#if application.gitSource?.htmlUrl && application.repository && application.branch}
|
||||
<a
|
||||
href={application.fqdn}
|
||||
href="{application.gitSource.htmlUrl}/{application.repository}/tree/{application.branch}"
|
||||
target="_blank"
|
||||
class="icons tooltip-bottom flex items-center bg-transparent text-sm"
|
||||
><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" />
|
||||
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
|
||||
<line x1="10" y1="14" x2="20" y2="4" />
|
||||
<polyline points="15 4 20 4 20 9" />
|
||||
</svg></a
|
||||
class="w-10"
|
||||
>
|
||||
{/if}
|
||||
<a
|
||||
href="{application.gitSource.htmlUrl}/{application.repository}/tree/{application.branch}"
|
||||
target="_blank"
|
||||
class="w-10"
|
||||
>
|
||||
{#if application.gitSource?.type === 'gitlab'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
<path
|
||||
fill="#FC6D26"
|
||||
d="M126.615 72.31l-7.034-21.647L105.64 7.76c-.716-2.206-3.84-2.206-4.556 0l-13.94 42.903H40.856L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664 1.385 72.31a4.792 4.792 0 001.74 5.358L64 121.894l60.874-44.227a4.793 4.793 0 001.74-5.357"
|
||||
/><path fill="#E24329" d="M64 121.894l23.144-71.23H40.856L64 121.893z" /><path
|
||||
fill="#FC6D26"
|
||||
d="M64 121.894l-23.144-71.23H8.42L64 121.893z"
|
||||
/><path
|
||||
fill="#FCA326"
|
||||
d="M8.42 50.663L1.384 72.31a4.79 4.79 0 001.74 5.357L64 121.894 8.42 50.664z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M8.42 50.663h32.436L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664z"
|
||||
/><path fill="#FC6D26" d="M64 121.894l23.144-71.23h32.437L64 121.893z" /><path
|
||||
fill="#FCA326"
|
||||
d="M119.58 50.663l7.035 21.647a4.79 4.79 0 01-1.74 5.357L64 121.894l55.58-71.23z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M119.58 50.663H87.145l13.94-42.902c.717-2.206 3.84-2.206 4.557 0l13.94 42.903z"
|
||||
/>
|
||||
</svg>
|
||||
{:else if application.gitSource?.type === 'github'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
<g fill="#ffffff"
|
||||
><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
|
||||
{#if application.gitSource?.type === 'gitlab'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
<path
|
||||
fill="#FC6D26"
|
||||
d="M126.615 72.31l-7.034-21.647L105.64 7.76c-.716-2.206-3.84-2.206-4.556 0l-13.94 42.903H40.856L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664 1.385 72.31a4.792 4.792 0 001.74 5.358L64 121.894l60.874-44.227a4.793 4.793 0 001.74-5.357"
|
||||
/><path fill="#E24329" d="M64 121.894l23.144-71.23H40.856L64 121.893z" /><path
|
||||
fill="#FC6D26"
|
||||
d="M64 121.894l-23.144-71.23H8.42L64 121.893z"
|
||||
/><path
|
||||
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm2.446 2.729c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zM31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm3.261 3.361c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm4.5 1.951c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm4.943.361c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm4.598-.782c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
|
||||
/></g
|
||||
>
|
||||
</svg>
|
||||
{/if}
|
||||
</a>
|
||||
fill="#FCA326"
|
||||
d="M8.42 50.663L1.384 72.31a4.79 4.79 0 001.74 5.357L64 121.894 8.42 50.664z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M8.42 50.663h32.436L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664z"
|
||||
/><path fill="#FC6D26" d="M64 121.894l23.144-71.23h32.437L64 121.893z" /><path
|
||||
fill="#FCA326"
|
||||
d="M119.58 50.663l7.035 21.647a4.79 4.79 0 01-1.74 5.357L64 121.894l55.58-71.23z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M119.58 50.663H87.145l13.94-42.902c.717-2.206 3.84-2.206 4.557 0l13.94 42.903z"
|
||||
/>
|
||||
</svg>
|
||||
{:else if application.gitSource?.type === 'github'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
<g fill="#ffffff"
|
||||
><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
|
||||
/><path
|
||||
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm2.446 2.729c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zM31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm3.261 3.361c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm4.5 1.951c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm4.943.361c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm4.598-.782c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
|
||||
/></g
|
||||
>
|
||||
</svg>
|
||||
{/if}
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="mx-auto max-w-6xl px-6 pt-4">
|
||||
<table class="mx-auto border-separate text-left">
|
||||
|
||||
@@ -41,70 +41,49 @@
|
||||
</div>
|
||||
<span class="text-xs">{application.name} </span>
|
||||
</div>
|
||||
|
||||
{#if application.fqdn}
|
||||
{#if application.gitSource?.htmlUrl && application.repository && application.branch}
|
||||
<a
|
||||
href={application.fqdn}
|
||||
href="{application.gitSource.htmlUrl}/{application.repository}/tree/{application.branch}"
|
||||
target="_blank"
|
||||
class="icons tooltip-bottom flex items-center bg-transparent text-sm"
|
||||
><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" />
|
||||
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
|
||||
<line x1="10" y1="14" x2="20" y2="4" />
|
||||
<polyline points="15 4 20 4 20 9" />
|
||||
</svg></a
|
||||
class="w-10"
|
||||
>
|
||||
{/if}
|
||||
<a
|
||||
href="{application.gitSource.htmlUrl}/{application.repository}/tree/{application.branch}"
|
||||
target="_blank"
|
||||
class="w-10"
|
||||
>
|
||||
{#if application.gitSource?.type === 'gitlab'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
<path
|
||||
fill="#FC6D26"
|
||||
d="M126.615 72.31l-7.034-21.647L105.64 7.76c-.716-2.206-3.84-2.206-4.556 0l-13.94 42.903H40.856L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664 1.385 72.31a4.792 4.792 0 001.74 5.358L64 121.894l60.874-44.227a4.793 4.793 0 001.74-5.357"
|
||||
/><path fill="#E24329" d="M64 121.894l23.144-71.23H40.856L64 121.893z" /><path
|
||||
fill="#FC6D26"
|
||||
d="M64 121.894l-23.144-71.23H8.42L64 121.893z"
|
||||
/><path
|
||||
fill="#FCA326"
|
||||
d="M8.42 50.663L1.384 72.31a4.79 4.79 0 001.74 5.357L64 121.894 8.42 50.664z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M8.42 50.663h32.436L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664z"
|
||||
/><path fill="#FC6D26" d="M64 121.894l23.144-71.23h32.437L64 121.893z" /><path
|
||||
fill="#FCA326"
|
||||
d="M119.58 50.663l7.035 21.647a4.79 4.79 0 01-1.74 5.357L64 121.894l55.58-71.23z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M119.58 50.663H87.145l13.94-42.902c.717-2.206 3.84-2.206 4.557 0l13.94 42.903z"
|
||||
/>
|
||||
</svg>
|
||||
{:else if application.gitSource?.type === 'github'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
<g fill="#ffffff"
|
||||
><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
|
||||
{#if application.gitSource?.type === 'gitlab'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
<path
|
||||
fill="#FC6D26"
|
||||
d="M126.615 72.31l-7.034-21.647L105.64 7.76c-.716-2.206-3.84-2.206-4.556 0l-13.94 42.903H40.856L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664 1.385 72.31a4.792 4.792 0 001.74 5.358L64 121.894l60.874-44.227a4.793 4.793 0 001.74-5.357"
|
||||
/><path fill="#E24329" d="M64 121.894l23.144-71.23H40.856L64 121.893z" /><path
|
||||
fill="#FC6D26"
|
||||
d="M64 121.894l-23.144-71.23H8.42L64 121.893z"
|
||||
/><path
|
||||
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm2.446 2.729c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zM31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm3.261 3.361c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm4.5 1.951c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm4.943.361c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm4.598-.782c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
|
||||
/></g
|
||||
>
|
||||
</svg>
|
||||
{/if}
|
||||
</a>
|
||||
fill="#FCA326"
|
||||
d="M8.42 50.663L1.384 72.31a4.79 4.79 0 001.74 5.357L64 121.894 8.42 50.664z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M8.42 50.663h32.436L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664z"
|
||||
/><path fill="#FC6D26" d="M64 121.894l23.144-71.23h32.437L64 121.893z" /><path
|
||||
fill="#FCA326"
|
||||
d="M119.58 50.663l7.035 21.647a4.79 4.79 0 01-1.74 5.357L64 121.894l55.58-71.23z"
|
||||
/><path
|
||||
fill="#E24329"
|
||||
d="M119.58 50.663H87.145l13.94-42.902c.717-2.206 3.84-2.206 4.557 0l13.94 42.903z"
|
||||
/>
|
||||
</svg>
|
||||
{:else if application.gitSource?.type === 'github'}
|
||||
<svg viewBox="0 0 128 128" class="icons">
|
||||
<g fill="#ffffff"
|
||||
><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
|
||||
/><path
|
||||
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm2.446 2.729c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zM31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm3.261 3.361c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm4.5 1.951c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm4.943.361c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm4.598-.782c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
|
||||
/></g
|
||||
>
|
||||
</svg>
|
||||
{/if}
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="mx-auto max-w-6xl rounded-xl px-6 pt-4">
|
||||
|
||||
@@ -140,7 +140,7 @@
|
||||
{#if application.fqdn}
|
||||
<div class="truncate text-center">{getDomain(application.fqdn) || ''}</div>
|
||||
{/if}
|
||||
{#if !application.gitSourceId}
|
||||
{#if !application.gitSourceId || !application.repository || !application.branch}
|
||||
<div class="truncate text-center font-bold text-red-500 group-hover:text-white">
|
||||
Git Source Missing
|
||||
</div>
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
loading = true;
|
||||
await post(`/databases/${id}`, { ...database, isRunning: $status.database.isRunning });
|
||||
generateDbDetails();
|
||||
toast.push('Settings saved.');
|
||||
toast.push('Configuration saved.');
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
try {
|
||||
await del(`/databases/${database.id}`, { id: database.id });
|
||||
return await goto('/databases');
|
||||
} catch ({ error }) {
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
loading = false;
|
||||
@@ -87,7 +87,7 @@
|
||||
try {
|
||||
await post(`/databases/${database.id}/stop`, {});
|
||||
return window.location.reload();
|
||||
} catch ({ error }) {
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
@@ -97,7 +97,7 @@
|
||||
try {
|
||||
await post(`/databases/${database.id}/start`, {});
|
||||
return window.location.reload();
|
||||
} catch ({ error }) {
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
} else {
|
||||
await startProxy();
|
||||
}
|
||||
} catch ({ error }) {
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
loadingProxy = false;
|
||||
@@ -88,7 +88,7 @@
|
||||
try {
|
||||
await post(`/destinations/${id}/stop`, { engine: destination.engine });
|
||||
return toast.push($t('destination.coolify_proxy_stopped'));
|
||||
} catch ({ error }) {
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
@@ -96,7 +96,7 @@
|
||||
try {
|
||||
await post(`/destinations/${id}/start`, { engine: destination.engine });
|
||||
return toast.push($t('destination.coolify_proxy_started'));
|
||||
} catch ({ error }) {
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
@@ -110,7 +110,7 @@
|
||||
engine: destination.engine,
|
||||
fqdn: settings.fqdn
|
||||
});
|
||||
} catch ({ error }) {
|
||||
} catch (error) {
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 5000);
|
||||
|
||||
@@ -82,7 +82,7 @@ import { appSession } from '$lib/store';
|
||||
} else {
|
||||
await startProxy();
|
||||
}
|
||||
} catch ({ error }) {
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
@@ -92,7 +92,7 @@ import { appSession } from '$lib/store';
|
||||
const engine = generateRemoteEngine(destination);
|
||||
await post(`/destinations/${id}/stop.json`, { engine });
|
||||
return toast.push($t('destination.coolify_proxy_stopped'));
|
||||
} catch ({ error }) {
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
@@ -101,7 +101,7 @@ import { appSession } from '$lib/store';
|
||||
const engine = generateRemoteEngine(destination);
|
||||
await post(`/destinations/${id}/start.json`, { engine });
|
||||
return toast.push($t('destination.coolify_proxy_started'));
|
||||
} catch ({ error }) {
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
@@ -115,7 +115,7 @@ import { appSession } from '$lib/store';
|
||||
engine: destination.engine,
|
||||
fqdn: settings.fqdn
|
||||
});
|
||||
} catch ({ error }) {
|
||||
} catch (error) {
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 5000);
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await del(`/iam/user/remove`, { uid: id });
|
||||
await del(`/iam/user/remove`, { id });
|
||||
toast.push('Account deleted.');
|
||||
const data = await get('/iam');
|
||||
accounts = data.accounts;
|
||||
|
||||
@@ -1,76 +1,63 @@
|
||||
<script lang="ts">
|
||||
export let service: any;
|
||||
|
||||
import Fider from '$lib/components/svg/services/Fider.svelte';
|
||||
|
||||
import Ghost from '$lib/components/svg/services/Ghost.svelte';
|
||||
import Hasura from '$lib/components/svg/services/Hasura.svelte';
|
||||
|
||||
import LanguageTool from '$lib/components/svg/services/LanguageTool.svelte';
|
||||
|
||||
import MinIo from '$lib/components/svg/services/MinIO.svelte';
|
||||
import N8n from '$lib/components/svg/services/N8n.svelte';
|
||||
|
||||
import NocoDb from '$lib/components/svg/services/NocoDB.svelte';
|
||||
|
||||
import PlausibleAnalytics from '$lib/components/svg/services/PlausibleAnalytics.svelte';
|
||||
import Umami from '$lib/components/svg/services/Umami.svelte';
|
||||
import UptimeKuma from '$lib/components/svg/services/UptimeKuma.svelte';
|
||||
import VaultWarden from '$lib/components/svg/services/VaultWarden.svelte';
|
||||
import VsCodeServer from '$lib/components/svg/services/VSCodeServer.svelte';
|
||||
import Wordpress from '$lib/components/svg/services/Wordpress.svelte';
|
||||
import * as Icons from '$lib/components/svg/services';
|
||||
</script>
|
||||
|
||||
{#if service.type === 'plausibleanalytics'}
|
||||
<a href="https://plausible.io" target="_blank">
|
||||
<PlausibleAnalytics />
|
||||
<Icons.PlausibleAnalytics />
|
||||
</a>
|
||||
{:else if service.type === 'nocodb'}
|
||||
<a href="https://nocodb.com" target="_blank">
|
||||
<NocoDb />
|
||||
<Icons.NocoDb />
|
||||
</a>
|
||||
{:else if service.type === 'minio'}
|
||||
<a href="https://min.io" target="_blank">
|
||||
<MinIo />
|
||||
<Icons.MinIo />
|
||||
</a>
|
||||
{:else if service.type === 'vscodeserver'}
|
||||
<a href="https://coder.com" target="_blank">
|
||||
<VsCodeServer />
|
||||
<Icons.VsCodeServer />
|
||||
</a>
|
||||
{:else if service.type === 'wordpress'}
|
||||
<a href="https://wordpress.org" target="_blank">
|
||||
<Wordpress />
|
||||
<Icons.Wordpress />
|
||||
</a>
|
||||
{:else if service.type === 'vaultwarden'}
|
||||
<a href="https://github.com/dani-garcia/vaultwarden" target="_blank">
|
||||
<VaultWarden />
|
||||
<Icons.VaultWarden />
|
||||
</a>
|
||||
{:else if service.type === 'languagetool'}
|
||||
<a href="https://languagetool.org/dev" target="_blank">
|
||||
<LanguageTool />
|
||||
<Icons.LanguageTool />
|
||||
</a>
|
||||
{:else if service.type === 'n8n'}
|
||||
<a href="https://n8n.io" target="_blank">
|
||||
<N8n />
|
||||
<Icons.N8n />
|
||||
</a>
|
||||
{:else if service.type === 'uptimekuma'}
|
||||
<a href="https://github.com/louislam/uptime-kuma" target="_blank">
|
||||
<UptimeKuma />
|
||||
<Icons.UptimeKuma />
|
||||
</a>
|
||||
{:else if service.type === 'ghost'}
|
||||
<a href="https://ghost.org" target="_blank">
|
||||
<Ghost />
|
||||
<Icons.Ghost />
|
||||
</a>
|
||||
{:else if service.type === 'umami'}
|
||||
<a href="https://umami.is" target="_blank">
|
||||
<Umami />
|
||||
<Icons.Umami />
|
||||
</a>
|
||||
{:else if service.type === 'hasura'}
|
||||
<a href="https://hasura.io" target="_blank">
|
||||
<Hasura />
|
||||
<Icons.Hasura />
|
||||
</a>
|
||||
{:else if service.type === 'fider'}
|
||||
<a href="https://fider.io" target="_blank">
|
||||
<Fider />
|
||||
<Icons.Fider />
|
||||
</a>
|
||||
{:else if service.type === 'moodle'}
|
||||
<a href="https://moodle.org" target="_blank">
|
||||
<Icons.Moodle />
|
||||
</a>
|
||||
{/if}
|
||||
|
||||
|
||||
36
apps/ui/src/routes/services/[id]/_Services.svelte
Normal file
36
apps/ui/src/routes/services/[id]/_Services.svelte
Normal file
@@ -0,0 +1,36 @@
|
||||
<script lang="ts">
|
||||
export let type: string;
|
||||
import * as Icons from '$lib/components/svg/services';
|
||||
</script>
|
||||
|
||||
{#if type === 'plausibleanalytics'}
|
||||
<Icons.PlausibleAnalytics isAbsolute />
|
||||
{:else if type === 'nocodb'}
|
||||
<Icons.NocoDb isAbsolute />
|
||||
{:else if type === 'minio'}
|
||||
<Icons.MinIo isAbsolute />
|
||||
{:else if type === 'vscodeserver'}
|
||||
<Icons.VsCodeServer isAbsolute />
|
||||
{:else if type === 'wordpress'}
|
||||
<Icons.Wordpress isAbsolute />
|
||||
{:else if type === 'vaultwarden'}
|
||||
<Icons.VaultWarden isAbsolute />
|
||||
{:else if type === 'languagetool'}
|
||||
<Icons.LanguageTool isAbsolute />
|
||||
{:else if type === 'n8n'}
|
||||
<Icons.N8n isAbsolute />
|
||||
{:else if type === 'uptimekuma'}
|
||||
<Icons.UptimeKuma isAbsolute />
|
||||
{:else if type === 'ghost'}
|
||||
<Icons.Ghost isAbsolute />
|
||||
{:else if type === 'meilisearch'}
|
||||
<Icons.MeiliSearch isAbsolute />
|
||||
{:else if type === 'umami'}
|
||||
<Icons.Umami isAbsolute />
|
||||
{:else if type === 'hasura'}
|
||||
<Icons.Hasura isAbsolute />
|
||||
{:else if type === 'fider'}
|
||||
<Icons.Fider isAbsolute />
|
||||
{:else if type === 'moodle'}
|
||||
<Icons.Moodle isAbsolute />
|
||||
{/if}
|
||||
102
apps/ui/src/routes/services/[id]/_Services/_Moodle.svelte
Normal file
102
apps/ui/src/routes/services/[id]/_Services/_Moodle.svelte
Normal file
@@ -0,0 +1,102 @@
|
||||
<script lang="ts">
|
||||
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
||||
import { t } from '$lib/translations';
|
||||
export let readOnly: any;
|
||||
export let service: any;
|
||||
</script>
|
||||
|
||||
<div class="flex space-x-1 py-5 font-bold">
|
||||
<div class="title">Moodle</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="email">{$t('forms.default_email_address')}</label>
|
||||
<input
|
||||
name="email"
|
||||
id="email"
|
||||
required
|
||||
readonly={readOnly}
|
||||
disabled={readOnly}
|
||||
placeholder={$t('forms.email')}
|
||||
value={service.moodle.defaultEmail}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="defaultUsername">Default Username</label>
|
||||
<CopyPasswordField
|
||||
id="defaultUsername"
|
||||
required
|
||||
readonly={readOnly}
|
||||
disabled={readOnly}
|
||||
name="defaultUsername"
|
||||
value={service.moodle.defaultUsername}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="defaultPassword">{$t('forms.default_password')}</label>
|
||||
<CopyPasswordField
|
||||
id="defaultPassword"
|
||||
isPasswordField
|
||||
required
|
||||
readonly={readOnly}
|
||||
disabled={readOnly}
|
||||
name="defaultPassword"
|
||||
value={service.moodle.defaultPassword}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex space-x-1 py-5 font-bold">
|
||||
<div class="title">MariaDB</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="mariadbUser">{$t('forms.username')}</label>
|
||||
<CopyPasswordField
|
||||
name="mariadbUser"
|
||||
id="mariadbUser"
|
||||
value={service.moodle.mariadbUser}
|
||||
readonly
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="mariadbPassword">{$t('forms.password')}</label>
|
||||
<CopyPasswordField
|
||||
id="mariadbPassword"
|
||||
isPasswordField
|
||||
readonly
|
||||
disabled
|
||||
name="mariadbPassword"
|
||||
value={service.moodle.mariadbPassword}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="mariadbDatabase">{$t('index.database')}</label>
|
||||
<input
|
||||
name="mariadbDatabase"
|
||||
id="mariadbDatabase"
|
||||
required
|
||||
readonly={readOnly}
|
||||
disabled={readOnly}
|
||||
bind:value={service.moodle.mariadbDatabase}
|
||||
placeholder="{$t('forms.eg')}: moodle_db"
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="mariadbRootUser">{$t('forms.root_db_user')}</label>
|
||||
<CopyPasswordField
|
||||
id="mariadbRootUser"
|
||||
readonly
|
||||
disabled
|
||||
name="mariadbRootUser"
|
||||
value={service.moodle.mariadbRootUser}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="mariadbRootUserPassword">{$t('forms.root_db_password')}</label>
|
||||
<CopyPasswordField
|
||||
id="mariadbRootUserPassword"
|
||||
isPasswordField
|
||||
readonly
|
||||
disabled
|
||||
name="mariadbRootUserPassword"
|
||||
value={service.moodle.mariadbRootUserPassword}
|
||||
/>
|
||||
</div>
|
||||
@@ -13,10 +13,10 @@
|
||||
import { get, post } from '$lib/api';
|
||||
import { errorNotification } from '$lib/common';
|
||||
import { t } from '$lib/translations';
|
||||
import { appSession, disabledButton, status } from '$lib/store';
|
||||
import { appSession, disabledButton, status, location, setLocation } from '$lib/store';
|
||||
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
||||
import Explainer from '$lib/components/Explainer.svelte';
|
||||
import Setting from '$lib/components/Setting.svelte'
|
||||
import Setting from '$lib/components/Setting.svelte';
|
||||
|
||||
import Fider from './_Fider.svelte';
|
||||
import Ghost from './_Ghost.svelte';
|
||||
@@ -27,6 +27,7 @@
|
||||
import Umami from './_Umami.svelte';
|
||||
import VsCodeServer from './_VSCodeServer.svelte';
|
||||
import Wordpress from './_Wordpress.svelte';
|
||||
import Moodle from './_Moodle.svelte';
|
||||
|
||||
const { id } = $page.params;
|
||||
|
||||
@@ -44,8 +45,9 @@
|
||||
exposePort: service.exposePort
|
||||
});
|
||||
await post(`/services/${id}`, { ...service });
|
||||
setLocation(service)
|
||||
$disabledButton = false;
|
||||
toast.push('Settings saved.');
|
||||
toast.push('Configuration saved.');
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
@@ -97,6 +99,7 @@
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="mx-auto max-w-4xl px-6 pb-12">
|
||||
<form on:submit|preventDefault={handleSubmit} class="py-4">
|
||||
<div class="flex space-x-1 pb-5 font-bold">
|
||||
@@ -271,6 +274,8 @@
|
||||
<Hasura bind:service />
|
||||
{:else if service.type === 'fider'}
|
||||
<Fider bind:service {readOnly} />
|
||||
{:else if service.type === 'moodle'}
|
||||
<Moodle bind:service {readOnly} />
|
||||
{/if}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
ownMysql
|
||||
});
|
||||
service.wordpress.ownMysql = ownMysql;
|
||||
} catch ({ error }) {
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
@@ -112,7 +112,7 @@ define('SUBDOMAIN_INSTALL', false);`
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="ftpPassword">Password</label>
|
||||
<CopyPasswordField id="ftpPassword" readonly disabled name="ftpPassword" value={ftpPassword} />
|
||||
<CopyPasswordField id="ftpPassword" isPasswordField readonly disabled name="ftpPassword" value={ftpPassword} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex space-x-1 py-5 font-bold">
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
}
|
||||
if (newStorage) toast.push('Storage saved.');
|
||||
else toast.push('Storage updated.');
|
||||
} catch ({ error }) {
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@
|
||||
await del(`/services/${id}/storages`, { path: storage.path });
|
||||
dispatch('refresh');
|
||||
toast.push('Storage deleted.');
|
||||
} catch ({ error }) {
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return handlerNotFoundLoad(error, url);
|
||||
}
|
||||
};
|
||||
@@ -60,7 +61,7 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { t } from '$lib/translations';
|
||||
import { errorNotification, handlerNotFoundLoad } from '$lib/common';
|
||||
import { appSession, disabledButton, status } from '$lib/store';
|
||||
import { appSession, disabledButton, status, location, setLocation } from '$lib/store';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
const { id } = $page.params;
|
||||
|
||||
@@ -109,6 +110,7 @@
|
||||
loading = true;
|
||||
try {
|
||||
await post(`/services/${service.id}/${service.type}/start`, {});
|
||||
return window.location.reload();
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
@@ -125,9 +127,11 @@
|
||||
}
|
||||
onDestroy(() => {
|
||||
$status.service.initialLoading = true;
|
||||
$location = null;
|
||||
clearInterval(statusInterval);
|
||||
});
|
||||
onMount(async () => {
|
||||
setLocation(service);
|
||||
$status.service.isRunning = false;
|
||||
$status.service.loading = false;
|
||||
if (service.type && service.destinationDockerId && service.version && service.fqdn) {
|
||||
@@ -146,6 +150,29 @@
|
||||
<Loading fullscreen cover />
|
||||
{:else}
|
||||
{#if service.type && service.destinationDockerId && service.version}
|
||||
{#if $location}
|
||||
<a
|
||||
href={$location}
|
||||
target="_blank"
|
||||
class="icons tooltip-bottom flex items-center bg-transparent text-sm"
|
||||
><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" />
|
||||
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
|
||||
<line x1="10" y1="14" x2="20" y2="4" />
|
||||
<polyline points="15 4 20 4 20 9" />
|
||||
</svg></a
|
||||
>
|
||||
<div class="border border-stone-700 h-8" />
|
||||
{/if}
|
||||
{#if $status.service.initialLoading}
|
||||
<button
|
||||
class="icons tooltip-bottom flex animate-spin items-center space-x-2 bg-transparent text-sm duration-500 ease-in-out"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user