mirror of
https://github.com/pockethost/pockethost.git
synced 2025-03-30 15:08:30 +00:00
ioc refactor
This commit is contained in:
parent
46aeaaa1c2
commit
4c43f9edc5
5
.changeset/funny-radios-behave.md
Normal file
5
.changeset/funny-radios-behave.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'pockethost': patch
|
||||
---
|
||||
|
||||
Chore: removed remining plugins code
|
@ -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`), {})
|
||||
|
@ -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,
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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 = {
|
||||
|
@ -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
|
||||
|
@ -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({})
|
||||
|
88
packages/pockethost/src/common/ConsoleLogger.ts
Normal file
88
packages/pockethost/src/common/ConsoleLogger.ts
Normal 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
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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'
|
||||
|
@ -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();
|
||||
|
||||
*/
|
||||
|
@ -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
|
||||
}
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user