feat: Searxng service
This commit is contained in:
parent
1c720d587c
commit
4f5fe3d383
@ -0,0 +1,13 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Searxng" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"secretKey" TEXT NOT NULL,
|
||||||
|
"redisPassword" TEXT NOT NULL,
|
||||||
|
"serviceId" TEXT NOT NULL,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
CONSTRAINT "Searxng_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "Searxng_serviceId_key" ON "Searxng"("serviceId");
|
@ -327,23 +327,23 @@ model Service {
|
|||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
destinationDocker DestinationDocker? @relation(fields: [destinationDockerId], references: [id])
|
destinationDocker DestinationDocker? @relation(fields: [destinationDockerId], references: [id])
|
||||||
|
|
||||||
fider Fider?
|
|
||||||
ghost Ghost?
|
|
||||||
glitchTip GlitchTip?
|
|
||||||
hasura Hasura?
|
|
||||||
meiliSearch MeiliSearch?
|
|
||||||
minio Minio?
|
|
||||||
moodle Moodle?
|
|
||||||
plausibleAnalytics PlausibleAnalytics?
|
|
||||||
persistentStorage ServicePersistentStorage[]
|
persistentStorage ServicePersistentStorage[]
|
||||||
serviceSecret ServiceSecret[]
|
serviceSecret ServiceSecret[]
|
||||||
umami Umami?
|
teams Team[]
|
||||||
vscodeserver Vscodeserver?
|
|
||||||
wordpress Wordpress?
|
|
||||||
appwrite Appwrite?
|
|
||||||
|
|
||||||
teams Team[]
|
fider Fider?
|
||||||
|
ghost Ghost?
|
||||||
|
glitchTip GlitchTip?
|
||||||
|
hasura Hasura?
|
||||||
|
meiliSearch MeiliSearch?
|
||||||
|
minio Minio?
|
||||||
|
moodle Moodle?
|
||||||
|
plausibleAnalytics PlausibleAnalytics?
|
||||||
|
umami Umami?
|
||||||
|
vscodeserver Vscodeserver?
|
||||||
|
wordpress Wordpress?
|
||||||
|
appwrite Appwrite?
|
||||||
|
searxng Searxng?
|
||||||
}
|
}
|
||||||
|
|
||||||
model PlausibleAnalytics {
|
model PlausibleAnalytics {
|
||||||
@ -545,3 +545,13 @@ model GlitchTip {
|
|||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
service Service @relation(fields: [serviceId], references: [id])
|
service Service @relation(fields: [serviceId], references: [id])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model Searxng {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
secretKey String
|
||||||
|
redisPassword String
|
||||||
|
serviceId String @unique
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
service Service @relation(fields: [serviceId], references: [id])
|
||||||
|
}
|
||||||
|
@ -81,6 +81,7 @@ export const include: any = {
|
|||||||
moodle: true,
|
moodle: true,
|
||||||
appwrite: true,
|
appwrite: true,
|
||||||
glitchTip: true,
|
glitchTip: true,
|
||||||
|
searxng: true
|
||||||
};
|
};
|
||||||
|
|
||||||
export const uniqueName = (): string => uniqueNamesGenerator(customConfig);
|
export const uniqueName = (): string => uniqueNamesGenerator(customConfig);
|
||||||
@ -311,6 +312,17 @@ export const supportedServiceTypesAndVersions = [
|
|||||||
main: 8000
|
main: 8000
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'searxng',
|
||||||
|
fancyName: 'SearXNG',
|
||||||
|
baseImage: 'searxng/searxng',
|
||||||
|
images: [],
|
||||||
|
versions: ['latest'],
|
||||||
|
recommendedVersion: 'latest',
|
||||||
|
ports: {
|
||||||
|
main: 8080
|
||||||
|
}
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export async function checkDoubleBranch(branch: string, projectId: number): Promise<boolean> {
|
export async function checkDoubleBranch(branch: string, projectId: number): Promise<boolean> {
|
||||||
@ -608,7 +620,7 @@ export async function createRemoteEngineConfiguration(id: string) {
|
|||||||
config.append({
|
config.append({
|
||||||
Host: remoteIpAddress,
|
Host: remoteIpAddress,
|
||||||
Hostname: 'localhost',
|
Hostname: 'localhost',
|
||||||
Port: Number(localPort),
|
Port: localPort.toString(),
|
||||||
User: remoteUser,
|
User: remoteUser,
|
||||||
IdentityFile: sshKeyFile,
|
IdentityFile: sshKeyFile,
|
||||||
StrictHostKeyChecking: 'no'
|
StrictHostKeyChecking: 'no'
|
||||||
@ -753,13 +765,18 @@ export async function listSettings(): Promise<any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function generatePassword(length = 24, symbols = false): string {
|
export function generatePassword({ length = 24, symbols = false, isHex = false }: { length?: number, symbols?: boolean, isHex?: boolean } | null): string {
|
||||||
return generator.generate({
|
if (isHex) {
|
||||||
|
return crypto.randomBytes(length).toString("hex");
|
||||||
|
}
|
||||||
|
const password = generator.generate({
|
||||||
length,
|
length,
|
||||||
numbers: true,
|
numbers: true,
|
||||||
strict: true,
|
strict: true,
|
||||||
symbols
|
symbols
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateDatabaseConfiguration(database: any, arch: string):
|
export function generateDatabaseConfiguration(database: any, arch: string):
|
||||||
@ -1418,11 +1435,11 @@ export async function configureServiceType({
|
|||||||
type: string;
|
type: string;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
if (type === 'plausibleanalytics') {
|
if (type === 'plausibleanalytics') {
|
||||||
const password = encrypt(generatePassword());
|
const password = encrypt(generatePassword({}));
|
||||||
const postgresqlUser = cuid();
|
const postgresqlUser = cuid();
|
||||||
const postgresqlPassword = encrypt(generatePassword());
|
const postgresqlPassword = encrypt(generatePassword({}));
|
||||||
const postgresqlDatabase = 'plausibleanalytics';
|
const postgresqlDatabase = 'plausibleanalytics';
|
||||||
const secretKeyBase = encrypt(generatePassword(64));
|
const secretKeyBase = encrypt(generatePassword({ length: 64 }));
|
||||||
|
|
||||||
await prisma.service.update({
|
await prisma.service.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
@ -1446,22 +1463,22 @@ export async function configureServiceType({
|
|||||||
});
|
});
|
||||||
} else if (type === 'minio') {
|
} else if (type === 'minio') {
|
||||||
const rootUser = cuid();
|
const rootUser = cuid();
|
||||||
const rootUserPassword = encrypt(generatePassword());
|
const rootUserPassword = encrypt(generatePassword({}));
|
||||||
await prisma.service.update({
|
await prisma.service.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: { type, minio: { create: { rootUser, rootUserPassword } } }
|
data: { type, minio: { create: { rootUser, rootUserPassword } } }
|
||||||
});
|
});
|
||||||
} else if (type === 'vscodeserver') {
|
} else if (type === 'vscodeserver') {
|
||||||
const password = encrypt(generatePassword());
|
const password = encrypt(generatePassword({}));
|
||||||
await prisma.service.update({
|
await prisma.service.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: { type, vscodeserver: { create: { password } } }
|
data: { type, vscodeserver: { create: { password } } }
|
||||||
});
|
});
|
||||||
} else if (type === 'wordpress') {
|
} else if (type === 'wordpress') {
|
||||||
const mysqlUser = cuid();
|
const mysqlUser = cuid();
|
||||||
const mysqlPassword = encrypt(generatePassword());
|
const mysqlPassword = encrypt(generatePassword({}));
|
||||||
const mysqlRootUser = cuid();
|
const mysqlRootUser = cuid();
|
||||||
const mysqlRootUserPassword = encrypt(generatePassword());
|
const mysqlRootUserPassword = encrypt(generatePassword({}));
|
||||||
await prisma.service.update({
|
await prisma.service.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: {
|
data: {
|
||||||
@ -1499,11 +1516,11 @@ export async function configureServiceType({
|
|||||||
});
|
});
|
||||||
} else if (type === 'ghost') {
|
} else if (type === 'ghost') {
|
||||||
const defaultEmail = `${cuid()}@example.com`;
|
const defaultEmail = `${cuid()}@example.com`;
|
||||||
const defaultPassword = encrypt(generatePassword());
|
const defaultPassword = encrypt(generatePassword({}));
|
||||||
const mariadbUser = cuid();
|
const mariadbUser = cuid();
|
||||||
const mariadbPassword = encrypt(generatePassword());
|
const mariadbPassword = encrypt(generatePassword({}));
|
||||||
const mariadbRootUser = cuid();
|
const mariadbRootUser = cuid();
|
||||||
const mariadbRootUserPassword = encrypt(generatePassword());
|
const mariadbRootUserPassword = encrypt(generatePassword({}));
|
||||||
|
|
||||||
await prisma.service.update({
|
await prisma.service.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
@ -1522,7 +1539,7 @@ export async function configureServiceType({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (type === 'meilisearch') {
|
} else if (type === 'meilisearch') {
|
||||||
const masterKey = encrypt(generatePassword(32));
|
const masterKey = encrypt(generatePassword({ length: 32 }));
|
||||||
await prisma.service.update({
|
await prisma.service.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: {
|
data: {
|
||||||
@ -1531,11 +1548,11 @@ export async function configureServiceType({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (type === 'umami') {
|
} else if (type === 'umami') {
|
||||||
const umamiAdminPassword = encrypt(generatePassword());
|
const umamiAdminPassword = encrypt(generatePassword({}));
|
||||||
const postgresqlUser = cuid();
|
const postgresqlUser = cuid();
|
||||||
const postgresqlPassword = encrypt(generatePassword());
|
const postgresqlPassword = encrypt(generatePassword({}));
|
||||||
const postgresqlDatabase = 'umami';
|
const postgresqlDatabase = 'umami';
|
||||||
const hashSalt = encrypt(generatePassword(64));
|
const hashSalt = encrypt(generatePassword({ length: 64 }));
|
||||||
await prisma.service.update({
|
await prisma.service.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: {
|
data: {
|
||||||
@ -1553,9 +1570,9 @@ export async function configureServiceType({
|
|||||||
});
|
});
|
||||||
} else if (type === 'hasura') {
|
} else if (type === 'hasura') {
|
||||||
const postgresqlUser = cuid();
|
const postgresqlUser = cuid();
|
||||||
const postgresqlPassword = encrypt(generatePassword());
|
const postgresqlPassword = encrypt(generatePassword({}));
|
||||||
const postgresqlDatabase = 'hasura';
|
const postgresqlDatabase = 'hasura';
|
||||||
const graphQLAdminPassword = encrypt(generatePassword());
|
const graphQLAdminPassword = encrypt(generatePassword({}));
|
||||||
await prisma.service.update({
|
await prisma.service.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: {
|
data: {
|
||||||
@ -1572,9 +1589,9 @@ export async function configureServiceType({
|
|||||||
});
|
});
|
||||||
} else if (type === 'fider') {
|
} else if (type === 'fider') {
|
||||||
const postgresqlUser = cuid();
|
const postgresqlUser = cuid();
|
||||||
const postgresqlPassword = encrypt(generatePassword());
|
const postgresqlPassword = encrypt(generatePassword({}));
|
||||||
const postgresqlDatabase = 'fider';
|
const postgresqlDatabase = 'fider';
|
||||||
const jwtSecret = encrypt(generatePassword(64, true));
|
const jwtSecret = encrypt(generatePassword({ length: 64, symbols: true }));
|
||||||
await prisma.service.update({
|
await prisma.service.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: {
|
data: {
|
||||||
@ -1591,13 +1608,13 @@ export async function configureServiceType({
|
|||||||
});
|
});
|
||||||
} else if (type === 'moodle') {
|
} else if (type === 'moodle') {
|
||||||
const defaultUsername = cuid();
|
const defaultUsername = cuid();
|
||||||
const defaultPassword = encrypt(generatePassword());
|
const defaultPassword = encrypt(generatePassword({}));
|
||||||
const defaultEmail = `${cuid()} @example.com`;
|
const defaultEmail = `${cuid()} @example.com`;
|
||||||
const mariadbUser = cuid();
|
const mariadbUser = cuid();
|
||||||
const mariadbPassword = encrypt(generatePassword());
|
const mariadbPassword = encrypt(generatePassword({}));
|
||||||
const mariadbDatabase = 'moodle_db';
|
const mariadbDatabase = 'moodle_db';
|
||||||
const mariadbRootUser = cuid();
|
const mariadbRootUser = cuid();
|
||||||
const mariadbRootUserPassword = encrypt(generatePassword());
|
const mariadbRootUserPassword = encrypt(generatePassword({}));
|
||||||
await prisma.service.update({
|
await prisma.service.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: {
|
data: {
|
||||||
@ -1617,15 +1634,15 @@ export async function configureServiceType({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (type === 'appwrite') {
|
} else if (type === 'appwrite') {
|
||||||
const opensslKeyV1 = encrypt(generatePassword());
|
const opensslKeyV1 = encrypt(generatePassword({}));
|
||||||
const executorSecret = encrypt(generatePassword());
|
const executorSecret = encrypt(generatePassword({}));
|
||||||
const redisPassword = encrypt(generatePassword());
|
const redisPassword = encrypt(generatePassword({}));
|
||||||
const mariadbHost = `${id}-mariadb`
|
const mariadbHost = `${id}-mariadb`
|
||||||
const mariadbUser = cuid();
|
const mariadbUser = cuid();
|
||||||
const mariadbPassword = encrypt(generatePassword());
|
const mariadbPassword = encrypt(generatePassword({}));
|
||||||
const mariadbDatabase = 'appwrite';
|
const mariadbDatabase = 'appwrite';
|
||||||
const mariadbRootUser = cuid();
|
const mariadbRootUser = cuid();
|
||||||
const mariadbRootUserPassword = encrypt(generatePassword());
|
const mariadbRootUserPassword = encrypt(generatePassword({}));
|
||||||
await prisma.service.update({
|
await prisma.service.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: {
|
data: {
|
||||||
@ -1648,11 +1665,11 @@ export async function configureServiceType({
|
|||||||
} else if (type === 'glitchTip') {
|
} else if (type === 'glitchTip') {
|
||||||
const defaultUsername = cuid();
|
const defaultUsername = cuid();
|
||||||
const defaultEmail = `${defaultUsername}@example.com`;
|
const defaultEmail = `${defaultUsername}@example.com`;
|
||||||
const defaultPassword = encrypt(generatePassword());
|
const defaultPassword = encrypt(generatePassword({}));
|
||||||
const postgresqlUser = cuid();
|
const postgresqlUser = cuid();
|
||||||
const postgresqlPassword = encrypt(generatePassword());
|
const postgresqlPassword = encrypt(generatePassword({}));
|
||||||
const postgresqlDatabase = 'glitchTip';
|
const postgresqlDatabase = 'glitchTip';
|
||||||
const secretKeyBase = encrypt(generatePassword(64));
|
const secretKeyBase = encrypt(generatePassword({ length: 64 }));
|
||||||
|
|
||||||
await prisma.service.update({
|
await prisma.service.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
@ -1671,6 +1688,21 @@ export async function configureServiceType({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else if (type === 'searxng') {
|
||||||
|
const secretKey = encrypt(generatePassword({ length: 32, isHex: true }))
|
||||||
|
const redisPassword = encrypt(generatePassword({}));
|
||||||
|
await prisma.service.update({
|
||||||
|
where: { id },
|
||||||
|
data: {
|
||||||
|
type,
|
||||||
|
searxng: {
|
||||||
|
create: {
|
||||||
|
secretKey,
|
||||||
|
redisPassword,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
await prisma.service.update({
|
await prisma.service.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
@ -1696,6 +1728,7 @@ export async function removeService({ id }: { id: string }): Promise<void> {
|
|||||||
await prisma.glitchTip.deleteMany({ where: { serviceId: id } });
|
await prisma.glitchTip.deleteMany({ where: { serviceId: id } });
|
||||||
await prisma.moodle.deleteMany({ where: { serviceId: id } });
|
await prisma.moodle.deleteMany({ where: { serviceId: id } });
|
||||||
await prisma.appwrite.deleteMany({ where: { serviceId: id } });
|
await prisma.appwrite.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.searxng.deleteMany({ where: { serviceId: id } });
|
||||||
await prisma.service.delete({ where: { id } });
|
await prisma.service.delete({ where: { id } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -671,3 +671,20 @@ export const glitchTip = [{
|
|||||||
isBoolean: true,
|
isBoolean: true,
|
||||||
isEncrypted: false
|
isEncrypted: false
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
export const searxng = [{
|
||||||
|
name: 'secretKey',
|
||||||
|
isEditable: false,
|
||||||
|
isLowerCase: false,
|
||||||
|
isNumber: false,
|
||||||
|
isBoolean: false,
|
||||||
|
isEncrypted: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'redisPassword',
|
||||||
|
isEditable: false,
|
||||||
|
isLowerCase: false,
|
||||||
|
isNumber: false,
|
||||||
|
isBoolean: false,
|
||||||
|
isEncrypted: true
|
||||||
|
}]
|
@ -29,9 +29,9 @@ export async function newDatabase(request: FastifyRequest, reply: FastifyReply)
|
|||||||
|
|
||||||
const name = uniqueName();
|
const name = uniqueName();
|
||||||
const dbUser = cuid();
|
const dbUser = cuid();
|
||||||
const dbUserPassword = encrypt(generatePassword());
|
const dbUserPassword = encrypt(generatePassword({}));
|
||||||
const rootUser = cuid();
|
const rootUser = cuid();
|
||||||
const rootUserPassword = encrypt(generatePassword());
|
const rootUserPassword = encrypt(generatePassword({}));
|
||||||
const defaultDatabase = cuid();
|
const defaultDatabase = cuid();
|
||||||
|
|
||||||
const { id } = await prisma.database.create({
|
const { id } = await prisma.database.create({
|
||||||
|
@ -583,6 +583,9 @@ export async function startService(request: FastifyRequest<ServiceStartStop>) {
|
|||||||
if (type === 'glitchTip') {
|
if (type === 'glitchTip') {
|
||||||
return await startGlitchTipService(request)
|
return await startGlitchTipService(request)
|
||||||
}
|
}
|
||||||
|
if (type === 'searxng') {
|
||||||
|
return await startSearXNGService(request)
|
||||||
|
}
|
||||||
throw `Service type ${type} not supported.`
|
throw `Service type ${type} not supported.`
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw { status: 500, message: error?.message || error }
|
throw { status: 500, message: error?.message || error }
|
||||||
@ -591,56 +594,6 @@ export async function startService(request: FastifyRequest<ServiceStartStop>) {
|
|||||||
export async function stopService(request: FastifyRequest<ServiceStartStop>) {
|
export async function stopService(request: FastifyRequest<ServiceStartStop>) {
|
||||||
try {
|
try {
|
||||||
return await stopServiceContainers(request)
|
return await stopServiceContainers(request)
|
||||||
// const { type } = request.params
|
|
||||||
// if (type === 'plausibleanalytics') {
|
|
||||||
// return await stopPlausibleAnalyticsService(request)
|
|
||||||
// }
|
|
||||||
// if (type === 'nocodb') {
|
|
||||||
// return await stopNocodbService(request)
|
|
||||||
// }
|
|
||||||
// if (type === 'minio') {
|
|
||||||
// return await stopMinioService(request)
|
|
||||||
// }
|
|
||||||
// if (type === 'vscodeserver') {
|
|
||||||
// return await stopVscodeService(request)
|
|
||||||
// }
|
|
||||||
// if (type === 'wordpress') {
|
|
||||||
// return await stopWordpressService(request)
|
|
||||||
// }
|
|
||||||
// if (type === 'vaultwarden') {
|
|
||||||
// return await stopVaultwardenService(request)
|
|
||||||
// }
|
|
||||||
// if (type === 'languagetool') {
|
|
||||||
// return await stopLanguageToolService(request)
|
|
||||||
// }
|
|
||||||
// if (type === 'n8n') {
|
|
||||||
// return await stopN8nService(request)
|
|
||||||
// }
|
|
||||||
// if (type === 'uptimekuma') {
|
|
||||||
// return await stopUptimekumaService(request)
|
|
||||||
// }
|
|
||||||
// if (type === 'ghost') {
|
|
||||||
// return await stopGhostService(request)
|
|
||||||
// }
|
|
||||||
// if (type === 'meilisearch') {
|
|
||||||
// return await stopMeilisearchService(request)
|
|
||||||
// }
|
|
||||||
// if (type === 'umami') {
|
|
||||||
// return await stopUmamiService(request)
|
|
||||||
// }
|
|
||||||
// if (type === 'hasura') {
|
|
||||||
// return await stopHasuraService(request)
|
|
||||||
// }
|
|
||||||
// if (type === 'fider') {
|
|
||||||
// return await stopFiderService(request)
|
|
||||||
// }
|
|
||||||
// if (type === 'moodle') {
|
|
||||||
// return await stopMoodleService(request)
|
|
||||||
// }
|
|
||||||
// if (type === 'glitchTip') {
|
|
||||||
// return await stopGlitchTipService(request)
|
|
||||||
// }
|
|
||||||
// throw `Service type ${type} not supported.`
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw { status: 500, message: error?.message || error }
|
throw { status: 500, message: error?.message || error }
|
||||||
}
|
}
|
||||||
@ -2415,6 +2368,7 @@ async function startAppWriteService(request: FastifyRequest<ServiceStartStop>) {
|
|||||||
}
|
}
|
||||||
async function startServiceContainers(dockerId, composeFileDestination) {
|
async function startServiceContainers(dockerId, composeFileDestination) {
|
||||||
await executeDockerCmd({ dockerId, command: `docker compose -f ${composeFileDestination} pull` })
|
await executeDockerCmd({ dockerId, command: `docker compose -f ${composeFileDestination} pull` })
|
||||||
|
await executeDockerCmd({ dockerId, command: `docker compose -f ${composeFileDestination} build --no-cache` })
|
||||||
await executeDockerCmd({ dockerId, command: `docker compose -f ${composeFileDestination} create` })
|
await executeDockerCmd({ dockerId, command: `docker compose -f ${composeFileDestination} create` })
|
||||||
await executeDockerCmd({ dockerId, command: `docker compose -f ${composeFileDestination} start` })
|
await executeDockerCmd({ dockerId, command: `docker compose -f ${composeFileDestination} start` })
|
||||||
await asyncSleep(1000);
|
await asyncSleep(1000);
|
||||||
@ -2662,37 +2616,19 @@ async function startGlitchTipService(request: FastifyRequest<ServiceStartStop>)
|
|||||||
container_name: id,
|
container_name: id,
|
||||||
image: config.glitchTip.image,
|
image: config.glitchTip.image,
|
||||||
environment: config.glitchTip.environmentVariables,
|
environment: config.glitchTip.environmentVariables,
|
||||||
networks: [network],
|
|
||||||
volumes,
|
volumes,
|
||||||
restart: 'always',
|
|
||||||
labels: makeLabelForServices('glitchTip'),
|
labels: makeLabelForServices('glitchTip'),
|
||||||
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||||
deploy: {
|
depends_on: [`${id}-postgresql`, `${id}-redis`],
|
||||||
restart_policy: {
|
...defaultComposeConfiguration(network),
|
||||||
condition: 'on-failure',
|
|
||||||
delay: '5s',
|
|
||||||
max_attempts: 3,
|
|
||||||
window: '120s'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
depends_on: [`${id}-postgresql`, `${id}-redis`]
|
|
||||||
},
|
},
|
||||||
[`${id}-worker`]: {
|
[`${id}-worker`]: {
|
||||||
container_name: `${id}-worker`,
|
container_name: `${id}-worker`,
|
||||||
image: config.glitchTip.image,
|
image: config.glitchTip.image,
|
||||||
command: './bin/run-celery-with-beat.sh',
|
command: './bin/run-celery-with-beat.sh',
|
||||||
environment: config.glitchTip.environmentVariables,
|
environment: config.glitchTip.environmentVariables,
|
||||||
networks: [network],
|
depends_on: [`${id}-postgresql`, `${id}-redis`],
|
||||||
restart: 'always',
|
...defaultComposeConfiguration(network),
|
||||||
deploy: {
|
|
||||||
restart_policy: {
|
|
||||||
condition: 'on-failure',
|
|
||||||
delay: '5s',
|
|
||||||
max_attempts: 3,
|
|
||||||
window: '120s'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
depends_on: [`${id}-postgresql`, `${id}-redis`]
|
|
||||||
},
|
},
|
||||||
[`${id}-setup`]: {
|
[`${id}-setup`]: {
|
||||||
container_name: `${id}-setup`,
|
container_name: `${id}-setup`,
|
||||||
@ -2707,32 +2643,14 @@ async function startGlitchTipService(request: FastifyRequest<ServiceStartStop>)
|
|||||||
image: config.postgresql.image,
|
image: config.postgresql.image,
|
||||||
container_name: `${id}-postgresql`,
|
container_name: `${id}-postgresql`,
|
||||||
environment: config.postgresql.environmentVariables,
|
environment: config.postgresql.environmentVariables,
|
||||||
networks: [network],
|
|
||||||
volumes: [config.postgresql.volume],
|
volumes: [config.postgresql.volume],
|
||||||
restart: 'always',
|
...defaultComposeConfiguration(network),
|
||||||
deploy: {
|
|
||||||
restart_policy: {
|
|
||||||
condition: 'on-failure',
|
|
||||||
delay: '5s',
|
|
||||||
max_attempts: 3,
|
|
||||||
window: '120s'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[`${id}-redis`]: {
|
[`${id}-redis`]: {
|
||||||
image: config.redis.image,
|
image: config.redis.image,
|
||||||
container_name: `${id}-redis`,
|
container_name: `${id}-redis`,
|
||||||
networks: [network],
|
|
||||||
volumes: [config.redis.volume],
|
volumes: [config.redis.volume],
|
||||||
restart: 'always',
|
...defaultComposeConfiguration(network),
|
||||||
deploy: {
|
|
||||||
restart_policy: {
|
|
||||||
condition: 'on-failure',
|
|
||||||
delay: '5s',
|
|
||||||
max_attempts: 3,
|
|
||||||
window: '120s'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
networks: {
|
networks: {
|
||||||
@ -2761,54 +2679,93 @@ async function startGlitchTipService(request: FastifyRequest<ServiceStartStop>)
|
|||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function stopGlitchTipService(request: FastifyRequest<ServiceStartStop>) {
|
|
||||||
|
async function startSearXNGService(request: FastifyRequest<ServiceStartStop>) {
|
||||||
try {
|
try {
|
||||||
const { id } = request.params;
|
const { id } = request.params;
|
||||||
const teamId = request.user.teamId;
|
const teamId = request.user.teamId;
|
||||||
const service = await getServiceFromDB({ id, teamId });
|
const service = await getServiceFromDB({ id, teamId });
|
||||||
const { destinationDockerId, destinationDocker } = service;
|
const { type, version, destinationDockerId, destinationDocker, serviceSecret, exposePort, persistentStorage, fqdn, searxng: { secretKey, redisPassword } } =
|
||||||
if (destinationDockerId) {
|
service;
|
||||||
try {
|
const network = destinationDockerId && destinationDocker.network;
|
||||||
const found = await checkContainer({ dockerId: destinationDocker.id, container: id });
|
const port = getServiceMainPort('searxng');
|
||||||
if (found) {
|
|
||||||
await removeContainer({ id, dockerId: destinationDocker.id });
|
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||||
}
|
const image = getServiceImage(type);
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
const config = {
|
||||||
}
|
searxng: {
|
||||||
try {
|
image: `${image}:${version}`,
|
||||||
const found = await checkContainer({ dockerId: destinationDocker.id, container: `${id}-worker` });
|
volume: `${id}-searxng:/etc/searxng`,
|
||||||
if (found) {
|
environmentVariables: {
|
||||||
await removeContainer({ id: `${id}-worker`, dockerId: destinationDocker.id });
|
SEARXNG_BASE_URL: `${fqdn}`
|
||||||
}
|
},
|
||||||
} catch (error) {
|
},
|
||||||
console.error(error);
|
redis: {
|
||||||
}
|
image: 'redis:7-alpine',
|
||||||
try {
|
|
||||||
const found = await checkContainer({ dockerId: destinationDocker.id, container: `${id}-setup` });
|
|
||||||
if (found) {
|
|
||||||
await removeContainer({ id: `${id}-setup`, dockerId: destinationDocker.id });
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const found = await checkContainer({ dockerId: destinationDocker.id, container: `${id}-postgresql` });
|
|
||||||
if (found) {
|
|
||||||
await removeContainer({ id: `${id}-postgresql`, dockerId: destinationDocker.id });
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const found = await checkContainer({ dockerId: destinationDocker.id, container: `${id}-redis` });
|
|
||||||
if (found) {
|
|
||||||
await removeContainer({ id: `${id}-redis`, dockerId: destinationDocker.id });
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const settingsYml = `
|
||||||
|
# see https://docs.searxng.org/admin/engines/settings.html#use-default-settings
|
||||||
|
use_default_settings: true
|
||||||
|
server:
|
||||||
|
secret_key: ${secretKey}
|
||||||
|
limiter: true
|
||||||
|
image_proxy: true
|
||||||
|
ui:
|
||||||
|
static_use_hash: true
|
||||||
|
redis:
|
||||||
|
url: redis://:${redisPassword}@${id}-redis:6379/0`
|
||||||
|
|
||||||
|
const Dockerfile = `
|
||||||
|
FROM ${config.searxng.image}
|
||||||
|
COPY ./settings.yml /etc/searxng/settings.yml`;
|
||||||
|
|
||||||
|
if (serviceSecret.length > 0) {
|
||||||
|
serviceSecret.forEach((secret) => {
|
||||||
|
config.searxng.environmentVariables[secret.name] = secret.value;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
const { volumes, volumeMounts } = persistentVolumes(id, persistentStorage, config)
|
||||||
|
const composeFile: ComposeFile = {
|
||||||
|
version: '3.8',
|
||||||
|
services: {
|
||||||
|
[id]: {
|
||||||
|
build: workdir,
|
||||||
|
container_name: id,
|
||||||
|
volumes,
|
||||||
|
environment: config.searxng.environmentVariables,
|
||||||
|
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||||
|
labels: makeLabelForServices('searxng'),
|
||||||
|
cap_drop: ['ALL'],
|
||||||
|
cap_add: ['CHOWN', 'SETGID', 'SETUID', 'DAC_OVERRIDE'],
|
||||||
|
depends_on: [`${id}-redis`],
|
||||||
|
...defaultComposeConfiguration(network),
|
||||||
|
},
|
||||||
|
[`${id}-redis`]: {
|
||||||
|
container_name: `${id}-redis`,
|
||||||
|
image: config.redis.image,
|
||||||
|
command: `redis-server --requirepass ${redisPassword} --save "" --appendonly "no"`,
|
||||||
|
labels: makeLabelForServices('searxng'),
|
||||||
|
cap_drop: ['ALL'],
|
||||||
|
cap_add: ['SETGID', 'SETUID', 'DAC_OVERRIDE'],
|
||||||
|
...defaultComposeConfiguration(network),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
networks: {
|
||||||
|
[network]: {
|
||||||
|
external: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
volumes: volumeMounts
|
||||||
|
};
|
||||||
|
const composeFileDestination = `${workdir}/docker-compose.yaml`;
|
||||||
|
await fs.writeFile(composeFileDestination, yaml.dump(composeFile));
|
||||||
|
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile);
|
||||||
|
await fs.writeFile(`${workdir}/settings.yml`, settingsYml);
|
||||||
|
|
||||||
|
await startServiceContainers(destinationDocker.id, composeFileDestination)
|
||||||
return {}
|
return {}
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
@ -2865,7 +2822,7 @@ export async function activateWordpressFtp(request: FastifyRequest<ActivateWordp
|
|||||||
const publicPort = await getFreePublicPort(id, dockerId);
|
const publicPort = await getFreePublicPort(id, dockerId);
|
||||||
|
|
||||||
let ftpUser = cuid();
|
let ftpUser = cuid();
|
||||||
let ftpPassword = generatePassword();
|
let ftpPassword = generatePassword({});
|
||||||
|
|
||||||
const hostkeyDir = isDev ? '/tmp/hostkeys' : '/app/ssl/hostkeys';
|
const hostkeyDir = isDev ? '/tmp/hostkeys' : '/app/ssl/hostkeys';
|
||||||
try {
|
try {
|
||||||
|
@ -181,6 +181,17 @@ export const supportedServiceTypesAndVersions = [
|
|||||||
main: 8000
|
main: 8000
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'searxng',
|
||||||
|
fancyName: 'SearXNG',
|
||||||
|
baseImage: 'searxng/searxng',
|
||||||
|
images: ['redis:6.2-alpine'],
|
||||||
|
versions: ['latest'],
|
||||||
|
recommendedVersion: 'latest',
|
||||||
|
ports: {
|
||||||
|
main: 8080
|
||||||
|
}
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const asyncSleep = (delay: number) =>
|
export const asyncSleep = (delay: number) =>
|
||||||
|
57
apps/ui/src/lib/components/svg/services/Searxng.svelte
Normal file
57
apps/ui/src/lib/components/svg/services/Searxng.svelte
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let isAbsolute = false;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
id="svg8"
|
||||||
|
version="1.1"
|
||||||
|
viewBox="0 0 92 92"
|
||||||
|
class={isAbsolute ? 'w-12 absolute top-0 left-0 -m-3 -mt-5' : 'w-8 mx-auto'}
|
||||||
|
>
|
||||||
|
<defs id="defs2" />
|
||||||
|
<metadata id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g transform="translate(-40.921303,-17.416526)" id="layer1">
|
||||||
|
<circle
|
||||||
|
r="0"
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:12;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
cy="92"
|
||||||
|
cx="75"
|
||||||
|
id="path3713"
|
||||||
|
/>
|
||||||
|
<circle
|
||||||
|
r="30"
|
||||||
|
cy="53.902557"
|
||||||
|
cx="75.921303"
|
||||||
|
id="path834"
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#3050ff;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="m 67.514849,37.91524 a 18,18 0 0 1 21.051475,3.312407 18,18 0 0 1 3.137312,21.078282"
|
||||||
|
id="path852"
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#3050ff;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
/>
|
||||||
|
<rect
|
||||||
|
transform="rotate(-46.234709)"
|
||||||
|
ry="1.8669105e-13"
|
||||||
|
y="122.08995"
|
||||||
|
x="3.7063529"
|
||||||
|
height="39.963303"
|
||||||
|
width="18.846331"
|
||||||
|
id="rect912"
|
||||||
|
style="opacity:1;fill:#3050ff;fill-opacity:1;stroke:none;stroke-width:8;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
@ -38,4 +38,6 @@
|
|||||||
<Icons.Moodle {isAbsolute} />
|
<Icons.Moodle {isAbsolute} />
|
||||||
{:else if type === 'glitchTip'}
|
{:else if type === 'glitchTip'}
|
||||||
<Icons.GlitchTip {isAbsolute} />
|
<Icons.GlitchTip {isAbsolute} />
|
||||||
|
{:else if type === 'searxng'}
|
||||||
|
<Icons.Searxng {isAbsolute} />
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -16,3 +16,4 @@ export { default as Fider } from './Fider.svelte';
|
|||||||
export { default as Appwrite } from './Appwrite.svelte';
|
export { default as Appwrite } from './Appwrite.svelte';
|
||||||
export { default as Moodle } from './Moodle.svelte';
|
export { default as Moodle } from './Moodle.svelte';
|
||||||
export { default as GlitchTip } from './GlitchTip.svelte';
|
export { default as GlitchTip } from './GlitchTip.svelte';
|
||||||
|
export { default as Searxng } from './Searxng.svelte';
|
@ -57,7 +57,7 @@
|
|||||||
</a>
|
</a>
|
||||||
{:else if service.type === 'appwrite'}
|
{:else if service.type === 'appwrite'}
|
||||||
<a href="https://appwrite.io" target="_blank">
|
<a href="https://appwrite.io" target="_blank">
|
||||||
<Icons.Appwrite/>
|
<Icons.Appwrite />
|
||||||
</a>
|
</a>
|
||||||
{:else if service.type === 'moodle'}
|
{:else if service.type === 'moodle'}
|
||||||
<a href="https://moodle.org" target="_blank">
|
<a href="https://moodle.org" target="_blank">
|
||||||
@ -67,4 +67,8 @@
|
|||||||
<a href="https://glitchtip.com" target="_blank">
|
<a href="https://glitchtip.com" target="_blank">
|
||||||
<Icons.GlitchTip />
|
<Icons.GlitchTip />
|
||||||
</a>
|
</a>
|
||||||
|
{:else if service.type === 'searxng'}
|
||||||
|
<a href="https://searxng.org" target="_blank">
|
||||||
|
<Icons.Searxng />
|
||||||
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
|
36
apps/ui/src/routes/services/[id]/_Services/_Searxng.svelte
Normal file
36
apps/ui/src/routes/services/[id]/_Services/_Searxng.svelte
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
||||||
|
import { t } from '$lib/translations';
|
||||||
|
export let service: any;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex space-x-1 py-5 font-bold">
|
||||||
|
<div class="title">SearXNG</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="secretKey">Secret Key</label>
|
||||||
|
<CopyPasswordField
|
||||||
|
name="secretKey"
|
||||||
|
id="secretKey"
|
||||||
|
isPasswordField
|
||||||
|
value={service.searxng.secretKey}
|
||||||
|
readonly
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex space-x-1 py-5 font-bold">
|
||||||
|
<div class="title">Redis</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="redisPassword">{$t('forms.password')}</label>
|
||||||
|
<CopyPasswordField
|
||||||
|
name="redisPassword"
|
||||||
|
id="redisPassword"
|
||||||
|
isPasswordField
|
||||||
|
value={service.searxng.redisPassword}
|
||||||
|
readonly
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</div>
|
@ -29,6 +29,7 @@
|
|||||||
import Wordpress from './_Wordpress.svelte';
|
import Wordpress from './_Wordpress.svelte';
|
||||||
import Appwrite from './_Appwrite.svelte';
|
import Appwrite from './_Appwrite.svelte';
|
||||||
import Moodle from './_Moodle.svelte';
|
import Moodle from './_Moodle.svelte';
|
||||||
|
import Searxng from './_Searxng.svelte';
|
||||||
|
|
||||||
const { id } = $page.params;
|
const { id } = $page.params;
|
||||||
$: isDisabled =
|
$: isDisabled =
|
||||||
@ -402,6 +403,8 @@
|
|||||||
<Moodle bind:service {readOnly} />
|
<Moodle bind:service {readOnly} />
|
||||||
{:else if service.type === 'glitchTip'}
|
{:else if service.type === 'glitchTip'}
|
||||||
<GlitchTip bind:service />
|
<GlitchTip bind:service />
|
||||||
|
{:else if service.type === 'searxng'}
|
||||||
|
<Searxng bind:service />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user