fix: docker log sequence

This commit is contained in:
Andras Bacsai 2023-01-16 10:06:41 +01:00
parent d641d32413
commit 2fd001f6d2
5 changed files with 1617 additions and 1380 deletions

View File

@ -1605,12 +1605,7 @@ export async function getApplicationLogs(request: FastifyRequest<GetApplicationL
.split('\n')
.map((l) => ansi(l))
.filter((a) => a);
const logs = stripLogsStderr.concat(stripLogsStdout);
const sortedLogs = logs.sort((a, b) =>
day(a.split(' ')[0]).isAfter(day(b.split(' ')[0])) ? 1 : -1
);
return { logs: sortedLogs };
// }
return { logs: stripLogsStderr.concat(stripLogsStdout) };
} catch (error) {
const { statusCode, stderr } = error;
if (stderr.startsWith('Error: No such container')) {

View File

@ -3,11 +3,44 @@ import type { FastifyRequest } from 'fastify';
import { FastifyReply } from 'fastify';
import yaml from 'js-yaml';
import fs from 'fs/promises';
import { ComposeFile, createDirectories, decrypt, defaultComposeConfiguration, encrypt, errorHandler, executeCommand, 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,
executeCommand,
generateDatabaseConfiguration,
generatePassword,
getContainerUsage,
getDatabaseImage,
getDatabaseVersions,
getFreePublicPort,
listSettings,
makeLabelForStandaloneDatabase,
prisma,
startTraefikTCPProxy,
stopDatabaseContainer,
stopTcpHttpProxy,
supportedDatabaseTypesAndVersions,
uniqueName,
updatePasswordInDb
} from '../../../../lib/common';
import { day } from '../../../../lib/dayjs';
import type { OnlyId } from '../../../../types';
import type { DeleteDatabase, DeleteDatabaseSecret, GetDatabaseLogs, SaveDatabase, SaveDatabaseDestination, SaveDatabaseSecret, SaveDatabaseSettings, SaveDatabaseType, SaveVersion } from './types';
import type {
DeleteDatabase,
DeleteDatabaseSecret,
GetDatabaseLogs,
SaveDatabase,
SaveDatabaseDestination,
SaveDatabaseSecret,
SaveDatabaseSettings,
SaveDatabaseType,
SaveVersion
} from './types';
export async function listDatabases(request: FastifyRequest) {
try {
@ -18,9 +51,9 @@ export async function listDatabases(request: FastifyRequest) {
});
return {
databases
}
};
} catch ({ status, message }) {
return errorHandler({ status, message })
return errorHandler({ status, message });
}
}
export async function newDatabase(request: FastifyRequest, reply: FastifyReply) {
@ -46,33 +79,34 @@ export async function newDatabase(request: FastifyRequest, reply: FastifyReply)
settings: { create: { isPublic: false } }
}
});
return reply.code(201).send({ id })
return reply.code(201).send({ id });
} catch ({ status, message }) {
return errorHandler({ status, message })
return errorHandler({ status, message });
}
}
export async function cleanupUnconfiguredDatabases(request: FastifyRequest) {
try {
const teamId = request.user.teamId;
let databases = await prisma.database.findMany({
where: { teams: { some: { id: teamId === "0" ? undefined : teamId } } },
include: { settings: true, destinationDocker: true, teams: true },
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } },
include: { settings: true, destinationDocker: true, teams: true }
});
for (const database of databases) {
if (!database?.version) {
const { id } = database;
if (database.destinationDockerId) {
const everStarted = await stopDatabaseContainer(database);
if (everStarted) await stopTcpHttpProxy(id, database.destinationDocker, database.publicPort);
if (everStarted)
await stopTcpHttpProxy(id, database.destinationDocker, database.publicPort);
}
await prisma.databaseSettings.deleteMany({ where: { databaseId: id } });
await prisma.databaseSecret.deleteMany({ where: { databaseId: id } });
await prisma.database.delete({ where: { id } });
}
}
return {}
return {};
} catch ({ status, message }) {
return errorHandler({ status, message })
return errorHandler({ status, message });
}
}
export async function getDatabaseStatus(request: FastifyRequest<OnlyId>) {
@ -89,7 +123,10 @@ export async function getDatabaseStatus(request: FastifyRequest<OnlyId>) {
const { destinationDockerId, destinationDocker } = database;
if (destinationDockerId) {
try {
const { stdout } = await executeCommand({ dockerId: destinationDocker.id, command: `docker inspect --format '{{json .State}}' ${id}` })
const { stdout } = await executeCommand({
dockerId: destinationDocker.id,
command: `docker inspect --format '{{json .State}}' ${id}`
});
if (JSON.parse(stdout).Running) {
isRunning = true;
@ -101,9 +138,9 @@ export async function getDatabaseStatus(request: FastifyRequest<OnlyId>) {
}
return {
isRunning
}
};
} catch ({ status, message }) {
return errorHandler({ status, message })
return errorHandler({ status, message });
}
}
@ -116,7 +153,7 @@ export async function getDatabase(request: FastifyRequest<OnlyId>) {
include: { destinationDocker: true, settings: true }
});
if (!database) {
throw { status: 404, message: 'Database not found.' }
throw { status: 404, message: 'Database not found.' };
}
const settings = await listSettings();
if (database.dbUserPassword) database.dbUserPassword = decrypt(database.dbUserPassword);
@ -129,19 +166,22 @@ export async function getDatabase(request: FastifyRequest<OnlyId>) {
settings
};
} catch ({ status, message }) {
return errorHandler({ status, message })
return errorHandler({ status, message });
}
}
export async function getDatabaseTypes(request: FastifyRequest) {
try {
return {
types: supportedDatabaseTypesAndVersions
}
};
} catch ({ status, message }) {
return errorHandler({ status, message })
return errorHandler({ status, message });
}
}
export async function saveDatabaseType(request: FastifyRequest<SaveDatabaseType>, reply: FastifyReply) {
export async function saveDatabaseType(
request: FastifyRequest<SaveDatabaseType>,
reply: FastifyReply
) {
try {
const { id } = request.params;
const { type } = request.body;
@ -149,9 +189,9 @@ export async function saveDatabaseType(request: FastifyRequest<SaveDatabaseType>
where: { id },
data: { type }
});
return reply.code(201).send({})
return reply.code(201).send({});
} catch ({ status, message }) {
return errorHandler({ status, message })
return errorHandler({ status, message });
}
}
export async function getVersions(request: FastifyRequest<OnlyId>) {
@ -166,9 +206,9 @@ export async function getVersions(request: FastifyRequest<OnlyId>) {
const versions = getDatabaseVersions(type, arch);
return {
versions
}
};
} catch ({ status, message }) {
return errorHandler({ status, message })
return errorHandler({ status, message });
}
}
export async function saveVersion(request: FastifyRequest<SaveVersion>, reply: FastifyReply) {
@ -179,15 +219,18 @@ export async function saveVersion(request: FastifyRequest<SaveVersion>, reply: F
await prisma.database.update({
where: { id },
data: {
version,
version
}
});
return reply.code(201).send({})
return reply.code(201).send({});
} catch ({ status, message }) {
return errorHandler({ status, message })
return errorHandler({ status, message });
}
}
export async function saveDatabaseDestination(request: FastifyRequest<SaveDatabaseDestination>, reply: FastifyReply) {
export async function saveDatabaseDestination(
request: FastifyRequest<SaveDatabaseDestination>,
reply: FastifyReply
) {
try {
const { id } = request.params;
const { destinationId } = request.body;
@ -208,12 +251,12 @@ export async function saveDatabaseDestination(request: FastifyRequest<SaveDataba
if (destinationDockerId) {
if (type && version) {
const baseImage = getDatabaseImage(type, arch);
executeCommand({ dockerId, command: `docker pull ${baseImage}:${version}` })
executeCommand({ dockerId, command: `docker pull ${baseImage}:${version}` });
}
}
return reply.code(201).send({})
return reply.code(201).send({});
} catch ({ status, message }) {
return errorHandler({ status, message })
return errorHandler({ status, message });
}
}
export async function getDatabaseUsage(request: FastifyRequest<OnlyId>) {
@ -233,9 +276,9 @@ export async function getDatabaseUsage(request: FastifyRequest<OnlyId>) {
}
return {
usage
}
};
} catch ({ status, message }) {
return errorHandler({ status, message })
return errorHandler({ status, message });
}
}
export async function startDatabase(request: FastifyRequest<OnlyId>) {
@ -282,7 +325,7 @@ export async function startDatabase(request: FastifyRequest<OnlyId>) {
volumes: [volume],
ulimits,
labels,
...defaultComposeConfiguration(network),
...defaultComposeConfiguration(network)
}
},
networks: {
@ -292,18 +335,20 @@ export async function startDatabase(request: FastifyRequest<OnlyId>) {
},
volumes: {
[volumeName]: {
name: volumeName,
name: volumeName
}
}
};
const composeFileDestination = `${workdir}/docker-compose.yaml`;
await fs.writeFile(composeFileDestination, yaml.dump(composeFile));
await executeCommand({ dockerId: destinationDocker.id, command: `docker compose -f ${composeFileDestination} up -d` })
await executeCommand({
dockerId: destinationDocker.id,
command: `docker compose -f ${composeFileDestination} up -d`
});
if (isPublic) await startTraefikTCPProxy(destinationDocker, id, publicPort, privatePort);
return {};
} catch ({ status, message }) {
return errorHandler({ status, message })
return errorHandler({ status, message });
}
}
export async function stopDatabase(request: FastifyRequest<OnlyId>) {
@ -326,34 +371,42 @@ export async function stopDatabase(request: FastifyRequest<OnlyId>) {
});
await prisma.database.update({ where: { id }, data: { publicPort: null } });
return {};
} catch ({ status, message }) {
return errorHandler({ status, message })
return errorHandler({ status, message });
}
}
export async function getDatabaseLogs(request: FastifyRequest<GetDatabaseLogs>) {
try {
const { id } = request.params;
let { since = 0 } = request.query
let { since = 0 } = request.query;
if (since !== 0) {
since = day(since).unix();
}
const { destinationDockerId, destinationDocker: { id: dockerId } } = await prisma.database.findUnique({
const {
destinationDockerId,
destinationDocker: { id: dockerId }
} = await prisma.database.findUnique({
where: { id },
include: { destinationDocker: true }
});
if (destinationDockerId) {
try {
// const found = await checkContainer({ dockerId, container: id })
// if (found) {
const { default: ansi } = await import('strip-ansi')
const { stdout, stderr } = await executeCommand({ dockerId, command: `docker logs --since ${since} --tail 5000 --timestamps ${id}` })
const stripLogsStdout = stdout.toString().split('\n').map((l) => ansi(l)).filter((a) => a);
const stripLogsStderr = stderr.toString().split('\n').map((l) => ansi(l)).filter((a) => a);
const logs = stripLogsStderr.concat(stripLogsStdout)
const sortedLogs = logs.sort((a, b) => (day(a.split(' ')[0]).isAfter(day(b.split(' ')[0])) ? 1 : -1))
return { logs: sortedLogs }
// }
const { default: ansi } = await import('strip-ansi');
const { stdout, stderr } = await executeCommand({
dockerId,
command: `docker logs --since ${since} --tail 5000 --timestamps ${id}`
});
const stripLogsStdout = stdout
.toString()
.split('\n')
.map((l) => ansi(l))
.filter((a) => a);
const stripLogsStderr = stderr
.toString()
.split('\n')
.map((l) => ansi(l))
.filter((a) => a);
return { logs: stripLogsStderr.concat(stripLogsStdout) };
} catch (error) {
const { statusCode } = error;
if (statusCode === 404) {
@ -365,9 +418,9 @@ export async function getDatabaseLogs(request: FastifyRequest<GetDatabaseLogs>)
}
return {
message: 'No logs found.'
}
};
} catch ({ status, message }) {
return errorHandler({ status, message })
return errorHandler({ status, message });
}
}
export async function deleteDatabase(request: FastifyRequest<DeleteDatabase>) {
@ -384,15 +437,16 @@ export async function deleteDatabase(request: FastifyRequest<DeleteDatabase>) {
if (database.rootUserPassword) database.rootUserPassword = decrypt(database.rootUserPassword);
if (database.destinationDockerId) {
const everStarted = await stopDatabaseContainer(database);
if (everStarted) await stopTcpHttpProxy(id, database.destinationDocker, database.publicPort);
if (everStarted)
await stopTcpHttpProxy(id, database.destinationDocker, database.publicPort);
}
}
await prisma.databaseSettings.deleteMany({ where: { databaseId: id } });
await prisma.databaseSecret.deleteMany({ where: { databaseId: id } });
await prisma.database.delete({ where: { id } });
return {}
return {};
} catch ({ status, message }) {
return errorHandler({ status, message })
return errorHandler({ status, message });
}
}
export async function saveDatabase(request: FastifyRequest<SaveDatabase>, reply: FastifyReply) {
@ -436,9 +490,9 @@ export async function saveDatabase(request: FastifyRequest<SaveDatabase>, reply:
version
}
});
return reply.code(201).send({})
return reply.code(201).send({});
} catch ({ status, message }) {
return errorHandler({ status, message })
return errorHandler({ status, message });
}
}
export async function saveDatabaseSettings(request: FastifyRequest<SaveDatabaseSettings>) {
@ -447,9 +501,11 @@ export async function saveDatabaseSettings(request: FastifyRequest<SaveDatabaseS
const { id } = request.params;
const { isPublic, appendOnly = true } = request.body;
let publicPort = null
let publicPort = null;
const { destinationDocker: { remoteEngine, engine, remoteIpAddress } } = 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, remoteEngine, engine, remoteIpAddress });
@ -480,14 +536,14 @@ export async function saveDatabaseSettings(request: FastifyRequest<SaveDatabaseS
await stopTcpHttpProxy(id, destinationDocker, oldPublicPort);
}
}
return { publicPort }
return { publicPort };
} catch ({ status, message }) {
return errorHandler({ status, message })
return errorHandler({ status, message });
}
}
export async function getDatabaseSecrets(request: FastifyRequest<OnlyId>) {
try {
const { id } = request.params
const { id } = request.params;
let secrets = await prisma.databaseSecret.findMany({
where: { databaseId: id },
orderBy: { createdAt: 'desc' }
@ -499,21 +555,24 @@ export async function getDatabaseSecrets(request: FastifyRequest<OnlyId>) {
return {
secrets
}
};
} catch ({ status, message }) {
return errorHandler({ status, message })
return errorHandler({ status, message });
}
}
export async function saveDatabaseSecret(request: FastifyRequest<SaveDatabaseSecret>, reply: FastifyReply) {
export async function saveDatabaseSecret(
request: FastifyRequest<SaveDatabaseSecret>,
reply: FastifyReply
) {
try {
const { id } = request.params
let { name, value, isNew } = request.body
const { id } = request.params;
let { name, value, isNew } = request.body;
if (isNew) {
const found = await prisma.databaseSecret.findFirst({ where: { name, databaseId: id } });
if (found) {
throw `Secret ${name} already exists.`
throw `Secret ${name} already exists.`;
} else {
value = encrypt(value.trim());
await prisma.databaseSecret.create({
@ -535,18 +594,18 @@ export async function saveDatabaseSecret(request: FastifyRequest<SaveDatabaseSec
});
}
}
return reply.code(201).send()
return reply.code(201).send();
} catch ({ status, message }) {
return errorHandler({ status, message })
return errorHandler({ status, message });
}
}
export async function deleteDatabaseSecret(request: FastifyRequest<DeleteDatabaseSecret>) {
try {
const { id } = request.params
const { name } = request.body
const { id } = request.params;
const { name } = request.body;
await prisma.databaseSecret.deleteMany({ where: { databaseId: id, name } });
return {}
return {};
} catch ({ status, message }) {
return errorHandler({ status, message })
return errorHandler({ status, message });
}
}

File diff suppressed because it is too large Load Diff

View File

@ -561,12 +561,7 @@ export const applicationsRouter = router({
.split('\n')
.map((l) => ansi(l))
.filter((a) => a);
const logs = stripLogsStderr.concat(stripLogsStdout);
const sortedLogs = logs.sort((a, b) =>
day(a.split(' ')[0]).isAfter(day(b.split(' ')[0])) ? 1 : -1
);
return { logs: sortedLogs };
// }
return { logs: stripLogsStderr.concat(stripLogsStdout) };
} catch (error) {
const { statusCode, stderr } = error;
if (stderr.startsWith('Error: No such container')) {

View File

@ -68,16 +68,11 @@ export const servicesRouter = router({
.split('\n')
.map((l) => ansi(l))
.filter((a) => a);
const logs = stripLogsStderr.concat(stripLogsStdout);
const sortedLogs = logs.sort((a, b) =>
day(a.split(' ')[0]).isAfter(day(b.split(' ')[0])) ? 1 : -1
);
return {
data: {
logs: sortedLogs
logs: stripLogsStderr.concat(stripLogsStdout)
}
};
// }
} catch (error) {
const { statusCode, stderr } = error;
if (stderr.startsWith('Error: No such container')) {
@ -92,7 +87,6 @@ export const servicesRouter = router({
return {
data: {
logs: []
}
};
}