diff --git a/src/config/load.ts b/src/config/load.ts index 13ea237e2..3a82d45b4 100644 --- a/src/config/load.ts +++ b/src/config/load.ts @@ -4,10 +4,9 @@ import * as fs from 'fs'; import { URL } from 'url'; -import $ from 'cafy'; import * as yaml from 'js-yaml'; +import { Source, Mixin } from './types'; import * as pkg from '../../package.json'; -import { fromNullable } from '../prelude/maybe'; /** * Path of configuration directory @@ -22,148 +21,31 @@ const path = process.env.NODE_ENV == 'test' : `${dir}/default.yml`; export default function load() { - const config = yaml.safeLoad(fs.readFileSync(path, 'utf-8')); + const config = yaml.safeLoad(fs.readFileSync(path, 'utf-8')) as Source; - if (typeof config.url !== 'string') { - throw 'You need to configure the URL.'; - } + const mixin = {} as Mixin; const url = validateUrl(config.url); - if (typeof config.port !== 'number') { - throw 'You need to configure the port.'; - } + config.url = normalizeUrl(config.url); - if (config.https != null) { - if (typeof config.https.key !== 'string') { - throw 'You need to configure the https key.'; - } - if (typeof config.https.cert !== 'string') { - throw 'You need to configure the https cert.'; - } - } + mixin.host = url.host; + mixin.hostname = url.hostname; + mixin.scheme = url.protocol.replace(/:$/, ''); + mixin.ws_scheme = mixin.scheme.replace('http', 'ws'); + mixin.ws_url = `${mixin.ws_scheme}://${mixin.host}`; + mixin.api_url = `${mixin.scheme}://${mixin.host}/api`; + mixin.auth_url = `${mixin.scheme}://${mixin.host}/auth`; + mixin.dev_url = `${mixin.scheme}://${mixin.host}/dev`; + mixin.docs_url = `${mixin.scheme}://${mixin.host}/docs`; + mixin.stats_url = `${mixin.scheme}://${mixin.host}/stats`; + mixin.status_url = `${mixin.scheme}://${mixin.host}/status`; + mixin.drive_url = `${mixin.scheme}://${mixin.host}/files`; + mixin.user_agent = `Misskey/${pkg.version} (${config.url})`; - if (config.mongodb == null) { - throw 'You need to configure the MongoDB.'; - } + if (config.autoAdmin == null) config.autoAdmin = false; - if (typeof config.mongodb.host !== 'string') { - throw 'You need to configure the MongoDB host.'; - } - - if (typeof config.mongodb.port !== 'number') { - throw 'You need to configure the MongoDB port.'; - } - - if (typeof config.mongodb.db !== 'string') { - throw 'You need to configure the MongoDB database name.'; - } - - if (config.drive == null) { - throw 'You need to configure the drive.'; - } - - if (typeof config.drive.storage !== 'string') { - throw 'You need to configure the drive storage type.'; - } - - if (!$.str.or(['db', 'minio']).ok(config.drive.storage)) { - throw 'Unrecognized drive storage type is specified.'; - } - - if (config.drive.storage === 'minio') { - if (typeof config.drive.bucket !== 'string') { - throw 'You need to configure the minio bucket.'; - } - - if (typeof config.drive.prefix !== 'string') { - throw 'You need to configure the minio prefix.'; - } - - if (config.drive.prefix.config == null) { - throw 'You need to configure the minio.'; - } - } - - if (config.redis != null) { - if (typeof config.redis.host !== 'string') { - throw 'You need to configure the Redis host.'; - } - - if (typeof config.redis.port !== 'number') { - throw 'You need to configure the Redis port.'; - } - } - - if (config.elasticsearch != null) { - if (typeof config.elasticsearch.host !== 'string') { - throw 'You need to configure the Elasticsearch host.'; - } - - if (typeof config.elasticsearch.port !== 'number') { - throw 'You need to configure the Elasticsearch port.'; - } - } - - const source = { - url: normalizeUrl(config.url as string), - port: config.port as number, - https: fromNullable(config.https).map(x => ({ - key: x.key as string, - cert: x.cert as string, - ca: fromNullable(x.ca) - })), - mongodb: { - host: config.mongodb.host as string, - port: config.mongodb.port as number, - db: config.mongodb.db as string, - user: fromNullable(config.mongodb.user), - pass: fromNullable(config.mongodb.pass) - }, - redis: fromNullable(config.redis).map(x => ({ - host: x.host as string, - port: x.port as number, - pass: fromNullable(x.pass) - })), - elasticsearch: fromNullable(config.elasticsearch).map(x => ({ - host: x.host as string, - port: x.port as number, - pass: fromNullable(x.pass) - })), - disableHsts: typeof config.disableHsts === 'boolean' ? config.disableHsts as boolean : false, - drive: { - storage: config.drive.storage as string, - bucket: config.drive.bucket as string, - prefix: config.drive.prefix as string, - baseUrl: fromNullable(config.drive.baseUrl), - config: config.drive.config - }, - autoAdmin: typeof config.autoAdmin === 'boolean' ? config.autoAdmin as boolean : false, - proxy: fromNullable(config.proxy), - clusterLimit: typeof config.clusterLimit === 'number' ? config.clusterLimit as number : Infinity, - }; - - const host = url.host; - const scheme = url.protocol.replace(/:$/, ''); - const ws_scheme = scheme.replace('http', 'ws'); - - const mixin = { - host: url.host, - hostname: url.hostname, - scheme: scheme, - ws_scheme: ws_scheme, - ws_url: `${ws_scheme}://${host}`, - api_url: `${scheme}://${host}/api`, - auth_url: `${scheme}://${host}/auth`, - dev_url: `${scheme}://${host}/dev`, - docs_url: `${scheme}://${host}/docs`, - stats_url: `${scheme}://${host}/stats`, - status_url: `${scheme}://${host}/status`, - drive_url: `${scheme}://${host}/files`, - user_agent: `Misskey/${pkg.version} (${config.url})` - }; - - return Object.assign(source, mixin); + return Object.assign(config, mixin); } function tryCreateUrl(url: string) { diff --git a/src/config/types.ts b/src/config/types.ts index b133e64c2..2ce9c0c80 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -1,3 +1,64 @@ -import load from "./load"; +/** + * ユーザーが設定する必要のある情報 + */ +export type Source = { + repository_url?: string; + feedback_url?: string; + url: string; + port: number; + https?: { [x: string]: string }; + disableHsts?: boolean; + mongodb: { + host: string; + port: number; + db: string; + user: string; + pass: string; + }; + redis: { + host: string; + port: number; + pass: string; + }; + elasticsearch: { + host: string; + port: number; + pass: string; + }; + drive?: { + storage: string; + bucket?: string; + prefix?: string; + baseUrl?: string; + config?: any; + }; -export type Config = ReturnType; + autoAdmin?: boolean; + + proxy?: string; + + accesslog?: string; + + clusterLimit?: number; +}; + +/** + * Misskeyが自動的に(ユーザーが設定した情報から推論して)設定する情報 + */ +export type Mixin = { + host: string; + hostname: string; + scheme: string; + ws_scheme: string; + api_url: string; + ws_url: string; + auth_url: string; + docs_url: string; + stats_url: string; + status_url: string; + dev_url: string; + drive_url: string; + user_agent: string; +}; + +export type Config = Source & Mixin; diff --git a/src/db/elasticsearch.ts b/src/db/elasticsearch.ts index 68ad736b2..cbe6afbbb 100644 --- a/src/db/elasticsearch.ts +++ b/src/db/elasticsearch.ts @@ -42,10 +42,9 @@ const index = { }; // Init ElasticSearch connection - -const client = config.elasticsearch.map(({ host, port }) => { - return new elasticsearch.Client({ host: `${host}:${port}` }); -}).getOrElse(null); +const client = config.elasticsearch ? new elasticsearch.Client({ + host: `${config.elasticsearch.host}:${config.elasticsearch.port}` +}) : null; if (client) { // Send a HEAD request diff --git a/src/db/mongodb.ts b/src/db/mongodb.ts index 3e7d40fde..dedb289ce 100644 --- a/src/db/mongodb.ts +++ b/src/db/mongodb.ts @@ -1,7 +1,7 @@ import config from '../config'; -const u = config.mongodb.user.map(x => encodeURIComponent(x)).getOrElse(null); -const p = config.mongodb.pass.map(x => encodeURIComponent(x)).getOrElse(null); +const u = config.mongodb.user ? encodeURIComponent(config.mongodb.user) : null; +const p = config.mongodb.pass ? encodeURIComponent(config.mongodb.pass) : null; const uri = `mongodb://${u && p ? `${u}:${p}@` : ''}${config.mongodb.host}:${config.mongodb.port}/${config.mongodb.db}`; diff --git a/src/db/redis.ts b/src/db/redis.ts index 4193ac7e7..48e3f4e43 100644 --- a/src/db/redis.ts +++ b/src/db/redis.ts @@ -1,8 +1,10 @@ import * as redis from 'redis'; import config from '../config'; -export default config.redis.map(({ host, port, pass }) => { - return redis.createClient(port, host, { - auth_pass: pass.getOrElse(null) - }); -}).getOrElse(null); +export default config.redis ? redis.createClient( + config.redis.port, + config.redis.host, + { + auth_pass: config.redis.pass + } +) : null; diff --git a/src/index.ts b/src/index.ts index 13f2d0b7d..6983ec722 100644 --- a/src/index.ts +++ b/src/index.ts @@ -228,7 +228,7 @@ async function init(): Promise { return config; } -async function spawnWorkers(limit: number) { +async function spawnWorkers(limit: number = Infinity) { const workers = Math.min(limit, os.cpus().length); bootLogger.info(`Starting ${workers} worker${workers === 1 ? '' : 's'}...`); await Promise.all([...Array(workers)].map(spawnWorker)); diff --git a/src/prelude/maybe.ts b/src/prelude/maybe.ts index 857fc8019..f9ac95c0b 100644 --- a/src/prelude/maybe.ts +++ b/src/prelude/maybe.ts @@ -1,7 +1,5 @@ export interface Maybe { isJust(): this is Just; - map(f: (x: T) => S): Maybe; - getOrElse(x: T): T; } export type Just = Maybe & { @@ -11,8 +9,6 @@ export type Just = Maybe & { export function just(value: T): Just { return { isJust: () => true, - getOrElse: (_: T) => value, - map: (f: (x: T) => S) => just(f(value)), get: () => value }; } @@ -20,11 +16,5 @@ export function just(value: T): Just { export function nothing(): Maybe { return { isJust: () => false, - getOrElse: (value: T) => value, - map: (_: (x: T) => S) => nothing() }; } - -export function fromNullable(value: T): Maybe { - return value == null ? nothing() : just(value); -} diff --git a/src/queue/index.ts b/src/queue/index.ts index 28768bf38..5d3baa824 100644 --- a/src/queue/index.ts +++ b/src/queue/index.ts @@ -8,17 +8,17 @@ import handler from './processors'; import { queueLogger } from './logger'; const enableQueue = !program.disableQueue; -const queueAvailable = config.redis.isJust(); +const queueAvailable = config.redis != null; const queue = initializeQueue(); function initializeQueue() { - return config.redis.map(({ port, host, pass }) => { + if (queueAvailable) { return new Queue('misskey', { redis: { - port: port, - host: host, - password: pass.getOrElse(null) + port: config.redis.port, + host: config.redis.host, + password: config.redis.pass }, removeOnSuccess: true, @@ -27,7 +27,9 @@ function initializeQueue() { sendEvents: false, storeJobs: false }); - }).getOrElse(null); + } else { + return null; + } } export function deliver(user: ILocalUser, content: any, to: any) { diff --git a/src/remote/activitypub/resolver.ts b/src/remote/activitypub/resolver.ts index cae37cacd..049e645e4 100644 --- a/src/remote/activitypub/resolver.ts +++ b/src/remote/activitypub/resolver.ts @@ -57,7 +57,7 @@ export default class Resolver { const object = await request({ url: value, - proxy: config.proxy.getOrElse(null), + proxy: config.proxy, timeout: this.timeout, headers: { 'User-Agent': config.user_agent, diff --git a/src/server/api/endpoints/meta.ts b/src/server/api/endpoints/meta.ts index d08d0b073..91cb095c9 100644 --- a/src/server/api/endpoints/meta.ts +++ b/src/server/api/endpoints/meta.ts @@ -46,7 +46,7 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { description: instance.description, langs: instance.langs, - secure: config.https.isJust(), + secure: config.https != null, machine: os.hostname(), os: os.platform(), node: process.version, @@ -83,9 +83,9 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { registration: !instance.disableRegistration, localTimeLine: !instance.disableLocalTimeline, globalTimeLine: !instance.disableGlobalTimeline, - elasticsearch: config.elasticsearch.isJust(), + elasticsearch: config.elasticsearch ? true : false, recaptcha: instance.enableRecaptcha, - objectStorage: config.drive.storage === 'minio', + objectStorage: config.drive && config.drive.storage === 'minio', twitter: instance.enableTwitterIntegration, github: instance.enableGithubIntegration, discord: instance.enableDiscordIntegration, diff --git a/src/server/api/endpoints/users/recommendation.ts b/src/server/api/endpoints/users/recommendation.ts index d07c4b08f..e3a03888b 100644 --- a/src/server/api/endpoints/users/recommendation.ts +++ b/src/server/api/endpoints/users/recommendation.ts @@ -50,7 +50,7 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { request({ url: url, - proxy: config.proxy.getOrElse(null), + proxy: config.proxy, timeout: timeout, json: true, followRedirect: true, diff --git a/src/server/api/streaming.ts b/src/server/api/streaming.ts index d70cec03d..f8f3c0ff4 100644 --- a/src/server/api/streaming.ts +++ b/src/server/api/streaming.ts @@ -23,10 +23,10 @@ module.exports = (server: http.Server) => { let ev: EventEmitter; - if (config.redis.isJust()) { + if (config.redis) { // Connect to Redis const subscriber = redis.createClient( - config.redis.get().port, config.redis.get().host); + config.redis.port, config.redis.host); subscriber.subscribe('misskey'); diff --git a/src/server/index.ts b/src/server/index.ts index df252a357..0e1c70105 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -96,14 +96,13 @@ app.use(router.routes()); app.use(mount(require('./web'))); function createServer() { - if (config.https.isJust()) { - const opts = { - key: fs.readFileSync(config.https.get().key), - cert: fs.readFileSync(config.https.get().cert), - ...config.https.get().ca.map(path => ({ ca: fs.readFileSync(path) })).getOrElse({}), - allowHTTP1: true - }; - return http2.createSecureServer(opts, app.callback()) as https.Server; + if (config.https) { + const certs: any = {}; + for (const k of Object.keys(config.https)) { + certs[k] = fs.readFileSync(config.https[k]); + } + certs['allowHTTP1'] = true; + return http2.createSecureServer(certs, app.callback()) as https.Server; } else { return http.createServer(app.callback()); } diff --git a/src/server/proxy/proxy-media.ts b/src/server/proxy/proxy-media.ts index 95491b614..3f234a727 100644 --- a/src/server/proxy/proxy-media.ts +++ b/src/server/proxy/proxy-media.ts @@ -69,7 +69,7 @@ async function fetch(url: string, path: string) { const req = request({ url: requestUrl, - proxy: config.proxy.getOrElse(null), + proxy: config.proxy, timeout: 10 * 1000, headers: { 'User-Agent': config.user_agent diff --git a/src/server/web/index.ts b/src/server/web/index.ts index d8a8d75c7..3f2e1ed19 100644 --- a/src/server/web/index.ts +++ b/src/server/web/index.ts @@ -31,9 +31,7 @@ const app = new Koa(); app.use(views(__dirname + '/views', { extension: 'pug', options: { - config: { - url: config.url - } + config } })); diff --git a/src/services/drive/add-file.ts b/src/services/drive/add-file.ts index b19794b8e..31902b242 100644 --- a/src/services/drive/add-file.ts +++ b/src/services/drive/add-file.ts @@ -50,9 +50,8 @@ async function save(path: string, name: string, type: string, hash: string, size if (type === 'image/webp') ext = '.webp'; } - const baseUrl = config.drive.baseUrl.getOrElse( - `${ config.drive.config.useSSL ? 'https' : 'http' }://${ config.drive.config.endPoint }${ config.drive.config.port ? `:${config.drive.config.port}` : '' }/${ config.drive.bucket }` - ); + const baseUrl = config.drive.baseUrl + || `${ config.drive.config.useSSL ? 'https' : 'http' }://${ config.drive.config.endPoint }${ config.drive.config.port ? `:${config.drive.config.port}` : '' }/${ config.drive.bucket }`; // for original const key = `${config.drive.prefix}/${uuid.v4()}${ext}`; diff --git a/src/services/drive/upload-from-url.ts b/src/services/drive/upload-from-url.ts index c5c9763d5..8daecbadb 100644 --- a/src/services/drive/upload-from-url.ts +++ b/src/services/drive/upload-from-url.ts @@ -55,7 +55,7 @@ export default async ( const req = request({ url: requestUrl, - proxy: config.proxy.getOrElse(null), + proxy: config.proxy, timeout: 10 * 1000, headers: { 'User-Agent': config.user_agent diff --git a/src/services/note/create.ts b/src/services/note/create.ts index 7b5975bac..d47f4ea9f 100644 --- a/src/services/note/create.ts +++ b/src/services/note/create.ts @@ -500,7 +500,7 @@ async function insertNote(user: IUser, data: Option, tags: string[], emojis: str } function index(note: INote) { - if (note.text == null || !config.elasticsearch.isJust()) return; + if (note.text == null || config.elasticsearch == null) return; es.index({ index: 'misskey', diff --git a/src/tools/move-drive-files.ts b/src/tools/move-drive-files.ts index 263e5e125..8a1e94450 100644 --- a/src/tools/move-drive-files.ts +++ b/src/tools/move-drive-files.ts @@ -37,9 +37,8 @@ async function job(file: IDriveFile): Promise { const thumbnailKeyDir = `${config.drive.prefix}/${uuid.v4()}`; const thumbnailKey = `${thumbnailKeyDir}/${name}.thumbnail.jpg`; - const baseUrl = config.drive.baseUrl.getOrElse( - `${ config.drive.config.useSSL ? 'https' : 'http' }://${ config.drive.config.endPoint }${ config.drive.config.port ? `:${config.drive.config.port}` : '' }/${ config.drive.bucket }` - ); + const baseUrl = config.drive.baseUrl + || `${ config.drive.config.useSSL ? 'https' : 'http' }://${ config.drive.config.endPoint }${ config.drive.config.port ? `:${config.drive.config.port}` : '' }/${ config.drive.bucket }`; const bucket = await getDriveFileBucket(); const readable = bucket.openDownloadStream(file._id);