Merge remote-tracking branch 'origin' into some-tweaks

This commit is contained in:
Kaname 2022-09-09 23:57:03 +00:00
commit 02a336a25d
23 changed files with 1040 additions and 946 deletions

View File

@ -26,7 +26,7 @@
// Use 'forwardPorts' to make a list of ports inside the container available locally. // Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [3000, 3001], "forwardPorts": [3000, 3001],
// Use 'postCreateCommand' to run commands after the container is created. // Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "cp apps/api/.env.example pps/api/.env && pnpm install && pnpm db:push && pnpm db:seed", "postCreateCommand": "cp apps/api/.env.example apps/api/.env && pnpm install && pnpm db:push && pnpm db:seed",
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "node", "remoteUser": "node",
"features": { "features": {

View File

@ -21,14 +21,17 @@ async function autoUpdater() {
const activeCount = 0 const activeCount = 0
if (activeCount === 0) { if (activeCount === 0) {
if (!isDev) { if (!isDev) {
const { isAutoUpdateEnabled } = await prisma.setting.findFirst();
if (isAutoUpdateEnabled) {
await asyncExecShell(`docker pull coollabsio/coolify:${latestVersion}`); await asyncExecShell(`docker pull coollabsio/coolify:${latestVersion}`);
await asyncExecShell(`env | grep COOLIFY > .env`); await asyncExecShell(`env | grep COOLIFY > .env`);
await asyncExecShell( await asyncExecShell(
`sed -i '/COOLIFY_AUTO_UPDATE=/cCOOLIFY_AUTO_UPDATE=true' .env` `sed -i '/COOLIFY_AUTO_UPDATE=/cCOOLIFY_AUTO_UPDATE=${isAutoUpdateEnabled}' .env`
); );
await asyncExecShell( await asyncExecShell(
`docker run --rm -tid --env-file .env -v /var/run/docker.sock:/var/run/docker.sock -v coolify-db coollabsio/coolify:${latestVersion} /bin/sh -c "env | grep COOLIFY > .env && echo 'TAG=${latestVersion}' >> .env && docker stop -t 0 coolify && docker rm coolify && docker compose up -d --force-recreate"` `docker run --rm -tid --env-file .env -v /var/run/docker.sock:/var/run/docker.sock -v coolify-db coollabsio/coolify:${latestVersion} /bin/sh -c "env | grep COOLIFY > .env && echo 'TAG=${latestVersion}' >> .env && docker stop -t 0 coolify && docker rm coolify && docker compose up -d --force-recreate"`
); );
}
} else { } else {
console.log('Updating (not really in dev mode).'); console.log('Updating (not really in dev mode).');
} }

View File

@ -205,7 +205,7 @@ export async function isDNSValid(hostname: any, domain: string): Promise<any> {
const { isIP } = await import('is-ip'); const { isIP } = await import('is-ip');
const { DNSServers } = await listSettings(); const { DNSServers } = await listSettings();
if (DNSServers) { if (DNSServers) {
dns.setServers([DNSServers]); dns.setServers([...DNSServers.split(',')]);
} }
let resolves = []; let resolves = [];
try { try {
@ -316,7 +316,7 @@ export async function checkDomainsIsValidInDNS({ hostname, fqdn, dualCerts }): P
const { DNSServers } = await listSettings(); const { DNSServers } = await listSettings();
if (DNSServers) { if (DNSServers) {
dns.setServers([DNSServers]); dns.setServers([...DNSServers.split(',')]);
} }
let resolves = []; let resolves = [];
@ -547,22 +547,26 @@ export async function createRemoteEngineConfiguration(id: string) {
} }
return await fs.writeFile(`${homedir}/.ssh/config`, sshConfig.stringify(config)); return await fs.writeFile(`${homedir}/.ssh/config`, sshConfig.stringify(config));
} }
export async function executeDockerCmd({ export async function executeSSHCmd({ dockerId, command }) {
debug, const { execaCommand } = await import('execa')
buildId, let { remoteEngine, remoteIpAddress, engine, remoteUser } = await prisma.destinationDocker.findUnique({ where: { id: dockerId } })
applicationId, if (remoteEngine) {
dockerId, await createRemoteEngineConfiguration(dockerId)
command engine = `ssh://${remoteIpAddress}`
}: { } else {
debug?: boolean; engine = 'unix:///var/run/docker.sock'
buildId?: string; }
applicationId?: string; if (process.env.CODESANDBOX_HOST) {
dockerId: string; if (command.startsWith('docker compose')) {
command: string; command = command.replace(/docker compose/gi, 'docker-compose')
}): Promise<any> { }
let { remoteEngine, remoteIpAddress, engine } = await prisma.destinationDocker.findUnique({ }
where: { id: dockerId } command = `ssh ${remoteIpAddress} ${command}`
}); return await execaCommand(command)
}
export async function executeDockerCmd({ debug, buildId, applicationId, dockerId, command }: { debug?: boolean, buildId?: string, applicationId?: string, dockerId: string, command: string }): Promise<any> {
const { execaCommand } = await import('execa')
let { remoteEngine, remoteIpAddress, engine, remoteUser } = await prisma.destinationDocker.findUnique({ where: { id: dockerId } })
if (remoteEngine) { if (remoteEngine) {
await createRemoteEngineConfiguration(dockerId); await createRemoteEngineConfiguration(dockerId);
engine = `ssh://${remoteIpAddress}`; engine = `ssh://${remoteIpAddress}`;
@ -577,7 +581,7 @@ export async function executeDockerCmd({
if (command.startsWith(`docker build --progress plain`)) { if (command.startsWith(`docker build --progress plain`)) {
return await asyncExecShellStream({ debug, buildId, applicationId, command, engine }); return await asyncExecShellStream({ debug, buildId, applicationId, command, engine });
} }
return await asyncExecShell(`DOCKER_BUILDKIT=1 DOCKER_HOST="${engine}" ${command}`); return await execaCommand(command, { env: { DOCKER_BUILDKIT: "1", DOCKER_HOST: engine }, shell: true })
} }
export async function startTraefikProxy(id: string): Promise<void> { export async function startTraefikProxy(id: string): Promise<void> {
const { engine, network, remoteEngine, remoteIpAddress } = const { engine, network, remoteEngine, remoteIpAddress } =
@ -822,7 +826,6 @@ export function generateDatabaseConfiguration(database: any, arch: string): Data
defaultDatabase, defaultDatabase,
version, version,
type, type,
settings: { appendOnly }
} = database; } = database;
const baseImage = getDatabaseImage(type, arch); const baseImage = getDatabaseImage(type, arch);
if (type === 'mysql') { if (type === 'mysql') {
@ -903,6 +906,7 @@ export function generateDatabaseConfiguration(database: any, arch: string): Data
} }
return configuration; return configuration;
} else if (type === 'redis') { } else if (type === 'redis') {
const { settings: { appendOnly } } = database;
const configuration: DatabaseConfiguration = { const configuration: DatabaseConfiguration = {
privatePort: 6379, privatePort: 6379,
command: undefined, command: undefined,
@ -1182,113 +1186,150 @@ export async function updatePasswordInDb(database, user, newPassword, isRoot) {
} }
} }
} }
export async function checkExposedPort({ export async function checkExposedPort({ id, configuredPort, exposePort, engine, remoteEngine, remoteIpAddress }: { id: string, configuredPort?: number, exposePort: number, engine: string, remoteEngine: boolean, remoteIpAddress?: string }) {
id,
configuredPort,
exposePort,
dockerId,
remoteIpAddress
}: {
id: string;
configuredPort?: number;
exposePort: number;
dockerId: string;
remoteIpAddress?: string;
}) {
if (exposePort < 1024 || exposePort > 65535) { if (exposePort < 1024 || exposePort > 65535) {
throw { status: 500, message: `Exposed Port needs to be between 1024 and 65535.` }; throw { status: 500, message: `Exposed Port needs to be between 1024 and 65535.` };
} }
if (configuredPort) { if (configuredPort) {
if (configuredPort !== exposePort) { if (configuredPort !== exposePort) {
const availablePort = await getFreeExposedPort(id, exposePort, dockerId, remoteIpAddress); const availablePort = await getFreeExposedPort(id, exposePort, engine, remoteEngine, remoteIpAddress);
if (availablePort.toString() !== exposePort.toString()) { if (availablePort.toString() !== exposePort.toString()) {
throw { status: 500, message: `Port ${exposePort} is already in use.` }; throw { status: 500, message: `Port ${exposePort} is already in use.` };
} }
} }
} else { } else {
const availablePort = await getFreeExposedPort(id, exposePort, dockerId, remoteIpAddress); const availablePort = await getFreeExposedPort(id, exposePort, engine, remoteEngine, remoteIpAddress);
if (availablePort.toString() !== exposePort.toString()) { if (availablePort.toString() !== exposePort.toString()) {
throw { status: 500, message: `Port ${exposePort} is already in use.` }; throw { status: 500, message: `Port ${exposePort} is already in use.` };
} }
} }
} }
export async function getFreeExposedPort(id, exposePort, dockerId, remoteIpAddress) { export async function getFreeExposedPort(id, exposePort, engine, remoteEngine, remoteIpAddress) {
const { default: checkPort } = await import('is-port-reachable'); const { default: checkPort } = await import('is-port-reachable');
if (remoteEngine) {
const applicationUsed = await ( const applicationUsed = await (
await prisma.application.findMany({ await prisma.application.findMany({
where: { exposePort: { not: null }, id: { not: id }, destinationDockerId: dockerId }, where: { exposePort: { not: null }, id: { not: id }, destinationDocker: { remoteIpAddress } },
select: { exposePort: true } select: { exposePort: true }
}) })
).map((a) => a.exposePort); ).map((a) => a.exposePort);
const serviceUsed = await ( const serviceUsed = await (
await prisma.service.findMany({ await prisma.service.findMany({
where: { exposePort: { not: null }, id: { not: id }, destinationDockerId: dockerId }, where: { exposePort: { not: null }, id: { not: id }, destinationDocker: { remoteIpAddress } },
select: { exposePort: true } select: { exposePort: true }
}) })
).map((a) => a.exposePort); ).map((a) => a.exposePort);
const usedPorts = [...applicationUsed, ...serviceUsed]; const usedPorts = [...applicationUsed, ...serviceUsed];
if (usedPorts.includes(exposePort)) { if (usedPorts.includes(exposePort)) {
return false; return false
} }
const found = await checkPort(exposePort, { host: remoteIpAddress || 'localhost' }); const found = await checkPort(exposePort, { host: remoteIpAddress });
if (!found) { if (!found) {
return exposePort; return exposePort
}
return false
} else {
const applicationUsed = await (
await prisma.application.findMany({
where: { exposePort: { not: null }, id: { not: id }, destinationDocker: { engine } },
select: { exposePort: true }
})
).map((a) => a.exposePort);
const serviceUsed = await (
await prisma.service.findMany({
where: { exposePort: { not: null }, id: { not: id }, destinationDocker: { engine } },
select: { exposePort: true }
})
).map((a) => a.exposePort);
const usedPorts = [...applicationUsed, ...serviceUsed];
if (usedPorts.includes(exposePort)) {
return false
}
const found = await checkPort(exposePort, { host: 'localhost' });
if (!found) {
return exposePort
}
return false
} }
return false;
} }
export function generateRangeArray(start, end) { export function generateRangeArray(start, end) {
return Array.from({ length: end - start }, (v, k) => k + start); return Array.from({ length: end - start }, (v, k) => k + start);
} }
export async function getFreePublicPort(id, dockerId) { export async function getFreePublicPort({ id, remoteEngine, engine, remoteIpAddress }) {
const { default: isReachable } = await import('is-port-reachable'); const { default: isReachable } = await import('is-port-reachable');
const data = await prisma.setting.findFirst(); const data = await prisma.setting.findFirst();
const { minPort, maxPort } = data; const { minPort, maxPort } = data;
if (remoteEngine) {
const dbUsed = await ( const dbUsed = await (
await prisma.database.findMany({ await prisma.database.findMany({
where: { publicPort: { not: null }, id: { not: id }, destinationDockerId: dockerId }, where: { publicPort: { not: null }, id: { not: id }, destinationDocker: { remoteIpAddress } },
select: { publicPort: true } select: { publicPort: true }
}) })
).map((a) => a.publicPort); ).map((a) => a.publicPort);
const wpFtpUsed = await ( const wpFtpUsed = await (
await prisma.wordpress.findMany({ await prisma.wordpress.findMany({
where: { where: { ftpPublicPort: { not: null }, id: { not: id }, service: { destinationDocker: { remoteIpAddress } } },
ftpPublicPort: { not: null },
id: { not: id },
service: { destinationDockerId: dockerId }
},
select: { ftpPublicPort: true } select: { ftpPublicPort: true }
}) })
).map((a) => a.ftpPublicPort); ).map((a) => a.ftpPublicPort);
const wpUsed = await ( const wpUsed = await (
await prisma.wordpress.findMany({ await prisma.wordpress.findMany({
where: { where: { mysqlPublicPort: { not: null }, id: { not: id }, service: { destinationDocker: { remoteIpAddress } } },
mysqlPublicPort: { not: null },
id: { not: id },
service: { destinationDockerId: dockerId }
},
select: { mysqlPublicPort: true } select: { mysqlPublicPort: true }
}) })
).map((a) => a.mysqlPublicPort); ).map((a) => a.mysqlPublicPort);
const minioUsed = await ( const minioUsed = await (
await prisma.minio.findMany({ await prisma.minio.findMany({
where: { where: { publicPort: { not: null }, id: { not: id }, service: { destinationDocker: { remoteIpAddress } } },
publicPort: { not: null },
id: { not: id },
service: { destinationDockerId: dockerId }
},
select: { publicPort: true } select: { publicPort: true }
}) })
).map((a) => a.publicPort); ).map((a) => a.publicPort);
const usedPorts = [...dbUsed, ...wpFtpUsed, ...wpUsed, ...minioUsed]; const usedPorts = [...dbUsed, ...wpFtpUsed, ...wpUsed, ...minioUsed];
const range = generateRangeArray(minPort, maxPort); const range = generateRangeArray(minPort, maxPort)
const availablePorts = range.filter((port) => !usedPorts.includes(port)); const availablePorts = range.filter(port => !usedPorts.includes(port))
for (const port of availablePorts) { for (const port of availablePorts) {
const found = await isReachable(port, { host: 'localhost' }); const found = await isReachable(port, { host: remoteIpAddress })
if (!found) { if (!found) {
return port; return port
} }
} }
return false; return false
} else {
const dbUsed = await (
await prisma.database.findMany({
where: { publicPort: { not: null }, id: { not: id }, destinationDocker: { engine } },
select: { publicPort: true }
})
).map((a) => a.publicPort);
const wpFtpUsed = await (
await prisma.wordpress.findMany({
where: { ftpPublicPort: { not: null }, id: { not: id }, service: { destinationDocker: { engine } } },
select: { ftpPublicPort: true }
})
).map((a) => a.ftpPublicPort);
const wpUsed = await (
await prisma.wordpress.findMany({
where: { mysqlPublicPort: { not: null }, id: { not: id }, service: { destinationDocker: { engine } } },
select: { mysqlPublicPort: true }
})
).map((a) => a.mysqlPublicPort);
const minioUsed = await (
await prisma.minio.findMany({
where: { publicPort: { not: null }, id: { not: id }, service: { destinationDocker: { engine } } },
select: { publicPort: true }
})
).map((a) => a.publicPort);
const usedPorts = [...dbUsed, ...wpFtpUsed, ...wpUsed, ...minioUsed];
const range = generateRangeArray(minPort, maxPort)
const availablePorts = range.filter(port => !usedPorts.includes(port))
for (const port of availablePorts) {
const found = await isReachable(port, { host: 'localhost' })
if (!found) {
return port
}
}
return false
}
} }
export async function startTraefikTCPProxy( export async function startTraefikTCPProxy(

View File

@ -321,8 +321,8 @@ async function startMinioService(request: FastifyRequest<ServiceStartStop>) {
const network = destinationDockerId && destinationDocker.network; const network = destinationDockerId && destinationDocker.network;
const port = getServiceMainPort('minio'); const port = getServiceMainPort('minio');
const { service: { destinationDocker: { id: dockerId } } } = await prisma.minio.findUnique({ where: { serviceId: id }, include: { service: { include: { destinationDocker: true } } } }) const { service: { destinationDocker: { remoteEngine, engine, remoteIpAddress } } } = await prisma.minio.findUnique({ where: { serviceId: id }, include: { service: { include: { destinationDocker: true } } } })
const publicPort = await getFreePublicPort(id, dockerId); const publicPort = await getFreePublicPort({ id, remoteEngine, engine, remoteIpAddress });
const consolePort = 9001; const consolePort = 9001;
const { workdir } = await createDirectories({ repository: type, buildId: id }); const { workdir } = await createDirectories({ repository: type, buildId: id });
@ -1979,8 +1979,8 @@ async function startGlitchTipService(request: FastifyRequest<ServiceStartStop>)
EMAIL_PORT: emailSmtpPort, EMAIL_PORT: emailSmtpPort,
EMAIL_HOST_USER: emailSmtpUser, EMAIL_HOST_USER: emailSmtpUser,
EMAIL_HOST_PASSWORD: emailSmtpPassword, EMAIL_HOST_PASSWORD: emailSmtpPassword,
EMAIL_USE_TLS: emailSmtpUseTls, EMAIL_USE_TLS: emailSmtpUseTls ? 'True' : 'False',
EMAIL_USE_SSL: emailSmtpUseSsl, EMAIL_USE_SSL: emailSmtpUseSsl ? 'True' : 'False',
EMAIL_BACKEND: emailBackend, EMAIL_BACKEND: emailBackend,
MAILGUN_API_KEY: mailgunApiKey, MAILGUN_API_KEY: mailgunApiKey,
SENDGRID_API_KEY: sendgridApiKey, SENDGRID_API_KEY: sendgridApiKey,

View File

@ -252,8 +252,8 @@ export async function saveApplication(request: FastifyRequest<SaveApplication>,
exposePort = Number(exposePort); exposePort = Number(exposePort);
} }
const { destinationDocker: { id: dockerId, remoteIpAddress }, exposePort: configuredPort } = await prisma.application.findUnique({ where: { id }, include: { destinationDocker: true } }) const { destinationDocker: { engine, remoteEngine, remoteIpAddress }, exposePort: configuredPort } = await prisma.application.findUnique({ where: { id }, include: { destinationDocker: true } })
if (exposePort) await checkExposedPort({ id, configuredPort, exposePort, dockerId, remoteIpAddress }) if (exposePort) await checkExposedPort({ id, configuredPort, exposePort, engine, remoteEngine, remoteIpAddress })
if (denoOptions) denoOptions = denoOptions.trim(); if (denoOptions) denoOptions = denoOptions.trim();
const defaultConfiguration = await setDefaultConfiguration({ const defaultConfiguration = await setDefaultConfiguration({
buildPack, buildPack,
@ -534,14 +534,14 @@ export async function checkDNS(request: FastifyRequest<CheckDNS>) {
} }
if (exposePort) exposePort = Number(exposePort); if (exposePort) exposePort = Number(exposePort);
const { destinationDocker: { id: dockerId, remoteIpAddress, remoteEngine }, exposePort: configuredPort } = await prisma.application.findUnique({ where: { id }, include: { destinationDocker: true } }) const { destinationDocker: { engine, remoteIpAddress, remoteEngine }, exposePort: configuredPort } = await prisma.application.findUnique({ where: { id }, include: { destinationDocker: true } })
const { isDNSCheckEnabled } = await prisma.setting.findFirst({}); const { isDNSCheckEnabled } = await prisma.setting.findFirst({});
const found = await isDomainConfigured({ id, fqdn, remoteIpAddress }); const found = await isDomainConfigured({ id, fqdn, remoteIpAddress });
if (found) { if (found) {
throw { status: 500, message: `Domain ${getDomain(fqdn).replace('www.', '')} is already in use!` } throw { status: 500, message: `Domain ${getDomain(fqdn).replace('www.', '')} is already in use!` }
} }
if (exposePort) await checkExposedPort({ id, configuredPort, exposePort, dockerId, remoteIpAddress }) if (exposePort) await checkExposedPort({ id, configuredPort, exposePort, engine, remoteEngine, remoteIpAddress })
if (isDNSCheckEnabled && !isDev && !forceSave) { if (isDNSCheckEnabled && !isDev && !forceSave) {
let hostname = request.hostname.split(':')[0]; let hostname = request.hostname.split(':')[0];
if (remoteEngine) hostname = remoteIpAddress; if (remoteEngine) hostname = remoteIpAddress;

View File

@ -6,8 +6,8 @@ import fs from 'fs/promises';
import { ComposeFile, createDirectories, decrypt, defaultComposeConfiguration, encrypt, errorHandler, executeDockerCmd, generateDatabaseConfiguration, generatePassword, getContainerUsage, getDatabaseImage, getDatabaseVersions, getFreePublicPort, listSettings, makeLabelForStandaloneDatabase, prisma, startTraefikTCPProxy, stopDatabaseContainer, stopTcpHttpProxy, supportedDatabaseTypesAndVersions, uniqueName, updatePasswordInDb } from '../../../../lib/common'; import { ComposeFile, createDirectories, decrypt, defaultComposeConfiguration, encrypt, errorHandler, executeDockerCmd, generateDatabaseConfiguration, generatePassword, getContainerUsage, getDatabaseImage, getDatabaseVersions, getFreePublicPort, listSettings, makeLabelForStandaloneDatabase, prisma, startTraefikTCPProxy, stopDatabaseContainer, stopTcpHttpProxy, supportedDatabaseTypesAndVersions, uniqueName, updatePasswordInDb } from '../../../../lib/common';
import { day } from '../../../../lib/dayjs'; import { day } from '../../../../lib/dayjs';
import { DeleteDatabaseSecret, GetDatabaseLogs, OnlyId, SaveDatabase, SaveDatabaseDestination, SaveDatabaseSecret, SaveDatabaseSettings, SaveVersion } from '../../../../types'; import type { OnlyId } from '../../../../types';
import { DeleteDatabase, SaveDatabaseType } from './types'; import type { DeleteDatabase, DeleteDatabaseSecret, GetDatabaseLogs, SaveDatabase, SaveDatabaseDestination, SaveDatabaseSecret, SaveDatabaseSettings, SaveDatabaseType, SaveVersion } from './types';
export async function listDatabases(request: FastifyRequest) { export async function listDatabases(request: FastifyRequest) {
try { try {
@ -94,15 +94,14 @@ export async function getDatabase(request: FastifyRequest<OnlyId>) {
if (!database) { if (!database) {
throw { status: 404, message: 'Database not found.' } throw { status: 404, message: 'Database not found.' }
} }
const { arch } = await listSettings(); const settings = await listSettings();
if (database.dbUserPassword) database.dbUserPassword = decrypt(database.dbUserPassword); if (database.dbUserPassword) database.dbUserPassword = decrypt(database.dbUserPassword);
if (database.rootUserPassword) database.rootUserPassword = decrypt(database.rootUserPassword); if (database.rootUserPassword) database.rootUserPassword = decrypt(database.rootUserPassword);
const configuration = generateDatabaseConfiguration(database, arch); const configuration = generateDatabaseConfiguration(database, settings.arch);
const settings = await listSettings();
return { return {
privatePort: configuration?.privatePort, privatePort: configuration?.privatePort,
database, database,
versions: await getDatabaseVersions(database.type, arch), versions: await getDatabaseVersions(database.type, settings.arch),
settings settings
}; };
} catch ({ status, message }) { } catch ({ status, message }) {
@ -426,10 +425,10 @@ export async function saveDatabaseSettings(request: FastifyRequest<SaveDatabaseS
let publicPort = null let publicPort = null
const { destinationDocker: { id: dockerId } } = await prisma.database.findUnique({ where: { id }, include: { destinationDocker: true } }) const { destinationDocker: { remoteEngine, engine, remoteIpAddress } } = await prisma.database.findUnique({ where: { id }, include: { destinationDocker: true } })
if (isPublic) { if (isPublic) {
publicPort = await getFreePublicPort(id, dockerId); publicPort = await getFreePublicPort({ id, remoteEngine, engine, remoteIpAddress });
} }
await prisma.database.update({ await prisma.database.update({
where: { id }, where: { id },

View File

@ -1,5 +1,4 @@
import os from 'node:os';
import osu from 'node-os-utils';
import axios from 'axios'; import axios from 'axios';
import { compareVersions } from 'compare-versions'; import { compareVersions } from 'compare-versions';
import cuid from 'cuid'; import cuid from 'cuid';
@ -15,9 +14,10 @@ export async function hashPassword(password: string): Promise<string> {
return bcrypt.hash(password, saltRounds); return bcrypt.hash(password, saltRounds);
} }
export async function cleanupManually() { export async function cleanupManually(request: FastifyRequest) {
try { try {
const destination = await prisma.destinationDocker.findFirst({ where: { engine: '/var/run/docker.sock' } }) const { serverId } = request.body;
const destination = await prisma.destinationDocker.findUnique({ where: { id: serverId } })
await cleanupDockerStorage(destination.id, true, true) await cleanupDockerStorage(destination.id, true, true)
return {} return {}
} catch ({ status, message }) { } catch ({ status, message }) {
@ -86,25 +86,7 @@ export async function restartCoolify(request: FastifyRequest<any>) {
return errorHandler({ status, message }) return errorHandler({ status, message })
} }
} }
export async function showUsage() {
try {
return {
usage: {
uptime: os.uptime(),
memory: await osu.mem.info(),
cpu: {
load: os.loadavg(),
usage: await osu.cpu.usage(),
count: os.cpus().length
},
disk: await osu.drive.info('/')
}
};
} catch ({ status, message }) {
return errorHandler({ status, message })
}
}
export async function showDashboard(request: FastifyRequest) { export async function showDashboard(request: FastifyRequest) {
try { try {
const userId = request.user.userId; const userId = request.user.userId;

View File

@ -43,17 +43,13 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => {
onRequest: [fastify.authenticate] onRequest: [fastify.authenticate]
}, async (request) => await showDashboard(request)); }, async (request) => await showDashboard(request));
fastify.get('/usage', {
onRequest: [fastify.authenticate]
}, async () => await showUsage());
fastify.post('/internal/restart', { fastify.post('/internal/restart', {
onRequest: [fastify.authenticate] onRequest: [fastify.authenticate]
}, async (request) => await restartCoolify(request)); }, async (request) => await restartCoolify(request));
fastify.post('/internal/cleanup', { fastify.post('/internal/cleanup', {
onRequest: [fastify.authenticate] onRequest: [fastify.authenticate]
}, async () => await cleanupManually()); }, async (request) => await cleanupManually(request));
}; };
export default root; export default root;

View File

@ -0,0 +1,119 @@
import type { FastifyRequest } from 'fastify';
import { errorHandler, executeDockerCmd, prisma, createRemoteEngineConfiguration, executeSSHCmd } from '../../../../lib/common';
import os from 'node:os';
import osu from 'node-os-utils';
export async function listServers(request: FastifyRequest) {
try {
const userId = request.user.userId;
const teamId = request.user.teamId;
const servers = await prisma.destinationDocker.findMany({ where: { teams: { some: { id: teamId === '0' ? undefined : teamId } }, remoteEngine: false }, distinct: ['engine'] })
// const remoteServers = await prisma.destinationDocker.findMany({ where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }, distinct: ['remoteIpAddress', 'engine'] })
return {
servers
}
} catch ({ status, message }) {
return errorHandler({ status, message })
}
}
const mappingTable = [
['K total memory', 'totalMemoryKB'],
['K used memory', 'usedMemoryKB'],
['K active memory', 'activeMemoryKB'],
['K inactive memory', 'inactiveMemoryKB'],
['K free memory', 'freeMemoryKB'],
['K buffer memory', 'bufferMemoryKB'],
['K swap cache', 'swapCacheKB'],
['K total swap', 'totalSwapKB'],
['K used swap', 'usedSwapKB'],
['K free swap', 'freeSwapKB'],
['non-nice user cpu ticks', 'nonNiceUserCpuTicks'],
['nice user cpu ticks', 'niceUserCpuTicks'],
['system cpu ticks', 'systemCpuTicks'],
['idle cpu ticks', 'idleCpuTicks'],
['IO-wait cpu ticks', 'ioWaitCpuTicks'],
['IRQ cpu ticks', 'irqCpuTicks'],
['softirq cpu ticks', 'softIrqCpuTicks'],
['stolen cpu ticks', 'stolenCpuTicks'],
['pages paged in', 'pagesPagedIn'],
['pages paged out', 'pagesPagedOut'],
['pages swapped in', 'pagesSwappedIn'],
['pages swapped out', 'pagesSwappedOut'],
['interrupts', 'interrupts'],
['CPU context switches', 'cpuContextSwitches'],
['boot time', 'bootTime'],
['forks', 'forks']
];
function parseFromText(text) {
var data = {};
var lines = text.split(/\r?\n/);
for (const line of lines) {
for (const [key, value] of mappingTable) {
if (line.indexOf(key) >= 0) {
const values = line.match(/[0-9]+/)[0];
data[value] = parseInt(values, 10);
}
}
}
return data;
}
export async function showUsage(request: FastifyRequest) {
const { id } = request.params;
let { remoteEngine } = request.query
remoteEngine = remoteEngine === 'true' ? true : false
if (remoteEngine) {
const { stdout: stats } = await executeSSHCmd({ dockerId: id, command: `vmstat -s` })
const { stdout: disks } = await executeSSHCmd({ dockerId: id, command: `df -m / --output=size,used,pcent|grep -v 'Used'| xargs` })
const { stdout: cpus } = await executeSSHCmd({ dockerId: id, command: `nproc --all` })
// const { stdout: cpuUsage } = await executeSSHCmd({ dockerId: id, command: `echo $[100-$(vmstat 1 2|tail -1|awk '{print $15}')]` })
// console.log(cpuUsage)
const parsed: any = parseFromText(stats)
return {
usage: {
uptime: parsed.bootTime / 1024,
memory: {
totalMemMb: parsed.totalMemoryKB / 1024,
usedMemMb: parsed.usedMemoryKB / 1024,
freeMemMb: parsed.freeMemoryKB / 1024,
usedMemPercentage: (parsed.usedMemoryKB / parsed.totalMemoryKB) * 100,
freeMemPercentage: (parsed.totalMemoryKB - parsed.usedMemoryKB) / parsed.totalMemoryKB * 100
},
cpu: {
load: 0,
usage: 0,
count: cpus
},
disk: {
totalGb: (disks.split(' ')[0] / 1024).toFixed(1),
usedGb: (disks.split(' ')[1] / 1024).toFixed(1),
freeGb: (disks.split(' ')[0] - disks.split(' ')[1]).toFixed(1),
usedPercentage: disks.split(' ')[2].replace('%', ''),
freePercentage: 100 - disks.split(' ')[2].replace('%', '')
}
}
}
} else {
try {
return {
usage: {
uptime: os.uptime(),
memory: await osu.mem.info(),
cpu: {
load: os.loadavg(),
usage: await osu.cpu.usage(),
count: os.cpus().length
},
disk: await osu.drive.info('/')
}
};
} catch ({ status, message }) {
return errorHandler({ status, message })
}
}
}

View File

@ -0,0 +1,14 @@
import { FastifyPluginAsync } from 'fastify';
import { listServers, showUsage } from './handlers';
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
fastify.addHook('onRequest', async (request) => {
return await request.jwtVerify()
})
fastify.get('/', async (request) => await listServers(request));
fastify.get('/usage/:id', async (request) => await showUsage(request));
};
export default root;

View 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
}
}

View File

@ -1,7 +1,7 @@
import type { FastifyReply, FastifyRequest } from 'fastify'; import type { FastifyReply, FastifyRequest } from 'fastify';
import fs from 'fs/promises'; import fs from 'fs/promises';
import yaml from 'js-yaml'; import yaml from 'js-yaml';
import { prisma, uniqueName, asyncExecShell, getServiceFromDB, getContainerUsage, isDomainConfigured, saveUpdateableFields, fixType, decrypt, encrypt, ComposeFile, getFreePublicPort, getDomain, errorHandler, generatePassword, isDev, stopTcpHttpProxy, executeDockerCmd, checkDomainsIsValidInDNS, checkExposedPort } from '../../../../lib/common'; import { prisma, uniqueName, asyncExecShell, getServiceFromDB, getContainerUsage, isDomainConfigured, saveUpdateableFields, fixType, decrypt, encrypt, ComposeFile, getFreePublicPort, getDomain, errorHandler, generatePassword, isDev, stopTcpHttpProxy, executeDockerCmd, checkDomainsIsValidInDNS, checkExposedPort, listSettings } from '../../../../lib/common';
import { day } from '../../../../lib/dayjs'; import { day } from '../../../../lib/dayjs';
import { checkContainer, isContainerExited } from '../../../../lib/docker'; import { checkContainer, isContainerExited } from '../../../../lib/docker';
import cuid from 'cuid'; import cuid from 'cuid';
@ -70,6 +70,7 @@ export async function getService(request: FastifyRequest<OnlyId>) {
throw { status: 404, message: 'Service not found.' } throw { status: 404, message: 'Service not found.' }
} }
return { return {
settings: await listSettings(),
service service
} }
} catch ({ status, message }) { } catch ({ status, message }) {
@ -232,7 +233,7 @@ export async function checkService(request: FastifyRequest<CheckService>) {
if (otherFqdns && otherFqdns.length > 0) otherFqdns = otherFqdns.map((f) => f.toLowerCase()); if (otherFqdns && otherFqdns.length > 0) otherFqdns = otherFqdns.map((f) => f.toLowerCase());
if (exposePort) exposePort = Number(exposePort); if (exposePort) exposePort = Number(exposePort);
const { destinationDocker: { id: dockerId, remoteIpAddress, remoteEngine }, exposePort: configuredPort } = await prisma.service.findUnique({ where: { id }, include: { destinationDocker: true } }) const { destinationDocker: { remoteIpAddress, remoteEngine, engine }, exposePort: configuredPort } = await prisma.service.findUnique({ where: { id }, include: { destinationDocker: true } })
const { isDNSCheckEnabled } = await prisma.setting.findFirst({}); const { isDNSCheckEnabled } = await prisma.setting.findFirst({});
let found = await isDomainConfigured({ id, fqdn, remoteIpAddress }); let found = await isDomainConfigured({ id, fqdn, remoteIpAddress });
@ -247,7 +248,7 @@ export async function checkService(request: FastifyRequest<CheckService>) {
} }
} }
} }
if (exposePort) await checkExposedPort({ id, configuredPort, exposePort, dockerId, remoteIpAddress }) if (exposePort) await checkExposedPort({ id, configuredPort, exposePort, engine, remoteEngine, remoteIpAddress })
if (isDNSCheckEnabled && !isDev && !forceSave) { if (isDNSCheckEnabled && !isDev && !forceSave) {
let hostname = request.hostname.split(':')[0]; let hostname = request.hostname.split(':')[0];
if (remoteEngine) hostname = remoteIpAddress; if (remoteEngine) hostname = remoteIpAddress;
@ -484,9 +485,9 @@ export async function activateWordpressFtp(request: FastifyRequest<ActivateWordp
const { id } = request.params const { id } = request.params
const { ftpEnabled } = request.body; const { ftpEnabled } = request.body;
const { service: { destinationDocker: { id: dockerId } } } = await prisma.wordpress.findUnique({ where: { serviceId: id }, include: { service: { include: { destinationDocker: true } } } }) const { service: { destinationDocker: { engine, remoteEngine, remoteIpAddress } } } = await prisma.wordpress.findUnique({ where: { serviceId: id }, include: { service: { include: { destinationDocker: true } } } })
const publicPort = await getFreePublicPort(id, dockerId); const publicPort = await getFreePublicPort({ id, remoteEngine, engine, remoteIpAddress });
let ftpUser = cuid(); let ftpUser = cuid();
let ftpPassword = generatePassword({}); let ftpPassword = generatePassword({});

View File

@ -58,7 +58,7 @@ export async function deleteDomain(request: FastifyRequest<DeleteDomain>, reply:
const { fqdn } = request.body const { fqdn } = request.body
const { DNSServers } = await listSettings(); const { DNSServers } = await listSettings();
if (DNSServers) { if (DNSServers) {
dns.setServers([DNSServers]); dns.setServers([...DNSServers.split(',')]);
} }
let ip; let ip;
try { try {

View File

@ -4,6 +4,7 @@
import { addToast, appSession, features } from '$lib/store'; import { addToast, appSession, features } from '$lib/store';
import { asyncSleep, errorNotification } from '$lib/common'; import { asyncSleep, errorNotification } from '$lib/common';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import Tooltip from './Tooltip.svelte';
let isUpdateAvailable = false; let isUpdateAvailable = false;
let updateStatus: any = { let updateStatus: any = {
@ -75,14 +76,14 @@
}); });
</script> </script>
<div class="flex flex-col space-y-4 py-2"> <div class="py-2">
{#if $appSession.teamId === '0'} {#if $appSession.teamId === '0'}
{#if isUpdateAvailable} {#if isUpdateAvailable}
<button <button
id="update"
disabled={updateStatus.success === false} disabled={updateStatus.success === false}
on:click={update} on:click={update}
class="icons tooltip tooltip-right tooltip-primary bg-gradient-to-r from-purple-500 via-pink-500 to-red-500 text-white duration-75 hover:scale-105" class="icons bg-gradient-to-r from-purple-500 via-pink-500 to-red-500 text-white duration-75 hover:scale-105"
data-tip="Update Available!"
> >
{#if updateStatus.loading} {#if updateStatus.loading}
<svg <svg
@ -183,6 +184,7 @@
> >
{/if} {/if}
</button> </button>
<Tooltip triggeredBy="#update" placement="right" color="bg-gradient-to-r from-purple-500 via-pink-500 to-red-500">New Version Available!</Tooltip>
{/if} {/if}
{/if} {/if}
</div> </div>

View File

@ -1,4 +1,5 @@
<script lang="ts"> <script lang="ts">
export let server: any;
let usage = { let usage = {
cpu: { cpu: {
load: [0, 0, 0], load: [0, 0, 0],
@ -29,7 +30,7 @@
async function getStatus() { async function getStatus() {
if (loading.usage) return; if (loading.usage) return;
loading.usage = true; loading.usage = true;
const data = await get('/usage'); const data = await get(`/servers/usage/${server.id}?remoteEngine=${server.remoteEngine}`);
usage = data.usage; usage = data.usage;
loading.usage = false; loading.usage = false;
} }
@ -52,7 +53,7 @@
async function manuallyCleanupStorage() { async function manuallyCleanupStorage() {
try { try {
loading.cleanup = true; loading.cleanup = true;
await post('/internal/cleanup', {}); await post('/internal/cleanup', { serverId: server.id });
return addToast({ return addToast({
message: 'Cleanup done.', message: 'Cleanup done.',
type: 'success' type: 'success'
@ -65,16 +66,42 @@
} }
</script> </script>
<div class="w-full"> <div class="w-full relative p-5 ">
<div class="flex lg:flex-row flex-col gap-4"> {#if loading.usage}
<h1 class="title lg:text-3xl">Hardware Details</h1> <span class="indicator-item badge bg-yellow-500 badge-sm" />
<div class="flex lg:flex-row flex-col space-x-0 lg:space-x-2 space-y-2 lg:space-y-0"> {:else}
<span class="indicator-item badge bg-success badge-sm" />
{/if}
{#if server.remoteEngine}
<div
class="absolute top-0 right-0 text-xl font-bold uppercase bg-gradient-to-r from-purple-500 via-pink-500 to-red-500 p-1 rounded m-2"
>
BETA
</div>
{/if}
<div class="w-full flex flex-row space-x-4">
<div class="flex flex-col">
<h1 class="font-bold text-lg lg:text-xl truncate">
{server.name}
</h1>
<div class="text-xs ">
{#if server?.remoteIpAddress}
<h2>{server?.remoteIpAddress}</h2>
{:else}
<h2>localhost</h2>
{/if}
</div>
</div>
{#if $appSession.teamId === '0'} {#if $appSession.teamId === '0'}
<button on:click={manuallyCleanupStorage} class:loading={loading.cleanup} class="btn btn-sm" <button
>Cleanup Storage</button on:click={manuallyCleanupStorage}
class:loading={loading.cleanup}
class="btn btn-sm bg-coollabs">Cleanup Storage</button
> >
{/if} {/if}
</div> </div>
<div class="flex lg:flex-row flex-col gap-4">
<div class="flex lg:flex-row flex-col space-x-0 lg:space-x-2 space-y-2 lg:space-y-0" />
</div> </div>
<div class="divider" /> <div class="divider" />
<div class="grid grid-flow-col gap-4 grid-rows-3 justify-start lg:justify-center lg:grid-rows-1"> <div class="grid grid-flow-col gap-4 grid-rows-3 justify-start lg:justify-center lg:grid-rows-1">
@ -82,21 +109,21 @@
<div class="stat"> <div class="stat">
<div class="stat-title">Total Memory</div> <div class="stat-title">Total Memory</div>
<div class="stat-value text-2xl"> <div class="stat-value text-2xl">
{(usage?.memory.totalMemMb).toFixed(0)}<span class="text-sm">MB</span> {(usage?.memory?.totalMemMb).toFixed(0)}<span class="text-sm">MB</span>
</div> </div>
</div> </div>
<div class="stat"> <div class="stat">
<div class="stat-title">Used Memory</div> <div class="stat-title">Used Memory</div>
<div class="stat-value text-2xl"> <div class="stat-value text-2xl">
{(usage?.memory.usedMemMb).toFixed(0)}<span class="text-sm">MB</span> {(usage?.memory?.usedMemMb).toFixed(0)}<span class="text-sm">MB</span>
</div> </div>
</div> </div>
<div class="stat"> <div class="stat">
<div class="stat-title">Free Memory</div> <div class="stat-title">Free Memory</div>
<div class="stat-value text-2xl"> <div class="stat-value text-2xl">
{usage?.memory.freeMemPercentage}<span class="text-sm">%</span> {(usage?.memory?.freeMemPercentage).toFixed(0)}<span class="text-sm">%</span>
</div> </div>
</div> </div>
</div> </div>
@ -105,41 +132,41 @@
<div class="stat"> <div class="stat">
<div class="stat-title">Total CPU</div> <div class="stat-title">Total CPU</div>
<div class="stat-value text-2xl"> <div class="stat-value text-2xl">
{usage?.cpu.count} {usage?.cpu?.count}
</div> </div>
</div> </div>
<div class="stat"> <div class="stat">
<div class="stat-title">CPU Usage</div> <div class="stat-title">CPU Usage</div>
<div class="stat-value text-2xl"> <div class="stat-value text-2xl">
{usage?.cpu.usage}<span class="text-sm">%</span> {usage?.cpu?.usage}<span class="text-sm">%</span>
</div> </div>
</div> </div>
<div class="stat"> <div class="stat">
<div class="stat-title">Load Average (5,10,30mins)</div> <div class="stat-title">Load Average (5,10,30mins)</div>
<div class="stat-value text-2xl">{usage?.cpu.load}</div> <div class="stat-value text-2xl">{usage?.cpu?.load}</div>
</div> </div>
</div> </div>
<div class="stats stats-vertical min-w-[16rem] mb-5 bg-transparent rounded"> <div class="stats stats-vertical min-w-[16rem] mb-5 bg-transparent rounded">
<div class="stat"> <div class="stat">
<div class="stat-title">Total Disk</div> <div class="stat-title">Total Disk</div>
<div class="stat-value text-2xl"> <div class="stat-value text-2xl">
{usage?.disk.totalGb}<span class="text-sm">GB</span> {usage?.disk?.totalGb}<span class="text-sm">GB</span>
</div> </div>
</div> </div>
<div class="stat"> <div class="stat">
<div class="stat-title">Used Disk</div> <div class="stat-title">Used Disk</div>
<div class="stat-value text-2xl"> <div class="stat-value text-2xl">
{usage?.disk.usedGb}<span class="text-sm">GB</span> {usage?.disk?.usedGb}<span class="text-sm">GB</span>
</div> </div>
</div> </div>
<div class="stat"> <div class="stat">
<div class="stat-title">Free Disk</div> <div class="stat-title">Free Disk</div>
<div class="stat-value text-2xl"> <div class="stat-value text-2xl">
{usage?.disk.freePercentage}<span class="text-sm">%</span> {usage?.disk?.freePercentage}<span class="text-sm">%</span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -26,6 +26,8 @@ interface AddToast {
message: string, message: string,
timeout?: number | undefined timeout?: number | undefined
} }
export const search: any = writable('')
export const loginEmail: Writable<string | undefined> = writable() export const loginEmail: Writable<string | undefined> = writable()
export const appSession: Writable<AppSession> = writable({ export const appSession: Writable<AppSession> = writable({
isRegistrationEnabled: false, isRegistrationEnabled: false,
@ -84,7 +86,8 @@ export const status: Writable<any> = writable({
isRunning: false, isRunning: false,
isExited: false, isExited: false,
loading: false, loading: false,
initialLoading: true initialLoading: true,
isPublic: false
} }
}); });

View File

@ -38,13 +38,13 @@
<ul <ul
id="new" id="new"
tabindex="0" tabindex="0"
class="dropdown-content menu p-2 shadow bg-coolgray-300 rounded-box w-52" class="dropdown-content menu p-2 shadow bg-coolgray-300 rounded w-52"
> >
<li> <li>
<button on:click={newApplication} class="no-underline hover:bg-applications"> <button on:click={newApplication} class="no-underline hover:bg-applications rounded-none ">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="h-8 w-8" class="h-6 w-6"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke-width="1.5" stroke-width="1.5"
stroke="currentcolor" stroke="currentcolor"
@ -62,10 +62,10 @@
> >
</li> </li>
<li> <li>
<button on:click={newService} class="no-underline hover:bg-services"> <button on:click={newService} class="no-underline hover:bg-services rounded-none ">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="h-8 w-8" class="h-6 w-6"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke-width="1.5" stroke-width="1.5"
stroke="currentColor" stroke="currentColor"
@ -79,10 +79,10 @@
> >
</li> </li>
<li> <li>
<button on:click={newDatabase} class="no-underline hover:bg-databases"> <button on:click={newDatabase} class="no-underline hover:bg-databases rounded-none ">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="h-8 w-8" class="h-6 w-6"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke-width="1.5" stroke-width="1.5"
stroke="currentColor" stroke="currentColor"
@ -98,10 +98,10 @@
> >
</li> </li>
<li> <li>
<a href="/sources/new" class="no-underline hover:bg-sources"> <a href="/sources/new" class="no-underline hover:bg-sources rounded-none ">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="h-8 w-8" class="h-6 w-6"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke-width="1.5" stroke-width="1.5"
stroke="currentColor" stroke="currentColor"
@ -120,10 +120,10 @@
> >
</li> </li>
<li> <li>
<a href="/destinations/new" class="no-underline hover:bg-destinations"> <a href="/destinations/new" class="no-underline hover:bg-destinations rounded-none ">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="h-8 w-8" class="h-6 w-6"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke-width="1.5" stroke-width="1.5"
stroke="currentColor" stroke="currentColor"

View File

@ -140,9 +140,10 @@
id="dashboard" id="dashboard"
sveltekit:prefetch sveltekit:prefetch
href="/" href="/"
class="icons bg-coolgray-200 hover:text-white" class="icons hover:text-white"
class:text-white={$page.url.pathname === '/'} class:text-white={$page.url.pathname === '/'}
class:bg-coolgray-500={$page.url.pathname === '/'} class:bg-coolgray-500={$page.url.pathname === '/'}
class:bg-coolgray-200={!($page.url.pathname === '/')}
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@ -161,52 +162,19 @@
<path d="M16 15c-2.21 1.333 -5.792 1.333 -8 0" /> <path d="M16 15c-2.21 1.333 -5.792 1.333 -8 0" />
</svg> </svg>
</a> </a>
{#if $appSession.teamId === '0'}
<div class="border-t border-stone-700" />
<a <a
id="applications" id="servers"
sveltekit:prefetch sveltekit:prefetch
href="/applications" href="/servers"
class="icons bg-coolgray-200" class="icons hover:text-white"
class:text-applications={$page.url.pathname.startsWith('/applications') || class:text-white={$page.url.pathname === '/servers'}
$page.url.pathname.startsWith('/new/application')} class:bg-coolgray-500={$page.url.pathname === '/servers'}
class:bg-coolgray-500={$page.url.pathname.startsWith('/applications') || class:bg-coolgray-200={!($page.url.pathname === '/servers')}
$page.url.pathname.startsWith('/new/application')}
data-tip="Applications"
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="h-9 w-9" class="w-8 h-8 mx-auto"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentcolor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<rect x="4" y="4" width="6" height="6" rx="1" />
<rect x="4" y="14" width="6" height="6" rx="1" />
<rect x="14" y="14" width="6" height="6" rx="1" />
<line x1="14" y1="7" x2="20" y2="7" />
<line x1="17" y1="4" x2="17" y2="10" />
</svg>
</a>
<a
id="sources"
sveltekit:prefetch
href="/sources"
class="icons bg-coolgray-200"
class:text-sources={$page.url.pathname.startsWith('/sources') ||
$page.url.pathname.startsWith('/new/source')}
class:bg-coolgray-500={$page.url.pathname.startsWith('/sources') ||
$page.url.pathname.startsWith('/new/source')}
data-tip="Git Sources"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-9 w-9"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke-width="1.5" stroke-width="1.5"
stroke="currentColor" stroke="currentColor"
@ -215,103 +183,16 @@
stroke-linejoin="round" stroke-linejoin="round"
> >
<path stroke="none" d="M0 0h24v24H0z" fill="none" /> <path stroke="none" d="M0 0h24v24H0z" fill="none" />
<circle cx="6" cy="6" r="2" /> <rect x="3" y="4" width="18" height="8" rx="3" />
<circle cx="18" cy="18" r="2" /> <rect x="3" y="12" width="18" height="8" rx="3" />
<path d="M11 6h5a2 2 0 0 1 2 2v8" /> <line x1="7" y1="8" x2="7" y2="8.01" />
<polyline points="14 9 11 6 14 3" /> <line x1="7" y1="16" x2="7" y2="16.01" />
<path d="M13 18h-5a2 2 0 0 1 -2 -2v-8" />
<polyline points="10 15 13 18 10 21" />
</svg>
</a>
<a
id="destinations"
sveltekit:prefetch
href="/destinations"
class="icons bg-coolgray-200"
class:text-destinations={$page.url.pathname.startsWith('/destinations') ||
$page.url.pathname.startsWith('/new/destination')}
class:bg-coolgray-500={$page.url.pathname.startsWith('/destinations') ||
$page.url.pathname.startsWith('/new/destination')}
data-tip="Destinations"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-9 w-9"
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="M22 12.54c-1.804 -.345 -2.701 -1.08 -3.523 -2.94c-.487 .696 -1.102 1.568 -.92 2.4c.028 .238 -.32 1.002 -.557 1h-14c0 5.208 3.164 7 6.196 7c4.124 .022 7.828 -1.376 9.854 -5c1.146 -.101 2.296 -1.505 2.95 -2.46z"
/>
<path d="M5 10h3v3h-3z" />
<path d="M8 10h3v3h-3z" />
<path d="M11 10h3v3h-3z" />
<path d="M8 7h3v3h-3z" />
<path d="M11 7h3v3h-3z" />
<path d="M11 4h3v3h-3z" />
<path d="M4.571 18c1.5 0 2.047 -.074 2.958 -.78" />
<line x1="10" y1="16" x2="10" y2="16.01" />
</svg>
</a>
<div class="border-t border-stone-700" />
<a
id="databases"
sveltekit:prefetch
href="/databases"
class="icons bg-coolgray-200"
class:text-databases={$page.url.pathname.startsWith('/databases') ||
$page.url.pathname.startsWith('/new/database')}
class:bg-coolgray-500={$page.url.pathname.startsWith('/databases') ||
$page.url.pathname.startsWith('/new/database')}
data-tip="Databases"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-9 w-9"
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" />
<ellipse cx="12" cy="6" rx="8" ry="3" />
<path d="M4 6v6a8 3 0 0 0 16 0v-6" />
<path d="M4 12v6a8 3 0 0 0 16 0v-6" />
</svg>
</a>
<a
id="services"
sveltekit:prefetch
href="/services"
class="icons bg-coolgray-200"
class:text-services={$page.url.pathname.startsWith('/services') ||
$page.url.pathname.startsWith('/new/service')}
class:bg-coolgray-500={$page.url.pathname.startsWith('/services') ||
$page.url.pathname.startsWith('/new/service')}
data-tip="Services"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-9 w-9"
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="M7 18a4.6 4.4 0 0 1 0 -9a5 4.5 0 0 1 11 2h1a3.5 3.5 0 0 1 0 7h-12" />
</svg> </svg>
</a> </a>
{/if}
</div> </div>
<Tooltip triggeredBy="#dashboard" placement="right">Dashboard</Tooltip>
<Tooltip triggeredBy="#servers" placement="right">Servers</Tooltip>
<div class="flex-1" /> <div class="flex-1" />
<UpdateAvailable /> <UpdateAvailable />
@ -684,14 +565,6 @@
</div> </div>
</div> </div>
<Tooltip triggeredBy="#dashboard" placement="right">Dashboard</Tooltip>
<Tooltip triggeredBy="#applications" placement="right" color="bg-applications">Applications</Tooltip
>
<Tooltip triggeredBy="#sources" placement="right" color="bg-sources">Git Sources</Tooltip>
<Tooltip triggeredBy="#destinations" placement="right" color="bg-destinations">Destinations</Tooltip
>
<Tooltip triggeredBy="#databases" placement="right" color="bg-databases">Databases</Tooltip>
<Tooltip triggeredBy="#services" placement="right" color="bg-services">Services</Tooltip>
<Tooltip triggeredBy="#iam" placement="right" color="bg-iam">IAM</Tooltip> <Tooltip triggeredBy="#iam" placement="right" color="bg-iam">IAM</Tooltip>
<Tooltip triggeredBy="#settings" placement="right" color="bg-settings text-black">Settings</Tooltip <Tooltip triggeredBy="#settings" placement="right" color="bg-settings text-black">Settings</Tooltip
> >

View File

@ -170,6 +170,7 @@
if ($status.application.isRunning) return; if ($status.application.isRunning) return;
isBot = !isBot; isBot = !isBot;
application.settings.isBot = isBot; application.settings.isBot = isBot;
application.fqdn = null;
setLocation(application, settings); setLocation(application, settings);
} }
if (name === 'isDBBranching') { if (name === 'isDBBranching') {

View File

@ -24,7 +24,6 @@
let loading = false; let loading = false;
let publicLoading = false; let publicLoading = false;
let isPublic = database.settings.isPublic || false;
let appendOnly = database.settings.appendOnly; let appendOnly = database.settings.appendOnly;
let databaseDefault: any; let databaseDefault: any;
@ -52,12 +51,12 @@
return `${database.type}://${ return `${database.type}://${
databaseDbUser ? databaseDbUser + ':' : '' databaseDbUser ? databaseDbUser + ':' : ''
}${databaseDbUserPassword}@${ }${databaseDbUserPassword}@${
isPublic $status.database.isPublic
? database.destinationDocker.remoteEngine ? database.destinationDocker.remoteEngine
? database.destinationDocker.remoteIpAddress ? database.destinationDocker.remoteIpAddress
: $appSession.ipv4 : $appSession.ipv4
: database.id : database.id
}:${isPublic ? database.publicPort : privatePort}/${databaseDefault}`; }:${$status.database.isPublic ? database.publicPort : privatePort}/${databaseDefault}`;
} }
async function changeSettings(name: any) { async function changeSettings(name: any) {
@ -66,11 +65,11 @@
} }
publicLoading = true; publicLoading = true;
let data = { let data = {
isPublic, isPublic: $status.database.isPublic,
appendOnly appendOnly
}; };
if (name === 'isPublic') { if (name === 'isPublic') {
data.isPublic = !isPublic; data.isPublic = !$status.database.isPublic;
} }
if (name === 'appendOnly') { if (name === 'appendOnly') {
data.appendOnly = !appendOnly; data.appendOnly = !appendOnly;
@ -80,9 +79,9 @@
isPublic: data.isPublic, isPublic: data.isPublic,
appendOnly: data.appendOnly appendOnly: data.appendOnly
}); });
isPublic = data.isPublic; $status.database.isPublic = data.isPublic;
appendOnly = data.appendOnly; appendOnly = data.appendOnly;
if (isPublic) { if ($status.database.isPublic) {
database.publicPort = publicPort; database.publicPort = publicPort;
} }
} catch (error) { } catch (error) {
@ -228,7 +227,7 @@
<Setting <Setting
id="isPublic" id="isPublic"
loading={publicLoading} loading={publicLoading}
bind:setting={isPublic} bind:setting={$status.database.isPublic}
on:click={() => changeSettings('isPublic')} on:click={() => changeSettings('isPublic')}
title={$t('database.set_public')} title={$t('database.set_public')}
description={$t('database.warning_database_public')} description={$t('database.warning_database_public')}

View File

@ -65,6 +65,7 @@
import DatabaseLinks from './_DatabaseLinks.svelte'; import DatabaseLinks from './_DatabaseLinks.svelte';
const { id } = $page.params; const { id } = $page.params;
$status.database.isPublic = database.settings.isPublic || false;
let statusInterval: any = false; let statusInterval: any = false;
let forceDelete = false; let forceDelete = false;
@ -91,6 +92,7 @@ import DatabaseLinks from './_DatabaseLinks.svelte';
$status.database.loading = true; $status.database.loading = true;
try { try {
await post(`/databases/${database.id}/stop`, {}); await post(`/databases/${database.id}/stop`, {});
$status.database.isPublic = false;
} catch (error) { } catch (error) {
return errorNotification(error); return errorNotification(error);
} finally { } finally {

View File

@ -27,36 +27,12 @@
export let gitSources: any; export let gitSources: any;
export let destinations: any; export let destinations: any;
let filtered = { let filtered: any = setInitials();
applications: applications.filter(
(application: any) => application.teams[0].id === $appSession.teamId
),
otherApplications: applications.filter(
(application: any) => application.teams[0].id !== $appSession.teamId
),
databases: databases.filter((database: any) => database.teams[0].id === $appSession.teamId),
otherDatabases: databases.filter(
(database: any) => database.teams[0].id !== $appSession.teamId
),
services: services.filter((service: any) => service.teams[0].id === $appSession.teamId),
otherServices: services.filter((service: any) => service.teams[0].id !== $appSession.teamId),
settings,
gitSources: gitSources.filter((gitSource: any) => gitSource.teams[0].id === $appSession.teamId),
otherGitSources: gitSources.filter(
(gitSource: any) => gitSource.teams[0].id !== $appSession.teamId
),
destinations: destinations.filter(
(destination: any) => destination.teams[0].id === $appSession.teamId
),
otherDestinations: destinations.filter(
(destination: any) => destination.teams[0].id !== $appSession.teamId
)
};
import { get, post } from '$lib/api'; import { get, post } from '$lib/api';
import Usage from '$lib/components/Usage.svelte'; import Usage from '$lib/components/Usage.svelte';
import { t } from '$lib/translations'; import { t } from '$lib/translations';
import { asyncSleep } from '$lib/common'; import { asyncSleep } from '$lib/common';
import { appSession } from '$lib/store'; import { appSession, search } from '$lib/store';
import ApplicationsIcons from '$lib/components/svg/applications/ApplicationIcons.svelte'; import ApplicationsIcons from '$lib/components/svg/applications/ApplicationIcons.svelte';
import DatabaseIcons from '$lib/components/svg/databases/DatabaseIcons.svelte'; import DatabaseIcons from '$lib/components/svg/databases/DatabaseIcons.svelte';
@ -65,9 +41,52 @@
import NewResource from './_NewResource.svelte'; import NewResource from './_NewResource.svelte';
let numberOfGetStatus = 0; let numberOfGetStatus = 0;
let search = '';
let status: any = {}; let status: any = {};
doSearch();
function setInitials(onlyOthers: boolean = false) {
return {
applications:
!onlyOthers &&
applications.filter((application: any) => application.teams[0].id === $appSession.teamId),
otherApplications: applications.filter(
(application: any) => application.teams[0].id !== $appSession.teamId
),
databases:
!onlyOthers &&
databases.filter((database: any) => database.teams[0].id === $appSession.teamId),
otherDatabases: databases.filter(
(database: any) => database.teams[0].id !== $appSession.teamId
),
services:
!onlyOthers &&
services.filter((service: any) => service.teams[0].id === $appSession.teamId),
otherServices: services.filter((service: any) => service.teams[0].id !== $appSession.teamId),
gitSources:
!onlyOthers &&
gitSources.filter((gitSource: any) => gitSource.teams[0].id === $appSession.teamId),
otherGitSources: gitSources.filter(
(gitSource: any) => gitSource.teams[0].id !== $appSession.teamId
),
destinations:
!onlyOthers &&
destinations.filter((destination: any) => destination.teams[0].id === $appSession.teamId),
otherDestinations: destinations.filter(
(destination: any) => destination.teams[0].id !== $appSession.teamId
)
};
}
function clearFiltered() {
filtered.applications = [];
filtered.otherApplications = [];
filtered.databases = [];
filtered.otherDatabases = [];
filtered.services = [];
filtered.otherServices = [];
filtered.gitSources = [];
filtered.otherGitSources = [];
filtered.destinations = [];
filtered.otherDestinations = [];
}
function getRndInteger(min: number, max: number) { function getRndInteger(min: number, max: number) {
return Math.floor(Math.random() * (max - min + 1)) + min; return Math.floor(Math.random() * (max - min + 1)) + min;
} }
@ -92,264 +111,165 @@
isRunning = response.isRunning; isRunning = response.isRunning;
} }
if (isRunning) { if (isRunning) {
status[id] = 'Running'; status[id] = 'running';
return 'Running'; return 'running';
} else { } else {
status[id] = 'Stopped'; status[id] = 'stopped';
return 'Stopped'; return 'stopped';
} }
} catch (error) { } catch (error) {
status[id] = 'Error'; status[id] = 'error';
return 'Error'; return 'error';
} finally { } finally {
numberOfGetStatus--; numberOfGetStatus--;
} }
} }
function filterState(state: string) {
clearFiltered();
filtered.applications = applications.filter((application: any) => {
if (status[application.id] === state && application.teams[0].id === $appSession.teamId)
return application;
});
filtered.otherApplications = applications.filter((application: any) => {
if (status[application.id] === state && application.teams[0].id !== $appSession.teamId)
return application;
});
filtered.databases = databases.filter((database: any) => {
if (status[database.id] === state && database.teams[0].id === $appSession.teamId)
return database;
});
filtered.otherDatabases = databases.filter((database: any) => {
if (status[database.id] === state && database.teams[0].id !== $appSession.teamId)
return database;
});
filtered.services = services.filter((service: any) => {
if (status[service.id] === state && service.teams[0].id === $appSession.teamId)
return service;
});
filtered.otherServices = services.filter((service: any) => {
if (status[service.id] === state && service.teams[0].id !== $appSession.teamId)
return service;
});
}
function filterSpecific(type: any) {
clearFiltered();
const otherType = 'other' + type[0].toUpperCase() + type.substring(1);
filtered[type] = eval(type).filter(
(resource: any) => resource.teams[0].id === $appSession.teamId
);
filtered[otherType] = eval(type).filter(
(resource: any) => resource.teams[0].id !== $appSession.teamId
);
}
function applicationFilters(application: any) {
return (
(application.name && application.name.toLowerCase().includes($search.toLowerCase())) ||
(application.fqdn && application.fqdn.toLowerCase().includes($search.toLowerCase())) ||
(application.repository &&
application.repository.toLowerCase().includes($search.toLowerCase())) ||
(application.buildpack &&
application.buildpack.toLowerCase().includes($search.toLowerCase())) ||
(application.branch && application.branch.toLowerCase().includes($search.toLowerCase())) ||
(application.destinationDockerId &&
application.destinationDocker.name.toLowerCase().includes($search.toLowerCase())) ||
('bot'.includes($search) && application.settings.isBot)
);
}
function databaseFilters(database: any) {
return (
(database.name && database.name.toLowerCase().includes($search.toLowerCase())) ||
(database.type && database.type.toLowerCase().includes($search.toLowerCase())) ||
(database.version && database.version.toLowerCase().includes($search.toLowerCase())) ||
(database.destinationDockerId &&
database.destinationDocker.name.toLowerCase().includes($search.toLowerCase()))
);
}
function serviceFilters(service: any) {
return (
(service.name && service.name.toLowerCase().includes($search.toLowerCase())) ||
(service.type && service.type.toLowerCase().includes($search.toLowerCase())) ||
(service.version && service.version.toLowerCase().includes($search.toLowerCase())) ||
(service.destinationDockerId &&
service.destinationDocker.name.toLowerCase().includes($search.toLowerCase()))
);
}
function gitSourceFilters(source: any) {
return (
(source.name && source.name.toLowerCase().includes($search.toLowerCase())) ||
(source.type && source.type.toLowerCase().includes($search.toLowerCase())) ||
(source.htmlUrl && source.htmlUrl.toLowerCase().includes($search.toLowerCase())) ||
(source.apiUrl && source.apiUrl.toLowerCase().includes($search.toLowerCase()))
);
}
function destinationFilters(destination: any) {
return (
(destination.name && destination.name.toLowerCase().includes($search.toLowerCase())) ||
(destination.type && destination.type.toLowerCase().includes($search.toLowerCase()))
);
}
function doSearch(bang?: string) { function doSearch(bang?: string) {
if (bang || bang === '') search = bang; if (bang || bang === '') $search = bang;
if (search) { if ($search) {
if (search.startsWith('!')) { filtered = setInitials();
if (search === '!running') { if ($search.startsWith('!')) {
filtered.applications = applications.filter((application: any) => { if ($search === '!running') {
if ( filterState('running');
status[application.id] === 'Running' && } else if ($search === '!stopped') {
application.teams[0].id === $appSession.teamId filterState('stopped');
) } else if ($search === '!error') {
return application; filterState('error');
}); } else if ($search === '!app') {
filtered.otherApplications = applications.filter((application: any) => { filterSpecific('applications');
if ( } else if ($search === '!db') {
status[application.id] === 'Running' && filterSpecific('databases');
application.teams[0].id !== $appSession.teamId } else if ($search === '!service') {
) filterSpecific('services');
return application; } else if ($search === '!git') {
}); filterSpecific('gitSources');
filtered.databases = databases.filter((database: any) => { } else if ($search === '!destination') {
if (status[database.id] === 'Running' && database.teams[0].id === $appSession.teamId) filterSpecific('destinations');
return database; } else if ($search === '!bot') {
}); clearFiltered();
filtered.otherDatabases = databases.filter((database: any) => {
if (status[database.id] === 'Running' && database.teams[0].id !== $appSession.teamId)
return database;
});
filtered.services = services.filter((service: any) => {
if (status[service.id] === 'Running' && service.teams[0].id === $appSession.teamId)
return service;
});
filtered.otherServices = services.filter((service: any) => {
if (status[service.id] === 'Running' && service.teams[0].id !== $appSession.teamId)
return service;
});
filtered.gitSources = [];
filtered.otherGitSources = [];
filtered.destinations = [];
filtered.otherDestinations = [];
} else if (search === '!stopped') {
filtered.applications = applications.filter((application: any) => {
if (
status[application.id] === 'Stopped' &&
application.teams[0].id === $appSession.teamId
)
return application;
});
filtered.otherApplications = applications.filter((application: any) => {
if (
status[application.id] === 'Stopped' &&
application.teams[0].id !== $appSession.teamId
)
return application;
});
filtered.databases = databases.filter((database: any) => {
if (status[database.id] === 'Stopped' && database.teams[0].id === $appSession.teamId)
return database;
});
filtered.otherDatabases = databases.filter((database: any) => {
if (status[database.id] === 'Stopped' && database.teams[0].id !== $appSession.teamId)
return database;
});
filtered.services = services.filter((service: any) => {
if (status[service.id] === 'Stopped' && service.teams[0].id === $appSession.teamId)
return service;
});
filtered.otherServices = services.filter((service: any) => {
if (status[service.id] === 'Stopped' && service.teams[0].id !== $appSession.teamId)
return service;
});
filtered.gitSources = [];
filtered.otherGitSources = [];
filtered.destinations = [];
filtered.otherDestinations = [];
} else if (search === '!error') {
filtered.applications = applications.filter((application: any) => {
if (
status[application.id] === 'Error' &&
application.teams[0].id === $appSession.teamId
)
return application;
});
filtered.otherApplications = applications.filter((application: any) => {
if (
status[application.id] === 'Error' &&
application.teams[0].id !== $appSession.teamId
)
return application;
});
filtered.databases = databases.filter((database: any) => {
if (status[database.id] === 'Error' && database.teams[0].id === $appSession.teamId)
return database;
});
filtered.otherDatabases = databases.filter((database: any) => {
if (status[database.id] === 'Error' && database.teams[0].id !== $appSession.teamId)
return database;
});
filtered.services = services.filter((service: any) => {
if (status[service.id] === 'Error' && service.teams[0].id === $appSession.teamId)
return service;
});
filtered.otherServices = services.filter((service: any) => {
if (status[service.id] === 'Error' && service.teams[0].id !== $appSession.teamId)
return service;
});
filtered.gitSources = [];
filtered.otherGitSources = [];
filtered.destinations = [];
filtered.otherDestinations = [];
} else if (search === '!app') {
filtered.applications = applications.filter(
(application: any) => application.teams[0].id === $appSession.teamId
);
filtered.databases = [];
filtered.otherDatabases = [];
filtered.services = [];
filtered.otherServices = [];
filtered.gitSources = [];
filtered.otherGitSources = [];
filtered.destinations = [];
filtered.otherDestinations = [];
} else if (search === '!db') {
filtered.applications = [];
filtered.otherApplications = [];
filtered.databases = databases.filter(
(database: any) => database.teams[0].id === $appSession.teamId
);
filtered.services = [];
filtered.otherServices = [];
filtered.gitSources = [];
filtered.otherGitSources = [];
filtered.destinations = [];
filtered.otherDestinations = [];
} else if (search === '!service') {
filtered.applications = [];
filtered.otherApplications = [];
filtered.databases = [];
filtered.otherDatabases = [];
filtered.services = services.filter(
(service: any) => service.teams[0].id === $appSession.teamId
);
filtered.gitSources = [];
filtered.otherGitSources = [];
filtered.destinations = [];
filtered.otherDestinations = [];
} else if (search === '!git') {
filtered.applications = [];
filtered.otherApplications = [];
filtered.databases = [];
filtered.otherDatabases = [];
filtered.services = [];
filtered.otherServices = [];
filtered.gitSources = gitSources.filter(
(source: any) => source.teams[0].id === $appSession.teamId
);
filtered.destinations = [];
filtered.otherDestinations = [];
} else if (search === '!destination') {
filtered.applications = [];
filtered.otherApplications = [];
filtered.databases = [];
filtered.otherDatabases = [];
filtered.services = [];
filtered.otherServices = [];
filtered.gitSources = [];
filtered.otherGitSources = [];
filtered.destinations = destinations.filter(
(destination: any) => destination.teams[0].id === $appSession.teamId
);
} else if (search === '!bot') {
filtered.applications = applications.filter((application: any) => { filtered.applications = applications.filter((application: any) => {
return application.settings.isBot; return application.settings.isBot;
}); });
filtered.otherApplications = applications.filter((application: any) => { filtered.otherApplications = applications.filter((application: any) => {
return application.settings.isBot && application.teams[0].id !== $appSession.teamId; return application.settings.isBot && application.teams[0].id !== $appSession.teamId;
}); });
filtered.databases = []; } else if ($search === '!notmine') {
filtered.otherDatabases = []; clearFiltered();
filtered.services = []; filtered = setInitials(true);
filtered.otherServices = [];
filtered.gitSources = [];
filtered.otherGitSources = [];
} else if (search === '!notmine') {
filtered.applications = [];
filtered.databases = [];
filtered.services = [];
filtered.gitSources = [];
filtered.destinations = [];
} }
} else { } else {
filtered.applications = applications.filter((application: any) => { filtered.applications = filtered.applications.filter((application: any) =>
return ( applicationFilters(application)
(application.name && application.name.toLowerCase().includes(search.toLowerCase())) ||
(application.fqdn && application.fqdn.toLowerCase().includes(search.toLowerCase())) ||
(application.repository &&
application.repository.toLowerCase().includes(search.toLowerCase())) ||
(application.buildpack &&
application.buildpack.toLowerCase().includes(search.toLowerCase())) ||
(application.branch &&
application.branch.toLowerCase().includes(search.toLowerCase())) ||
(application.destinationDockerId &&
application.destinationDocker.name.toLowerCase().includes(search.toLowerCase())) ||
('bot'.includes(search) && application.settings.isBot)
); );
}); filtered.otherApplications = filtered.otherApplications.filter((application: any) =>
filtered.databases = databases.filter((database: any) => { applicationFilters(application)
return (
(database.name && database.name.toLowerCase().includes(search.toLowerCase())) ||
(database.type && database.type.toLowerCase().includes(search.toLowerCase())) ||
(database.version && database.version.toLowerCase().includes(search.toLowerCase())) ||
(database.destinationDockerId &&
database.destinationDocker.name.toLowerCase().includes(search.toLowerCase()))
); );
}); filtered.databases = filtered.databases.filter((database: any) =>
filtered.services = services.filter((service: any) => { databaseFilters(database)
return (
(service.name && service.name.toLowerCase().includes(search.toLowerCase())) ||
(service.type && service.type.toLowerCase().includes(search.toLowerCase())) ||
(service.version && service.version.toLowerCase().includes(search.toLowerCase())) ||
(service.destinationDockerId &&
service.destinationDocker.name.toLowerCase().includes(search.toLowerCase()))
); );
}); filtered.otherDatabases = filtered.otherDatabases.filter((database: any) =>
filtered.gitSources = gitSources.filter((source: any) => { databaseFilters(database)
return (
(source.name && source.name.toLowerCase().includes(search.toLowerCase())) ||
(source.type && source.type.toLowerCase().includes(search.toLowerCase())) ||
(source.htmlUrl && source.htmlUrl.toLowerCase().includes(search.toLowerCase())) ||
(source.apiUrl && source.apiUrl.toLowerCase().includes(search.toLowerCase()))
); );
}); filtered.services = filtered.services.filter((service: any) => serviceFilters(service));
filtered.destinations = destinations.filter((destination: any) => { filtered.otherServices = filtered.otherServices.filter((service: any) =>
return ( serviceFilters(service)
(destination.name && destination.name.toLowerCase().includes(search.toLowerCase())) || );
(destination.type && destination.type.toLowerCase().includes(search.toLowerCase())) filtered.gitSources = filtered.gitSources.filter((source: any) => gitSourceFilters(source));
filtered.otherGitSources = filtered.otherGitSources.filter((source: any) =>
gitSourceFilters(source)
);
filtered.destinations = filtered.destinations.filter((destination: any) =>
destinationFilters(destination)
);
filtered.otherDestinations = filtered.otherDestinations.filter((destination: any) =>
destinationFilters(destination)
); );
});
} }
} else { } else {
filtered.applications = applications; filtered = setInitials();
filtered.databases = databases;
filtered.services = services;
filtered.gitSources = gitSources;
filtered.destinations = destinations;
} }
} }
</script> </script>
@ -370,96 +290,98 @@
{#if applications.length !== 0 || destinations.length !== 0 || databases.length !== 0 || services.length !== 0 || gitSources.length !== 0 || destinations.length !== 0} {#if applications.length !== 0 || destinations.length !== 0 || databases.length !== 0 || services.length !== 0 || gitSources.length !== 0 || destinations.length !== 0}
<div class="form-control"> <div class="form-control">
<div class="input-group flex w-full"> <div class="input-group flex w-full">
<div class="btn btn-square cursor-default no-animation"> <div
class="btn btn-square cursor-default no-animation hover:bg-error"
on:click={() => doSearch('')}
>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6" class="w-6 h-6"
fill="none"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke="currentColor" stroke-width="1.5"
><path stroke="currentcolor"
fill="none"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
stroke-width="2"
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
/></svg
> >
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
</div> </div>
<input <input
id="search" id="search"
type="text" type="text"
placeholder="Search: You can search for names, domains, types, database types, version, servers etc..." placeholder="Search: You can search for names, domains, types, database types, version, servers etc..."
class=" w-full input input-bordered input-primary" class="w-full input input-bordered input-primary"
bind:value={search} bind:value={$search}
on:input={() => doSearch()} on:input={() => doSearch()}
/> />
</div> </div>
<label for="search" class="label w-full"> <label for="search" class="label w-full">
<span <span class="label-text text-xs flex flex-wrap gap-2 items-center">
class="label-text text-xs flex flex-row space-x-3 space-y-3 flex-wrap items-center justify-center lg:justify-start mt-4" <button
class:bg-coollabs={$search === '!notmine'}
class="badge badge-lg text-white text-xs rounded"
on:click={() => doSearch('!notmine')}>Other Teams</button
> >
<button <button
class:bg-coollabs={search === '!app'} class:bg-coollabs={$search === '!app'}
class="badge badge-lg text-white text-xs -mb-3" class="badge badge-lg text-white text-xs rounded"
on:click={() => doSearch('!app')}>Applications</button on:click={() => doSearch('!app')}>Applications</button
> >
<button <button
class:bg-coollabs={search === '!bot'} class:bg-coollabs={$search === '!bot'}
class="badge badge-lg text-white text-xs" class="badge badge-lg text-white text-xs rounded"
on:click={() => doSearch('!bot')}>Bots</button on:click={() => doSearch('!bot')}>Bots</button
> >
<button <button
class:bg-coollabs={search === '!service'} class:bg-coollabs={$search === '!service'}
class="badge badge-lg bg-coollabs text-white text-xs" class="badge badge-lg text-white text-xs rounded"
on:click={() => doSearch('!service')}>Services</button on:click={() => doSearch('!service')}>Services</button
> >
<button <button
class:bg-coollabs={search === '!db'} class:bg-coollabs={$search === '!db'}
class="badge badge-lg bg-coollabs text-white text-xs" class="badge badge-lg text-white text-xs rounded"
on:click={() => doSearch('!db')}>Databases</button on:click={() => doSearch('!db')}>Databases</button
> >
<button <button
class:bg-coollabs={search === '!git'} class:bg-coollabs={$search === '!git'}
class="badge badge-lg bg-coollabs text-white text-xs" class="badge badge-lg text-white text-xs rounded"
on:click={() => doSearch('!git')}>Git Sources</button on:click={() => doSearch('!git')}>Git Sources</button
> >
<button <button
class:bg-coollabs={search === '!destination'} class:bg-coollabs={$search === '!destination'}
class="badge badge-lg bg-coollabs text-white text-xs" class="badge badge-lg text-white text-xs rounded"
on:click={() => doSearch('!destination')}>Destinations</button on:click={() => doSearch('!destination')}>Destinations</button
> >
<button <button
class:bg-coollabs={search === '!running'} class:bg-coollabs={$search === '!running'}
class="badge badge-lg bg-coollabs text-white text-xs" class="badge badge-lg text-white text-xs rounded"
on:click={() => doSearch('!running')}>Running</button on:click={() => doSearch('!running')}>Running</button
> >
<button <button
class:bg-coollabs={search === '!stopped'} class:bg-coollabs={$search === '!stopped'}
class="badge badge-lg bg-coollabs text-white text-xs" class="badge badge-lg text-white text-xs rounded"
on:click={() => doSearch('!stopped')}>Stopped</button on:click={() => doSearch('!stopped')}>Stopped</button
> >
<button <button
class:bg-coollabs={search === '!error'} class:bg-coollabs={$search === '!error'}
class="badge badge-lg bg-coollabs text-white text-xs" class="badge badge-lg text-white text-xs rounded"
on:click={() => doSearch('!error')}>Error</button on:click={() => doSearch('!error')}>Error</button
> >
<button
class="badge badge-lg bg-coollabs text-white text-xs"
on:click={() => doSearch('')}>Clear</button
>
</span> </span>
</label> </label>
</div> </div>
{/if} {/if}
{#if (filtered.applications.length > 0 && applications.length > 0) || filtered.otherApplications.length > 0} {#if (filtered.applications.length > 0 && applications.length > 0) || filtered.otherApplications.length > 0}
<div class="flex items-center mt-14"> <div class="flex items-center mt-10">
<h1 class="title lg:text-3xl">Applications</h1> <h1 class="title lg:text-3xl">Applications</h1>
</div> </div>
{/if} {/if}
{#if filtered.applications.length > 0 && applications.length > 0} {#if filtered.applications.length > 0 && applications.length > 0}
<div class="divider" /> <div class="divider" />
<div <div
class="grid grid-col gap-8 auto-cols-max grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 p-4" class="grid grid-col gap-8 auto-cols-max grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 p-4"
> >
@ -470,7 +392,7 @@
{#await getStatus(application)} {#await getStatus(application)}
<span class="indicator-item badge bg-yellow-500 badge-sm" /> <span class="indicator-item badge bg-yellow-500 badge-sm" />
{:then status} {:then status}
{#if status === 'Running'} {#if status === 'running'}
<span class="indicator-item badge bg-success badge-sm" /> <span class="indicator-item badge bg-success badge-sm" />
{:else} {:else}
<span class="indicator-item badge bg-error badge-sm" /> <span class="indicator-item badge bg-error badge-sm" />
@ -481,7 +403,7 @@
<div class="w-full flex flex-col"> <div class="w-full flex flex-col">
<h1 class="font-bold text-lg lg:text-xl truncate"> <h1 class="font-bold text-lg lg:text-xl truncate">
{application.name} {application.name}
{#if application.settings.isBot} {#if application.settings?.isBot}
<span class="text-xs badge bg-coolblack border-none text-applications" <span class="text-xs badge bg-coolblack border-none text-applications"
>BOT</span >BOT</span
> >
@ -490,17 +412,20 @@
<div class="h-10 text-xs"> <div class="h-10 text-xs">
{#if application?.fqdn} {#if application?.fqdn}
<h2>{application?.fqdn.replace('https://', '').replace('http://', '')}</h2> <h2>{application?.fqdn.replace('https://', '').replace('http://', '')}</h2>
{:else if !application.settings.isBot && !application?.fqdn} {:else if !application.settings?.isBot && !application?.fqdn}
<h2 class="text-red-500">Not configured</h2> <h2 class="text-red-500">Not configured</h2>
{/if} {/if}
{#if application.destinationDocker?.name} {#if application.destinationDocker?.name}
<div class="truncate">{application.destinationDocker.name}</div> <div class="truncate">{application.destinationDocker?.name}</div>
{/if}
{#if application.teams.length > 0 && application.teams[0]?.name}
<div class="truncate">{application.teams[0]?.name}</div>
{/if} {/if}
</div> </div>
<div class="flex justify-end items-end space-x-2 h-10"> <div class="flex justify-end items-end space-x-2 h-10">
{#if application.fqdn} {#if application?.fqdn}
<a href={application.fqdn} target="_blank" class="icons hover:bg-green-500"> <a href={application?.fqdn} target="_blank" class="icons hover:bg-green-500">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6" class="h-6 w-6"
@ -519,7 +444,7 @@
</a> </a>
{/if} {/if}
{#if application.settings.isBot && application.exposePort} {#if application.settings?.isBot && application.exposePort}
<a <a
href={`http://${dev ? 'localhost' : settings.ipv4}:${ href={`http://${dev ? 'localhost' : settings.ipv4}:${
application.exposePort application.exposePort
@ -563,17 +488,17 @@
<h1 class="text-lg font-bold">Other Teams</h1> <h1 class="text-lg font-bold">Other Teams</h1>
</div> </div>
{/if} {/if}
{#if filtered.otherApplications.length > 0}
<div <div
class="grid grid-col gap-8 auto-cols-max grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 p-4" class="grid grid-col gap-8 auto-cols-max grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 p-4"
> >
{#if filtered.otherApplications.length > 0}
{#each filtered.otherApplications as application} {#each filtered.otherApplications as application}
<a class="no-underline mb-5" href={`/applications/${application.id}`}> <a class="no-underline mb-5" href={`/applications/${application.id}`}>
<div class="w-full rounded p-5 bg-coolgray-200 hover:bg-green-600 indicator"> <div class="w-full rounded p-5 bg-coolgray-200 hover:bg-green-600 indicator">
{#await getStatus(application)} {#await getStatus(application)}
<span class="indicator-item badge bg-yellow-500 badge-sm" /> <span class="indicator-item badge bg-yellow-500 badge-sm" />
{:then status} {:then status}
{#if status === 'Running'} {#if status === 'running'}
<span class="indicator-item badge bg-success badge-sm" /> <span class="indicator-item badge bg-success badge-sm" />
{:else} {:else}
<span class="indicator-item badge bg-error badge-sm" /> <span class="indicator-item badge bg-error badge-sm" />
@ -584,7 +509,7 @@
<div class="w-full flex flex-col"> <div class="w-full flex flex-col">
<h1 class="font-bold text-lg lg:text-xl truncate"> <h1 class="font-bold text-lg lg:text-xl truncate">
{application.name} {application.name}
{#if application.settings.isBot} {#if application.settings?.isBot}
<span class="text-xs badge bg-coolblack border-none text-applications">BOT</span <span class="text-xs badge bg-coolblack border-none text-applications">BOT</span
> >
{/if} {/if}
@ -592,17 +517,20 @@
<div class="h-10 text-xs"> <div class="h-10 text-xs">
{#if application?.fqdn} {#if application?.fqdn}
<h2>{application?.fqdn.replace('https://', '').replace('http://', '')}</h2> <h2>{application?.fqdn.replace('https://', '').replace('http://', '')}</h2>
{:else if !application.settings.isBot && !application?.fqdn} {:else if !application.settings?.isBot && !application?.fqdn}
<h2 class="text-red-500">Not configured</h2> <h2 class="text-red-500">Not configured</h2>
{/if} {/if}
{#if application.destinationDocker?.name} {#if application.destinationDocker?.name}
<div class="truncate">{application.destinationDocker.name}</div> <div class="truncate">{application.destinationDocker?.name}</div>
{/if}
{#if application.teams.length > 0 && application.teams[0]?.name}
<div class="truncate">{application.teams[0]?.name}</div>
{/if} {/if}
</div> </div>
<div class="flex justify-end items-end space-x-2 h-10"> <div class="flex justify-end items-end space-x-2 h-10">
{#if application.fqdn} {#if application?.fqdn}
<a href={application.fqdn} target="_blank" class="icons hover:bg-green-500"> <a href={application?.fqdn} target="_blank" class="icons hover:bg-green-500">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6" class="h-6 w-6"
@ -621,7 +549,7 @@
</a> </a>
{/if} {/if}
{#if application.settings.isBot && application.exposePort} {#if application.settings?.isBot && application.exposePort}
<a <a
href={`http://${dev ? 'localhost' : settings.ipv4}:${application.exposePort}`} href={`http://${dev ? 'localhost' : settings.ipv4}:${application.exposePort}`}
target="_blank" target="_blank"
@ -650,9 +578,9 @@
</div> </div>
</a> </a>
{/each} {/each}
{/if}
</div> </div>
{#if (filtered.services.length > 0 && services.length > 0) || filtered.services.length > 0} {/if}
{#if (filtered.services.length > 0 && services.length > 0) || filtered.otherServices.length > 0}
<div class="flex items-center mt-10"> <div class="flex items-center mt-10">
<h1 class="title lg:text-3xl">Services</h1> <h1 class="title lg:text-3xl">Services</h1>
</div> </div>
@ -669,7 +597,7 @@
{#await getStatus(service)} {#await getStatus(service)}
<span class="indicator-item badge bg-yellow-500 badge-sm" /> <span class="indicator-item badge bg-yellow-500 badge-sm" />
{:then status} {:then status}
{#if status === 'Running'} {#if status === 'running'}
<span class="indicator-item badge bg-success badge-sm" /> <span class="indicator-item badge bg-success badge-sm" />
{:else} {:else}
<span class="indicator-item badge bg-error badge-sm" /> <span class="indicator-item badge bg-error badge-sm" />
@ -686,12 +614,15 @@
<h2 class="text-red-500">URL not configured</h2> <h2 class="text-red-500">URL not configured</h2>
{/if} {/if}
{#if service.destinationDocker?.name} {#if service.destinationDocker?.name}
<div class="truncate">{service.destinationDocker.name}</div> <div class="truncate">{service.destinationDocker?.name}</div>
{/if}
{#if service.teams.length > 0 && service.teams[0]?.name}
<div class="truncate">{service.teams[0]?.name}</div>
{/if} {/if}
</div> </div>
<div class="flex justify-end items-end space-x-2 h-10"> <div class="flex justify-end items-end space-x-2 h-10">
{#if service.fqdn} {#if service?.fqdn}
<a href={service.fqdn} target="_blank" class="icons hover:bg-pink-500"> <a href={service?.fqdn} target="_blank" class="icons hover:bg-pink-500">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6" class="h-6 w-6"
@ -719,6 +650,7 @@
<h1 class="">Nothing here.</h1> <h1 class="">Nothing here.</h1>
{/if} {/if}
</div> </div>
{/if}
{#if filtered.otherServices.length > 0} {#if filtered.otherServices.length > 0}
{#if filtered.services.length > 0} {#if filtered.services.length > 0}
<div class="divider w-32 mx-auto" /> <div class="divider w-32 mx-auto" />
@ -737,7 +669,7 @@
{#await getStatus(service)} {#await getStatus(service)}
<span class="indicator-item badge bg-yellow-500 badge-sm" /> <span class="indicator-item badge bg-yellow-500 badge-sm" />
{:then status} {:then status}
{#if status === 'Running'} {#if status === 'running'}
<span class="indicator-item badge bg-success badge-sm" /> <span class="indicator-item badge bg-success badge-sm" />
{:else} {:else}
<span class="indicator-item badge bg-error badge-sm" /> <span class="indicator-item badge bg-error badge-sm" />
@ -754,12 +686,15 @@
<h2 class="text-red-500">URL not configured</h2> <h2 class="text-red-500">URL not configured</h2>
{/if} {/if}
{#if service.destinationDocker?.name} {#if service.destinationDocker?.name}
<div class="truncate">{service.destinationDocker.name}</div> <div class="truncate">{service.destinationDocker?.name}</div>
{/if}
{#if service.teams.length > 0 && service.teams[0]?.name}
<div class="truncate">{service.teams[0]?.name}</div>
{/if} {/if}
</div> </div>
<div class="flex justify-end items-end space-x-2 h-10"> <div class="flex justify-end items-end space-x-2 h-10">
{#if service.fqdn} {#if service?.fqdn}
<a href={service.fqdn} target="_blank" class="icons hover:bg-pink-500"> <a href={service?.fqdn} target="_blank" class="icons hover:bg-pink-500">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6" class="h-6 w-6"
@ -785,8 +720,7 @@
{/each} {/each}
</div> </div>
{/if} {/if}
{/if} {#if (filtered.databases.length > 0 && databases.length > 0) || filtered.otherDatabases.length > 0}
{#if (filtered.databases.length > 0 && databases.length > 0) || filtered.databases.length > 0}
<div class="flex items-center mt-10"> <div class="flex items-center mt-10">
<h1 class="title lg:text-3xl">Databases</h1> <h1 class="title lg:text-3xl">Databases</h1>
</div> </div>
@ -803,7 +737,7 @@
{#await getStatus(database)} {#await getStatus(database)}
<span class="indicator-item badge bg-yellow-500 badge-sm" /> <span class="indicator-item badge bg-yellow-500 badge-sm" />
{:then status} {:then status}
{#if status === 'Running'} {#if status === 'running'}
<span class="indicator-item badge bg-success badge-sm" /> <span class="indicator-item badge bg-success badge-sm" />
{:else} {:else}
<span class="indicator-item badge bg-error badge-sm" /> <span class="indicator-item badge bg-error badge-sm" />
@ -821,12 +755,15 @@
<h2 class="text-red-500">Not version not configured</h2> <h2 class="text-red-500">Not version not configured</h2>
{/if} {/if}
{#if database.destinationDocker?.name} {#if database.destinationDocker?.name}
<div class="truncate">{database.destinationDocker.name}</div> <div class="truncate">{database.destinationDocker?.name}</div>
{/if}
{#if database.teams.length > 0 && database.teams[0]?.name}
<div class="truncate">{database.teams[0]?.name}</div>
{/if} {/if}
</div> </div>
</div> </div>
<div class="flex justify-end items-end space-x-2 h-10"> <div class="flex justify-end items-end space-x-2 h-10">
{#if database.settings.isPublic} {#if database.settings?.isPublic}
<div title="Public"> <div title="Public">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@ -857,8 +794,9 @@
<h1 class="">Nothing here.</h1> <h1 class="">Nothing here.</h1>
{/if} {/if}
</div> </div>
{/if}
{#if filtered.otherDatabases.length > 0} {#if filtered.otherDatabases.length > 0}
{#if databases.length > 0} {#if filtered.databases.length > 0}
<div class="divider w-32 mx-auto" /> <div class="divider w-32 mx-auto" />
{/if} {/if}
<div class="flex items-center mt-10"> <div class="flex items-center mt-10">
@ -875,7 +813,7 @@
{#await getStatus(database)} {#await getStatus(database)}
<span class="indicator-item badge bg-yellow-500 badge-sm" /> <span class="indicator-item badge bg-yellow-500 badge-sm" />
{:then status} {:then status}
{#if status === 'Running'} {#if status === 'running'}
<span class="indicator-item badge bg-success badge-sm" /> <span class="indicator-item badge bg-success badge-sm" />
{:else} {:else}
<span class="indicator-item badge bg-error badge-sm" /> <span class="indicator-item badge bg-error badge-sm" />
@ -893,12 +831,15 @@
<h2 class="text-red-500">Not version not configured</h2> <h2 class="text-red-500">Not version not configured</h2>
{/if} {/if}
{#if database.destinationDocker?.name} {#if database.destinationDocker?.name}
<div class="truncate">{database.destinationDocker.name}</div> <div class="truncate">{database.destinationDocker?.name}</div>
{/if}
{#if database.teams.length > 0 && database.teams[0]?.name}
<div class="truncate">{database.teams[0]?.name}</div>
{/if} {/if}
</div> </div>
</div> </div>
<div class="flex justify-end items-end space-x-2 h-10"> <div class="flex justify-end items-end space-x-2 h-10">
{#if database.settings.isPublic} {#if database.settings?.isPublic}
<div title="Public"> <div title="Public">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@ -927,8 +868,7 @@
{/each} {/each}
</div> </div>
{/if} {/if}
{/if} {#if (filtered.gitSources.length > 0 && gitSources.length > 0) || filtered.otherGitSources.length > 0}
{#if (filtered.gitSources.length > 0 && gitSources.length > 0) || filtered.gitSources.length > 0}
<div class="flex items-center mt-10"> <div class="flex items-center mt-10">
<h1 class="title lg:text-3xl">Git Sources</h1> <h1 class="title lg:text-3xl">Git Sources</h1>
</div> </div>
@ -983,7 +923,11 @@
<div class="w-full flex flex-col"> <div class="w-full flex flex-col">
<div class="h-10"> <div class="h-10">
<h1 class="font-bold text-lg lg:text-xl truncate">{source.name}</h1> <h1 class="font-bold text-lg lg:text-xl truncate">{source.name}</h1>
{#if source.teams.length > 0 && source.teams[0]?.name}
<div class="truncate text-xs">{source.teams[0]?.name}</div>
{/if}
</div> </div>
<div class="flex justify-end items-end space-x-2 h-10" /> <div class="flex justify-end items-end space-x-2 h-10" />
</div> </div>
</div> </div>
@ -994,8 +938,9 @@
<h1 class="">Nothing here.</h1> <h1 class="">Nothing here.</h1>
{/if} {/if}
</div> </div>
{/if}
{#if filtered.otherGitSources.length > 0} {#if filtered.otherGitSources.length > 0}
{#if gitSources.length > 0} {#if filtered.gitSources.length > 0}
<div class="divider w-32 mx-auto" /> <div class="divider w-32 mx-auto" />
{/if} {/if}
<div class="flex items-center mt-10"> <div class="flex items-center mt-10">
@ -1050,6 +995,9 @@
<div class="w-full flex flex-col"> <div class="w-full flex flex-col">
<div class="h-10"> <div class="h-10">
<h1 class="font-bold text-lg lg:text-xl truncate">{source.name}</h1> <h1 class="font-bold text-lg lg:text-xl truncate">{source.name}</h1>
{#if source.teams.length > 0 && source.teams[0]?.name}
<div class="truncate text-xs">{source.teams[0]?.name}</div>
{/if}
</div> </div>
<div class="flex justify-end items-end space-x-2 h-10" /> <div class="flex justify-end items-end space-x-2 h-10" />
</div> </div>
@ -1059,8 +1007,7 @@
{/each} {/each}
</div> </div>
{/if} {/if}
{/if} {#if (filtered.destinations.length > 0 && destinations.length > 0) || filtered.otherDestinations.length > 0}
{#if (filtered.destinations.length > 0 && destinations.length > 0) || filtered.destinations.length > 0}
<div class="flex items-center mt-10"> <div class="flex items-center mt-10">
<h1 class="title lg:text-3xl">Destinations</h1> <h1 class="title lg:text-3xl">Destinations</h1>
</div> </div>
@ -1127,6 +1074,9 @@
{#if destination.remoteEngine && !destination.sshKeyId} {#if destination.remoteEngine && !destination.sshKeyId}
<h2 class="text-red-500">SSH key missing</h2> <h2 class="text-red-500">SSH key missing</h2>
{/if} {/if}
{#if destination.teams.length > 0 && destination.teams[0]?.name}
<div class="truncate">{destination.teams[0]?.name}</div>
{/if}
</div> </div>
</div> </div>
</div> </div>
@ -1137,8 +1087,9 @@
<h1 class="">Nothing here.</h1> <h1 class="">Nothing here.</h1>
{/if} {/if}
</div> </div>
{/if}
{#if filtered.otherDestinations.length > 0} {#if filtered.otherDestinations.length > 0}
{#if destinations.length > 0} {#if filtered.destinations.length > 0}
<div class="divider w-32 mx-auto" /> <div class="divider w-32 mx-auto" />
{/if} {/if}
<div class="flex items-center mt-10"> <div class="flex items-center mt-10">
@ -1205,6 +1156,9 @@
{#if destination.remoteEngine && !destination.sshKeyId} {#if destination.remoteEngine && !destination.sshKeyId}
<h2 class="text-red-500">SSH key missing</h2> <h2 class="text-red-500">SSH key missing</h2>
{/if} {/if}
{#if destination.teams.length > 0 && destination.teams[0]?.name}
<div class="truncate">{destination.teams[0]?.name}</div>
{/if}
</div> </div>
</div> </div>
</div> </div>
@ -1213,12 +1167,11 @@
{/each} {/each}
</div> </div>
{/if} {/if}
{/if}
{#if filtered.applications.length === 0 && filtered.destinations.length === 0 && filtered.databases.length === 0 && filtered.services.length === 0 && filtered.gitSources.length === 0 && filtered.destinations.length === 0 && search} {#if filtered.applications.length === 0 && filtered.destinations.length === 0 && filtered.databases.length === 0 && filtered.services.length === 0 && filtered.gitSources.length === 0 && filtered.destinations.length === 0 && $search}
<div class="flex flex-col items-center justify-center h-full pt-20"> <div class="flex flex-col items-center justify-center h-full pt-20">
<h1 class="text-2xl font-bold pb-4"> <h1 class="text-2xl font-bold pb-4">
Nothing found with <span class="text-error font-bold">{search}</span>. Nothing found with <span class="text-error font-bold">{$search}</span>.
</h1> </h1>
</div> </div>
{/if} {/if}

View File

@ -0,0 +1,52 @@
<script context="module" lang="ts">
import { get } from '$lib/api';
import Usage from '$lib/components/Usage.svelte';
import type { Load } from '@sveltejs/kit';
export const load: Load = async ({}) => {
try {
const { servers } = await get('/servers');
return {
props: {
servers
}
};
} catch (error: any) {
return {
status: 500,
error: new Error(error)
};
}
};
</script>
<script lang="ts">
export let servers: any;
import { appSession } from '$lib/store';
import { goto } from '$app/navigation';
if ($appSession.teamId !== '0') {
goto('/');
}
</script>
<div class="flex space-x-1 p-6 font-bold">
<div class="mr-4 text-2xl tracking-tight">Servers</div>
</div>
<div class="container lg:mx-auto lg:p-0 px-8 p-5">
{#if servers.length > 0}
<div class="grid grid-col gap-8 auto-cols-max grid-cols-1 p-4">
{#each servers as server}
<div class="no-underline mb-5">
<div class="w-full rounded bg-coolgray-100 indicator">
{#if $appSession.teamId === '0'}
<Usage {server} />
{/if}
</div>
</div>
{/each}
</div>
{:else}
<h1 class="text-center text-xs">Nothing here.</h1>
{/if}
</div>
<div class="text-xs text-center">Remote servers will be here soon</div>