mirror of
https://github.com/biersoeckli/QuickStack.git
synced 2026-02-10 21:49:19 -06:00
271 lines
12 KiB
TypeScript
271 lines
12 KiB
TypeScript
'use server'
|
|
|
|
import { getAdminUserSession, getAuthUserSession, saveFormAction, simpleAction, fileUploadAction } from "@/server/utils/action-wrapper.utils";
|
|
import paramService, { ParamService } from "@/server/services/param.service";
|
|
import { QsIngressSettingsModel, qsIngressSettingsZodModel } from "@/shared/model/qs-settings.model";
|
|
import { QsLetsEncryptSettingsModel, qsLetsEncryptSettingsZodModel } from "@/shared/model/qs-letsencrypt-settings.model";
|
|
import quickStackService from "@/server/services/qs.service";
|
|
import { ServerActionResult, SuccessActionResult } from "@/shared/model/server-action-error-return.model";
|
|
import registryService from "@/server/services/registry.service";
|
|
import { RegistryStorageLocationSettingsModel, registryStorageLocationSettingsZodModel } from "@/shared/model/registry-storage-location-settings.model";
|
|
import { SystemBackupLocationSettingsModel, systemBackupLocationSettingsZodModel } from "@/shared/model/system-backup-location-settings.model";
|
|
import { Constants } from "@/shared/utils/constants";
|
|
import { QsPublicIpv4SettingsModel, qsPublicIpv4SettingsZodModel } from "@/shared/model/qs-public-ipv4-settings.model";
|
|
import ipAddressFinderAdapter from "@/server/adapter/ip-adress-finder.adapter";
|
|
import { KubeSizeConverter } from "@/shared/utils/kubernetes-size-converter.utils";
|
|
import buildService from "@/server/services/build.service";
|
|
import standalonePodService from "@/server/services/standalone-services/standalone-pod.service";
|
|
import maintenanceService from "@/server/services/standalone-services/maintenance.service";
|
|
import appLogsService from "@/server/services/standalone-services/app-logs.service";
|
|
import systemBackupService from "@/server/services/standalone-services/system-backup.service";
|
|
import backupService from "@/server/services/standalone-services/backup.service";
|
|
import networkPolicyService from "@/server/services/network-policy.service";
|
|
import traefikService from "@/server/services/traefik.service";
|
|
import { PathUtils } from "@/server/utils/path.utils";
|
|
import { FsUtils } from "@/server/utils/fs.utils";
|
|
import fs from "fs";
|
|
import { z } from "zod";
|
|
import { revalidateTag } from "next/cache";
|
|
import { Tags } from "@/server/utils/cache-tag-generator.utils";
|
|
|
|
|
|
export const updateIngressSettings = async (prevState: any, inputData: QsIngressSettingsModel) =>
|
|
saveFormAction(inputData, qsIngressSettingsZodModel, async (validatedData) => {
|
|
await getAdminUserSession();
|
|
|
|
const url = new URL(validatedData.serverUrl.includes('://') ? validatedData.serverUrl : `https://${validatedData.serverUrl}`);
|
|
|
|
await paramService.save({
|
|
name: ParamService.QS_SERVER_HOSTNAME,
|
|
value: url.hostname
|
|
});
|
|
|
|
await paramService.save({
|
|
name: ParamService.DISABLE_NODEPORT_ACCESS,
|
|
value: validatedData.disableNodePortAccess + ''
|
|
});
|
|
|
|
await quickStackService.createOrUpdateService(!validatedData.disableNodePortAccess);
|
|
await quickStackService.createOrUpdateIngress(validatedData.serverUrl);
|
|
});
|
|
|
|
|
|
export const updatePublicIpv4Settings = async (prevState: any, inputData: QsPublicIpv4SettingsModel) =>
|
|
saveFormAction(inputData, qsPublicIpv4SettingsZodModel, async (validatedData) => {
|
|
await getAdminUserSession();
|
|
|
|
await paramService.save({
|
|
name: ParamService.PUBLIC_IPV4_ADDRESS,
|
|
value: validatedData.publicIpv4
|
|
});
|
|
});
|
|
|
|
|
|
export const updatePublicIpv4SettingsAutomatically = async () =>
|
|
simpleAction(async () => {
|
|
await getAdminUserSession();
|
|
|
|
const publicIpv4 = await ipAddressFinderAdapter.getPublicIpOfServer();
|
|
await paramService.save({
|
|
name: ParamService.PUBLIC_IPV4_ADDRESS,
|
|
value: publicIpv4
|
|
});
|
|
});
|
|
|
|
export const updateLetsEncryptSettings = async (prevState: any, inputData: QsLetsEncryptSettingsModel) =>
|
|
saveFormAction(inputData, qsLetsEncryptSettingsZodModel, async (validatedData) => {
|
|
await getAdminUserSession();
|
|
|
|
await paramService.save({
|
|
name: ParamService.LETS_ENCRYPT_MAIL,
|
|
value: validatedData.letsEncryptMail
|
|
});
|
|
|
|
await quickStackService.createOrUpdateCertIssuer(validatedData.letsEncryptMail);
|
|
});
|
|
|
|
export const getConfiguredHostname: () => Promise<ServerActionResult<unknown, string | undefined>> = async () =>
|
|
simpleAction(async () => {
|
|
await getAdminUserSession();
|
|
|
|
return await paramService.getString(ParamService.QS_SERVER_HOSTNAME);
|
|
});
|
|
|
|
|
|
export const cleanupOldTmpFiles = async () =>
|
|
simpleAction(async () => {
|
|
await getAdminUserSession();
|
|
await maintenanceService.deleteAllTempFiles();
|
|
return new SuccessActionResult(undefined, 'Successfully cleaned up temp files.');
|
|
});
|
|
|
|
export const cleanupOldBuildJobs = async () =>
|
|
simpleAction(async () => {
|
|
await getAdminUserSession();
|
|
await buildService.deleteAllFailedOrSuccededBuilds();
|
|
return new SuccessActionResult(undefined, 'Successfully cleaned up old build jobs.');
|
|
});
|
|
|
|
export const revalidateQuickStackVersionCache = async () =>
|
|
simpleAction(async () => {
|
|
revalidateTag(Tags.quickStackVersionInfo()); // separated because updateFunction restarts backend wich results in error
|
|
});
|
|
|
|
export const updateQuickstack = async () =>
|
|
simpleAction(async () => {
|
|
await getAdminUserSession();
|
|
const useCaranyChannel = await paramService.getBoolean(ParamService.USE_CANARY_CHANNEL, false);
|
|
await quickStackService.updateQuickStack(useCaranyChannel);
|
|
return new SuccessActionResult(undefined, 'QuickStack will be updated, refresh the page in a few seconds.');
|
|
});
|
|
|
|
export const updateRegistry = async () =>
|
|
simpleAction(async () => {
|
|
await getAdminUserSession();
|
|
const registryLocation = await paramService.getString(ParamService.REGISTRY_SOTRAGE_LOCATION, Constants.INTERNAL_REGISTRY_LOCATION);
|
|
await registryService.deployRegistry(registryLocation!, true);
|
|
return new SuccessActionResult(undefined, 'Registry will be updated, this might take a few seconds.');
|
|
});
|
|
|
|
export const deleteAllFailedAndSuccededPods = async () =>
|
|
simpleAction(async () => {
|
|
await getAdminUserSession();
|
|
await standalonePodService.deleteAllFailedAndSuccededPods();
|
|
return new SuccessActionResult(undefined, 'Successfully deleted all failed and succeeded pods.');
|
|
});
|
|
|
|
export const purgeRegistryImages = async () =>
|
|
simpleAction(async () => {
|
|
await getAdminUserSession();
|
|
const deletedSize = await registryService.purgeRegistryImages();
|
|
return new SuccessActionResult(undefined, `Successfully purged ${KubeSizeConverter.convertBytesToReadableSize(deletedSize)} of images.`);
|
|
});
|
|
|
|
export const deleteOldAppLogs = async () =>
|
|
simpleAction(async () => {
|
|
await getAdminUserSession();
|
|
await appLogsService.deleteOldAppLogs();
|
|
return new SuccessActionResult(undefined, `Successfully deletes old app logs.`);
|
|
});
|
|
|
|
export const setCanaryChannel = async (useCanaryChannel: boolean) =>
|
|
simpleAction(async () => {
|
|
await getAdminUserSession();
|
|
await paramService.save({
|
|
name: ParamService.USE_CANARY_CHANNEL,
|
|
value: !!useCanaryChannel ? 'true' : 'false'
|
|
});
|
|
return new SuccessActionResult(undefined, `Turned ${useCanaryChannel ? 'on' : 'off'} the canary channel.`);
|
|
});
|
|
|
|
export const setRegistryStorageLocation = async (prevState: any, inputData: RegistryStorageLocationSettingsModel) =>
|
|
saveFormAction(inputData, registryStorageLocationSettingsZodModel, async (validatedData) => {
|
|
await getAdminUserSession();
|
|
|
|
await registryService.deployRegistry(validatedData.registryStorageLocation, true);
|
|
|
|
await paramService.save({
|
|
name: ParamService.REGISTRY_SOTRAGE_LOCATION,
|
|
value: validatedData.registryStorageLocation
|
|
});
|
|
});
|
|
|
|
export const setSystemBackupLocation = async (prevState: any, inputData: SystemBackupLocationSettingsModel) =>
|
|
saveFormAction(inputData, systemBackupLocationSettingsZodModel, async (validatedData) => {
|
|
await getAdminUserSession();
|
|
|
|
await paramService.save({
|
|
name: ParamService.QS_SYSTEM_BACKUP_LOCATION,
|
|
value: validatedData.systemBackupLocation
|
|
});
|
|
});
|
|
|
|
export const listSystemBackups = async () =>
|
|
simpleAction(async () => {
|
|
await getAdminUserSession();
|
|
|
|
const systemBackupLocationId = await paramService.getString(ParamService.QS_SYSTEM_BACKUP_LOCATION, Constants.QS_SYSTEM_BACKUP_DEACTIVATED);
|
|
|
|
if (systemBackupLocationId === Constants.QS_SYSTEM_BACKUP_DEACTIVATED || !systemBackupLocationId) {
|
|
return new SuccessActionResult([], 'No backup location configured');
|
|
}
|
|
|
|
const backups = await systemBackupService.listSystemBackups(systemBackupLocationId);
|
|
|
|
return new SuccessActionResult(backups, 'Backups loaded');
|
|
}) as Promise<ServerActionResult<any, any[]>>;
|
|
|
|
export const runSystemBackupNow = async () =>
|
|
simpleAction(async () => {
|
|
await getAdminUserSession();
|
|
|
|
const systemBackupLocationId = await paramService.getString(ParamService.QS_SYSTEM_BACKUP_LOCATION, Constants.QS_SYSTEM_BACKUP_DEACTIVATED);
|
|
|
|
if (systemBackupLocationId === Constants.QS_SYSTEM_BACKUP_DEACTIVATED || !systemBackupLocationId) {
|
|
throw new Error('System backup is not configured. Please select an S3 storage target first.');
|
|
}
|
|
|
|
await backupService.runSystemBackup();
|
|
|
|
return new SuccessActionResult(undefined, 'System backup started successfully');
|
|
});
|
|
|
|
export const deleteAllNetworkPolicies = async () =>
|
|
simpleAction(async () => {
|
|
await getAdminUserSession();
|
|
|
|
const deletedCount = await networkPolicyService.deleteAllNetworkPolicies();
|
|
|
|
return new SuccessActionResult(undefined, `Successfully deleted all (${deletedCount}) network policies.`);
|
|
});
|
|
|
|
export const uploadAndRestoreSystemBackup = async (formData: FormData) =>
|
|
fileUploadAction(formData, 'backupFile', async (file: File) => {
|
|
await getAdminUserSession();
|
|
|
|
const backupTempDir = PathUtils.tempBackupDataFolder;
|
|
await FsUtils.createDirIfNotExistsAsync(backupTempDir, true);
|
|
|
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
const uploadPath = `${backupTempDir}/uploaded-backup-${timestamp}.tar.gz`;
|
|
|
|
// Write uploaded file to disk
|
|
const arrayBuffer = await file.arrayBuffer();
|
|
const buffer = Buffer.from(arrayBuffer);
|
|
await fs.promises.writeFile(uploadPath, buffer);
|
|
|
|
try {
|
|
// Restore the backup
|
|
await systemBackupService.restoreSystemBackup(uploadPath);
|
|
|
|
return new SuccessActionResult(undefined, 'System backup restored successfully. Please restart QuickStack for changes to take effect.');
|
|
} finally {
|
|
// Clean up uploaded file
|
|
await FsUtils.deleteFileIfExists(uploadPath);
|
|
}
|
|
}) as Promise<ServerActionResult<any, void>>;
|
|
|
|
export const downloadSystemBackup = async (backupKey: string) =>
|
|
simpleAction(async () => {
|
|
await getAdminUserSession();
|
|
|
|
const systemBackupLocationId = await paramService.getString(ParamService.QS_SYSTEM_BACKUP_LOCATION, Constants.QS_SYSTEM_BACKUP_DEACTIVATED);
|
|
|
|
if (systemBackupLocationId === Constants.QS_SYSTEM_BACKUP_DEACTIVATED || !systemBackupLocationId) {
|
|
throw new Error('System backup is not configured. Please select an S3 storage target first.');
|
|
}
|
|
|
|
const fileName = await systemBackupService.downloadSystemBackup(systemBackupLocationId, backupKey);
|
|
|
|
return new SuccessActionResult(fileName, 'Starting download...');
|
|
}) as Promise<ServerActionResult<any, string>>;
|
|
|
|
export const setTraefikIpPropagation = async (prevState: any, inputData: { enableIpPreservation: boolean }) =>
|
|
saveFormAction(inputData, z.object({ enableIpPreservation: z.boolean() }), async (validatedData) => {
|
|
await getAdminUserSession();
|
|
|
|
await traefikService.applyExternalTrafficPolicy(validatedData.enableIpPreservation);
|
|
|
|
return new SuccessActionResult(undefined, `Traefik externalTrafficPolicy set to ${validatedData.enableIpPreservation ? 'Local' : 'Cluster'}.`);
|
|
});
|