mirror of
https://github.com/steelbrain/ffmpeg-over-ip.git
synced 2024-10-12 01:34:56 +03:00
🆕 Setup config loading
This commit is contained in:
3
lib/package.json
Normal file
3
lib/package.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "commonjs"
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
"version": "0.0.0",
|
||||
"description": "TODO",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build:server": "esbuild src/server.ts --platform=node --bundle --outdir=lib",
|
||||
"watch:server": "esbuild src/server.ts --platform=node --bundle --outdir=lib --watch"
|
||||
@@ -14,5 +15,9 @@
|
||||
"@types/node": "20",
|
||||
"esbuild": "^0.20.0",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"strip-json-comments": "^5.0.1",
|
||||
"zod": "^3.22.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,93 @@
|
||||
import { CONFIG_FILE_ENV, CONFIG_FILE_NAMES, Runtime } from './constants'
|
||||
import fs from 'node:fs'
|
||||
import os from 'node:os'
|
||||
import path from 'node:path'
|
||||
import { z } from 'zod'
|
||||
|
||||
export function loadConfig(runtime: Runtime): Config {
|
||||
const configFile = process.env[CONFIG_FILE_ENV] || CONFIG_FILE_NAMES[runtime]
|
||||
const config = require(`./${configFile}`)
|
||||
return config
|
||||
import stripJsonComments from 'strip-json-comments'
|
||||
|
||||
import {
|
||||
CONFIG_FILE_SEARCH_PATHS_CLIENT,
|
||||
CONFIG_FILE_SEARCH_PATHS_SERVER,
|
||||
Runtime,
|
||||
} from './constants.js'
|
||||
import createLogger from './logger.js'
|
||||
|
||||
const configSchemaServer = z
|
||||
.object({
|
||||
log: z.union([z.literal(false), z.string()]),
|
||||
listenAddress: z.string(),
|
||||
listenPort: z.number(),
|
||||
authSecret: z.string().min(15).max(100),
|
||||
})
|
||||
.strict()
|
||||
|
||||
const configSchemaClient = z.object({
|
||||
log: z.union([z.literal(false), z.string()]),
|
||||
connectAddress: z.string(),
|
||||
connectPort: z.number(),
|
||||
authSecret: z.string().min(15).max(100),
|
||||
})
|
||||
|
||||
export async function loadConfig<T extends Runtime.Client | Runtime.Server>(
|
||||
runtime: Runtime
|
||||
): Promise<
|
||||
T extends Runtime.Server ? z.infer<typeof configSchemaServer> : z.infer<typeof configSchemaClient>
|
||||
> {
|
||||
const configFilePaths =
|
||||
runtime === Runtime.Server ? CONFIG_FILE_SEARCH_PATHS_SERVER : CONFIG_FILE_SEARCH_PATHS_CLIENT
|
||||
|
||||
let selectedConfigFilePath: string | null = null
|
||||
for (const [directory, files] of configFilePaths) {
|
||||
let directoryToUse = directory
|
||||
if (directoryToUse.includes('$TMPDIR')) {
|
||||
directoryToUse = directoryToUse.replace('$TMPDIR', os.tmpdir())
|
||||
}
|
||||
for (const file of files) {
|
||||
const filePath = path.join(directory, file)
|
||||
const fileStat = await fs.promises.stat(filePath).catch(() => null)
|
||||
if (fileStat != null) {
|
||||
selectedConfigFilePath = filePath
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedConfigFilePath == null) {
|
||||
throw new Error(
|
||||
'No config file found. Try running with --debug-print-search-paths to print search paths'
|
||||
)
|
||||
}
|
||||
const schemaToUse = runtime === Runtime.Server ? configSchemaServer : configSchemaClient
|
||||
let configContent: z.infer<typeof schemaToUse>
|
||||
|
||||
let configLogger: ReturnType<typeof createLogger> | null = null
|
||||
|
||||
try {
|
||||
const textContents = await fs.promises.readFile(selectedConfigFilePath, 'utf-8')
|
||||
let parsed: unknown
|
||||
try {
|
||||
parsed = JSON.parse(stripJsonComments(textContents))
|
||||
} catch (err) {
|
||||
throw new Error('Malformed JSON in config file')
|
||||
}
|
||||
if (
|
||||
parsed != null &&
|
||||
typeof parsed === 'object' &&
|
||||
'log' in parsed &&
|
||||
typeof parsed.log === 'string'
|
||||
) {
|
||||
configLogger = createLogger(parsed.log)
|
||||
}
|
||||
|
||||
configContent = schemaToUse.parse(parsed)
|
||||
} catch (err) {
|
||||
const message = `Failed to read config file at ${selectedConfigFilePath}: ${err}`
|
||||
configLogger?.error(message)
|
||||
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
return configContent as T extends Runtime.Server
|
||||
? z.infer<typeof configSchemaServer>
|
||||
: z.infer<typeof configSchemaClient>
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ const CONFIG_FILE_NAMES_SERVER_SHORT = ['config.server.json', 'config.server.jso
|
||||
const CONFIG_FILE_NAMES_CLIENT_FULL = ['ffmpeg-over-ip.client.json', 'ffmpeg-over-ip.client.jsonc']
|
||||
const CONFIG_FILE_NAMES_CLIENT_SHORT = ['config.client.json', 'config.client.jsonc']
|
||||
|
||||
const CONFIG_FILE_SEARCH_PATHS = [
|
||||
const CONFIG_FILE_SEARCH_PATHS: [string, string[], string[]][] = [
|
||||
['/etc/ffmpeg-over-ip', CONFIG_FILE_NAMES_SERVER_SHORT, CONFIG_FILE_NAMES_CLIENT_SHORT],
|
||||
['/etc', CONFIG_FILE_NAMES_SERVER_FULL, CONFIG_FILE_NAMES_CLIENT_FULL],
|
||||
[
|
||||
@@ -39,12 +39,10 @@ if (process.argv.length > 1) {
|
||||
])
|
||||
}
|
||||
|
||||
export const CONFIG_FILE_SEARCH_PATHS_SERVER = CONFIG_FILE_SEARCH_PATHS.map(([path, server]) => [
|
||||
path,
|
||||
server,
|
||||
])
|
||||
export const CONFIG_FILE_SEARCH_PATHS_SERVER: [string, string[]][] = CONFIG_FILE_SEARCH_PATHS.map(
|
||||
([path, server]) => [path, server]
|
||||
)
|
||||
|
||||
export const CONFIG_FILE_SEARCH_PATHS_CLIENT = CONFIG_FILE_SEARCH_PATHS.map(([path, , client]) => [
|
||||
path,
|
||||
client,
|
||||
])
|
||||
export const CONFIG_FILE_SEARCH_PATHS_CLIENT: [string, string[]][] = CONFIG_FILE_SEARCH_PATHS.map(
|
||||
([path, , client]) => [path, client]
|
||||
)
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
import { CONFIG_FILE_SEARCH_PATHS_SERVER } from './constants'
|
||||
import { loadConfig } from './config.js'
|
||||
import { CONFIG_FILE_SEARCH_PATHS_SERVER, Runtime } from './constants.js'
|
||||
|
||||
async function main() {
|
||||
if (process.argv.includes('--internal-print-search-paths')) {
|
||||
if (process.argv.includes('--debug-print-search-paths')) {
|
||||
console.log(CONFIG_FILE_SEARCH_PATHS_SERVER)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const config = await loadConfig(Runtime.Server)
|
||||
|
||||
if (process.argv.includes('--debug-print-config')) {
|
||||
console.log(config)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
|
||||
@@ -3,12 +3,19 @@
|
||||
|
||||
"log": "stdout", // type: "stdout" | "stderr" | any string (file path) | false
|
||||
// Other possibilities:
|
||||
"log": "$TMP/ffmpeg-over-ip.server.log",
|
||||
// ^ $TMP is a special variable here, only supported in "log" config where it uses the operating system
|
||||
"log": "$TMPDIR/ffmpeg-over-ip.server.log",
|
||||
// ^ $TMPDIR is a special variable here, only supported in "log" config where it uses the operating system
|
||||
// temp folder
|
||||
"log": false,
|
||||
// ^ This turns off logging completely
|
||||
"log": "stdout",
|
||||
"log": "stderr",
|
||||
"log": "/var/log/messages.log"
|
||||
"log": "/var/log/messages.log",
|
||||
|
||||
"listenAddress": "0.0.0.0", // type: string
|
||||
// You can specify a specific address to listen to, by default, listens on all addresses
|
||||
"listenPort": 5050, // type: number
|
||||
|
||||
"authSecret": "YOUR-CLIENT-PASSWORD-HERE" // type: string
|
||||
// ^ Ideally keep this within reason (It'll be received in an HTTP header) but definitely not less than 15 characters
|
||||
}
|
||||
|
||||
10
yarn.lock
10
yarn.lock
@@ -207,6 +207,11 @@ esbuild@^0.20.0:
|
||||
"@esbuild/win32-ia32" "0.20.0"
|
||||
"@esbuild/win32-x64" "0.20.0"
|
||||
|
||||
strip-json-comments@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-5.0.1.tgz#0d8b7d01b23848ed7dbdf4baaaa31a8250d8cfa0"
|
||||
integrity sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==
|
||||
|
||||
typescript@^5.3.3:
|
||||
version "5.3.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37"
|
||||
@@ -216,3 +221,8 @@ undici-types@~5.26.4:
|
||||
version "5.26.5"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
|
||||
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
|
||||
|
||||
zod@^3.22.4:
|
||||
version "3.22.4"
|
||||
resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.4.tgz#f31c3a9386f61b1f228af56faa9255e845cf3fff"
|
||||
integrity sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==
|
||||
|
||||
Reference in New Issue
Block a user