ioc refactor

This commit is contained in:
Ben Allfree 2024-10-04 06:03:15 -07:00
parent 46aeaaa1c2
commit 4c43f9edc5
17 changed files with 219 additions and 234 deletions

View File

@ -0,0 +1,5 @@
---
'pockethost': patch
---
Chore: removed remining plugins code

View File

@ -12,7 +12,6 @@ import {
DOCKER_INSTANCE_IMAGE_NAME,
MothershipAdminClientService,
PocketbaseService,
PortService,
instanceService,
proxyService,
realtimeLog,
@ -44,7 +43,6 @@ export async function daemon() {
}),
)
await PortService({})
await PocketbaseService({})
await tryFetch(MOTHERSHIP_URL(`/api/health`), {})

View File

@ -1,7 +1,7 @@
import { Request } from 'express'
import { createProxyMiddleware } from 'http-proxy-middleware'
import vhost from 'vhost'
import { logger } from '../../../../../core/ioc'
import { logger } from '../../../../../common/Logger'
export function createVhostProxyMiddleware(
host: string,

View File

@ -7,6 +7,7 @@ import fs from 'fs'
import http from 'http'
import { createProxyMiddleware } from 'http-proxy-middleware'
import https from 'https'
import { logger } from '../../../../../common/Logger'
import {
APEX_DOMAIN,
APP_NAME,
@ -18,7 +19,6 @@ import {
SSL_CERT,
SSL_KEY,
} from '../../../../../core'
import { logger } from '../../../../../core/ioc'
import { createIpWhitelistMiddleware } from './cidr'
import { createVhostProxyMiddleware } from './createVhostProxyMiddleware'

View File

@ -1,7 +1,7 @@
import { exec } from 'child_process'
import { globSync } from 'glob'
import { DATA_ROOT } from '../../../../core'
import { logger } from '../../../core/ioc'
import { logger } from '../../../common/Logger'
export const compact = async () => {
const { info } = logger()

View File

@ -13,7 +13,6 @@ import {
MOTHERSHIP_SEMVER,
mkContainerHomePath,
} from '../../../../../core'
import { PortService } from '../../../../services'
import { GobotService } from '../../../../services/GobotService'
export type MothershipConfig = {}
@ -44,8 +43,6 @@ export async function mothership(cfg: MothershipConfig) {
const { dbg, error, info, warn } = logger
info(`Starting`)
await PortService({})
/** Launch central database */
info(`Serving`)
const env = {

View File

@ -2,12 +2,7 @@
import { program } from 'commander'
import EventSource from 'eventsource'
import {
LogLevelName,
LoggerService,
PH_PLUGINS,
loadPlugins,
} from '../../core'
import { LogLevelName, LoggerService } from '../../core'
import { version } from '../../package.json'
import { EdgeCommand } from './commands/EdgeCommand'
import { FirewallCommand } from './commands/FirewallCommand'
@ -27,7 +22,6 @@ export type GlobalOptions = {
global.EventSource = EventSource
export const main = async () => {
await loadPlugins(PH_PLUGINS())
program.name('pockethost').description('Multitenant PocketBase hosting')
program

View File

@ -1,11 +1,9 @@
import { LoggerService } from '../common'
import { ioc } from '../common'
import { RegisterEnvSettingsService } from '../constants'
import { ioc } from '../core/ioc'
import { WinstonLoggerService } from '../core/winston'
import { GobotService } from '../services/GobotService'
ioc.register('logger', WinstonLoggerService({}))
ioc('logger', WinstonLoggerService({}))
RegisterEnvSettingsService()
LoggerService({})
GobotService({})

View File

@ -0,0 +1,88 @@
import { LogLevelName, Logger, LoggerConfig, isLevelGte } from './Logger'
const CONSOLE_METHODS = {
[LogLevelName.Trace]: console.trace,
[LogLevelName.Raw]: console.log,
[LogLevelName.Debug]: console.debug,
[LogLevelName.Info]: console.info,
[LogLevelName.Warn]: console.warn,
[LogLevelName.Error]: console.error,
[LogLevelName.Abort]: console.error,
}
export class ConsoleLogger implements Logger {
private config: LoggerConfig
constructor(config: Partial<LoggerConfig> = {}) {
this.config = {
level: LogLevelName.Info,
pfx: [],
...config,
}
}
private log(level: LogLevelName, ...args: any[]) {
if (isLevelGte(level, this.config.level)) {
const prefix =
this.config.pfx.length > 0 ? `[${this.config.pfx.join(':')}] ` : ''
CONSOLE_METHODS[level](`${prefix}${level.toUpperCase()}:`, ...args)
}
}
raw(...args: any[]) {
this.log(LogLevelName.Raw, ...args)
}
trace(...args: any[]) {
this.log(LogLevelName.Trace, ...args)
}
debug(...args: any[]) {
this.log(LogLevelName.Debug, ...args)
}
dbg(...args: any[]) {
this.debug(...args)
}
info(...args: any[]) {
this.log(LogLevelName.Info, ...args)
}
warn(...args: any[]) {
this.log(LogLevelName.Warn, ...args)
}
error(...args: any[]) {
this.log(LogLevelName.Error, ...args)
}
criticalError(...args: any[]) {
this.error('CRITICAL:', ...args)
}
create(name: string, configOverride?: Partial<LoggerConfig>): Logger {
const newConfig = {
...this.config,
pfx: [...this.config.pfx, name],
...configOverride,
}
return new ConsoleLogger(newConfig)
}
child(name: string): Logger {
return this.create(name)
}
breadcrumb(s: object): Logger {
console.log('Breadcrumb:', s)
return this
}
abort(...args: any[]): never {
this.log(LogLevelName.Abort, ...args)
process.exit(1)
}
shutdown() {
// No-op for console logger
}
setLevel(level: LogLevelName) {
this.config.level = level
}
}

View File

@ -1,7 +1,5 @@
/// <require "node">
import { mkSingleton } from '.'
import { logger } from '../core/ioc'
import { ioc } from '.'
import { ConsoleLogger } from './ConsoleLogger'
export type LoggerConfig = {
level: LogLevelName
@ -67,6 +65,13 @@ export type Logger = {
setLevel: (level: LogLevelName) => void
}
export const LoggerService = mkSingleton((config: Partial<LoggerConfig> = {}) =>
logger(),
)
export const logger = () => {
try {
return ioc<Logger>('logger')
} catch (e) {
console.warn('No logger found, using default console logger')
return ioc('logger', new ConsoleLogger({ level: LogLevelName.Debug }))
}
}
export const LoggerService = logger

View File

@ -1,4 +1,5 @@
export * from './CleanupManager'
export * from './ConsoleLogger'
export * from './Logger'
export * from './ResourceAllocator'
export * from './TimerManager'
@ -9,7 +10,6 @@ export * from './mergeConfig'
export * from './mkSingleton'
export * from './newId'
export * from './now'
export * from './plugin'
export * from './pocketbase'
export * from './pocketbase-client-helpers'
export * from './schema'

View File

@ -2,43 +2,17 @@ type ServiceMap = {
[serviceName: string]: unknown
}
export class IoCManager<M extends ServiceMap = {}> {
private services: Map<string, any> = new Map()
const services: ServiceMap = {}
register<K extends keyof M>(key: K, instance: M[K]): void
register<T = unknown>(key: string, instance: T): void
register(key: string, instance: any): void {
if (this.services.has(key)) {
throw new Error(`Service with key '${key}' already registered.`)
export const ioc = <T = unknown>(serviceName: string, register?: T): T => {
if (register) {
if (serviceName in services) {
throw new Error(`Service with key '${serviceName}' already registered.`)
}
this.services.set(key, instance)
services[serviceName] = register
}
// For known services
service<K extends keyof M>(key: K): M[K]
// For unknown services with explicit type
service<T = unknown>(key: string): T
service(key: string): any {
const service = this.services.get(key)
if (!service) {
throw new Error(`No service registered with key '${key}'.`)
}
return service
if (!(serviceName in services)) {
throw new Error(`Service with key '${serviceName}' not found.`)
}
return services[serviceName] as T
}
/*
// Use IoC Manager
const ioc = new IoCManager();
ioc.register("logger", new Logger());
ioc.register("database", new Database());
const loggerService = ioc.service<Logger>("logger");
loggerService.log("This is a log message.");
const databaseService = ioc.service<Database>("database");
databaseService.connect();
*/

View File

@ -1,65 +0,0 @@
import { isString } from '@s-libs/micro-dash'
export type PocketHostAction = string
export type PocketHostFilter = string
export type PocketHostPlugin = (api: PocketHostPluginApi) => Promise<void>
const filters: { [key in PocketHostFilter]: FilterHandler[] } = {}
const actions: {
[key in PocketHostAction]: ActionHandler[]
} = {}
export type FilterHandler = (carry: any) => any
export type ActionHandler = (...args: any[]) => void
export async function registerFilter(
filter: PocketHostFilter,
handler: (carry: any) => any,
) {
if (!(filter in filters)) filters[filter] = []
filters[filter]!.push(handler)
}
export async function registerAction(
action: PocketHostAction,
handler: (...args: any[]) => Promise<void>,
) {
if (!(action in actions)) actions[action] = []
actions[action]!.push(handler)
}
export async function filter<T>(filterName: PocketHostFilter, initialValue: T) {
const filter = filters[filterName]
if (!filter) return initialValue
return filter.reduce(
(carry, handler) => carry.then((carryRes) => handler(carryRes)),
Promise.resolve(initialValue),
)
}
export async function action(actionName: PocketHostAction, ...rest: any[]) {
const action = actions[actionName]
if (!action) return
await Promise.all(action.map((handler) => handler(...rest)))
}
export type PocketHostPluginApi = {
registerAction: typeof registerAction
}
export const loadPlugins = async (plugins: (string | PocketHostPlugin)[]) => {
await Promise.all(
plugins.map(async (pluginPathOrModule) => {
const plugin = await (async () => {
if (!isString(pluginPathOrModule)) return pluginPathOrModule
const plugin = await import(pluginPathOrModule)
return plugin.default
})()
await plugin({ registerAction })
}),
)
return plugins
}

View File

@ -11,6 +11,7 @@ import {
InstanceId,
SettingsHandlerFactory,
SettingsService,
ioc,
} from '../core'
import {
mkBoolean,
@ -19,7 +20,7 @@ import {
mkPath,
mkString,
} from './core/Settings'
import { ioc, settings } from './core/ioc'
import { settings } from './core/ioc'
const __dirname = dirname(fileURLToPath(import.meta.url))
@ -78,8 +79,6 @@ if (_IS_DEV) {
}
export const SETTINGS = {
PH_PLUGINS: mkCsvString([`@pockethost/plugin-console-logger`]),
PH_HOME: mkPath(_PH_HOME, { create: true }),
PH_PROJECT_ROOT: mkPath(PH_PROJECT_ROOT()),
@ -152,7 +151,7 @@ export type SettingsDefinition = {
export const RegisterEnvSettingsService = () => {
const _settings = SettingsService(SETTINGS)
ioc.register('settings', _settings)
ioc('settings', _settings)
if (DEBUG()) {
logConstants()
@ -162,8 +161,6 @@ export const RegisterEnvSettingsService = () => {
}
/** Accessors */
export const PH_PLUGINS = () => settings().PH_PLUGINS
export const PH_HOME = (...paths: string[]) =>
join(settings().PH_HOME, ...paths)
@ -280,7 +277,6 @@ export const mkInstanceDataPath = (instanceId: string, ...path: string[]) =>
export const logConstants = () => {
const vars = {
DEBUG,
PH_PLUGINS,
PH_HOME,
PH_PROJECT_ROOT,
HTTP_PROTOCOL,

View File

@ -1,6 +1,5 @@
import { LogEntry } from 'winston'
import { InstanceFields, InstanceId, Logger, UserFields } from '../common'
import { IoCManager } from '../common/ioc'
import { InstanceFields, InstanceId, ioc, UserFields } from '../common'
import { Settings } from '../constants'
export type MothershipProvider = {
@ -22,14 +21,6 @@ export type InstanceLogProvider = (
tail(linesBack: number, data: (line: LogEntry) => void): UnsubFunc
}
export const ioc = new IoCManager<{
settings: Settings
mothership: MothershipProvider
instanceLogger: InstanceLogProvider
logger: Logger
}>()
export const settings = () => ioc.service('settings')
export const mothership = () => ioc.service('mothership')
export const instanceLogger = () => ioc.service('instanceLogger')
export const logger = () => ioc.service('logger')
export const settings = () => ioc('settings') as Settings
export const mothership = () => ioc('mothership') as MothershipProvider
export const instanceLogger = () => ioc('instanceLogger') as InstanceLogProvider

View File

@ -1008,6 +1008,84 @@ migrate(
deleteRule: null,
options: {},
},
{
id: '4kshuv7r3jdrst4',
name: 'verified_users',
type: 'view',
system: false,
schema: [
{
system: false,
id: 'p84zuh5s',
name: 'username',
type: 'text',
required: false,
presentable: false,
unique: false,
options: {
min: null,
max: null,
pattern: '',
},
},
{
system: false,
id: '9cvv0zph',
name: 'email',
type: 'email',
required: false,
presentable: false,
unique: false,
options: {
exceptDomains: null,
onlyDomains: null,
},
},
{
system: false,
id: 's1xad73d',
name: 'subscription',
type: 'select',
required: true,
presentable: false,
unique: false,
options: {
maxSelect: 1,
values: ['free', 'premium', 'lifetime', 'legacy'],
},
},
{
system: false,
id: 'jbcgvipx',
name: 'isLegacy',
type: 'bool',
required: false,
presentable: false,
unique: false,
options: {},
},
{
system: false,
id: 'fhu3o5ub',
name: 'isFounder',
type: 'bool',
required: false,
presentable: false,
unique: false,
options: {},
},
],
indexes: [],
listRule: null,
viewRule: null,
createRule: null,
updateRule: null,
deleteRule: null,
options: {
query:
'select id, username, email, subscription, isLegacy, isFounder,created,updated from users where verified = 1',
},
},
{
id: '18rfmj8aklx6bjq',
name: 'sent_messages',
@ -1818,84 +1896,6 @@ migrate(
"SELECT \n (ROW_NUMBER() OVER()) as id,\n strftime('%Y-%m', created) AS t, \n COUNT(*) AS c \nFROM \n verified_users as users\nGROUP BY \n t\n\torder by t desc\n",
},
},
{
id: '4kshuv7r3jdrst4',
name: 'verified_users',
type: 'view',
system: false,
schema: [
{
system: false,
id: 'p84zuh5s',
name: 'username',
type: 'text',
required: false,
presentable: false,
unique: false,
options: {
min: null,
max: null,
pattern: '',
},
},
{
system: false,
id: '9cvv0zph',
name: 'email',
type: 'email',
required: false,
presentable: false,
unique: false,
options: {
exceptDomains: null,
onlyDomains: null,
},
},
{
system: false,
id: 's1xad73d',
name: 'subscription',
type: 'select',
required: true,
presentable: false,
unique: false,
options: {
maxSelect: 1,
values: ['free', 'premium', 'lifetime', 'legacy'],
},
},
{
system: false,
id: 'jbcgvipx',
name: 'isLegacy',
type: 'bool',
required: false,
presentable: false,
unique: false,
options: {},
},
{
system: false,
id: 'fhu3o5ub',
name: 'isFounder',
type: 'bool',
required: false,
presentable: false,
unique: false,
options: {},
},
],
indexes: [],
listRule: null,
viewRule: null,
createRule: null,
updateRule: null,
deleteRule: null,
options: {
query:
'select id, username, email, subscription, isLegacy, isFounder,created,updated from users where verified = 1',
},
},
]
const collections = snapshot.map((item) => new Collection(item))

View File

@ -1,12 +1,16 @@
import getPort from 'get-port'
import { INITIAL_PORT_POOL_SIZE, mergeConfig, mkSingleton } from '../../core'
import { ResourceAllocator } from '../common'
import { INITIAL_PORT_POOL_SIZE } from '../../core'
import { Allocator, ResourceAllocator, ioc } from '../common'
export type Config = { maxPorts: number }
export const PortService = mkSingleton((config: Partial<Config> = {}) => {
const { maxPorts } = mergeConfig(
{ maxPorts: INITIAL_PORT_POOL_SIZE() },
config,
)
return ResourceAllocator(maxPorts, getPort)
})
export const PortService = () => {
try {
return ioc<Allocator<number>>('portAllocator')
} catch (e) {
return ioc(
'portAllocator',
ResourceAllocator(INITIAL_PORT_POOL_SIZE(), getPort),
)
}
}