diff --git a/instance/daemon.ts b/instance/daemon.ts index 655d8c9..35d3353 100755 --- a/instance/daemon.ts +++ b/instance/daemon.ts @@ -2,6 +2,7 @@ import { daemon_listen, Runner } from "./src/lib.ts"; import { ContainerConfig } from "./src/lib/config.ts"; +import { NginxController } from "./src/lib/nginx.ts"; import { socket_path, state_path } from "./src/lib/paths.ts"; import { log_from, sleep } from "./src/lib/utils.ts"; @@ -78,9 +79,11 @@ async function main() { class State { enabled; + nginx; constructor() { this.enabled = new Map(); + this.nginx = new NginxController("barnulf.net", "/etc/nginx/sites-enabled"); } static async load() { @@ -104,7 +107,7 @@ class State { const config = await ContainerConfig.load(name); // TODO if (config === null) throw new Error("can't read config"); log("starting container", name); - const runner = new Runner(config); + const runner = new Runner(config, this.nginx); runner.start(); this.enabled.set(name, runner); log("container", name, "started"); diff --git a/instance/src/lib.ts b/instance/src/lib.ts index 44ec2f2..9d9d876 100644 --- a/instance/src/lib.ts +++ b/instance/src/lib.ts @@ -6,6 +6,7 @@ import { ContainerConfig } from "./lib/config.ts"; import { container_paths } from "./lib/paths.ts"; import { container_command } from "./lib/nspawn.ts"; import { LoopProcess } from "./lib/utils.ts"; +import { NginxController } from "./lib/nginx.ts"; const log = log_from("lib"); @@ -70,15 +71,22 @@ export async function daemon_send(sock_path: string, command: Cmd) { export class Runner { name; config; - loop: LoopProcess | null; + nginx; + loop; - constructor(config: ContainerConfig) { + constructor(config: ContainerConfig, nginx: NginxController) { this.name = config.name; this.config = config; - this.loop = null; + this.nginx = nginx; + this.loop = null as LoopProcess | null; } start() { + this.update_proxies(); + this.start_process(); + } + + private start_process() { const paths = container_paths(name); const command = container_command(name, paths.root, { boot: true, @@ -99,4 +107,18 @@ export class Runner { async stop() { await this.loop?.kill(); } + + async update_proxies() { + const paths = container_paths(name); + await Deno.mkdir(paths.sites, { recursive: true }); + const sites = new Set(); + for (const redir of this.config.redirections) { + if (redir.kind !== "http") continue; + await this.nginx.add_proxy(redir.domain, redir.port, paths.sites); + sites.add(redir.domain); + } + for await (const domains of this.nginx.read_all_in_dir(paths.sites)) { + if (!sites.has(domains)) this.nginx.remove_proxy(domains, paths.sites); + } + } } diff --git a/instance/src/lib/nginx.ts b/instance/src/lib/nginx.ts index c2c3044..64c3dc3 100644 --- a/instance/src/lib/nginx.ts +++ b/instance/src/lib/nginx.ts @@ -1,41 +1,50 @@ import { run } from "./utils.ts"; import * as path from "https://deno.land/std@0.214.0/path/mod.ts"; -class NginxController { - proxy_target_url; - enabled_conf_dir; +export class NginxController { + private proxy_target_domain; + private enabled_conf_dir; - constructor(proxy_target_url: string, enabled_conf_dir: string) { - this.proxy_target_url = proxy_target_url; + constructor(proxy_target_domain: string, enabled_conf_dir: string) { + this.proxy_target_domain = proxy_target_domain; this.enabled_conf_dir = enabled_conf_dir; } - public async add_proxy(url: string, port: number, conf_dir: string) { + public async add_proxy(domain: string, port: number, conf_dir: string) { const conf_file_content = ` server { listen 80; listen [::]:80; - server_name ${url}; + server_name ${domain}; location / { - proxy_pass http://${this.proxy_target_url}:${port}; + proxy_pass http://${this.proxy_target_domain}:${port}; } } `; - const conf_file_path = path.join(conf_dir, url + ".conf"); - const enabled_conf_file_path = path.join(this.enabled_conf_dir, url + ".conf"); + const conf_file_path = path.join(conf_dir, domain + ".conf"); + const enabled_conf_file_path = path.join(this.enabled_conf_dir, domain + ".conf"); await Deno.writeTextFile(conf_file_path, conf_file_content); await run("ln", "-s", await Deno.realPath(conf_file_path), enabled_conf_file_path); await this.reload(); + return conf_file_path; } - public async remove_proxy(url: string, conf_dir: string) { - const conf_file_path = path.join(conf_dir, url + ".conf"); - const enabled_conf_file_path = path.join(this.enabled_conf_dir, url + ".conf"); - await Deno.remove(enabled_conf_file_path); - await Deno.remove(conf_file_path); + public async remove_proxy(domain: string, conf_dir: string) { + const conf_file_path = path.join(conf_dir, domain + ".conf"); + const enabled_conf_file_path = path.join(this.enabled_conf_dir, domain + ".conf"); + await Deno.remove(enabled_conf_file_path, { recursive: true }); + await Deno.remove(conf_file_path, { recursive: true }); await this.reload(); } + public async *read_all_in_dir(conf_dir: string) { + for await (const { name } of Deno.readDir(conf_dir)) { + const [domain, rest] = name.split(".conf"); + if (rest !== "") continue; + yield domain; + } + } + private async reload() { await run("systemctl", "restart", "nginx"); } diff --git a/instance/src/lib/paths.ts b/instance/src/lib/paths.ts index 3fa1aa9..dc3d3fc 100644 --- a/instance/src/lib/paths.ts +++ b/instance/src/lib/paths.ts @@ -18,7 +18,8 @@ export function container_paths(name: string) { const base = containers_path() + "/" + name; const configuration = base + "/config.json"; const root = base + "/root"; - return { base, configuration, root }; + const sites = base + "/sites"; + return { base, configuration, root, sites }; } export function state_path() {