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.
"forwardPorts": [3000, 3001],
// 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.
"remoteUser": "node",
"features": {

View File

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

View File

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

View File

@ -252,8 +252,8 @@ export async function saveApplication(request: FastifyRequest<SaveApplication>,
exposePort = Number(exposePort);
}
const { destinationDocker: { id: dockerId, remoteIpAddress }, exposePort: configuredPort } = await prisma.application.findUnique({ where: { id }, include: { destinationDocker: true } })
if (exposePort) await checkExposedPort({ id, configuredPort, exposePort, dockerId, remoteIpAddress })
const { destinationDocker: { engine, remoteEngine, remoteIpAddress }, exposePort: configuredPort } = await prisma.application.findUnique({ where: { id }, include: { destinationDocker: true } })
if (exposePort) await checkExposedPort({ id, configuredPort, exposePort, engine, remoteEngine, remoteIpAddress })
if (denoOptions) denoOptions = denoOptions.trim();
const defaultConfiguration = await setDefaultConfiguration({
buildPack,
@ -534,14 +534,14 @@ export async function checkDNS(request: FastifyRequest<CheckDNS>) {
}
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 found = await isDomainConfigured({ id, fqdn, remoteIpAddress });
if (found) {
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) {
let hostname = request.hostname.split(':')[0];
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 { day } from '../../../../lib/dayjs';
import { DeleteDatabaseSecret, GetDatabaseLogs, OnlyId, SaveDatabase, SaveDatabaseDestination, SaveDatabaseSecret, SaveDatabaseSettings, SaveVersion } from '../../../../types';
import { DeleteDatabase, SaveDatabaseType } from './types';
import type { OnlyId } from '../../../../types';
import type { DeleteDatabase, DeleteDatabaseSecret, GetDatabaseLogs, SaveDatabase, SaveDatabaseDestination, SaveDatabaseSecret, SaveDatabaseSettings, SaveDatabaseType, SaveVersion } from './types';
export async function listDatabases(request: FastifyRequest) {
try {
@ -94,15 +94,14 @@ export async function getDatabase(request: FastifyRequest<OnlyId>) {
if (!database) {
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.rootUserPassword) database.rootUserPassword = decrypt(database.rootUserPassword);
const configuration = generateDatabaseConfiguration(database, arch);
const settings = await listSettings();
const configuration = generateDatabaseConfiguration(database, settings.arch);
return {
privatePort: configuration?.privatePort,
database,
versions: await getDatabaseVersions(database.type, arch),
versions: await getDatabaseVersions(database.type, settings.arch),
settings
};
} catch ({ status, message }) {
@ -426,10 +425,10 @@ export async function saveDatabaseSettings(request: FastifyRequest<SaveDatabaseS
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) {
publicPort = await getFreePublicPort(id, dockerId);
publicPort = await getFreePublicPort({ id, remoteEngine, engine, remoteIpAddress });
}
await prisma.database.update({
where: { id },

View File

@ -1,5 +1,4 @@
import os from 'node:os';
import osu from 'node-os-utils';
import axios from 'axios';
import { compareVersions } from 'compare-versions';
import cuid from 'cuid';
@ -15,9 +14,10 @@ export async function hashPassword(password: string): Promise<string> {
return bcrypt.hash(password, saltRounds);
}
export async function cleanupManually() {
export async function cleanupManually(request: FastifyRequest) {
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)
return {}
} catch ({ status, message }) {
@ -86,25 +86,7 @@ export async function restartCoolify(request: FastifyRequest<any>) {
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) {
try {
const userId = request.user.userId;

View File

@ -43,17 +43,13 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => {
onRequest: [fastify.authenticate]
}, async (request) => await showDashboard(request));
fastify.get('/usage', {
onRequest: [fastify.authenticate]
}, async () => await showUsage());
fastify.post('/internal/restart', {
onRequest: [fastify.authenticate]
}, async (request) => await restartCoolify(request));
fastify.post('/internal/cleanup', {
onRequest: [fastify.authenticate]
}, async () => await cleanupManually());
}, async (request) => await cleanupManually(request));
};
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 fs from 'fs/promises';
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 { checkContainer, isContainerExited } from '../../../../lib/docker';
import cuid from 'cuid';
@ -70,6 +70,7 @@ export async function getService(request: FastifyRequest<OnlyId>) {
throw { status: 404, message: 'Service not found.' }
}
return {
settings: await listSettings(),
service
}
} 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 (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({});
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) {
let hostname = request.hostname.split(':')[0];
if (remoteEngine) hostname = remoteIpAddress;
@ -484,9 +485,9 @@ export async function activateWordpressFtp(request: FastifyRequest<ActivateWordp
const { id } = request.params
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 ftpPassword = generatePassword({});

View File

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

View File

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

View File

@ -1,4 +1,5 @@
<script lang="ts">
export let server: any;
let usage = {
cpu: {
load: [0, 0, 0],
@ -29,7 +30,7 @@
async function getStatus() {
if (loading.usage) return;
loading.usage = true;
const data = await get('/usage');
const data = await get(`/servers/usage/${server.id}?remoteEngine=${server.remoteEngine}`);
usage = data.usage;
loading.usage = false;
}
@ -52,7 +53,7 @@
async function manuallyCleanupStorage() {
try {
loading.cleanup = true;
await post('/internal/cleanup', {});
await post('/internal/cleanup', { serverId: server.id });
return addToast({
message: 'Cleanup done.',
type: 'success'
@ -65,16 +66,42 @@
}
</script>
<div class="w-full">
<div class="flex lg:flex-row flex-col gap-4">
<h1 class="title lg:text-3xl">Hardware Details</h1>
<div class="flex lg:flex-row flex-col space-x-0 lg:space-x-2 space-y-2 lg:space-y-0">
{#if $appSession.teamId === '0'}
<button on:click={manuallyCleanupStorage} class:loading={loading.cleanup} class="btn btn-sm"
>Cleanup Storage</button
>
{/if}
<div class="w-full relative p-5 ">
{#if loading.usage}
<span class="indicator-item badge bg-yellow-500 badge-sm" />
{: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'}
<button
on:click={manuallyCleanupStorage}
class:loading={loading.cleanup}
class="btn btn-sm bg-coollabs">Cleanup Storage</button
>
{/if}
</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 class="divider" />
<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-title">Total Memory</div>
<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 class="stat">
<div class="stat-title">Used Memory</div>
<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 class="stat">
<div class="stat-title">Free Memory</div>
<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>
@ -105,41 +132,41 @@
<div class="stat">
<div class="stat-title">Total CPU</div>
<div class="stat-value text-2xl">
{usage?.cpu.count}
{usage?.cpu?.count}
</div>
</div>
<div class="stat">
<div class="stat-title">CPU Usage</div>
<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 class="stat">
<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 class="stats stats-vertical min-w-[16rem] mb-5 bg-transparent rounded">
<div class="stat">
<div class="stat-title">Total Disk</div>
<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 class="stat">
<div class="stat-title">Used Disk</div>
<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 class="stat">
<div class="stat-title">Free Disk</div>
<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>

View File

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

View File

@ -38,13 +38,13 @@
<ul
id="new"
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>
<button on:click={newApplication} class="no-underline hover:bg-applications">
<button on:click={newApplication} class="no-underline hover:bg-applications rounded-none ">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-8 w-8"
class="h-6 w-6"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentcolor"
@ -62,10 +62,10 @@
>
</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
xmlns="http://www.w3.org/2000/svg"
class="h-8 w-8"
class="h-6 w-6"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
@ -79,10 +79,10 @@
>
</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
xmlns="http://www.w3.org/2000/svg"
class="h-8 w-8"
class="h-6 w-6"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
@ -98,10 +98,10 @@
>
</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
xmlns="http://www.w3.org/2000/svg"
class="h-8 w-8"
class="h-6 w-6"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
@ -120,10 +120,10 @@
>
</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
xmlns="http://www.w3.org/2000/svg"
class="h-8 w-8"
class="h-6 w-6"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"

View File

@ -140,9 +140,10 @@
id="dashboard"
sveltekit:prefetch
href="/"
class="icons bg-coolgray-200 hover:text-white"
class="icons hover:text-white"
class:text-white={$page.url.pathname === '/'}
class:bg-coolgray-500={$page.url.pathname === '/'}
class:bg-coolgray-200={!($page.url.pathname === '/')}
>
<svg
xmlns="http://www.w3.org/2000/svg"
@ -161,157 +162,37 @@
<path d="M16 15c-2.21 1.333 -5.792 1.333 -8 0" />
</svg>
</a>
<div class="border-t border-stone-700" />
<a
id="applications"
sveltekit:prefetch
href="/applications"
class="icons bg-coolgray-200"
class:text-applications={$page.url.pathname.startsWith('/applications') ||
$page.url.pathname.startsWith('/new/application')}
class:bg-coolgray-500={$page.url.pathname.startsWith('/applications') ||
$page.url.pathname.startsWith('/new/application')}
data-tip="Applications"
>
<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"
{#if $appSession.teamId === '0'}
<a
id="servers"
sveltekit:prefetch
href="/servers"
class="icons hover:text-white"
class:text-white={$page.url.pathname === '/servers'}
class:bg-coolgray-500={$page.url.pathname === '/servers'}
class:bg-coolgray-200={!($page.url.pathname === '/servers')}
>
<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"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<circle cx="6" cy="6" r="2" />
<circle cx="18" cy="18" r="2" />
<path d="M11 6h5a2 2 0 0 1 2 2v8" />
<polyline points="14 9 11 6 14 3" />
<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>
</a>
<svg
xmlns="http://www.w3.org/2000/svg"
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="3" y="4" width="18" height="8" rx="3" />
<rect x="3" y="12" width="18" height="8" rx="3" />
<line x1="7" y1="8" x2="7" y2="8.01" />
<line x1="7" y1="16" x2="7" y2="16.01" />
</svg>
</a>
{/if}
</div>
<Tooltip triggeredBy="#dashboard" placement="right">Dashboard</Tooltip>
<Tooltip triggeredBy="#servers" placement="right">Servers</Tooltip>
<div class="flex-1" />
<UpdateAvailable />
@ -684,14 +565,6 @@
</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="#settings" placement="right" color="bg-settings text-black">Settings</Tooltip
>

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

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>