Clean up index.ts and implement basic http server

This commit is contained in:
Garrett Mills 2025-01-20 21:50:40 -05:00
parent 36e21fcf7d
commit a0e9061352
7 changed files with 152 additions and 14 deletions

View File

@ -1,7 +1,17 @@
import {ensureDirectoriesExist} from "./src/config.ts"; import {config, ensureDirectoriesExist} from "./src/config.ts";
import {refreshThreadsEntirely} from "./src/threads/refresh.ts"; import {UnitManager} from "./src/bones/Unit.ts";
import {ThreadRefresher} from "./src/units/threads.ts";
import {HttpServer} from "./src/units/http-server.ts";
;(async () => { ;(async () => {
await ensureDirectoriesExist() await ensureDirectoriesExist()
await refreshThreadsEntirely()
const manager = new UnitManager()
await manager.addUnit(new ThreadRefresher)
if ( config.http.enabled ) {
await manager.addUnit(new HttpServer)
}
await manager.run()
})() })()

65
src/bones/Unit.ts Normal file
View File

@ -0,0 +1,65 @@
import type {Maybe} from "./types.ts";
export abstract class Unit {
public abstract up(): Promise<void>
public abstract down(): Promise<void>
}
export class UnitManager {
private state: 'init' | 'starting' | 'running' | 'stopping' | 'stopped' = 'init'
private pendingUnits: Unit[] = []
private runningUnits: Unit[] = []
public async addUnit(unit: Unit): Promise<void> {
this.pendingUnits.push(unit)
}
public getState(): typeof this.state {
return this.state
}
public async run(): Promise<void> {
await this.start()
console.log('Press Ctrl-C to stop.')
await new Promise<void>(res => {
process.on('SIGINT', () => res())
})
await this.stop()
}
public async start(): Promise<void> {
this.state = 'starting'
let unit: Maybe<Unit>
while ( unit = this.pendingUnits.shift() ) {
try {
await unit.up()
} catch (e) {
console.error('Error when starting unit')
console.error(e)
await this.stop()
throw e
}
this.runningUnits.push(unit)
}
this.state = 'running'
}
public async stop(): Promise<void> {
this.state = 'stopping'
let unit: Maybe<Unit>
while ( unit = this.runningUnits.shift() ) {
try {
await unit.down()
} catch (e) {
console.error('Error while stopping:')
console.error(e)
}
}
this.state = 'stopped'
}
}

View File

@ -3,6 +3,7 @@ import {castCommentsConfig, type CommentsConfig} from "./types.ts";
const maybeConfig: any = { const maybeConfig: any = {
mail: { mail: {
refreshIntervalInSeconds: process.env.CHROUS_REFRESH_INTERVAL_IN_SECONDS || 60,
imap: { imap: {
host: process.env.CHORUS_IMAP_HOST, host: process.env.CHORUS_IMAP_HOST,
port: process.env.CHORUS_IMAP_PORT || 993, port: process.env.CHORUS_IMAP_PORT || 993,

View File

@ -1,11 +0,0 @@
import {config} from "../config.ts";
export async function runHttpServer() {
Bun.serve({
hostname: config.http.address,
port: config.http.port,
fetch: async request => {
},
})
}

View File

@ -2,6 +2,7 @@ import {z} from "zod";
const commentsConfigSchema = z.object({ const commentsConfigSchema = z.object({
mail: z.object({ mail: z.object({
refreshIntervalInSeconds: z.number({ coerce: true }),
imap: z.object({ imap: z.object({
host: z.string(), host: z.string(),
port: z.number({ coerce: true }), port: z.number({ coerce: true }),

34
src/units/http-server.ts Normal file
View File

@ -0,0 +1,34 @@
import {Unit} from "../bones/Unit.ts";
import type {Maybe} from "../bones";
import {config} from "../config.ts";
export class HttpServer extends Unit {
private server: Maybe<ReturnType<typeof Bun.serve>>
async up(): Promise<void> {
this.server = Bun.serve({
hostname: config.http.address,
port: config.http.port,
fetch: async request => {
const path = `${config.dirs.data}${new URL(request.url).pathname}`
const file = Bun.file(path)
return new Response(file)
},
error: () => new Response(
JSON.stringify({ success: false, message: 'Not found.' }),
{
status: 404,
headers: {
'Content-Type': 'application/json',
},
}
)
})
console.log(`Listening on ${config.http.address}:${config.http.port}`)
}
async down(): Promise<void> {
console.log('Stopping server...')
await this.server?.stop?.(true)
}
}

38
src/units/threads.ts Normal file
View File

@ -0,0 +1,38 @@
import {Unit} from "../bones/Unit.ts";
import type {Maybe} from "../bones";
import {undefined} from "zod";
import {refreshThreadsEntirely} from "../threads/refresh.ts";
import {config} from "../config.ts";
export class ThreadRefresher extends Unit {
private handle: Maybe<Timer>
private running: boolean = false
async up(): Promise<void> {
this.handle = setInterval(async () => {
if ( this.running ) {
return;
}
this.running = true
try {
console.log('Refreshing threads...')
await refreshThreadsEntirely()
} catch (e) {
this.running = false
throw e
}
console.log('Done.')
this.running = false
}, config.mail.refreshIntervalInSeconds * 1000)
}
async down(): Promise<void> {
if ( this.handle ) {
console.log('Stopping refresh interval...')
clearInterval(this.handle)
console.log('Done.')
}
}
}