read port from config file

Massimo Melina committed Dec 27, 2021 at 00:01 UTC d46386166fa46aeb88d0eb7dd4087b6fe6596b9e
6 files changed +94 -10
config.yaml new
+1
@@ -0,0 +1 @@
1 +port: 80
src/apis.ts
+1 -1
@@ -107,7 +107,7 @@ export const frontEndApis: ApiHandlers = {
107 async logout({}, ctx) {
108 const sid = ctx.cookies.get(SESSION_COOKIE)
109 if (!sid)
110 - return ctx.status = 404
110 + return ctx.status = 401
111 if (!sessions.destroy(sid))
112 return ctx.status = 500
113 ctx.status = 200
src/config.ts new
+30
@@ -0,0 +1,30 @@
1 +import EventEmitter from 'events'
2 +import { watchLoad } from './misc'
3 +
4 +const PATH = 'config.yaml'
5 +
6 +let state:Record<string,any> = {}
7 +const emitter = new EventEmitter()
8 +watchLoad(PATH, data => {
9 + for (const k in data)
10 + check(k)
11 + for (const k in state)
12 + if (!(k in data))
13 + check(k)
14 +
15 + function check(k: string) {
16 + const oldV = state[k]
17 + const newV = data[k]
18 + if (JSON.stringify(newV) === JSON.stringify(oldV)) return
19 + state[k] = newV
20 + emitter.emit('new.'+k, newV, oldV)
21 + }
22 +})
23 +
24 +export function subscribe(k:string, cb:(v:any,was:any)=>void) {
25 + emitter.on('new.'+k, cb)
26 +}
27 +
28 +export function getConfig(k:string) {
29 + return state[k]
30 +}
src/index.ts
+31 -8
@@ -9,20 +9,20 @@ import { vfs } from './vfs'
9 import { isDirectory } from './misc'
10 import proxy from 'koa-better-http-proxy'
11 import compress from 'koa-compress'
12 -
13 -const PORT = argv.port || 80
12 +import { Server } from 'http'
13 +import { subscribe } from './config'
14
15 const BUILD_TIMESTAMP = ""
16
17 -const srv = new Koa()
18 -srv.use(async (ctx, next) => {
17 +const app = new Koa()
18 +app.use(async (ctx, next) => {
19 // log all requests
20 console.debug(new Date().toLocaleTimeString(), ctx.request.method, ctx.url)
21 await next()
22 })
23
24 // serve apis
25 -srv.use(mount(API_URI, new Koa()
25 +app.use(mount(API_URI, new Koa()
26 .use(bodyParser())
27 .use(apiMw(frontEndApis))
28 .use(compress({
@@ -35,7 +35,7 @@ srv.use(mount(API_URI, new Koa()
35
36 // serve shared files and front-end files
37 const serveFrontendPrefixed = mount(FRONTEND_URI.slice(0,-1), serveFrontend)
38 -srv.use(async (ctx, next) => {
38 +app.use(async (ctx, next) => {
39 const { path } = ctx
40 if (ctx.body)
41 return await next()
@@ -63,9 +63,32 @@ srv.use(async (ctx, next) => {
63 await next()
64 })
65
66 -srv.on('error', err => {
66 +app.on('error', err => {
67 if (DEV && err.code === 'ENOENT' && err.path.endsWith('sockjs-node')) return // spam out
68 if (err.code === 'ECONNRESET') return // someone interrupted, don't care
69 console.error('server error', err)
70 })
71 -srv.listen(PORT, ()=> console.log('running on port', PORT, DEV, new Date().toLocaleString()))
71 +
72 +let srv: Server
73 +if (argv.port)
74 + listenOn(argv.port).then()
75 +else
76 + subscribe('port', port =>
77 + listenOn(port||80))
78 +
79 +async function listenOn(port: number) {
80 + await new Promise(resolve => {
81 + if (!srv)
82 + return resolve(null)
83 + srv.close(err => {
84 + if (err)
85 + console.debug('failed to stop server', err)
86 + resolve(err)
87 + })
88 + })
89 + await new Promise(resolve =>
90 + srv = app.listen(port, () => {
91 + console.log('running on port', port, DEV, new Date().toLocaleString())
92 + resolve(null)
93 + }))
94 +}
src/misc.ts
+31
@@ -1,5 +1,7 @@
1 import fs from 'fs/promises'
2 import { objSameKeys } from './obj'
3 +import { watch } from 'fs'
4 +import yaml from 'yaml'
5
6 export function enforceFinal(sub:string, s:string) {
7 return s.endsWith(sub) ? s : s+sub
@@ -43,3 +45,32 @@ export async function readFileBusy(path: string): Promise<string> {
45 export function wantArray(x:any) {
46 return x == null ? [] : Array.isArray(x) ? x : [x]
47 }
48 +
49 +export function watchLoad(path:string, parser:(data:any)=>void|Promise<void>) {
50 + let doing = false
51 + const timer = setInterval(()=>{
52 + try {
53 + watch(path, load)
54 + load().then()
55 + clearInterval(timer)
56 + }
57 + catch(e){
58 + }
59 + }, 1000)
60 +
61 + async function load(){
62 + if (doing) return
63 + doing = true
64 + console.debug('loading', path)
65 + let data: any
66 + try {
67 + data = yaml.parse(await readFileBusy(path))
68 + } catch (e) {
69 + doing = false
70 + console.warn('cannot read', path, String(e))
71 + return
72 + }
73 + await parser(data)
74 + doing = false
75 + }
76 +}
todo.md
-1
@@ -3,7 +3,6 @@
3 - folders before?
4 - upload
5 - log file
6 -- config: port
6 - config: mime
7 - node.comment
8 - streamable zip archives (no compression, resumable?)