misskey/packages/backend/src/GlobalModule.ts

162 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Global, Inject, Module } from '@nestjs/common';
import * as Redis from 'ioredis';
import { DataSource } from 'typeorm';
import { MeiliSearch } from 'meilisearch';
import { DI } from './di-symbols.js';
import { Config, loadConfig } from './config.js';
import { createPostgresDataSource } from './postgres.js';
import { RepositoryModule } from './models/RepositoryModule.js';
import { allSettled } from './misc/promise-tracker.js';
import type { Provider, OnApplicationShutdown } from '@nestjs/common';
import { MiMeta } from '@/models/Meta.js';
const $config: Provider = {
provide: DI.config,
useValue: loadConfig(),
};
const $db: Provider = {
provide: DI.db,
useFactory: async (config) => {
const db = createPostgresDataSource(config);
return await db.initialize();
},
inject: [DI.config],
};
const $meilisearch: Provider = {
provide: DI.meilisearch,
useFactory: (config: Config) => {
if (config.meilisearch) {
return new MeiliSearch({
host: `${config.meilisearch.ssl ? 'https' : 'http'}://${config.meilisearch.host}:${config.meilisearch.port}`,
apiKey: config.meilisearch.apiKey,
});
} else {
return null;
}
},
inject: [DI.config],
};
const $redis: Provider = {
provide: DI.redis,
useFactory: (config: Config) => {
return new Redis.Redis(config.redis);
},
inject: [DI.config],
};
const $redisForPub: Provider = {
provide: DI.redisForPub,
useFactory: (config: Config) => {
const redis = new Redis.Redis(config.redisForPubsub);
return redis;
},
inject: [DI.config],
};
const $redisForSub: Provider = {
provide: DI.redisForSub,
useFactory: (config: Config) => {
const redis = new Redis.Redis(config.redisForPubsub);
redis.subscribe(config.host);
return redis;
},
inject: [DI.config],
};
const $redisForTimelines: Provider = {
provide: DI.redisForTimelines,
useFactory: (config: Config) => {
return new Redis.Redis(config.redisForTimelines);
},
inject: [DI.config],
};
const $redisForReactions: Provider = {
provide: DI.redisForReactions,
useFactory: (config: Config) => {
return new Redis.Redis(config.redisForReactions);
},
inject: [DI.config],
};
const $meta: Provider = {
provide: DI.meta,
useFactory: async (db: DataSource) => {
const meta = await db.transaction(async transactionalEntityManager => {
// 過去のバグでレコードが複数出来てしまっている可能性があるので新しいIDを優先する
const metas = await transactionalEntityManager.find(MiMeta, {
order: {
id: 'DESC',
},
});
const meta = metas[0];
if (meta) {
return meta;
} else {
// metaが空のときfetchMetaが同時に呼ばれるとここが同時に呼ばれてしまうことがあるのでフェイルセーフなupsertを使う
const saved = await transactionalEntityManager
.upsert(
MiMeta,
{
id: 'x',
},
['id'],
)
.then((x) => transactionalEntityManager.findOneByOrFail(MiMeta, x.identifiers[0]));
return saved;
}
});
// TODO: redisに繋いでmeta更新
return meta;
},
inject: [DI.db],
};
@Global()
@Module({
imports: [RepositoryModule],
providers: [$config, $db, $meta, $meilisearch, $redis, $redisForPub, $redisForSub, $redisForTimelines, $redisForReactions],
exports: [$config, $db, $meta, $meilisearch, $redis, $redisForPub, $redisForSub, $redisForTimelines, $redisForReactions, RepositoryModule],
})
export class GlobalModule implements OnApplicationShutdown {
constructor(
@Inject(DI.db) private db: DataSource,
@Inject(DI.redis) private redisClient: Redis.Redis,
@Inject(DI.redisForPub) private redisForPub: Redis.Redis,
@Inject(DI.redisForSub) private redisForSub: Redis.Redis,
@Inject(DI.redisForTimelines) private redisForTimelines: Redis.Redis,
@Inject(DI.redisForReactions) private redisForReactions: Redis.Redis,
) { }
public async dispose(): Promise<void> {
// Wait for all potential DB queries
await allSettled();
// And then disconnect from DB
await Promise.all([
this.db.destroy(),
this.redisClient.disconnect(),
this.redisForPub.disconnect(),
this.redisForSub.disconnect(),
this.redisForTimelines.disconnect(),
this.redisForReactions.disconnect(),
]);
}
async onApplicationShutdown(signal: string): Promise<void> {
await this.dispose();
}
}