diff --git a/src/routes/health/+server.ts b/src/routes/health/+server.ts new file mode 100644 index 0000000..bc2ae7e --- /dev/null +++ b/src/routes/health/+server.ts @@ -0,0 +1,50 @@ +import { storagePath } from '$lib'; +import { json } from '@sveltejs/kit'; +import { stat, access, constants } from 'fs/promises'; +import type { RequestHandler } from './$types'; + +type Status = 'OK' | 'ERROR'; +type Result = { status: Status; checks: Checks }; + +type Checks = { [key: string]: CheckResult }; +type CheckResult = true | string; + +const fileExists = async (path: string) => !!(await stat(path).catch(() => false)); +const isDirectory = async (path: string) => + !!(await stat(path) + .then((s) => s.isDirectory()) + .catch(() => false)); +const isWritable = async (path: string) => + await access(path, constants.W_OK) + .then(() => true) + .catch(() => false); + +const checkStoragePath = async (path: string): Promise => { + if (!fileExists(path)) { + return '`STORAGE_PATH` does not exist'; + } else if (!isDirectory(path)) { + return '`STORAGE_PATH` is not a directory'; + } else if (!isWritable(path)) { + return '`STORAGE_PATH` is not writable'; + } + return true; +}; + +export const GET: RequestHandler = async () => { + const storagePathResult = await checkStoragePath(storagePath); + + const checks: Checks = { + storagePath: storagePathResult, + }; + + const healthy = Object.values(checks) + .map((r) => (r === true ? true : false)) + .reduce((prev, next) => prev && next, true); + + const status = healthy ? 'OK' : 'ERROR'; + + const result: Result = { status, checks }; + const httpStatus = healthy ? 200 : 500; + + return json(result, { status: httpStatus, headers: { healthy: healthy.toString() } }); +};