From 1b718dae8625f3c37249e22e16114d3ab30a3845 Mon Sep 17 00:00:00 2001 From: videogame hacker Date: Thu, 16 Nov 2023 04:28:37 +0000 Subject: [PATCH] Rename to 'ngx' --- ngx.ts | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ test.ts | 13 ------ 2 files changed, 129 insertions(+), 13 deletions(-) create mode 100644 ngx.ts delete mode 100644 test.ts diff --git a/ngx.ts b/ngx.ts new file mode 100644 index 0000000..75f6dde --- /dev/null +++ b/ngx.ts @@ -0,0 +1,129 @@ +// #region Implementation +type ConfigNode = ConfigStatement | ConfigBlock | ConfigBreak | ConfigFile; + +class ConfigBuildable { + build(): string { + throw new Error("build(..) not implemented!"); + } + + write(path: string) { + const contents = this.build(); + Deno.writeTextFileSync(path, contents); + } +} + +class ConfigBlock extends ConfigBuildable { + value: string; + children: ConfigNode[]; + + constructor(value: string, children: ConfigNode[]) { + super(); + + this.value = value; + this.children = children; + } + + build(): string { + let output = this.value; + output += " {\n "; + output += this.children + .map((child) => child.build().split("\n").join("\n ")) + .join("\n "); + output += "\n}"; + return output; + } +} + +class ConfigStatement extends ConfigBuildable { + value: string; + constructor(value: string) { + super(); + + this.value = value; + } + + build(): string { + return this.value + ";"; + } +} + +class ConfigBreak extends ConfigBuildable { + build(): string { + return ""; + } +} + +class ConfigFile extends ConfigBuildable { + nodes: ConfigNode[]; + constructor(nodes: ConfigNode[]) { + super(); + + this.nodes = nodes; + } + + build(): string { + return this.nodes.map((n) => n.build()).join("\n"); + } +} + +type LooseConfigNode = ConfigNode | string | LooseConfigNode[]; + +function conform(looseNode: LooseConfigNode): ConfigNode[] { + if (typeof looseNode === "string") { + return [new ConfigStatement(looseNode)]; + } + if (typeof looseNode === "object" && looseNode instanceof Array) { + if (looseNode.length === 0) { + return [new ConfigBreak()]; + } else if (looseNode.length === 1) { + return conform(looseNode[0]); + } else { + return looseNode + .map((n) => conform(n)) + .reduceRight((b, a) => + a.length === 1 ? [...a, ...b] : [...a, new ConfigBreak(), ...b] + ); + } + } + return [looseNode]; +} +// #endregion + +export function ngx(value?: string, children?: LooseConfigNode[]): ConfigNode { + const hasValue = value !== undefined && value !== ""; + const hasChildren = children !== undefined; + + if (!hasValue && !hasChildren) { + return new ConfigBreak(); + } else if (hasValue && !hasChildren) { + return new ConfigStatement(value); + } else if (hasValue && hasChildren) { + return new ConfigBlock(value, conform(children)); + } else if (!hasValue && hasChildren) { + return new ConfigFile(conform(children)); + } + + throw new Error("unreachable"); +} + +export const br = new ConfigBreak(); + +export const listen = (...extras: string[]) => + conform([ + `listen 443 ${["ssl", "http2", ...extras].join(" ")}`, + `listen [::]:443 ${["ssl", "http2", ...extras].join(" ")}`, + ]); + +export const letsEncrypt = ( + domain: string, + liveDir = "/etc/letsencrypt/live" +) => + conform([ + `ssl_certificate ${liveDir}/${domain}/fullchain.pem`, + `ssl_certificate_key ${liveDir}/${domain}/privkey.pem`, + ]); + +export default Object.assign( + (value?: string, children?: LooseConfigNode[]) => ngx(value, children), + { br, listen, letsEncrypt } +); diff --git a/test.ts b/test.ts deleted file mode 100644 index b4cc1d2..0000000 --- a/test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { letsEncrypt, listenv4v6, br, ng } from "./ngineer.ts"; - -const conf = ng("server", [ - ...listenv4v6(), - br, - "server_name mydomain.com", - br, - ...letsEncrypt("mydomain.com"), - br, - ng("location /", ["root /srv/http/mydomain.com"]), -]); - -console.log(conf.build());