- store records in SQLite via built-in node:sqlite, no native deps - wrap DB reads in await io() so they stay out of the static shell - add CRUD pages backed by Server Actions with per-request data - generate managed config and restart container via Docker socket - add /resolve page to query dnsmasq for A and CNAME records - add Dockerfile and compose setup with shared config volume
53 lines
1.5 KiB
TypeScript
53 lines
1.5 KiB
TypeScript
import { mkdirSync, renameSync, writeFileSync } from "node:fs"
|
|
import path from "node:path"
|
|
import type { DnsRecord } from "./db"
|
|
import { env } from "./env"
|
|
|
|
const HEADER = "# Generated by dnsmasq-admin — 请勿手动编辑,每次「应用并重启」都会覆盖此文件"
|
|
|
|
/**
|
|
* dnsmasq's `address=/domain/ip` already answers for the domain *and* every subdomain, so
|
|
* there is no need for a separate wildcard syntax.
|
|
*/
|
|
export function renderRecord(record: DnsRecord): string {
|
|
return record.type === "A"
|
|
? `address=/${record.domain}/${record.value}`
|
|
: `cname=${record.domain},${record.value}`
|
|
}
|
|
|
|
export function renderConfig(records: DnsRecord[]): string {
|
|
const lines = records
|
|
.filter((record) => record.enabled)
|
|
.map(renderRecord)
|
|
|
|
if (lines.length === 0) {
|
|
return `${HEADER}\n\n# 当前没有启用的记录\n`
|
|
}
|
|
|
|
return `${HEADER}\n\n${lines.join("\n")}\n`
|
|
}
|
|
|
|
/**
|
|
* Regenerate the managed config file for the given records.
|
|
*
|
|
* Written to a sibling temp file first and then renamed, so dnsmasq can never read a
|
|
* half-written file.
|
|
*/
|
|
export async function writeManagedConfig(records: DnsRecord[]): Promise<{
|
|
path: string
|
|
recordCount: number
|
|
}> {
|
|
const dir = path.dirname(env.confPath)
|
|
mkdirSync(dir, { recursive: true })
|
|
|
|
const content = renderConfig(records)
|
|
const tempPath = `${env.confPath}.${process.pid}.tmp`
|
|
writeFileSync(tempPath, content, "utf8")
|
|
renameSync(tempPath, env.confPath)
|
|
|
|
return {
|
|
path: env.confPath,
|
|
recordCount: records.filter((record) => record.enabled).length,
|
|
}
|
|
}
|