use TimeService everywhere in the backend

This commit is contained in:
Hazelnoot 2025-10-01 19:11:33 -04:00
parent ed750fd990
commit 6cceca90f9
123 changed files with 550 additions and 285 deletions

View file

@ -23,6 +23,7 @@ import { RecipientMethod } from '@/models/AbuseReportNotificationRecipient.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { TimeService } from '@/core/TimeService.js';
import { IdService } from './IdService.js';
@Injectable()
@ -44,6 +45,7 @@ export class AbuseReportNotificationService implements OnApplicationShutdown {
private moderationLogService: ModerationLogService,
private globalEventService: GlobalEventService,
private userEntityService: UserEntityService,
private readonly timeService: TimeService,
) {
this.redisForSub.on('message', this.onMessage);
}
@ -326,7 +328,7 @@ export class AbuseReportNotificationService implements OnApplicationShutdown {
await this.abuseReportNotificationRecipientRepository.update(params.id, {
isActive: params.isActive,
updatedAt: new Date(),
updatedAt: this.timeService.date,
name: params.name,
method: params.method,
userId: params.userId,

View file

@ -28,6 +28,7 @@ import { RoleService } from '@/core/RoleService.js';
import { AntennaService } from '@/core/AntennaService.js';
import { CacheService } from '@/core/CacheService.js';
import { UserListService } from '@/core/UserListService.js';
import { TimeService } from '@/core/TimeService.js';
@Injectable()
export class AccountMoveService {
@ -72,6 +73,7 @@ export class AccountMoveService {
private antennaService: AntennaService,
private readonly cacheService: CacheService,
private readonly userListService: UserListService,
private readonly timeService: TimeService,
) {
}
@ -89,7 +91,7 @@ export class AccountMoveService {
const update = {} as Partial<MiLocalUser>;
update.alsoKnownAs = src.alsoKnownAs?.includes(dstUri) ? src.alsoKnownAs : src.alsoKnownAs?.concat([dstUri]) ?? [dstUri];
update.movedToUri = dstUri;
update.movedAt = new Date();
update.movedAt = this.timeService.date;
await this.usersRepository.update(src.id, update);
Object.assign(src, update);
@ -181,7 +183,7 @@ export class AccountMoveService {
// Insert new mutings with the same values except mutee
const oldMutings = await this.mutingsRepository.findBy([
{ muteeId: src.id, expiresAt: IsNull() },
{ muteeId: src.id, expiresAt: MoreThan(new Date()) },
{ muteeId: src.id, expiresAt: MoreThan(this.timeService.date) },
]);
if (oldMutings.length === 0) return;
@ -348,7 +350,7 @@ export class AccountMoveService {
let resultUser: MiLocalUser | MiRemoteUser | null = null;
if (this.userEntityService.isRemoteUser(dst)) {
if (Date.now() - (dst.lastFetchedAt?.getTime() ?? 0) > 10 * 1000) {
if (this.timeService.now - (dst.lastFetchedAt?.getTime() ?? 0) > 10 * 1000) {
await this.apPersonService.updatePerson(dst.uri);
}
dst = await this.apPersonService.fetchPerson(dst.uri) ?? dst;
@ -364,7 +366,7 @@ export class AccountMoveService {
if (!src) continue; // oldAccountを探してもこのサーバーに存在しない場合はフォロー関係もないということなのでスルー
if (this.userEntityService.isRemoteUser(dst)) {
if (Date.now() - (src.lastFetchedAt?.getTime() ?? 0) > 10 * 1000) {
if (this.timeService.now - (src.lastFetchedAt?.getTime() ?? 0) > 10 * 1000) {
await this.apPersonService.updatePerson(srcUri);
}

View file

@ -9,6 +9,7 @@ import type { MiUser } from '@/models/User.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { NotificationService } from '@/core/NotificationService.js';
import { TimeService } from '@/core/TimeService.js';
import { ACHIEVEMENT_TYPES } from '@/models/UserProfile.js';
@Injectable()
@ -18,6 +19,7 @@ export class AchievementService {
private userProfilesRepository: UserProfilesRepository,
private notificationService: NotificationService,
private readonly timeService: TimeService,
) {
}
@ -28,7 +30,7 @@ export class AchievementService {
): Promise<void> {
if (!ACHIEVEMENT_TYPES.includes(type)) return;
const date = Date.now();
const date = this.timeService.now;
const profile = await this.userProfilesRepository.findOneByOrFail({ userId: userId });

View file

@ -17,6 +17,7 @@ import { ModerationLogService } from '@/core/ModerationLogService.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import type { Config } from '@/config.js';
import { RoleService } from '@/core/RoleService.js';
import { TimeService } from '@/core/TimeService.js';
@Injectable()
export class AnnouncementService {
@ -38,6 +39,7 @@ export class AnnouncementService {
private moderationLogService: ModerationLogService,
private announcementEntityService: AnnouncementEntityService,
private roleService: RoleService,
private readonly timeService: TimeService,
) {
}
@ -143,7 +145,7 @@ export class AnnouncementService {
}
await this.announcementsRepository.update(announcement.id, {
updatedAt: new Date(),
updatedAt: this.timeService.date,
title: values.title,
text: values.text,
/* eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- 空の文字列の場合、nullを渡すようにするため */

View file

@ -12,6 +12,7 @@ import type { ApContextsRepository, ApFetchLogsRepository, ApInboxLogsRepository
import type { Config } from '@/config.js';
import { JsonValue } from '@/misc/json-value.js';
import { UtilityService } from '@/core/UtilityService.js';
import { TimeService } from '@/core/TimeService.js';
import { IdService } from '@/core/IdService.js';
import { IActivity, IObject } from './activitypub/type.js';
@ -32,6 +33,7 @@ export class ApLogService {
private readonly utilityService: UtilityService,
private readonly idService: IdService,
private readonly timeService: TimeService,
) {}
/**
@ -46,7 +48,7 @@ export class ApLogService {
const log = new SkApInboxLog({
id: this.idService.gen(),
at: new Date(),
at: this.timeService.date,
verified: false,
accepted: false,
host,
@ -85,7 +87,7 @@ export class ApLogService {
}): Promise<SkApFetchLog> {
const log = new SkApFetchLog({
id: this.idService.gen(),
at: new Date(),
at: this.timeService.date,
accepted: false,
...data,
});
@ -163,7 +165,7 @@ export class ApLogService {
*/
public async deleteExpiredLogs(): Promise<number> {
// This is the date in UTC of the oldest log to KEEP
const oldestAllowed = new Date(Date.now() - this.config.activityLogging.maxAge);
const oldestAllowed = new Date(this.timeService.now - this.config.activityLogging.maxAge);
// Delete all logs older than the threshold.
const inboxDeleted = await this.deleteExpiredInboxLogs(oldestAllowed);

View file

@ -15,6 +15,7 @@ import type { GlobalEvents } from '@/core/GlobalEventService.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';
import { CacheManagementService, type ManagedMemorySingleCache } from '@/core/CacheManagementService.js';
import { InternalEventService } from '@/core/InternalEventService.js';
import { TimeService } from '@/core/TimeService.js';
@Injectable()
export class AvatarDecorationService implements OnApplicationShutdown {
@ -31,6 +32,8 @@ export class AvatarDecorationService implements OnApplicationShutdown {
private moderationLogService: ModerationLogService,
private globalEventService: GlobalEventService,
private readonly internalEventService: InternalEventService,
private readonly timeService: TimeService,
cacheManagementService: CacheManagementService,
) {
this.cache = cacheManagementService.createMemorySingleCache<MiAvatarDecoration[]>(1000 * 60 * 30); // 30s
@ -68,7 +71,7 @@ export class AvatarDecorationService implements OnApplicationShutdown {
public async update(id: MiAvatarDecoration['id'], params: Partial<MiAvatarDecoration>, moderator?: MiUser): Promise<void> {
const avatarDecoration = await this.avatarDecorationsRepository.findOneByOrFail({ id });
const date = new Date();
const date = this.timeService.date;
await this.avatarDecorationsRepository.update(avatarDecoration.id, {
updatedAt: date,
...params,

View file

@ -28,6 +28,7 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js';
import { emojiRegex } from '@/misc/emoji-regex.js';
import { NotificationService } from '@/core/NotificationService.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';
import { TimeService } from '@/core/TimeService.js';
const MAX_ROOM_MEMBERS = 30;
const MAX_REACTIONS_PER_MESSAGE = 100;
@ -91,6 +92,7 @@ export class ChatService {
private userFollowingService: UserFollowingService,
private customEmojiService: CustomEmojiService,
private moderationLogService: ModerationLogService,
private readonly timeService: TimeService,
) {
}
@ -225,7 +227,7 @@ export class ChatService {
// 3秒経っても既読にならなかったらイベント発行
if (this.userEntityService.isLocalUser(toUser)) {
setTimeout(async () => {
this.timeService.startTimer(async () => {
const marker = await this.redisClient.get(`newUserChatMessageExists:${toUser.id}:${fromUser.id}`);
if (marker == null) return; // 既読
@ -285,7 +287,7 @@ export class ChatService {
redisPipeline.exec();
// 3秒経っても既読にならなかったらイベント発行
setTimeout(async () => {
this.timeService.startTimer(async () => {
const redisPipeline = this.redisClient.pipeline();
for (const membership of membershipsOtherThanMe) {
redisPipeline.get(`newRoomChatMessageExists:${membership.userId}:${toRoom.id}`);

View file

@ -12,6 +12,7 @@ import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js
import { RoleService } from '@/core/RoleService.js';
import { IdService } from '@/core/IdService.js';
import type { MiLocalUser } from '@/models/User.js';
import { TimeService } from '@/core/TimeService.js';
@Injectable()
export class ClipService {
@ -33,6 +34,7 @@ export class ClipService {
private roleService: RoleService,
private idService: IdService,
private readonly timeService: TimeService,
) {
}
@ -125,7 +127,7 @@ export class ClipService {
}
this.clipsRepository.update(clip.id, {
lastClippedAt: new Date(),
lastClippedAt: this.timeService.date,
});
this.notesRepository.increment({ id: noteId }, 'clippedCount', 1);

View file

@ -362,7 +362,7 @@ export class CustomEmojiService {
await this.bulkUpdateEmojis(ids, async emojis => {
for (const emoji of emojis) {
await this.emojisRepository.update(emoji.id, {
updatedAt: new Date(),
updatedAt: this.timeService.date,
aliases: [...new Set(emoji.aliases.concat(aliases))],
});
}
@ -374,7 +374,7 @@ export class CustomEmojiService {
await this.emojisRepository.update({
id: In(ids),
}, {
updatedAt: new Date(),
updatedAt: this.timeService.date,
aliases: aliases,
});
@ -386,7 +386,7 @@ export class CustomEmojiService {
await this.bulkUpdateEmojis(ids, async emojis => {
for (const emoji of emojis) {
await this.emojisRepository.update(emoji.id, {
updatedAt: new Date(),
updatedAt: this.timeService.date,
aliases: emoji.aliases.filter(x => !aliases.includes(x)),
});
}
@ -398,7 +398,7 @@ export class CustomEmojiService {
await this.emojisRepository.update({
id: In(ids),
}, {
updatedAt: new Date(),
updatedAt: this.timeService.date,
category: category,
});
@ -410,7 +410,7 @@ export class CustomEmojiService {
await this.emojisRepository.update({
id: In(ids),
}, {
updatedAt: new Date(),
updatedAt: this.timeService.date,
license: license,
});

View file

@ -8,6 +8,7 @@ import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import { TimeService } from '@/core/TimeService.js';
export type FanoutTimelineName = (
// home timeline
@ -46,6 +47,7 @@ export class FanoutTimelineService {
private redisForTimelines: Redis.Redis,
private idService: IdService,
private readonly timeService: TimeService,
) {
}
@ -53,7 +55,7 @@ export class FanoutTimelineService {
public push(tl: FanoutTimelineName, id: string, maxlen: number, pipeline: Redis.ChainableCommander) {
// リモートから遅れて届いた(もしくは後から追加された)投稿日時が古い投稿が追加されるとページネーション時に問題を引き起こすため、
// 3分以内に投稿されたものでない場合、Redisにある最古のIDより新しい場合のみ追加する
if (this.idService.parse(id).date.getTime() > Date.now() - 1000 * 60 * 3) {
if (this.idService.parse(id).date.getTime() > this.timeService.now - 1000 * 60 * 3) {
pipeline.lpush('list:' + tl, id);
if (Math.random() < 0.1) { // 10%の確率でトリム
pipeline.ltrim('list:' + tl, 0, maxlen - 1);

View file

@ -9,6 +9,7 @@ import type { MiGalleryPost, MiNote, MiUser } from '@/models/_.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { RoleService } from '@/core/RoleService.js';
import { TimeService } from '@/core/TimeService.js';
const GLOBAL_NOTES_RANKING_WINDOW = 1000 * 60 * 60 * 24 * 3; // 3日ごと
export const GALLERY_POSTS_RANKING_WINDOW = 1000 * 60 * 60 * 24 * 3; // 3日ごと
@ -24,12 +25,13 @@ export class FeaturedService {
private redisClient: Redis.Redis, // TODO: 専用のRedisサーバーを設定できるようにする
private readonly roleService: RoleService,
private readonly timeService: TimeService,
) {
}
@bindThis
private getCurrentWindow(windowRange: number): number {
const passed = new Date().getTime() - featuredEpoc;
const passed = this.timeService.now - featuredEpoc;
return Math.floor(passed / windowRange);
}

View file

@ -16,6 +16,7 @@ import { CacheManagementService, type ManagedQuantumKVCache } from '@/core/Cache
import { InternalEventService } from '@/core/InternalEventService.js';
import { diffArraysSimple } from '@/misc/diff-arrays.js';
import { bindThis } from '@/decorators.js';
import { TimeService } from '@/core/TimeService.js';
import type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity.js';
@Injectable()
@ -32,6 +33,7 @@ export class FederatedInstanceService implements OnApplicationShutdown {
private utilityService: UtilityService,
private idService: IdService,
private readonly internalEventService: InternalEventService,
private readonly timeService: TimeService,
cacheManagementService: CacheManagementService,
) {
@ -47,7 +49,7 @@ export class FederatedInstanceService implements OnApplicationShutdown {
.values({
id: this.idService.gen(),
host,
firstRetrievedAt: new Date(),
firstRetrievedAt: this.timeService.date,
isBlocked: this.utilityService.isBlockedHost(host),
isSilenced: this.utilityService.isSilencedHost(host),
isMediaSilenced: this.utilityService.isMediaSilencedHost(host),
@ -87,7 +89,7 @@ export class FederatedInstanceService implements OnApplicationShutdown {
.values({
id: this.idService.gen(),
host,
firstRetrievedAt: new Date(),
firstRetrievedAt: this.timeService.date,
isBlocked: this.utilityService.isBlockedHost(host),
isSilenced: this.utilityService.isSilencedHost(host),
isMediaSilenced: this.utilityService.isMediaSilencedHost(host),

View file

@ -15,6 +15,7 @@ import type { HashtagsRepository, MiMeta } from '@/models/_.js';
import { bindThis } from '@/decorators.js';
import { FeaturedService } from '@/core/FeaturedService.js';
import { UtilityService } from '@/core/UtilityService.js';
import { TimeService } from '@/core/TimeService.js';
@Injectable()
export class HashtagService {
@ -31,6 +32,7 @@ export class HashtagService {
private featuredService: FeaturedService,
private idService: IdService,
private utilityService: UtilityService,
private readonly timeService: TimeService,
) {
}
@ -165,7 +167,7 @@ export class HashtagService {
if (this.utilityService.isKeyWordIncluded(hashtag, this.meta.sensitiveWords)) return;
// YYYYMMDDHHmm (10分間隔)
const now = new Date();
const now = this.timeService.date;
now.setMinutes(Math.floor(now.getMinutes() / 10) * 10, 0, 0);
const window = `${now.getUTCFullYear()}${(now.getUTCMonth() + 1).toString().padStart(2, '0')}${now.getUTCDate().toString().padStart(2, '0')}${now.getUTCHours().toString().padStart(2, '0')}${now.getUTCMinutes().toString().padStart(2, '0')}`;
@ -196,7 +198,7 @@ export class HashtagService {
@bindThis
public async getChart(hashtag: string, range: number): Promise<number[]> {
const now = new Date();
const now = this.timeService.date;
now.setMinutes(Math.floor(now.getMinutes() / 10) * 10, 0, 0);
const redisPipeline = this.redisClient.pipeline();
@ -216,7 +218,7 @@ export class HashtagService {
@bindThis
public async getCharts(hashtags: string[], range: number): Promise<Record<string, number[]>> {
const now = new Date();
const now = this.timeService.date;
now.setMinutes(Math.floor(now.getMinutes() / 10) * 10, 0, 0);
const redisPipeline = this.redisClient.pipeline();

View file

@ -19,6 +19,7 @@ import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/val
import type { IObject, IObjectWithId } from '@/core/activitypub/type.js';
import { UtilityService } from '@/core/UtilityService.js';
import { ApUtilityService } from '@/core/activitypub/ApUtilityService.js';
import { TimeService } from '@/core/TimeService.js';
import type { Response } from 'node-fetch';
import type { Socket } from 'node:net';
@ -156,8 +157,10 @@ export class HttpRequestService {
constructor(
@Inject(DI.config)
private config: Config,
private readonly apUtilityService: ApUtilityService,
private readonly utilityService: UtilityService,
private readonly timeService: TimeService,
) {
const cache = new CacheableLookup({
maxTtl: 3600, // 1hours
@ -343,7 +346,7 @@ export class HttpRequestService {
this.utilityService.assertUrl(parsedUrl, allowHttp);
const controller = new AbortController();
setTimeout(() => {
this.timeService.startTimer(() => {
controller.abort();
}, timeout);

View file

@ -5,6 +5,7 @@
import { Inject, Injectable } from '@nestjs/common';
import Logger from '@/logger.js';
import { TimeService } from '@/core/TimeService.js';
import { bindThis } from '@/decorators.js';
import type { KEYWORD } from 'color-convert/conversions.js';
import { envOption } from '@/env.js';
@ -16,12 +17,13 @@ export class LoggerService {
constructor(
@Inject(DI.config)
private config: Config,
private readonly timeService: TimeService,
) {
}
@bindThis
public getLogger(domain: string, color?: KEYWORD | undefined) {
const verbose = this.config.logging?.verbose || envOption.verbose;
return new Logger(domain, color, verbose);
return new Logger(domain, color, verbose, undefined, this.timeService);
}
}

View file

@ -12,6 +12,7 @@ import { GlobalEventService } from '@/core/GlobalEventService.js';
import { bindThis } from '@/decorators.js';
import type { GlobalEvents } from '@/core/GlobalEventService.js';
import { FeaturedService } from '@/core/FeaturedService.js';
import { TimeService, type TimerHandle } from '@/core/TimeService.js';
import { MiInstance } from '@/models/Instance.js';
import { diffArrays } from '@/misc/diff-arrays.js';
import type { MetasRepository } from '@/models/_.js';
@ -20,7 +21,7 @@ import type { OnApplicationShutdown } from '@nestjs/common';
@Injectable()
export class MetaService implements OnApplicationShutdown {
private cache: MiMeta | undefined;
private intervalId: NodeJS.Timeout;
private intervalId: TimerHandle;
constructor(
@Inject(DI.redisForSub)
@ -34,16 +35,17 @@ export class MetaService implements OnApplicationShutdown {
private featuredService: FeaturedService,
private globalEventService: GlobalEventService,
private readonly timeService: TimeService,
) {
//this.onMessage = this.onMessage.bind(this);
if (process.env.NODE_ENV !== 'test') {
this.intervalId = setInterval(() => {
this.intervalId = this.timeService.startTimer(() => {
this.fetch(true).then(meta => {
// fetch内でもセットしてるけど仕様変更の可能性もあるため一応
this.cache = meta;
});
}, 1000 * 60 * 5);
}, 1000 * 60 * 5, { repeated: true });
}
this.redisForSub.on('message', this.onMessage);
@ -161,7 +163,7 @@ export class MetaService implements OnApplicationShutdown {
@bindThis
public dispose(): void {
clearInterval(this.intervalId);
this.timeService.stopTimer(this.intervalId);
this.redisForSub.off('message', this.onMessage);
}

View file

@ -57,6 +57,7 @@ import { IdentifiableError } from '@/misc/identifiable-error.js';
import { LatestNoteService } from '@/core/LatestNoteService.js';
import { CollapsedQueue } from '@/misc/collapsed-queue.js';
import { CacheService } from '@/core/CacheService.js';
import { TimeService } from '@/core/TimeService.js';
import { NoteVisibilityService } from '@/core/NoteVisibilityService.js';
import { isPureRenote } from '@/misc/is-renote.js';
@ -224,9 +225,10 @@ export class NoteCreateService implements OnApplicationShutdown {
private userBlockingService: UserBlockingService,
private cacheService: CacheService,
private latestNoteService: LatestNoteService,
private readonly timeService: TimeService,
private readonly noteVisibilityService: NoteVisibilityService,
) {
this.updateNotesCountQueue = new CollapsedQueue(process.env.NODE_ENV !== 'test' ? 60 * 1000 * 5 : 0, this.collapseNotesCount, this.performUpdateNotesCount);
this.updateNotesCountQueue = new CollapsedQueue(this.timeService, process.env.NODE_ENV !== 'test' ? 60 * 1000 * 5 : 0, this.collapseNotesCount, this.performUpdateNotesCount);
}
@bindThis
@ -253,7 +255,7 @@ export class NoteCreateService implements OnApplicationShutdown {
data.channel = await this.channelsRepository.findOneBy({ id: data.reply.channelId });
}
if (data.createdAt == null) data.createdAt = new Date();
if (data.createdAt == null) data.createdAt = this.timeService.date;
if (data.visibility == null) data.visibility = 'public';
if (data.localOnly == null) data.localOnly = false;
if (data.channel != null) data.visibility = 'public';
@ -613,7 +615,7 @@ export class NoteCreateService implements OnApplicationShutdown {
// Increment notes count (user)
this.incNotesCountOfUser(user);
} else {
this.usersRepository.update({ id: user.id }, { updatedAt: new Date() });
this.usersRepository.update({ id: user.id }, { updatedAt: this.timeService.date });
}
this.pushToTl(note, user);
@ -657,7 +659,7 @@ export class NoteCreateService implements OnApplicationShutdown {
}
if (data.poll && data.poll.expiresAt) {
const delay = data.poll.expiresAt.getTime() - Date.now();
const delay = data.poll.expiresAt.getTime() - this.timeService.now;
this.queueService.endedPollNotificationQueue.add(note.id, {
noteId: note.id,
}, {
@ -791,7 +793,7 @@ export class NoteCreateService implements OnApplicationShutdown {
if (data.channel) {
this.channelsRepository.increment({ id: data.channel.id }, 'notesCount', 1);
this.channelsRepository.update(data.channel.id, {
lastNotedAt: new Date(),
lastNotedAt: this.timeService.date,
});
this.notesRepository.countBy({
@ -838,7 +840,7 @@ export class NoteCreateService implements OnApplicationShutdown {
.execute();
// 30%の確率、3日以内に投稿されたートの場合ハイライト用ランキング更新
if (user.isExplorable && Math.random() < 0.3 && (Date.now() - this.idService.parse(renote.id).date.getTime()) < 1000 * 60 * 60 * 24 * 3) {
if (user.isExplorable && Math.random() < 0.3 && (this.timeService.now - this.idService.parse(renote.id).date.getTime()) < 1000 * 60 * 60 * 24 * 3) {
const policies = await this.roleService.getUserPolicies(user);
if (policies.canTrend) {
if (renote.channelId != null) {
@ -905,7 +907,7 @@ export class NoteCreateService implements OnApplicationShutdown {
private incNotesCountOfUser(user: { id: MiUser['id']; }) {
this.usersRepository.createQueryBuilder().update()
.set({
updatedAt: new Date(),
updatedAt: this.timeService.date,
notesCount: () => '"notesCount" + 1',
})
.where('id = :id', { id: user.id })
@ -1064,7 +1066,7 @@ export class NoteCreateService implements OnApplicationShutdown {
const hibernatedUsers = await this.usersRepository.find({
where: {
id: In(samples.map(x => x.followerId)),
lastActiveDate: LessThan(new Date(Date.now() - (1000 * 60 * 60 * 24 * 50))),
lastActiveDate: LessThan(new Date(this.timeService.now - (1000 * 60 * 60 * 24 * 50))),
},
select: ['id'],
});

View file

@ -26,7 +26,8 @@ import { isQuote, isRenote } from '@/misc/is-renote.js';
import { LatestNoteService } from '@/core/LatestNoteService.js';
import { ApLogService } from '@/core/ApLogService.js';
import type Logger from '@/logger.js';
import { LoggerService } from './LoggerService.js';
import { TimeService } from '@/core/TimeService.js';
import { LoggerService } from '@/core/LoggerService.js';
@Injectable()
export class NoteDeleteService {
@ -60,6 +61,8 @@ export class NoteDeleteService {
private instanceChart: InstanceChart,
private latestNoteService: LatestNoteService,
private readonly apLogService: ApLogService,
private readonly timeService: TimeService,
loggerService: LoggerService,
) {
this.logger = loggerService.getLogger('note-delete-service');
@ -71,7 +74,7 @@ export class NoteDeleteService {
* @param note 稿
*/
async delete(user: { id: MiUser['id']; uri: MiUser['uri']; host: MiUser['host']; isBot: MiUser['isBot']; }, note: MiNote, quiet = false, deleter?: MiUser) {
const deletedAt = new Date();
const deletedAt = this.timeService.date;
const cascadingNotes = await this.findCascadingNotes(note);
if (note.replyId) {
@ -127,7 +130,7 @@ export class NoteDeleteService {
// Decrement notes count (user)
this.decNotesCountOfUser(user);
} else {
this.usersRepository.update({ id: user.id }, { updatedAt: new Date() });
this.usersRepository.update({ id: user.id }, { updatedAt: this.timeService.date });
}
if (this.meta.enableStatsForFederatedInstances) {
@ -179,7 +182,7 @@ export class NoteDeleteService {
private decNotesCountOfUser(user: { id: MiUser['id']; }) {
this.usersRepository.createQueryBuilder().update()
.set({
updatedAt: new Date(),
updatedAt: this.timeService.date,
notesCount: () => '"notesCount" - 1',
})
.where('id = :id', { id: user.id })

View file

@ -52,6 +52,7 @@ import { IdentifiableError } from '@/misc/identifiable-error.js';
import { LatestNoteService } from '@/core/LatestNoteService.js';
import { CollapsedQueue } from '@/misc/collapsed-queue.js';
import { NoteCreateService } from '@/core/NoteCreateService.js';
import { TimeService } from '@/core/TimeService.js';
import { NoteVisibilityService } from '@/core/NoteVisibilityService.js';
import { isPureRenote } from '@/misc/is-renote.js';
@ -221,9 +222,10 @@ export class NoteEditService implements OnApplicationShutdown {
private cacheService: CacheService,
private latestNoteService: LatestNoteService,
private noteCreateService: NoteCreateService,
private readonly timeService: TimeService,
private readonly noteVisibilityService: NoteVisibilityService,
) {
this.updateNotesCountQueue = new CollapsedQueue(process.env.NODE_ENV !== 'test' ? 60 * 1000 * 5 : 0, this.collapseNotesCount, this.performUpdateNotesCount);
this.updateNotesCountQueue = new CollapsedQueue(this.timeService, process.env.NODE_ENV !== 'test' ? 60 * 1000 * 5 : 0, this.collapseNotesCount, this.performUpdateNotesCount);
}
@bindThis
@ -272,7 +274,7 @@ export class NoteEditService implements OnApplicationShutdown {
data.channel = await this.channelsRepository.findOneBy({ id: data.reply.channelId });
}
if (data.updatedAt == null) data.updatedAt = new Date();
if (data.updatedAt == null) data.updatedAt = this.timeService.date;
if (data.visibility == null) data.visibility = 'public';
if (data.localOnly == null) data.localOnly = false;
if (data.channel != null) data.visibility = 'public';
@ -493,12 +495,12 @@ export class NoteEditService implements OnApplicationShutdown {
cw: update.cw || undefined,
fileIds: undefined,
oldDate: exists ? oldnote.updatedAt as Date : this.idService.parse(oldnote.id).date,
updatedAt: new Date(),
updatedAt: this.timeService.date,
});
const note = new MiNote({
id: oldnote.id,
updatedAt: data.updatedAt ? data.updatedAt : new Date(),
updatedAt: data.updatedAt ? data.updatedAt : this.timeService.date,
fileIds: data.files ? data.files.map(file => file.id) : [],
replyId: oldnote.replyId,
renoteId: data.renote ? data.renote.id : null,
@ -619,13 +621,13 @@ export class NoteEditService implements OnApplicationShutdown {
}
}
this.usersRepository.update({ id: user.id }, { updatedAt: new Date() });
this.usersRepository.update({ id: user.id }, { updatedAt: this.timeService.date });
// ハッシュタグ更新
this.pushToTl(note, user);
if (data.poll && data.poll.expiresAt) {
const delay = data.poll.expiresAt.getTime() - Date.now();
const delay = data.poll.expiresAt.getTime() - this.timeService.now;
this.queueService.endedPollNotificationQueue.remove(`pollEnd:${note.id}`);
this.queueService.endedPollNotificationQueue.add(note.id, {
noteId: note.id,
@ -737,7 +739,7 @@ export class NoteEditService implements OnApplicationShutdown {
if (data.channel) {
this.channelsRepository.increment({ id: data.channel.id }, 'notesCount', 1);
this.channelsRepository.update(data.channel.id, {
lastNotedAt: new Date(),
lastNotedAt: this.timeService.date,
});
this.notesRepository.countBy({
@ -935,7 +937,7 @@ export class NoteEditService implements OnApplicationShutdown {
const hibernatedUsers = await this.usersRepository.find({
where: {
id: In(samples.map(x => x.followerId)),
lastActiveDate: LessThan(new Date(Date.now() - (1000 * 60 * 60 * 24 * 50))),
lastActiveDate: LessThan(new Date(this.timeService.now - (1000 * 60 * 60 * 24 * 50))),
},
select: ['id'],
});

View file

@ -14,6 +14,7 @@ import type { Packed } from '@/misc/json-schema.js';
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
import { CacheService } from '@/core/CacheService.js';
import { IdService } from '@/core/IdService.js';
import { TimeService } from '@/core/TimeService.js';
import { bindThis } from '@/decorators.js';
import { awaitAll } from '@/misc/prelude/await-all.js';
import { DI } from '@/di-symbols.js';
@ -70,6 +71,7 @@ export class NoteVisibilityService {
private readonly cacheService: CacheService,
private readonly idService: IdService,
private readonly federatedInstanceService: FederatedInstanceService,
private readonly timeService: TimeService,
) {}
@bindThis
@ -293,7 +295,7 @@ export class NoteVisibilityService {
const createdAt = new Date(note.createdAt).valueOf();
// I don't understand this logic, but I tried to break it out for readability
const followersOnlyOpt1 = followersOnlyBefore <= 0 && (Date.now() - createdAt > 0 - followersOnlyBefore);
const followersOnlyOpt1 = followersOnlyBefore <= 0 && (this.timeService.now - createdAt > 0 - followersOnlyBefore);
const followersOnlyOpt2 = followersOnlyBefore > 0 && (createdAt < followersOnlyBefore);
if (followersOnlyOpt1 || followersOnlyOpt2) {
note.visibility = 'followers';
@ -323,7 +325,7 @@ export class NoteVisibilityService {
const createdAt = note.createdAt.valueOf();
// I don't understand this logic, but I tried to break it out for readability
const hiddenOpt1 = hiddenBefore <= 0 && (Date.now() - createdAt > 0 - hiddenBefore);
const hiddenOpt1 = hiddenBefore <= 0 && (this.timeService.now - createdAt > 0 - hiddenBefore);
const hiddenOpt2 = hiddenBefore > 0 && (createdAt < hiddenBefore);
if (hiddenOpt1 || hiddenOpt2) return true;
}

View file

@ -22,6 +22,7 @@ import type { Config } from '@/config.js';
import { UserListService } from '@/core/UserListService.js';
import { FilterUnionByProperty, groupedNotificationTypes, obsoleteNotificationTypes } from '@/types.js';
import { trackPromise } from '@/misc/promise-tracker.js';
import { TimeService } from '@/core/TimeService.js';
@Injectable()
export class NotificationService implements OnApplicationShutdown {
@ -43,6 +44,7 @@ export class NotificationService implements OnApplicationShutdown {
private pushNotificationService: PushNotificationService,
private cacheService: CacheService,
private userListService: UserListService,
private readonly timeService: TimeService,
) {
}
@ -146,7 +148,7 @@ export class NotificationService implements OnApplicationShutdown {
}
}
const createdAt = new Date();
const createdAt = this.timeService.date;
let notification: FilterUnionByProperty<MiNotification, 'type', T>;
let redisId: string;
@ -187,7 +189,7 @@ export class NotificationService implements OnApplicationShutdown {
// 2秒経っても(今回作成した)通知が既読にならなかったら「未読の通知がありますよ」イベントを発行する
// テスト通知の場合は即時発行
const interval = notification.type === 'test' ? 0 : 2000;
setTimeout(interval, 'unread notification', { signal: this.#shutdownController.signal }).then(async () => {
this.timeService.startPromiseTimer(interval, 'unread notification', { signal: this.#shutdownController.signal }).then(async () => {
const latestReadNotificationId = await this.redisClient.get(`latestReadNotification:${notifieeId}`);
if (latestReadNotificationId && (latestReadNotificationId >= redisId)) return;

View file

@ -13,6 +13,7 @@ import { getNoteSummary } from '@/misc/get-note-summary.js';
import type { MiMeta, MiSwSubscription, SwSubscriptionsRepository } from '@/models/_.js';
import { bindThis } from '@/decorators.js';
import { CacheManagementService, type ManagedQuantumKVCache } from '@/core/CacheManagementService.js';
import { TimeService } from '@/core/TimeService.js';
// Defined also packages/sw/types.ts#L13
type PushNotificationsTypes = {
@ -62,6 +63,9 @@ export class PushNotificationService {
@Inject(DI.swSubscriptionsRepository)
private swSubscriptionsRepository: SwSubscriptionsRepository,
private readonly timeService: TimeService,
cacheManagementService: CacheManagementService,
) {
this.subscriptionsCache = cacheManagementService.createQuantumKVCache<MiSwSubscription[]>('userSwSubscriptions', {
@ -98,7 +102,7 @@ export class PushNotificationService {
type,
body: (type === 'notification' || type === 'unreadAntennaNote') ? truncateBody(type, body) : body,
userId,
dateTime: Date.now(),
dateTime: this.timeService.now,
}), {
proxy: this.config.proxy,
}).catch((err: any) => {

View file

@ -16,6 +16,7 @@ import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import type { Antenna } from '@/server/api/endpoints/i/import-antennas.js';
import { ApRequestCreator } from '@/core/activitypub/ApRequestService.js';
import { TimeService } from '@/core/TimeService.js';
import type { SystemWebhookPayload } from '@/core/SystemWebhookService.js';
import type { MiNote } from '@/models/Note.js';
import { type UserWebhookPayload } from './UserWebhookService.js';
@ -71,6 +72,8 @@ export class QueueService implements OnModuleInit {
@Inject('queue:userWebhookDeliver') public userWebhookDeliverQueue: UserWebhookDeliverQueue,
@Inject('queue:systemWebhookDeliver') public systemWebhookDeliverQueue: SystemWebhookDeliverQueue,
@Inject('queue:scheduleNotePost') public ScheduleNotePostQueue: ScheduleNotePostQueue,
private readonly timeService: TimeService,
) {}
@bindThis
@ -791,7 +794,7 @@ export class QueueService implements OnModuleInit {
userId: webhook.userId,
to: webhook.url,
secret: webhook.secret,
createdAt: Date.now(),
createdAt: this.timeService.now,
eventId: randomUUID(),
};
@ -828,7 +831,7 @@ export class QueueService implements OnModuleInit {
webhookId: webhook.id,
to: webhook.url,
secret: webhook.secret,
createdAt: Date.now(),
createdAt: this.timeService.now,
eventId: randomUUID(),
};

View file

@ -32,6 +32,7 @@ import { ReactionsBufferingService } from '@/core/ReactionsBufferingService.js';
import { PER_NOTE_REACTION_USER_PAIR_CACHE_MAX } from '@/const.js';
import { CacheService } from '@/core/CacheService.js';
import { NoteVisibilityService } from '@/core/NoteVisibilityService.js';
import { TimeService } from '@/core/TimeService.js';
import type { DataSource } from 'typeorm';
const FALLBACK = '\u2764';
@ -108,6 +109,7 @@ export class ReactionService implements OnModuleInit {
private perUserReactionsChart: PerUserReactionsChart,
private readonly cacheService: CacheService,
private readonly noteVisibilityService: NoteVisibilityService,
private readonly timeService: TimeService,
) {
}
@ -222,13 +224,13 @@ export class ReactionService implements OnModuleInit {
.execute();
}
this.usersRepository.update({ id: user.id }, { updatedAt: new Date() });
this.usersRepository.update({ id: user.id }, { updatedAt: this.timeService.date });
// 30%の確率、セルフではない、3日以内に投稿されたートの場合ハイライト用ランキング更新
if (
Math.random() < 0.3 &&
note.userId !== user.id &&
(Date.now() - this.idService.parse(note.id).date.getTime()) < 1000 * 60 * 60 * 24 * 3
(this.timeService.now - this.idService.parse(note.id).date.getTime()) < 1000 * 60 * 60 * 24 * 3
) {
const author = await this.cacheService.findUserById(note.userId);
if (author.isExplorable) {
@ -338,7 +340,7 @@ export class ReactionService implements OnModuleInit {
.execute();
}
this.usersRepository.update({ id: user.id }, { updatedAt: new Date() });
this.usersRepository.update({ id: user.id }, { updatedAt: this.timeService.date });
this.globalEventService.publishNoteStream(note.id, 'unreacted', {
reaction: this.decodeReaction(exist.reaction).reaction,

View file

@ -6,6 +6,7 @@
import { Inject, Injectable } from '@nestjs/common';
import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import { TimeService } from '@/core/TimeService.js';
import type { MiNote } from '@/models/Note.js';
import { bindThis } from '@/decorators.js';
import type { MiUser, NotesRepository } from '@/models/_.js';
@ -31,6 +32,8 @@ export class ReactionsBufferingService implements OnApplicationShutdown {
@Inject(DI.notesRepository)
private notesRepository: NotesRepository,
private readonly timeService: TimeService,
) {
this.redisForSub.on('message', this.onMessage);
}
@ -62,7 +65,7 @@ export class ReactionsBufferingService implements OnApplicationShutdown {
for (let i = 0; i < currentPairs.length; i++) {
pipeline.zadd(`${REDIS_PAIR_PREFIX}:${noteId}`, i, currentPairs[i]);
}
pipeline.zadd(`${REDIS_PAIR_PREFIX}:${noteId}`, Date.now(), `${userId}/${reaction}`);
pipeline.zadd(`${REDIS_PAIR_PREFIX}:${noteId}`, this.timeService.now, `${userId}/${reaction}`);
pipeline.zremrangebyrank(`${REDIS_PAIR_PREFIX}:${noteId}`, 0, -(PER_NOTE_REACTION_USER_PAIR_CACHE_MAX + 1));
await pipeline.exec();
}

View file

@ -11,6 +11,7 @@ import type { MiUser } from '@/models/User.js';
import { IdService } from '@/core/IdService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { bindThis } from '@/decorators.js';
import { TimeService } from '@/core/TimeService.js';
@Injectable()
export class RegistryApiService {
@ -20,6 +21,7 @@ export class RegistryApiService {
private idService: IdService,
private globalEventService: GlobalEventService,
private readonly timeService: TimeService,
) {
}
@ -31,7 +33,7 @@ export class RegistryApiService {
.insert()
.values({
id: this.idService.gen(),
updatedAt: new Date(),
updatedAt: this.timeService.date,
userId: userId,
domain: domain,
scope: scope,

View file

@ -16,6 +16,7 @@ import { ILink, WebfingerService } from '@/core/WebfingerService.js';
import { RemoteLoggerService } from '@/core/RemoteLoggerService.js';
import { ApDbResolverService } from '@/core/activitypub/ApDbResolverService.js';
import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
import { TimeService } from '@/core/TimeService.js';
import { bindThis } from '@/decorators.js';
import { renderInlineError } from '@/misc/render-inline-error.js';
@ -35,6 +36,7 @@ export class RemoteUserResolveService {
private remoteLoggerService: RemoteLoggerService,
private apDbResolverService: ApDbResolverService,
private apPersonService: ApPersonService,
private readonly timeService: TimeService,
) {
this.logger = this.remoteLoggerService.logger.createSubLogger('resolve-user');
}
@ -81,10 +83,10 @@ export class RemoteUserResolveService {
}
// ユーザー情報が古い場合は、WebFingerからやりなおして返す
if (user.lastFetchedAt == null || Date.now() - user.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) {
if (user.lastFetchedAt == null || this.timeService.now - user.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) {
// 繋がらないインスタンスに何回も試行するのを防ぐ, 後続の同様処理の連続試行を防ぐ ため 試行前にも更新する
await this.usersRepository.update(user.id, {
lastFetchedAt: new Date(),
lastFetchedAt: this.timeService.date,
});
const self = await this.resolveSelf(acctLower);

View file

@ -19,6 +19,7 @@ import { bindThis } from '@/decorators.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { IdService } from '@/core/IdService.js';
import { TimeService } from '@/core/TimeService.js';
import type { NotificationService } from '@/core/NotificationService.js';
import { Serialized } from '@/types.js';
import { ReversiGameEntityService } from './entities/ReversiGameEntityService.js';
@ -43,6 +44,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
private globalEventService: GlobalEventService,
private reversiGameEntityService: ReversiGameEntityService,
private idService: IdService,
private readonly timeService: TimeService,
) {
}
@ -99,8 +101,8 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
// 既にマッチしている対局が無いか探す(3分以内)
const games = await this.reversiGamesRepository.find({
where: [
{ id: MoreThan(this.idService.gen(Date.now() - 1000 * 60 * 3)), user1Id: me.id, user2Id: targetUser.id, isStarted: false },
{ id: MoreThan(this.idService.gen(Date.now() - 1000 * 60 * 3)), user1Id: targetUser.id, user2Id: me.id, isStarted: false },
{ id: MoreThan(this.idService.gen(this.timeService.now - 1000 * 60 * 3)), user1Id: me.id, user2Id: targetUser.id, isStarted: false },
{ id: MoreThan(this.idService.gen(this.timeService.now - 1000 * 60 * 3)), user1Id: targetUser.id, user2Id: me.id, isStarted: false },
],
relations: ['user1', 'user2'],
order: { id: 'DESC' },
@ -113,7 +115,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
//#region 相手から既に招待されてないか確認
const invitations = await this.redisClient.zrange(
`reversi:matchSpecific:${me.id}`,
Date.now() - INVITATION_TIMEOUT_MS,
this.timeService.now - INVITATION_TIMEOUT_MS,
'+inf',
'BYSCORE');
@ -129,7 +131,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
//#endregion
const redisPipeline = this.redisClient.pipeline();
redisPipeline.zadd(`reversi:matchSpecific:${targetUser.id}`, Date.now(), me.id);
redisPipeline.zadd(`reversi:matchSpecific:${targetUser.id}`, this.timeService.now, me.id);
redisPipeline.expire(`reversi:matchSpecific:${targetUser.id}`, 120, 'NX');
await redisPipeline.exec();
@ -146,8 +148,8 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
// 既にマッチしている対局が無いか探す(3分以内)
const games = await this.reversiGamesRepository.find({
where: [
{ id: MoreThan(this.idService.gen(Date.now() - 1000 * 60 * 3)), user1Id: me.id, isStarted: false },
{ id: MoreThan(this.idService.gen(Date.now() - 1000 * 60 * 3)), user2Id: me.id, isStarted: false },
{ id: MoreThan(this.idService.gen(this.timeService.now - 1000 * 60 * 3)), user1Id: me.id, isStarted: false },
{ id: MoreThan(this.idService.gen(this.timeService.now - 1000 * 60 * 3)), user2Id: me.id, isStarted: false },
],
relations: ['user1', 'user2'],
order: { id: 'DESC' },
@ -160,7 +162,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
//#region まず自分宛ての招待を探す
const invitations = await this.redisClient.zrange(
`reversi:matchSpecific:${me.id}`,
Date.now() - INVITATION_TIMEOUT_MS,
this.timeService.now - INVITATION_TIMEOUT_MS,
'+inf',
'BYSCORE');
@ -201,9 +203,9 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
} else {
const redisPipeline = this.redisClient.pipeline();
if (options.noIrregularRules) {
redisPipeline.zadd('reversi:matchAny', Date.now(), me.id + ':noIrregularRules');
redisPipeline.zadd('reversi:matchAny', this.timeService.now, me.id + ':noIrregularRules');
} else {
redisPipeline.zadd('reversi:matchAny', Date.now(), me.id);
redisPipeline.zadd('reversi:matchAny', this.timeService.now, me.id);
}
redisPipeline.expire('reversi:matchAny', 15, 'NX');
await redisPipeline.exec();
@ -224,7 +226,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
@bindThis
public async cleanOutdatedGames() {
await this.reversiGamesRepository.delete({
id: LessThan(this.idService.gen(Date.now() - 1000 * 60 * 10)),
id: LessThan(this.idService.gen(this.timeService.now - 1000 * 60 * 10)),
isStarted: false,
});
}
@ -269,7 +271,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
if (isBothReady) {
// 3秒後、両者readyならゲーム開始
setTimeout(async () => {
this.timeService.startTimer(async () => {
const freshGame = await this.get(game.id);
if (freshGame == null || freshGame.isStarted || freshGame.isEnded) return;
if (!freshGame.user1Ready || !freshGame.user2Ready) return;
@ -323,7 +325,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
const updatedGame = await this.reversiGamesRepository.createQueryBuilder().update()
.set({
...this.getBakeProps(game),
startedAt: new Date(),
startedAt: this.timeService.date,
isStarted: true,
black: bw,
map: game.map,
@ -368,7 +370,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
.set({
...this.getBakeProps(game),
isEnded: true,
endedAt: new Date(),
endedAt: this.timeService.date,
winnerId: winnerId,
surrenderedUserId: reason === 'surrender' ? (winnerId === game.user1Id ? game.user2Id : game.user1Id) : null,
timeoutUserId: reason === 'timeout' ? (winnerId === game.user1Id ? game.user2Id : game.user1Id) : null,
@ -392,7 +394,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
public async getInvitations(user: MiUser): Promise<MiUser['id'][]> {
const invitations = await this.redisClient.zrange(
`reversi:matchSpecific:${user.id}`,
Date.now() - INVITATION_TIMEOUT_MS,
this.timeService.now - INVITATION_TIMEOUT_MS,
'+inf',
'BYSCORE');
return invitations;
@ -475,7 +477,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
const logs = Reversi.Serializer.deserializeLogs(game.logs);
const log = {
time: Date.now(),
time: this.timeService.now,
player: myColor,
operation: 'put',
pos,

View file

@ -23,6 +23,7 @@ import { UtilityService } from '@/core/UtilityService.js';
import { UserService } from '@/core/UserService.js';
import { SystemAccountService } from '@/core/SystemAccountService.js';
import { MetaService } from '@/core/MetaService.js';
import { TimeService } from '@/core/TimeService.js';
@Injectable()
export class SignupService {
@ -46,6 +47,7 @@ export class SignupService {
private systemAccountService: SystemAccountService,
private metaService: MetaService,
private usersChart: UsersChart,
private readonly timeService: TimeService,
) {
}
@ -150,7 +152,7 @@ export class SignupService {
}));
await transactionalEntityManager.save(new MiUsedUsername({
createdAt: new Date(),
createdAt: this.timeService.date,
username: username.toLowerCase(),
}));
});

View file

@ -22,6 +22,7 @@ import { genRsaKeyPair } from '@/misc/gen-key-pair.js';
import { CacheManagementService, type ManagedMemoryKVCache } from '@/core/CacheManagementService.js';
import { CacheService } from '@/core/CacheService.js';
import { InternalEventService } from '@/core/InternalEventService.js';
import { TimeService } from '@/core/TimeService.js';
export const SYSTEM_ACCOUNT_TYPES = ['actor', 'relay', 'proxy'] as const;
@ -51,6 +52,8 @@ export class SystemAccountService implements OnApplicationShutdown {
private idService: IdService,
private readonly cacheService: CacheService,
private readonly internalEventService: InternalEventService,
private readonly timeService: TimeService,
cacheManagementService: CacheManagementService,
) {
this.cache = cacheManagementService.createMemoryKVCache<string>(1000 * 60 * 10); // 10m
@ -173,7 +176,7 @@ export class SystemAccountService implements OnApplicationShutdown {
});
await transactionalEntityManager.insert(MiUsedUsername, {
createdAt: new Date(),
createdAt: this.timeService.date,
username: extra.username.toLowerCase(),
});

View file

@ -20,6 +20,7 @@ import { AbuseReportResolveType } from '@/models/AbuseUserReport.js';
import { ModeratorInactivityRemainingTime } from '@/queue/processors/CheckModeratorsActivityProcessorService.js';
import { CacheManagementService, type ManagedMemorySingleCache } from '@/core/CacheManagementService.js';
import { InternalEventService } from '@/core/InternalEventService.js';
import { TimeService } from '@/core/TimeService.js';
import type { OnApplicationShutdown } from '@nestjs/common';
export type AbuseReportPayload = {
@ -64,6 +65,7 @@ export class SystemWebhookService implements OnApplicationShutdown {
private moderationLogService: ModerationLogService,
private globalEventService: GlobalEventService,
private readonly internalEventService: InternalEventService,
private readonly timeService: TimeService,
cacheManagementService: CacheManagementService,
) {
@ -156,7 +158,7 @@ export class SystemWebhookService implements OnApplicationShutdown {
): Promise<MiSystemWebhook> {
const beforeEntity = await this.systemWebhooksRepository.findOneByOrFail({ id: params.id });
await this.systemWebhooksRepository.update(beforeEntity.id, {
updatedAt: new Date(),
updatedAt: this.timeService.date,
isActive: params.isActive,
name: params.name,
on: params.on,

View file

@ -8,6 +8,7 @@ import { CollapsedQueue } from '@/misc/collapsed-queue.js';
import { bindThis } from '@/decorators.js';
import { MiNote } from '@/models/Note.js';
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
import { TimeService } from '@/core/TimeService.js';
type UpdateInstanceJob = {
latestRequestReceivedAt: Date,
@ -19,8 +20,9 @@ type UpdateInstanceJob = {
export class UpdateInstanceQueue extends CollapsedQueue<MiNote['id'], UpdateInstanceJob> implements OnApplicationShutdown {
constructor(
private readonly federatedInstanceService: FederatedInstanceService,
timeService: TimeService,
) {
super(process.env.NODE_ENV !== 'test' ? 60 * 1000 * 5 : 0, (id, job) => this.collapseUpdateInstanceJobs(id, job), (id, job) => this.performUpdateInstance(id, job));
super(timeService, process.env.NODE_ENV !== 'test' ? 60 * 1000 * 5 : 0, (id, job) => this.collapseUpdateInstanceJobs(id, job), (id, job) => this.performUpdateInstance(id, job));
}
@bindThis
@ -38,7 +40,7 @@ export class UpdateInstanceQueue extends CollapsedQueue<MiNote['id'], UpdateInst
@bindThis
private async performUpdateInstance(id: string, job: UpdateInstanceJob) {
await this.federatedInstanceService.update(id, {
latestRequestReceivedAt: new Date(),
latestRequestReceivedAt: this.timeService.date,
isNotResponding: false,
// もしサーバーが死んでるために配信が止まっていた場合には自動的に復活させてあげる
suspensionState: job.shouldUnsuspend ? 'none' : undefined,

View file

@ -11,12 +11,9 @@ import { bindThis } from '@/decorators.js';
import { sqlLikeEscape } from '@/misc/sql-like-escape.js';
import type { Config } from '@/config.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { TimeService } from '@/core/TimeService.js';
import { Packed } from '@/misc/json-schema.js';
function defaultActiveThreshold() {
return new Date(Date.now() - 1000 * 60 * 60 * 24 * 30);
}
@Injectable()
export class UserSearchService {
constructor(
@ -36,9 +33,14 @@ export class UserSearchService {
private mutingsRepository: MutingsRepository,
private userEntityService: UserEntityService,
private readonly timeService: TimeService,
) {
}
private defaultActiveThreshold() {
return new Date(this.timeService.now - 1000 * 60 * 60 * 24 * 30);
}
/**
* .
*
@ -120,7 +122,7 @@ export class UserSearchService {
},
) {
// デフォルト30日以内に更新されたユーザーをアクティブユーザーとする
const activeThreshold = params.activeThreshold ?? defaultActiveThreshold();
const activeThreshold = params.activeThreshold ?? this.defaultActiveThreshold();
const followingUserQuery = this.followingsRepository.createQueryBuilder('following')
.select('following.followeeId')
@ -166,7 +168,7 @@ export class UserSearchService {
activeThreshold?: Date,
}) {
// デフォルト30日以内に更新されたユーザーをアクティブユーザーとする
const activeThreshold = params.activeThreshold ?? defaultActiveThreshold();
const activeThreshold = params.activeThreshold ?? this.defaultActiveThreshold();
const activeUserQuery = this.generateUserQueryBuilder(params)
.andWhere(new Brackets(qb => {
@ -218,7 +220,7 @@ export class UserSearchService {
offset: number;
origin: 'local' | 'remote' | 'combined';
}> = {}) {
const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30日
const activeThreshold = new Date(this.timeService.now - (1000 * 60 * 60 * 24 * 30)); // 30日
const isUsername = query.startsWith('@') && !query.includes(' ') && query.indexOf('@', 1) === -1;

View file

@ -11,6 +11,7 @@ import { bindThis } from '@/decorators.js';
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { CacheService } from '@/core/CacheService.js';
import { TimeService } from '@/core/TimeService.js';
@Injectable()
export class UserService {
@ -22,6 +23,7 @@ export class UserService {
private systemWebhookService: SystemWebhookService,
private userEntityService: UserEntityService,
private readonly cacheService: CacheService,
private readonly timeService: TimeService,
) {
}
@ -30,7 +32,7 @@ export class UserService {
if (user.isHibernated) {
const result = await this.usersRepository.createQueryBuilder().update()
.set({
lastActiveDate: new Date(),
lastActiveDate: this.timeService.date,
})
.where('id = :id', { id: user.id })
.returning('*')
@ -54,7 +56,7 @@ export class UserService {
}
} else {
this.usersRepository.update(user.id, {
lastActiveDate: new Date(),
lastActiveDate: this.timeService.date,
});
}
}

View file

@ -18,6 +18,7 @@ import { bindThis } from '@/decorators.js';
import { MiUser } from '@/models/_.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import { LoggerService } from '@/core/LoggerService.js';
import { TimeService } from '@/core/TimeService.js';
import Logger from '@/logger.js';
import type {
AuthenticationResponseJSON,
@ -44,6 +45,9 @@ export class WebAuthnService {
@Inject(DI.userSecurityKeysRepository)
private userSecurityKeysRepository: UserSecurityKeysRepository,
private readonly timeService: TimeService,
loggerService: LoggerService,
) {
this.logger = loggerService.getLogger('web-authn');
@ -239,7 +243,7 @@ export class WebAuthnService {
await this.userSecurityKeysRepository.update({
id: response.id,
}, {
lastUsed: new Date(),
lastUsed: this.timeService.date,
counter: authenticationInfo.newCounter,
credentialDeviceType: authenticationInfo.credentialDeviceType,
credentialBackedUp: authenticationInfo.credentialBackedUp,
@ -321,7 +325,7 @@ export class WebAuthnService {
id: response.id,
userId: userId,
}, {
lastUsed: new Date(),
lastUsed: this.timeService.date,
counter: authenticationInfo.newCounter,
credentialDeviceType: authenticationInfo.credentialDeviceType,
credentialBackedUp: authenticationInfo.credentialBackedUp,

View file

@ -39,6 +39,7 @@ import { FetchInstanceMetadataService } from '@/core/FetchInstanceMetadataServic
import { UpdateInstanceQueue } from '@/core/UpdateInstanceQueue.js';
import { CacheService } from '@/core/CacheService.js';
import { NoteVisibilityService } from '@/core/NoteVisibilityService.js';
import { TimeService } from '@/core/TimeService.js';
import { getApHrefNullable, getApId, getApIds, getApType, getNullableApId, isAccept, isActor, isAdd, isAnnounce, isApObject, isBlock, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isDislike, isMove, isPost, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost, isActivity, IObjectWithId } from './type.js';
import { ApNoteService } from './models/ApNoteService.js';
import { ApLoggerService } from './ApLoggerService.js';
@ -102,6 +103,7 @@ export class ApInboxService {
private readonly updateInstanceQueue: UpdateInstanceQueue,
private readonly cacheService: CacheService,
private readonly noteVisibilityService: NoteVisibilityService,
private readonly timeService: TimeService,
) {
this.logger = this.apLoggerService.logger;
}
@ -150,7 +152,7 @@ export class ApInboxService {
// ついでにリモートユーザーの情報が古かったら更新しておく
if (actor.uri) {
if (actor.lastFetchedAt == null || Date.now() - actor.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) {
if (actor.lastFetchedAt == null || this.timeService.now - actor.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) {
setImmediate(() => {
// 同一ユーザーの情報を再度処理するので、使用済みのresolverを再利用してはいけない
this.apPersonService.updatePerson(actor.uri)
@ -431,7 +433,7 @@ export class ApInboxService {
if (i == null) return;
this.updateInstanceQueue.enqueue(i.id, {
latestRequestReceivedAt: new Date(),
latestRequestReceivedAt: this.timeService.date,
shouldUnsuspend: i.suspensionState === 'autoSuspendedForNotResponding',
});
@ -446,7 +448,7 @@ export class ApInboxService {
return await this.performOneActivity(actor, activity, resolver)
.finally(() => {
// Update user (adapted from performActivity)
if (actor.lastFetchedAt == null || Date.now() - actor.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) {
if (actor.lastFetchedAt == null || this.timeService.now - actor.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) {
setImmediate(() => {
// Don't re-use the resolver, or it may throw recursion errors.
// Instead, create a new resolver with an appropriately-reduced recursion limit.

View file

@ -35,6 +35,7 @@ import { UtilityService } from '@/core/UtilityService.js';
import { CacheService } from '@/core/CacheService.js';
import { isPureRenote, isQuote, isRenote } from '@/misc/is-renote.js';
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
import { TimeService } from '@/core/TimeService.js';
import { JsonLdService } from './JsonLdService.js';
import { ApMfmService } from './ApMfmService.js';
import { CONTEXT } from './misc/contexts.js';
@ -80,6 +81,7 @@ export class ApRendererService {
private readonly queryService: QueryService,
private readonly cacheService: CacheService,
private readonly federatedInstanceService: FederatedInstanceService,
private readonly timeService: TimeService,
) {
}
@ -174,7 +176,7 @@ export class ApRendererService {
type: 'Delete',
actor: this.userEntityService.genLocalUserUri(user.id),
object,
published: new Date().toISOString(),
published: this.timeService.date.toISOString(),
};
}
@ -196,7 +198,7 @@ export class ApRendererService {
id: `${this.config.url}/emojis/${emoji.name}`,
type: 'Emoji',
name: `:${emoji.name}:`,
updated: emoji.updatedAt != null ? emoji.updatedAt.toISOString() : new Date().toISOString(),
updated: emoji.updatedAt != null ? emoji.updatedAt.toISOString() : this.timeService.date.toISOString(),
icon: {
type: 'Image',
mediaType: emoji.type ?? 'image/png',
@ -534,7 +536,7 @@ export class ApRendererService {
const asPoll = poll ? {
type: 'Question',
[poll.expiresAt && poll.expiresAt < new Date() ? 'closed' : 'endTime']: poll.expiresAt,
[poll.expiresAt && poll.expiresAt < this.timeService.date ? 'closed' : 'endTime']: poll.expiresAt,
[poll.multiple ? 'anyOf' : 'oneOf']: poll.choices.map((text, i) => ({
type: 'Note',
name: text,
@ -755,21 +757,21 @@ export class ApRendererService {
...(id ? { id } : {}),
actor: this.userEntityService.genLocalUserUri(user.id),
object,
published: new Date().toISOString(),
published: this.timeService.date.toISOString(),
};
}
@bindThis
public renderUpdate(object: IObject, user: { id: MiUser['id'] }): IUpdate {
// Deterministic activity IDs to allow de-duplication by remote instances
const updatedAt = object.updated ? new Date(object.updated).getTime() : Date.now();
const updatedAt = object.updated ? new Date(object.updated).getTime() : this.timeService.now;
return {
id: `${this.config.url}/users/${user.id}#updates/${updatedAt}`,
actor: this.userEntityService.genLocalUserUri(user.id),
type: 'Update',
to: ['https://www.w3.org/ns/activitystreams#Public'],
object,
published: new Date().toISOString(),
published: this.timeService.date.toISOString(),
};
}
@ -780,7 +782,7 @@ export class ApRendererService {
actor: this.userEntityService.genLocalUserUri(user.id),
type: 'Create',
to: [pollOwner.uri],
published: new Date().toISOString(),
published: this.timeService.date.toISOString(),
object: {
id: `${this.config.url}/users/${user.id}#votes/${vote.id}`,
type: 'Note',

View file

@ -11,6 +11,7 @@ import { bindThis } from '@/decorators.js';
import Logger from '@/logger.js';
import { LoggerService } from '@/core/LoggerService.js';
import { StatusError } from '@/misc/status-error.js';
import { TimeService } from '@/core/TimeService.js';
import { CONTEXT, PRELOADED_CONTEXTS } from './misc/contexts.js';
import { validateContentTypeSetAsJsonLD } from './misc/validator.js';
import type { ContextDefinition, JsonLdDocument } from 'jsonld';
@ -56,6 +57,8 @@ export class JsonLdService {
constructor(
private httpRequestService: HttpRequestService,
private readonly timeService: TimeService,
loggerService: LoggerService,
) {
this.logger = loggerService.getLogger('json-ld');
@ -73,7 +76,7 @@ export class JsonLdService {
type: 'RsaSignature2017',
creator,
nonce: crypto.randomBytes(16).toString('hex'),
created: (created ?? new Date()).toISOString(),
created: (created ?? this.timeService.date).toISOString(),
};
if (domain) {

View file

@ -33,6 +33,7 @@ import { extractMediaFromHtml } from '@/core/activitypub/misc/extract-media-from
import { extractMediaFromMfm } from '@/core/activitypub/misc/extract-media-from-mfm.js';
import { getContentByType } from '@/core/activitypub/misc/get-content-by-type.js';
import { CustomEmojiService, encodeEmojiKey, isValidEmojiName } from '@/core/CustomEmojiService.js';
import { TimeService } from '@/core/TimeService.js';
import { getOneApId, getApId, validPost, isEmoji, getApType, isApObject, isDocument, IApDocument, isLink } from '../type.js';
import { ApLoggerService } from '../ApLoggerService.js';
import { ApMfmService } from '../ApMfmService.js';
@ -91,6 +92,7 @@ export class ApNoteService implements OnModuleInit {
private apLoggerService: ApLoggerService,
private readonly apUtilityService: ApUtilityService,
private readonly customEmojiService: CustomEmojiService,
private readonly timeService: TimeService,
) {
this.logger = this.apLoggerService.logger;
}
@ -291,7 +293,7 @@ export class ApNoteService implements OnModuleInit {
const poll = await this.pollsRepository.findOneByOrFail({ noteId: reply.id });
const tryCreateVote = async (name: string, index: number): Promise<null> => {
if (poll.expiresAt && Date.now() > new Date(poll.expiresAt).getTime()) {
if (poll.expiresAt && this.timeService.now > new Date(poll.expiresAt).getTime()) {
this.logger.warn(`vote to expired poll from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`);
} else if (index >= 0) {
this.logger.info(`vote from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`);
@ -470,7 +472,7 @@ export class ApNoteService implements OnModuleInit {
const poll = await this.pollsRepository.findOneByOrFail({ noteId: reply.id });
const tryCreateVote = async (name: string, index: number): Promise<null> => {
if (poll.expiresAt && Date.now() > new Date(poll.expiresAt).getTime()) {
if (poll.expiresAt && this.timeService.now > new Date(poll.expiresAt).getTime()) {
this.logger.warn(`vote to expired poll from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`);
} else if (index >= 0) {
this.logger.info(`vote from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`);
@ -601,7 +603,7 @@ export class ApNoteService implements OnModuleInit {
uri: tag.id,
originalUrl: tag.icon.url,
publicUrl: tag.icon.url,
updatedAt: new Date(),
updatedAt: this.timeService.date,
// _misskey_license が存在しなければ `null`
license: (tag._misskey_license?.freeText ?? null),
});
@ -617,7 +619,7 @@ export class ApNoteService implements OnModuleInit {
uri: tag.id,
originalUrl: tag.icon.url,
publicUrl: tag.icon.url,
updatedAt: new Date(),
updatedAt: this.timeService.date,
aliases: [],
localOnly: false,
isSensitive: tag.sensitive === true,

View file

@ -41,6 +41,7 @@ import type { AccountMoveService } from '@/core/AccountMoveService.js';
import { ApUtilityService } from '@/core/activitypub/ApUtilityService.js';
import { AppLockService } from '@/core/AppLockService.js';
import { HttpRequestService } from '@/core/HttpRequestService.js';
import { TimeService } from '@/core/TimeService.js';
import { verifyFieldLinks } from '@/misc/verify-field-link.js';
import { isRetryableError } from '@/misc/is-retryable-error.js';
import { renderInlineError } from '@/misc/render-inline-error.js';
@ -116,6 +117,7 @@ export class ApPersonService implements OnModuleInit {
private readonly utilityService: UtilityService,
private readonly apUtilityService: ApUtilityService,
private readonly idService: IdService,
private readonly timeService: TimeService,
apLoggerService: ApLoggerService,
) {
@ -419,13 +421,13 @@ export class ApPersonService implements OnModuleInit {
avatarId: null,
bannerId: null,
backgroundId: null,
lastFetchedAt: new Date(),
lastFetchedAt: this.timeService.date,
name: truncate(person.name, nameLength),
noindex: (person as any).noindex ?? false,
enableRss: person.enableRss === true,
isLocked: person.manuallyApprovesFollowers,
movedToUri: person.movedTo,
movedAt: person.movedTo ? new Date() : null,
movedAt: person.movedTo ? this.timeService.date : null,
alsoKnownAs: person.alsoKnownAs,
// We use "!== false" to handle incorrect types, missing / null values, and "default to true" logic.
hideOnlineStatus: person.hideOnlineStatus !== false,
@ -634,7 +636,7 @@ export class ApPersonService implements OnModuleInit {
const verifiedLinks = await verifyFieldLinks(fields, profileUrls, this.httpRequestService);
const updates = {
lastFetchedAt: new Date(),
lastFetchedAt: this.timeService.date,
inbox: person.inbox,
sharedInbox: person.sharedInbox ?? person.endpoints?.sharedInbox ?? null,
followersUri: person.followers ? getApId(person.followers) : undefined,
@ -688,7 +690,7 @@ export class ApPersonService implements OnModuleInit {
return false;
})();
if (moving) updates.movedAt = new Date();
if (moving) updates.movedAt = this.timeService.date;
// Update user
if (!(await this.usersRepository.update({ id: exist.id, isDeleted: false }, updates)).affected) {
@ -894,7 +896,7 @@ export class ApPersonService implements OnModuleInit {
for (const note of featuredNotes.filter(x => x != null)) {
td -= 1000;
transactionalEntityManager.insert(MiUserNotePining, {
id: this.idService.gen(Date.now() + td),
id: this.idService.gen(this.timeService.now + td),
userId: user.id,
noteId: note.id,
});

View file

@ -7,6 +7,7 @@ import { Injectable } from '@nestjs/common';
import { bindThis } from '@/decorators.js';
import { ChartLoggerService } from '@/core/chart/ChartLoggerService.js';
import { TimeService, type TimerHandle } from '@/core/TimeService.js';
import Logger from '@/logger.js';
import { renderInlineError } from '@/misc/render-inline-error.js';
import FederationChart from './charts/federation.js';
@ -26,7 +27,7 @@ import type { OnApplicationShutdown } from '@nestjs/common';
@Injectable()
export class ChartManagementService implements OnApplicationShutdown {
private charts;
private saveIntervalId: NodeJS.Timeout;
private saveIntervalId: TimerHandle;
private readonly logger: Logger;
constructor(
@ -42,6 +43,8 @@ export class ChartManagementService implements OnApplicationShutdown {
private perUserFollowingChart: PerUserFollowingChart,
private perUserDriveChart: PerUserDriveChart,
private apRequestChart: ApRequestChart,
private readonly timeService: TimeService,
chartLoggerService: ChartLoggerService,
) {
this.charts = [
@ -64,17 +67,17 @@ export class ChartManagementService implements OnApplicationShutdown {
@bindThis
public async start() {
// 20分おきにメモリ情報をDBに書き込み
this.saveIntervalId = setInterval(async () => {
this.saveIntervalId = this.timeService.startTimer(async () => {
for (const chart of this.charts) {
await chart.save();
}
this.logger.info('All charts saved');
}, 1000 * 60 * 20);
}, 1000 * 60 * 20, { repeated: true });
}
@bindThis
public async dispose(): Promise<void> {
clearInterval(this.saveIntervalId);
this.timeService.stopTimer(this.saveIntervalId);
if (process.env.NODE_ENV !== 'test') {
this.logger.info('Saving charts for shutdown...');
for (const chart of this.charts) {

View file

@ -54,12 +54,12 @@ export default class ActiveUsersChart extends Chart<typeof schema> { // eslint-d
const createdAt = this.idService.parse(user.id).date;
await this.commit({
'read': [user.id],
'registeredWithinWeek': (Date.now() - createdAt.getTime() < week) ? [user.id] : [],
'registeredWithinMonth': (Date.now() - createdAt.getTime() < month) ? [user.id] : [],
'registeredWithinYear': (Date.now() - createdAt.getTime() < year) ? [user.id] : [],
'registeredOutsideWeek': (Date.now() - createdAt.getTime() > week) ? [user.id] : [],
'registeredOutsideMonth': (Date.now() - createdAt.getTime() > month) ? [user.id] : [],
'registeredOutsideYear': (Date.now() - createdAt.getTime() > year) ? [user.id] : [],
'registeredWithinWeek': (this.timeService.now - createdAt.getTime() < week) ? [user.id] : [],
'registeredWithinMonth': (this.timeService.now - createdAt.getTime() < month) ? [user.id] : [],
'registeredWithinYear': (this.timeService.now - createdAt.getTime() < year) ? [user.id] : [],
'registeredOutsideWeek': (this.timeService.now - createdAt.getTime() > week) ? [user.id] : [],
'registeredOutsideMonth': (this.timeService.now - createdAt.getTime() > month) ? [user.id] : [],
'registeredOutsideYear': (this.timeService.now - createdAt.getTime() > year) ? [user.id] : [],
});
}

View file

@ -14,6 +14,7 @@ import { SystemAccountService } from '@/core/SystemAccountService.js';
import type { Config } from '@/config.js';
import { DI } from '@/di-symbols.js';
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
import { TimeService } from '@/core/TimeService.js';
@Injectable()
export class MetaEntityService {
@ -28,6 +29,7 @@ export class MetaEntityService {
private adsRepository: AdsRepository,
private systemAccountService: SystemAccountService,
private readonly timeService: TimeService,
) { }
@bindThis
@ -39,11 +41,11 @@ export class MetaEntityService {
}
const ads = await this.adsRepository.createQueryBuilder('ads')
.where('ads.expiresAt > :now', { now: new Date() })
.andWhere('ads.startsAt <= :now', { now: new Date() })
.where('ads.expiresAt > :now', { now: this.timeService.date })
.andWhere('ads.startsAt <= :now', { now: this.timeService.date })
.andWhere(new Brackets(qb => {
// 曜日のビットフラグを確認する
qb.where('ads.dayOfWeek & :dayOfWeek > 0', { dayOfWeek: 1 << new Date().getDay() })
qb.where('ads.dayOfWeek & :dayOfWeek > 0', { dayOfWeek: 1 << this.timeService.date.getDay() })
.orWhere('ads.dayOfWeek = 0');
}))
.getMany();

View file

@ -17,6 +17,7 @@ import { DebounceLoader } from '@/misc/loader.js';
import type { IdService } from '@/core/IdService.js';
import type { ReactionsBufferingService } from '@/core/ReactionsBufferingService.js';
import { QueryService } from '@/core/QueryService.js';
import { TimeService } from '@/core/TimeService.js';
import type { Config } from '@/config.js';
import { NoteVisibilityService } from '@/core/NoteVisibilityService.js';
import type { NoteVisibilityData } from '@/core/NoteVisibilityService.js';
@ -106,6 +107,7 @@ export class NoteEntityService implements OnModuleInit {
public readonly noteVisibilityService: NoteVisibilityService,
private readonly queryService: QueryService,
private readonly timeService: TimeService,
//private userEntityService: UserEntityService,
//private driveFileEntityService: DriveFileEntityService,
//private customEmojiService: CustomEmojiService,
@ -134,7 +136,7 @@ export class NoteEntityService implements OnModuleInit {
const followersOnlyBefore = packedNote.user.makeNotesFollowersOnlyBefore;
if ((followersOnlyBefore != null)
&& (
(followersOnlyBefore <= 0 && (Date.now() - new Date(packedNote.createdAt).getTime() > 0 - (followersOnlyBefore * 1000)))
(followersOnlyBefore <= 0 && (this.timeService.now - new Date(packedNote.createdAt).getTime() > 0 - (followersOnlyBefore * 1000)))
|| (followersOnlyBefore > 0 && (new Date(packedNote.createdAt).getTime() < followersOnlyBefore * 1000))
)
) {
@ -388,7 +390,7 @@ export class NoteEntityService implements OnModuleInit {
}
// パフォーマンスのためートが作成されてから2秒以上経っていない場合はリアクションを取得しない
if (this.idService.parse(note.id).date.getTime() + 2000 > Date.now()) {
if (this.idService.parse(note.id).date.getTime() + 2000 > this.timeService.now) {
return undefined;
}

View file

@ -14,6 +14,7 @@ import { bindThis } from '@/decorators.js';
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
import { IdService } from '@/core/IdService.js';
import { Packed } from '@/misc/json-schema.js';
import { TimeService } from '@/core/TimeService.js';
@Injectable()
export class RoleEntityService {
@ -25,6 +26,7 @@ export class RoleEntityService {
private roleAssignmentsRepository: RoleAssignmentsRepository,
private idService: IdService,
private readonly timeService: TimeService,
) {
}
@ -40,7 +42,7 @@ export class RoleEntityService {
.andWhere(new Brackets(qb => {
qb
.where('assign.expiresAt IS NULL')
.orWhere('assign.expiresAt > :now', { now: new Date() });
.orWhere('assign.expiresAt > :now', { now: this.timeService.date });
}))
.getCount();

View file

@ -49,6 +49,7 @@ import type { RolePolicies, RoleService } from '@/core/RoleService.js';
import type { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
import type { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
import type { IdService } from '@/core/IdService.js';
import { TimeService } from '@/core/TimeService.js';
import type { AnnouncementService } from '@/core/AnnouncementService.js';
import type { CustomEmojiService } from '@/core/CustomEmojiService.js';
import type { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
@ -153,6 +154,8 @@ export class UserEntityService implements OnModuleInit {
@Inject(DI.userMemosRepository)
private userMemosRepository: UserMemoRepository,
private readonly timeService: TimeService,
) {
}
@ -398,7 +401,7 @@ export class UserEntityService implements OnModuleInit {
public getOnlineStatus(user: MiUser): 'unknown' | 'online' | 'active' | 'offline' {
if (user.hideOnlineStatus) return 'unknown';
if (user.lastActiveDate == null) return 'unknown';
const elapsed = Date.now() - user.lastActiveDate.getTime();
const elapsed = this.timeService.now - user.lastActiveDate.getTime();
return (
elapsed < USER_ONLINE_THRESHOLD ? 'online' :
elapsed < USER_ACTIVE_THRESHOLD ? 'active' :

View file

@ -8,6 +8,7 @@ import { bindThis } from '@/decorators.js';
import { LoggerService } from '@/core/LoggerService.js';
import Logger from '@/logger.js';
import { ApLogService } from '@/core/ApLogService.js';
import { TimeService, type TimerHandle } from '@/core/TimeService.js';
// 10 minutes
export const scanInterval = 1000 * 60 * 10;
@ -15,10 +16,12 @@ export const scanInterval = 1000 * 60 * 10;
@Injectable()
export class ApLogCleanupService implements OnApplicationShutdown {
private readonly logger: Logger;
private scanTimer: NodeJS.Timeout | null = null;
private scanTimer: TimerHandle | null = null;
constructor(
private readonly apLogService: ApLogService,
private readonly timeService: TimeService,
loggerService: LoggerService,
) {
this.logger = loggerService.getLogger('activity-log-cleanup');
@ -34,7 +37,7 @@ export class ApLogCleanupService implements OnApplicationShutdown {
this.tick();
// Prune on a regular interval for the lifetime of the server.
this.scanTimer = setInterval(this.tick, scanInterval);
this.scanTimer = this.timeService.startTimer(this.tick, scanInterval, { repeated: true });
}
@bindThis
@ -55,7 +58,7 @@ export class ApLogCleanupService implements OnApplicationShutdown {
@bindThis
public dispose(): void {
if (this.scanTimer) {
clearInterval(this.scanTimer);
this.timeService.stopTimer(this.scanTimer);
this.scanTimer = null;
}
}

View file

@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
import Xev from 'xev';
import * as Bull from 'bullmq';
import { QueueService } from '@/core/QueueService.js';
import { TimeService, type TimerHandle } from '@/core/TimeService.js';
import { bindThis } from '@/decorators.js';
import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js';
@ -31,7 +32,7 @@ const interval = 10000;
@Injectable()
export class QueueStatsService implements OnApplicationShutdown {
private intervalId?: NodeJS.Timeout;
private intervalId?: TimerHandle;
private activeDeliverJobs = 0;
private activeInboxJobs = 0;
@ -45,6 +46,7 @@ export class QueueStatsService implements OnApplicationShutdown {
private config: Config,
private queueService: QueueService,
private readonly timeService: TimeService,
) {
}
@ -114,13 +116,13 @@ export class QueueStatsService implements OnApplicationShutdown {
tick();
this.intervalId = setInterval(tick, interval);
this.intervalId = this.timeService.startTimer(tick, interval, { repeated: true });
}
@bindThis
public async stop() {
if (this.intervalId) {
clearInterval(this.intervalId);
this.timeService.stopTimer(this.intervalId);
}
this.log = undefined;

View file

@ -11,6 +11,7 @@ import { bindThis } from '@/decorators.js';
import type { OnApplicationShutdown } from '@nestjs/common';
import { MiMeta } from '@/models/_.js';
import { DI } from '@/di-symbols.js';
import { TimeService, type TimerHandle } from '@/core/TimeService.js';
export interface Stats {
cpu: number,
@ -37,13 +38,14 @@ const round = (num: number) => Math.round(num * 10) / 10;
@Injectable()
export class ServerStatsService implements OnApplicationShutdown {
private intervalId: NodeJS.Timeout | null = null;
private intervalId: TimerHandle | null = null;
private log: Stats[] = [];
constructor(
@Inject(DI.meta)
private meta: MiMeta,
private readonly timeService: TimeService,
) {
}
@ -90,13 +92,13 @@ export class ServerStatsService implements OnApplicationShutdown {
tick();
this.intervalId = setInterval(tick, interval);
this.intervalId = this.timeService.startTimer(tick, interval, { repeated: true });
}
@bindThis
public dispose(): void {
if (this.intervalId) {
clearInterval(this.intervalId);
this.timeService.stopTimer(this.intervalId);
}
this.log = [];

View file

@ -8,6 +8,7 @@ import chalk from 'chalk';
import { default as convertColor } from 'color-convert';
import { format as dateFormat } from 'date-fns';
import { bindThis } from '@/decorators.js';
import { TimeService, NativeTimeService } from '@/core/TimeService.js';
import { envOption } from './env.js';
import type { KEYWORD } from 'color-convert/conversions.js';
@ -26,6 +27,8 @@ export type DataObject = Record<string, unknown> | (object & { length?: never; }
export type Console = Pick<typeof global.console, 'error' | 'warn' | 'info' | 'log' | 'debug'>;
export const nativeConsole: Console = global.console;
const fallbackTimeService = new NativeTimeService();
const levelFuncs = {
error: 'error',
warning: 'warn',
@ -39,6 +42,7 @@ export default class Logger {
private context: Context;
private parentLogger: Logger | null = null;
public readonly verbose: boolean;
private readonly timeService: TimeService;
/**
* Where to send the actual log strings.
@ -46,18 +50,19 @@ export default class Logger {
*/
private readonly console: Console;
constructor(context: string, color?: KEYWORD, verbose?: boolean, console?: Console) {
constructor(context: string, color?: KEYWORD, verbose?: boolean, console?: Console, timeService?: TimeService) {
this.context = {
name: context,
color: color,
};
this.verbose = verbose ?? envOption.verbose;
this.console = console ?? nativeConsole;
this.timeService = timeService ?? fallbackTimeService;
}
@bindThis
public createSubLogger(context: string, color?: KEYWORD): Logger {
const logger = new Logger(context, color, this.verbose);
const logger = new Logger(context, color, this.verbose, this.console, this.timeService);
logger.parentLogger = this;
return logger;
}
@ -71,7 +76,7 @@ export default class Logger {
return;
}
const time = dateFormat(new Date(), 'HH:mm:ss');
const time = dateFormat(this.timeService.date, 'HH:mm:ss');
const worker = cluster.isPrimary ? '*' : cluster.worker!.id;
const l =
level === 'error' ? important ? chalk.bgRed.white('ERR ') : chalk.red('ERR ') :

View file

@ -3,9 +3,11 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { TimeService, TimerHandle } from '@/core/TimeService.js';
type Job<V> = {
value: V;
timer: NodeJS.Timeout;
timer: TimerHandle;
};
// TODO: redis使えるようにする
@ -13,6 +15,7 @@ export class CollapsedQueue<K, V> {
private jobs: Map<K, Job<V>> = new Map();
constructor(
protected readonly timeService: TimeService,
private timeout: number,
private collapse: (oldValue: V, newValue: V) => V,
private perform: (key: K, value: V) => Promise<void>,
@ -24,7 +27,7 @@ export class CollapsedQueue<K, V> {
const merged = this.collapse(old.value, value);
this.jobs.set(key, { ...old, value: merged });
} else {
const timer = setTimeout(() => {
const timer = this.timeService.startTimer(() => {
const job = this.jobs.get(key)!;
this.jobs.delete(key);
this.perform(key, job.value);
@ -37,7 +40,7 @@ export class CollapsedQueue<K, V> {
const entries = [...this.jobs.entries()];
this.jobs.clear();
for (const [_key, job] of entries) {
clearTimeout(job.timer);
this.timeService.stopTimer(job.timer);
}
await Promise.allSettled(entries.map(([key, job]) => this.perform(key, job.value)));
}

View file

@ -11,6 +11,7 @@ import { bindThis } from '@/decorators.js';
import type { RetentionAggregationsRepository, UsersRepository } from '@/models/_.js';
import { deepClone } from '@/misc/clone.js';
import { IdService } from '@/core/IdService.js';
import { TimeService } from '@/core/TimeService.js';
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type * as Bull from 'bullmq';
@ -28,6 +29,7 @@ export class AggregateRetentionProcessorService {
private idService: IdService,
private queueLoggerService: QueueLoggerService,
private readonly timeService: TimeService,
) {
this.logger = this.queueLoggerService.logger.createSubLogger('aggregate-retention');
}
@ -36,18 +38,18 @@ export class AggregateRetentionProcessorService {
public async process(): Promise<void> {
this.logger.info('Aggregating retention...');
const now = new Date();
const now = this.timeService.date;
const dateKey = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`;
// 過去(だいたい)30日分のレコードを取得
const pastRecords = await this.retentionAggregationsRepository.findBy({
createdAt: MoreThan(new Date(Date.now() - (1000 * 60 * 60 * 24 * 31))),
createdAt: MoreThan(new Date(this.timeService.now - (1000 * 60 * 60 * 24 * 31))),
});
// 今日登録したユーザーを全て取得
const targetUsers = await this.usersRepository.findBy({
host: IsNull(),
id: MoreThan(this.idService.gen(Date.now() - (1000 * 60 * 60 * 24))),
id: MoreThan(this.idService.gen(this.timeService.now - (1000 * 60 * 60 * 24))),
});
const targetUserIds = targetUsers.map(u => u.id);
@ -71,7 +73,7 @@ export class AggregateRetentionProcessorService {
// 今日活動したユーザーを全て取得
const activeUsers = await this.usersRepository.findBy({
host: IsNull(),
lastActiveDate: MoreThan(new Date(Date.now() - (1000 * 60 * 60 * 24))),
lastActiveDate: MoreThan(new Date(this.timeService.now - (1000 * 60 * 60 * 24))),
});
const activeUsersIds = activeUsers.map(u => u.id);

View file

@ -10,6 +10,7 @@ import type { MutingsRepository } from '@/models/_.js';
import type Logger from '@/logger.js';
import { bindThis } from '@/decorators.js';
import { UserMutingService } from '@/core/UserMutingService.js';
import { TimeService } from '@/core/TimeService.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type * as Bull from 'bullmq';
@ -23,6 +24,7 @@ export class CheckExpiredMutingsProcessorService {
private userMutingService: UserMutingService,
private queueLoggerService: QueueLoggerService,
private readonly timeService: TimeService,
) {
this.logger = this.queueLoggerService.logger.createSubLogger('check-expired-mutings');
}
@ -33,7 +35,7 @@ export class CheckExpiredMutingsProcessorService {
const expired = await this.mutingsRepository.createQueryBuilder('muting')
.where('muting.expiresAt IS NOT NULL')
.andWhere('muting.expiresAt < :now', { now: new Date() })
.andWhere('muting.expiresAt < :now', { now: this.timeService.date })
.innerJoinAndSelect('muting.mutee', 'mutee')
.getMany();

View file

@ -12,6 +12,7 @@ import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import type { Config } from '@/config.js';
import { ReversiService } from '@/core/ReversiService.js';
import { TimeService } from '@/core/TimeService.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type * as Bull from 'bullmq';
@ -35,6 +36,7 @@ export class CleanProcessorService {
private queueLoggerService: QueueLoggerService,
private reversiService: ReversiService,
private idService: IdService,
private readonly timeService: TimeService,
) {
this.logger = this.queueLoggerService.logger.createSubLogger('clean');
}
@ -44,13 +46,13 @@ export class CleanProcessorService {
this.logger.info('Cleaning...');
this.userIpsRepository.delete({
createdAt: LessThan(new Date(Date.now() - (1000 * 60 * 60 * 24 * 90))),
createdAt: LessThan(new Date(this.timeService.now - (1000 * 60 * 60 * 24 * 90))),
});
// 使われてないアンテナを停止
if (this.config.deactivateAntennaThreshold > 0) {
this.antennasRepository.update({
lastUsedAt: LessThan(new Date(Date.now() - this.config.deactivateAntennaThreshold)),
lastUsedAt: LessThan(new Date(this.timeService.now - this.config.deactivateAntennaThreshold)),
}, {
isActive: false,
});
@ -58,7 +60,7 @@ export class CleanProcessorService {
const expiredRoleAssignments = await this.roleAssignmentsRepository.createQueryBuilder('assign')
.where('assign.expiresAt IS NOT NULL')
.andWhere('assign.expiresAt < :now', { now: new Date() })
.andWhere('assign.expiresAt < :now', { now: this.timeService.date })
.getMany();
if (expiredRoleAssignments.length > 0) {

View file

@ -10,11 +10,12 @@ import type { MiDriveFile, DriveFilesRepository } from '@/models/_.js';
import { MiUser } from '@/models/_.js';
import type Logger from '@/logger.js';
import { DriveService } from '@/core/DriveService.js';
import { IdService } from '@/core/IdService.js';
import { TimeService } from '@/core/TimeService.js';
import { bindThis } from '@/decorators.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type * as Bull from 'bullmq';
import type { CleanRemoteFilesJobData } from '../types.js';
import { IdService } from '@/core/IdService.js';
@Injectable()
export class CleanRemoteFilesProcessorService {
@ -27,6 +28,7 @@ export class CleanRemoteFilesProcessorService {
private driveService: DriveService,
private queueLoggerService: QueueLoggerService,
private idService: IdService,
private readonly timeService: TimeService,
) {
this.logger = this.queueLoggerService.logger.createSubLogger('clean-remote-files');
}
@ -35,7 +37,7 @@ export class CleanRemoteFilesProcessorService {
public async process(job: Bull.Job<CleanRemoteFilesJobData>): Promise<void> {
this.logger.info('Deleting cached remote files...');
const olderThanTimestamp = Date.now() - (job.data.olderThanSeconds ?? 0) * 1000;
const olderThanTimestamp = this.timeService.now - (job.data.olderThanSeconds ?? 0) * 1000;
const olderThanDate = new Date(olderThanTimestamp);
const keepFilesInUse = job.data.keepFilesInUse ?? false;
let deletedCount = 0;

View file

@ -19,6 +19,7 @@ import ApRequestChart from '@/core/chart/charts/ap-request.js';
import FederationChart from '@/core/chart/charts/federation.js';
import { StatusError } from '@/misc/status-error.js';
import { UtilityService } from '@/core/UtilityService.js';
import { TimeService } from '@/core/TimeService.js';
import { bindThis } from '@/decorators.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type { DeliverJobData } from '../types.js';
@ -42,6 +43,7 @@ export class DeliverProcessorService {
private apRequestChart: ApRequestChart,
private federationChart: FederationChart,
private queueLoggerService: QueueLoggerService,
private readonly timeService: TimeService,
) {
this.logger = this.queueLoggerService.logger.createSubLogger('deliver');
}
@ -99,11 +101,11 @@ export class DeliverProcessorService {
if (!i.isNotResponding) {
this.federatedInstanceService.update(i.id, {
isNotResponding: true,
notRespondingSince: new Date(),
notRespondingSince: this.timeService.date,
});
} else if (i.notRespondingSince) {
// 1週間以上不通ならサスペンド
if (i.suspensionState === 'none' && i.notRespondingSince.getTime() <= Date.now() - 1000 * 60 * 60 * 24 * 7) {
if (i.suspensionState === 'none' && i.notRespondingSince.getTime() <= this.timeService.now - 1000 * 60 * 60 * 24 * 7) {
this.federatedInstanceService.update(i.id, {
suspensionState: 'autoSuspendedForNotResponding',
});
@ -112,7 +114,7 @@ export class DeliverProcessorService {
// isNotRespondingがtrueでnotRespondingSinceがnullの場合はnotRespondingSinceをセット
// notRespondingSinceは新たな機能なので、それ以前のデータにはnotRespondingSinceがない場合がある
this.federatedInstanceService.update(i.id, {
notRespondingSince: new Date(),
notRespondingSince: this.timeService.date,
});
}

View file

@ -22,6 +22,7 @@ import { Packed } from '@/misc/json-schema.js';
import { UtilityService } from '@/core/UtilityService.js';
import { DownloadService } from '@/core/DownloadService.js';
import { EmailService } from '@/core/EmailService.js';
import { TimeService } from '@/core/TimeService.js';
import { renderInlineError } from '@/misc/render-inline-error.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type * as Bull from 'bullmq';
@ -80,6 +81,7 @@ export class ExportAccountDataProcessorService {
private downloadService: DownloadService,
private emailService: EmailService,
private queueLoggerService: QueueLoggerService,
private readonly timeService: TimeService,
) {
this.logger = this.queueLoggerService.logger.createSubLogger('export-account-data');
}
@ -125,7 +127,7 @@ export class ExportAccountDataProcessorService {
});
};
await writeUser(`{"metaVersion":1,"host":"${this.config.host}","exportedAt":"${new Date().toString()}","user":[`);
await writeUser(`{"metaVersion":1,"host":"${this.config.host}","exportedAt":"${this.timeService.date.toString()}","user":[`);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { host, uri, sharedInbox, followersUri, lastFetchedAt, inbox, ...userTrimmed } = user;
@ -160,7 +162,7 @@ export class ExportAccountDataProcessorService {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { emailVerifyCode, twoFactorBackupSecret, twoFactorSecret, password, twoFactorTempSecret, userHost, ...profileTrimmed } = profile;
await writeProfile(`{"metaVersion":1,"host":"${this.config.host}","exportedAt":"${new Date().toString()}","profile":[`);
await writeProfile(`{"metaVersion":1,"host":"${this.config.host}","exportedAt":"${this.timeService.date.toString()}","profile":[`);
await writeProfile(JSON.stringify(profileTrimmed));
@ -191,7 +193,7 @@ export class ExportAccountDataProcessorService {
});
};
await writeIPs(`{"metaVersion":1,"host":"${this.config.host}","exportedAt":"${new Date().toString()}","ips":[`);
await writeIPs(`{"metaVersion":1,"host":"${this.config.host}","exportedAt":"${this.timeService.date.toString()}","ips":[`);
for (const signin of signins) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@ -226,7 +228,7 @@ export class ExportAccountDataProcessorService {
});
};
await writeNotes(`{"metaVersion":1,"host":"${this.config.host}","exportedAt":"${new Date().toString()}","notes":[`);
await writeNotes(`{"metaVersion":1,"host":"${this.config.host}","exportedAt":"${this.timeService.date.toString()}","notes":[`);
let noteCursor: MiNote['id'] | null = null;
let exportedNotesCount = 0;
@ -287,7 +289,7 @@ export class ExportAccountDataProcessorService {
});
};
await writeFollowing(`{"metaVersion":1,"host":"${this.config.host}","exportedAt":"${new Date().toString()}","followings":[`);
await writeFollowing(`{"metaVersion":1,"host":"${this.config.host}","exportedAt":"${this.timeService.date.toString()}","followings":[`);
let followingsCursor: MiFollowing['id'] | null = null;
let exportedFollowingsCount = 0;
@ -321,7 +323,7 @@ export class ExportAccountDataProcessorService {
continue;
}
if (u.updatedAt && (Date.now() - u.updatedAt.getTime() > 1000 * 60 * 60 * 24 * 90)) {
if (u.updatedAt && (this.timeService.now - u.updatedAt.getTime() > 1000 * 60 * 60 * 24 * 90)) {
continue;
}
@ -357,7 +359,7 @@ export class ExportAccountDataProcessorService {
});
};
await writeFollowers(`{"metaVersion":1,"host":"${this.config.host}","exportedAt":"${new Date().toString()}","followers":[`);
await writeFollowers(`{"metaVersion":1,"host":"${this.config.host}","exportedAt":"${this.timeService.date.toString()}","followers":[`);
let followersCursor: MiFollowing['id'] | null = null;
let exportedFollowersCount = 0;
@ -420,7 +422,7 @@ export class ExportAccountDataProcessorService {
fs.mkdirSync(`${path}/files`);
await writeDrive(`{"metaVersion":1,"host":"${this.config.host}","exportedAt":"${new Date().toString()}","drive":[`);
await writeDrive(`{"metaVersion":1,"host":"${this.config.host}","exportedAt":"${this.timeService.date.toString()}","drive":[`);
const driveFiles = await this.driveFilesRepository.find({ where: { userId: user.id } });
@ -476,7 +478,7 @@ export class ExportAccountDataProcessorService {
});
};
await writeMuting(`{"metaVersion":1,"host":"${this.config.host}","exportedAt":"${new Date().toString()}","mutings":[`);
await writeMuting(`{"metaVersion":1,"host":"${this.config.host}","exportedAt":"${this.timeService.date.toString()}","mutings":[`);
let exportedMutingCount = 0;
let mutingCursor: MiMuting['id'] | null = null;
@ -539,7 +541,7 @@ export class ExportAccountDataProcessorService {
});
};
await writeBlocking(`{"metaVersion":1,"host":"${this.config.host}","exportedAt":"${new Date().toString()}","blockings":[`);
await writeBlocking(`{"metaVersion":1,"host":"${this.config.host}","exportedAt":"${this.timeService.date.toString()}","blockings":[`);
let exportedBlockingCount = 0;
let blockingCursor: MiBlocking['id'] | null = null;
@ -601,7 +603,7 @@ export class ExportAccountDataProcessorService {
});
};
await writeFavorite(`{"metaVersion":1,"host":"${this.config.host}","exportedAt":"${new Date().toString()}","favorites":[`);
await writeFavorite(`{"metaVersion":1,"host":"${this.config.host}","exportedAt":"${this.timeService.date.toString()}","favorites":[`);
let exportedFavoritesCount = 0;
let favoriteCursor: MiNoteFavorite['id'] | null = null;
@ -662,7 +664,7 @@ export class ExportAccountDataProcessorService {
});
};
await writeAntenna(`{"metaVersion":1,"host":"${this.config.host}","exportedAt":"${new Date().toString()}","antennas":[`);
await writeAntenna(`{"metaVersion":1,"host":"${this.config.host}","exportedAt":"${this.timeService.date.toString()}","antennas":[`);
const antennas = await this.antennasRepository.findBy({ userId: user.id });
@ -749,7 +751,7 @@ export class ExportAccountDataProcessorService {
archiveStream.on('close', async () => {
this.logger.debug(`Exported to path: ${archivePath}`);
const fileName = 'data-request-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.zip';
const fileName = 'data-request-' + dateFormat(this.timeService.date, 'yyyy-MM-dd-HH-mm-ss') + '.zip';
const driveFile = await this.driveService.addFile({ user, path: archivePath, name: fileName, force: true });
this.logger.debug(`Exported to drive: ${driveFile.id}`);

View file

@ -15,6 +15,7 @@ import { bindThis } from '@/decorators.js';
import { createTemp } from '@/misc/create-temp.js';
import { UtilityService } from '@/core/UtilityService.js';
import { NotificationService } from '@/core/NotificationService.js';
import { TimeService } from '@/core/TimeService.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type { DBExportAntennasData } from '../types.js';
import type * as Bull from 'bullmq';
@ -37,6 +38,7 @@ export class ExportAntennasProcessorService {
private utilityService: UtilityService,
private queueLoggerService: QueueLoggerService,
private notificationService: NotificationService,
private readonly timeService: TimeService,
) {
this.logger = this.queueLoggerService.logger.createSubLogger('export-antennas');
}
@ -98,7 +100,7 @@ export class ExportAntennasProcessorService {
write(']');
stream.end();
const fileName = 'antennas-' + DateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.json';
const fileName = 'antennas-' + DateFormat(this.timeService.date, 'yyyy-MM-dd-HH-mm-ss') + '.json';
const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true, ext: 'json' });
this.logger.debug('Exported to: ' + driveFile.id);

View file

@ -14,6 +14,7 @@ import { DriveService } from '@/core/DriveService.js';
import { createTemp } from '@/misc/create-temp.js';
import { UtilityService } from '@/core/UtilityService.js';
import { NotificationService } from '@/core/NotificationService.js';
import { TimeService } from '@/core/TimeService.js';
import { bindThis } from '@/decorators.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type * as Bull from 'bullmq';
@ -34,6 +35,7 @@ export class ExportBlockingProcessorService {
private notificationService: NotificationService,
private driveService: DriveService,
private queueLoggerService: QueueLoggerService,
private readonly timeService: TimeService,
) {
this.logger = this.queueLoggerService.logger.createSubLogger('export-blocking');
}
@ -108,7 +110,7 @@ export class ExportBlockingProcessorService {
stream.end();
this.logger.debug(`Exported to: ${path}`);
const fileName = 'blocking-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv';
const fileName = 'blocking-' + dateFormat(this.timeService.date, 'yyyy-MM-dd-HH-mm-ss') + '.csv';
const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true, ext: 'csv' });
this.logger.debug(`Exported to: ${driveFile.id}`);

View file

@ -20,6 +20,7 @@ import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.j
import { Packed } from '@/misc/json-schema.js';
import { IdService } from '@/core/IdService.js';
import { NotificationService } from '@/core/NotificationService.js';
import { TimeService } from '@/core/TimeService.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type * as Bull from 'bullmq';
import type { DbJobDataWithUser } from '../types.js';
@ -45,6 +46,7 @@ export class ExportClipsProcessorService {
private queueLoggerService: QueueLoggerService,
private idService: IdService,
private notificationService: NotificationService,
private readonly timeService: TimeService,
) {
this.logger = this.queueLoggerService.logger.createSubLogger('export-clips');
}
@ -78,7 +80,7 @@ export class ExportClipsProcessorService {
this.logger.debug(`Exported to: ${path}`);
const fileName = 'clips-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.json';
const fileName = 'clips-' + dateFormat(this.timeService.date, 'yyyy-MM-dd-HH-mm-ss') + '.json';
const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true, ext: 'json' });
this.logger.debug(`Exported to: ${driveFile.id}`);

View file

@ -17,6 +17,7 @@ import { DriveService } from '@/core/DriveService.js';
import { createTemp, createTempDir } from '@/misc/create-temp.js';
import { DownloadService } from '@/core/DownloadService.js';
import { NotificationService } from '@/core/NotificationService.js';
import { TimeService } from '@/core/TimeService.js';
import { bindThis } from '@/decorators.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type * as Bull from 'bullmq';
@ -39,6 +40,7 @@ export class ExportCustomEmojisProcessorService {
private downloadService: DownloadService,
private queueLoggerService: QueueLoggerService,
private notificationService: NotificationService,
private readonly timeService: TimeService,
) {
this.logger = this.queueLoggerService.logger.createSubLogger('export-custom-emojis');
}
@ -133,7 +135,7 @@ export class ExportCustomEmojisProcessorService {
archiveStream.on('close', async () => {
this.logger.debug(`Exported to: ${archivePath}`);
const fileName = 'custom-emojis-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.zip';
const fileName = 'custom-emojis-' + dateFormat(this.timeService.date, 'yyyy-MM-dd-HH-mm-ss') + '.zip';
const driveFile = await this.driveService.addFile({ user, path: archivePath, name: fileName, force: true });
this.logger.debug(`Exported to: ${driveFile.id}`);

View file

@ -17,6 +17,7 @@ import type { MiNote } from '@/models/Note.js';
import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import { NotificationService } from '@/core/NotificationService.js';
import { TimeService } from '@/core/TimeService.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type * as Bull from 'bullmq';
import type { DbJobDataWithUser } from '../types.js';
@ -39,6 +40,7 @@ export class ExportFavoritesProcessorService {
private queueLoggerService: QueueLoggerService,
private idService: IdService,
private notificationService: NotificationService,
private readonly timeService: TimeService,
) {
this.logger = this.queueLoggerService.logger.createSubLogger('export-favorites');
}
@ -122,7 +124,7 @@ export class ExportFavoritesProcessorService {
stream.end();
this.logger.debug(`Exported to: ${path}`);
const fileName = 'favorites-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.json';
const fileName = 'favorites-' + dateFormat(this.timeService.date, 'yyyy-MM-dd-HH-mm-ss') + '.json';
const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true, ext: 'json' });
this.logger.debug(`Exported to: ${driveFile.id}`);

View file

@ -15,6 +15,7 @@ import { createTemp } from '@/misc/create-temp.js';
import type { MiFollowing } from '@/models/Following.js';
import { UtilityService } from '@/core/UtilityService.js';
import { NotificationService } from '@/core/NotificationService.js';
import { TimeService } from '@/core/TimeService.js';
import { bindThis } from '@/decorators.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type * as Bull from 'bullmq';
@ -38,6 +39,7 @@ export class ExportFollowingProcessorService {
private driveService: DriveService,
private queueLoggerService: QueueLoggerService,
private notificationService: NotificationService,
private readonly timeService: TimeService,
) {
this.logger = this.queueLoggerService.logger.createSubLogger('export-following');
}
@ -91,7 +93,7 @@ export class ExportFollowingProcessorService {
continue;
}
if (job.data.excludeInactive && u.updatedAt && (Date.now() - u.updatedAt.getTime() > 1000 * 60 * 60 * 24 * 90)) {
if (job.data.excludeInactive && u.updatedAt && (this.timeService.now - u.updatedAt.getTime() > 1000 * 60 * 60 * 24 * 90)) {
continue;
}
@ -112,7 +114,7 @@ export class ExportFollowingProcessorService {
stream.end();
this.logger.debug(`Exported to: ${path}`);
const fileName = 'following-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv';
const fileName = 'following-' + dateFormat(this.timeService.date, 'yyyy-MM-dd-HH-mm-ss') + '.csv';
const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true, ext: 'csv' });
this.logger.debug(`Exported to: ${driveFile.id}`);

View file

@ -15,6 +15,7 @@ import { createTemp } from '@/misc/create-temp.js';
import { UtilityService } from '@/core/UtilityService.js';
import { NotificationService } from '@/core/NotificationService.js';
import { bindThis } from '@/decorators.js';
import { TimeService } from '@/core/TimeService.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type * as Bull from 'bullmq';
import type { DbJobDataWithUser } from '../types.js';
@ -34,6 +35,7 @@ export class ExportMutingProcessorService {
private driveService: DriveService,
private queueLoggerService: QueueLoggerService,
private notificationService: NotificationService,
private readonly timeService: TimeService,
) {
this.logger = this.queueLoggerService.logger.createSubLogger('export-muting');
}
@ -109,7 +111,7 @@ export class ExportMutingProcessorService {
stream.end();
this.logger.debug(`Exported to: ${path}`);
const fileName = 'mute-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv';
const fileName = 'mute-' + dateFormat(this.timeService.date, 'yyyy-MM-dd-HH-mm-ss') + '.csv';
const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true, ext: 'csv' });
this.logger.debug(`Exported to: ${driveFile.id}`);

View file

@ -21,6 +21,7 @@ import { IdService } from '@/core/IdService.js';
import { NotificationService } from '@/core/NotificationService.js';
import { JsonArrayStream } from '@/misc/JsonArrayStream.js';
import { FileWriterStream } from '@/misc/FileWriterStream.js';
import { TimeService } from '@/core/TimeService.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type * as Bull from 'bullmq';
import type { DbJobDataWithUser } from '../types.js';
@ -114,6 +115,7 @@ export class ExportNotesProcessorService {
private driveFileEntityService: DriveFileEntityService,
private idService: IdService,
private notificationService: NotificationService,
private readonly timeService: TimeService,
) {
this.logger = this.queueLoggerService.logger.createSubLogger('export-notes');
}
@ -149,7 +151,7 @@ export class ExportNotesProcessorService {
this.logger.debug(`Exported to: ${path}`);
const fileName = 'notes-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.json';
const fileName = 'notes-' + dateFormat(this.timeService.date, 'yyyy-MM-dd-HH-mm-ss') + '.json';
const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true, ext: 'json' });
this.logger.debug(`Exported to: ${driveFile.id}`);

View file

@ -14,6 +14,7 @@ import { DriveService } from '@/core/DriveService.js';
import { createTemp } from '@/misc/create-temp.js';
import { UtilityService } from '@/core/UtilityService.js';
import { NotificationService } from '@/core/NotificationService.js';
import { TimeService } from '@/core/TimeService.js';
import { bindThis } from '@/decorators.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type * as Bull from 'bullmq';
@ -37,6 +38,7 @@ export class ExportUserListsProcessorService {
private driveService: DriveService,
private queueLoggerService: QueueLoggerService,
private notificationService: NotificationService,
private readonly timeService: TimeService,
) {
this.logger = this.queueLoggerService.logger.createSubLogger('export-user-lists');
}
@ -88,7 +90,7 @@ export class ExportUserListsProcessorService {
stream.end();
this.logger.debug(`Exported to: ${path}`);
const fileName = 'user-lists-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv';
const fileName = 'user-lists-' + dateFormat(this.timeService.date, 'yyyy-MM-dd-HH-mm-ss') + '.csv';
const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true, ext: 'csv' });
this.logger.debug(`Exported to: ${driveFile.id}`);

View file

@ -12,6 +12,7 @@ import type { AntennasRepository, UsersRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { NotificationService } from '@/core/NotificationService.js';
import { TimeService } from '@/core/TimeService.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import { DBAntennaImportJobData } from '../types.js';
import type * as Bull from 'bullmq';
@ -67,6 +68,7 @@ export class ImportAntennasProcessorService {
private idService: IdService,
private globalEventService: GlobalEventService,
private notificationService: NotificationService,
private readonly timeService: TimeService,
) {
this.logger = this.queueLoggerService.logger.createSubLogger('import-antennas');
}
@ -81,7 +83,7 @@ export class ImportAntennasProcessorService {
this.logger.debug(`Importing antennas of ${job.data.user.id} ...`);
const now = new Date();
const now = this.timeService.date;
try {
for (const antenna of job.data.antenna) {
if (antenna.keywords.length === 0 || antenna.keywords[0].every(x => x === '')) continue;

View file

@ -31,6 +31,7 @@ import { SkApInboxLog } from '@/models/_.js';
import type { Config } from '@/config.js';
import { ApLogService, calculateDurationSince } from '@/core/ApLogService.js';
import { UpdateInstanceQueue } from '@/core/UpdateInstanceQueue.js';
import { TimeService } from '@/core/TimeService.js';
import { isRetryableError } from '@/misc/is-retryable-error.js';
import { renderInlineError } from '@/misc/render-inline-error.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
@ -66,6 +67,7 @@ export class InboxProcessorService implements OnApplicationShutdown {
private queueLoggerService: QueueLoggerService,
private readonly apLogService: ApLogService,
private readonly updateInstanceQueue: UpdateInstanceQueue,
private readonly timeService: TimeService,
) {
this.logger = this.queueLoggerService.logger.createSubLogger('inbox');
}
@ -263,7 +265,7 @@ export class InboxProcessorService implements OnApplicationShutdown {
if (i == null) return;
this.updateInstanceQueue.enqueue(i.id, {
latestRequestReceivedAt: new Date(),
latestRequestReceivedAt: this.timeService.date,
shouldUnsuspend: i.suspensionState === 'autoSuspendedForNotResponding',
});
@ -322,7 +324,7 @@ export class InboxProcessorService implements OnApplicationShutdown {
@bindThis
public async performUpdateInstance(id: string, job: UpdateInstanceJob) {
await this.federatedInstanceService.update(id, {
latestRequestReceivedAt: new Date(),
latestRequestReceivedAt: this.timeService.date,
isNotResponding: false,
// もしサーバーが死んでるために配信が止まっていた場合には自動的に復活させてあげる
suspensionState: job.shouldUnsuspend ? 'none' : undefined,

View file

@ -13,6 +13,7 @@ import { NotificationService } from '@/core/NotificationService.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import type { MiScheduleNoteType } from '@/models/NoteSchedule.js';
import { renderInlineError } from '@/misc/render-inline-error.js';
import { TimeService } from '@/core/TimeService.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type * as Bull from 'bullmq';
import type { ScheduleNotePostJobData } from '../types.js';
@ -37,6 +38,7 @@ export class ScheduleNotePostProcessorService {
private noteCreateService: NoteCreateService,
private queueLoggerService: QueueLoggerService,
private notificationService: NotificationService,
private readonly timeService: TimeService,
) {
this.logger = this.queueLoggerService.logger.createSubLogger('schedule-note-post');
}
@ -118,7 +120,7 @@ export class ScheduleNotePostProcessorService {
const createdNote = await this.noteCreateService.create(me, {
...note,
createdAt: new Date(),
createdAt: this.timeService.date,
files,
poll: note.poll ? {
choices: note.poll.choices,

View file

@ -11,6 +11,7 @@ import type { Config } from '@/config.js';
import type Logger from '@/logger.js';
import { HttpRequestService } from '@/core/HttpRequestService.js';
import { StatusError } from '@/misc/status-error.js';
import { TimeService } from '@/core/TimeService.js';
import { bindThis } from '@/decorators.js';
import { renderInlineError } from '@/misc/render-inline-error.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
@ -29,6 +30,7 @@ export class SystemWebhookDeliverProcessorService {
private httpRequestService: HttpRequestService,
private queueLoggerService: QueueLoggerService,
private readonly timeService: TimeService,
) {
this.logger = this.queueLoggerService.logger.createSubLogger('webhook');
}
@ -58,7 +60,7 @@ export class SystemWebhookDeliverProcessorService {
});
this.systemWebhooksRepository.update({ id: job.data.webhookId }, {
latestSentAt: new Date(),
latestSentAt: this.timeService.date,
latestStatus: res.status,
});
@ -67,7 +69,7 @@ export class SystemWebhookDeliverProcessorService {
this.logger.error(`Failed to send webhook: ${renderInlineError(res)}`);
this.systemWebhooksRepository.update({ id: job.data.webhookId }, {
latestSentAt: new Date(),
latestSentAt: this.timeService.date,
latestStatus: res instanceof StatusError ? res.statusCode : 1,
});

View file

@ -11,6 +11,7 @@ import type { Config } from '@/config.js';
import type Logger from '@/logger.js';
import { HttpRequestService } from '@/core/HttpRequestService.js';
import { StatusError } from '@/misc/status-error.js';
import { TimeService } from '@/core/TimeService.js';
import { bindThis } from '@/decorators.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import { UserWebhookDeliverJobData } from '../types.js';
@ -28,6 +29,7 @@ export class UserWebhookDeliverProcessorService {
private httpRequestService: HttpRequestService,
private queueLoggerService: QueueLoggerService,
private readonly timeService: TimeService,
) {
this.logger = this.queueLoggerService.logger.createSubLogger('webhook');
}
@ -58,14 +60,14 @@ export class UserWebhookDeliverProcessorService {
});
this.webhooksRepository.update({ id: job.data.webhookId }, {
latestSentAt: new Date(),
latestSentAt: this.timeService.date,
latestStatus: res.status,
});
return 'Success';
} catch (res) {
this.webhooksRepository.update({ id: job.data.webhookId }, {
latestSentAt: new Date(),
latestSentAt: this.timeService.date,
latestStatus: res instanceof StatusError ? res.statusCode : 1,
});

View file

@ -20,6 +20,7 @@ import { RoleService } from '@/core/RoleService.js';
import type { Config } from '@/config.js';
import { sendRateLimitHeaders } from '@/misc/rate-limit-utils.js';
import { SkRateLimiterService } from '@/server/SkRateLimiterService.js';
import { TimeService, type TimerHandle } from '@/core/TimeService.js';
import { renderInlineError } from '@/misc/render-inline-error.js';
import { renderFullError } from '@/misc/render-full-error.js';
import { ApiError } from './error.js';
@ -39,7 +40,7 @@ const accessDenied = {
export class ApiCallService implements OnApplicationShutdown {
private logger: Logger;
private userIpHistories: Map<MiUser['id'], Set<string>>;
private userIpHistoriesClearIntervalId: NodeJS.Timeout;
private userIpHistoriesClearIntervalId: TimerHandle;
constructor(
@Inject(DI.meta)
@ -55,13 +56,14 @@ export class ApiCallService implements OnApplicationShutdown {
private rateLimiterService: SkRateLimiterService,
private roleService: RoleService,
private apiLoggerService: ApiLoggerService,
private readonly timeService: TimeService,
) {
this.logger = this.apiLoggerService.logger;
this.userIpHistories = new Map<MiUser['id'], Set<string>>();
this.userIpHistoriesClearIntervalId = setInterval(() => {
this.userIpHistoriesClearIntervalId = this.timeService.startTimer(() => {
this.userIpHistories.clear();
}, 1000 * 60 * 60);
}, 1000 * 60 * 60, { repeated: true });
}
#sendApiError(reply: FastifyReply, err: ApiError): void {
@ -284,7 +286,7 @@ export class ApiCallService implements OnApplicationShutdown {
try {
this.userIpsRepository.createQueryBuilder().insert().values({
createdAt: new Date(),
createdAt: this.timeService.date,
userId: user.id,
ip: ip,
}).orIgnore(true).execute();
@ -456,7 +458,7 @@ export class ApiCallService implements OnApplicationShutdown {
@bindThis
public dispose(): void {
clearInterval(this.userIpHistoriesClearIntervalId);
this.timeService.stopTimer(this.userIpHistoriesClearIntervalId);
}
@bindThis

View file

@ -15,6 +15,7 @@ import { isNativeUserToken } from '@/misc/token.js';
import { bindThis } from '@/decorators.js';
import { attachCallerId } from '@/misc/attach-caller-id.js';
import { CacheManagementService, type ManagedMemoryKVCache } from '@/core/CacheManagementService.js';
import { TimeService } from '@/core/TimeService.js';
export class AuthenticationError extends Error {
constructor(message: string) {
@ -38,6 +39,8 @@ export class AuthenticateService {
private appsRepository: AppsRepository,
private cacheService: CacheService,
private readonly timeService: TimeService,
cacheManagementService: CacheManagementService,
) {
this.appCache = cacheManagementService.createMemoryKVCache<MiApp>(1000 * 60 * 60 * 24); // 1d
@ -75,7 +78,7 @@ export class AuthenticateService {
}
this.accessTokensRepository.update(accessToken.id, {
lastUsedAt: new Date(),
lastUsedAt: this.timeService.date,
});
// Loaded by relation above

View file

@ -21,6 +21,7 @@ import { L_CHARS, secureRndstr } from '@/misc/secure-rndstr.js';
import { RoleService } from '@/core/RoleService.js';
import Logger from '@/logger.js';
import { LoggerService } from '@/core/LoggerService.js';
import { TimeService } from '@/core/TimeService.js';
import { SigninService } from './SigninService.js';
import type { FastifyRequest, FastifyReply } from 'fastify';
@ -60,6 +61,7 @@ export class SignupApiService {
private emailService: EmailService,
private roleService: RoleService,
private loggerService: LoggerService,
private readonly timeService: TimeService,
) {
this.logger = this.loggerService.getLogger('Signup');
}
@ -170,7 +172,7 @@ export class SignupApiService {
return;
}
if (ticket.expiresAt && ticket.expiresAt < new Date()) {
if (ticket.expiresAt && ticket.expiresAt < this.timeService.date) {
reply.code(400);
return;
}
@ -184,7 +186,7 @@ export class SignupApiService {
}
// 認証しておらず、メール送信から30分以内ならエラー
if (ticket.usedAt && ticket.usedAt.getTime() + (1000 * 60 * 30) > Date.now()) {
if (ticket.usedAt && ticket.usedAt.getTime() + (1000 * 60 * 30) > this.timeService.now) {
reply.code(400);
return;
}
@ -232,7 +234,7 @@ export class SignupApiService {
if (ticket) {
await this.registrationTicketsRepository.update(ticket.id, {
usedAt: new Date(),
usedAt: this.timeService.date,
pendingUserId: pendingUser.id,
});
}
@ -252,7 +254,7 @@ export class SignupApiService {
if (ticket) {
await this.registrationTicketsRepository.update(ticket.id, {
usedAt: new Date(),
usedAt: this.timeService.date,
usedBy: account,
usedById: account.id,
});
@ -289,7 +291,7 @@ export class SignupApiService {
if (ticket) {
await this.registrationTicketsRepository.update(ticket.id, {
usedAt: new Date(),
usedAt: this.timeService.date,
usedBy: account,
usedById: account.id,
});
@ -318,7 +320,7 @@ export class SignupApiService {
try {
const pendingUser = await this.userPendingsRepository.findOneByOrFail({ code });
if (this.idService.parse(pendingUser.id).date.getTime() + (1000 * 60 * 30) < Date.now()) {
if (this.idService.parse(pendingUser.id).date.getTime() + (1000 * 60 * 30) < this.timeService.now) {
throw new FastifyReplyError(400, 'EXPIRED');
}
@ -390,7 +392,7 @@ export class SignupApiService {
private logIp(ip: string, ipDate: Date | null, userId: MiLocalUser['id']) {
try {
this.userIpsRepository.createQueryBuilder().insert().values({
createdAt: ipDate ?? new Date(),
createdAt: ipDate ?? this.timeService.date,
userId,
ip,
}).orIgnore(true).execute();

View file

@ -24,6 +24,7 @@ import { LoggerService } from '@/core/LoggerService.js';
import type Logger from '@/logger.js';
import { SkRateLimiterService } from '@/server/SkRateLimiterService.js';
import { QueryService } from '@/core/QueryService.js';
import { TimeService, type TimerHandle } from '@/core/TimeService.js';
import { AuthenticateService, AuthenticationError } from './AuthenticateService.js';
import MainStreamConnection from './stream/Connection.js';
import { ChannelsService } from './stream/ChannelsService.js';
@ -38,7 +39,7 @@ export class StreamingApiServerService implements OnApplicationShutdown {
#wss?: WebSocket.WebSocketServer;
#connections = new Map<WebSocket.WebSocket, number>();
#connectionsByClient = new Map<string, Set<WebSocket.WebSocket>>(); // key: IP / user ID -> value: connection
#cleanConnectionsIntervalId: NodeJS.Timeout | null = null;
#cleanConnectionsIntervalId: TimerHandle | null = null;
readonly #globalEv = new EventEmitter();
#logger: Logger;
@ -58,7 +59,9 @@ export class StreamingApiServerService implements OnApplicationShutdown {
@Inject(DI.noteFavoritesRepository)
private readonly noteFavoritesRepository: NoteFavoritesRepository,
private readonly queryService: QueryService,
@Inject(DI.config)
private config: Config,
private cacheService: CacheService,
private authenticateService: AuthenticateService,
private channelsService: ChannelsService,
@ -67,9 +70,8 @@ export class StreamingApiServerService implements OnApplicationShutdown {
private channelFollowingService: ChannelFollowingService,
private rateLimiterService: SkRateLimiterService,
private loggerService: LoggerService,
@Inject(DI.config)
private config: Config,
private readonly queryService: QueryService,
private readonly timeService: TimeService,
) {
this.redisForSub.on('message', this.onRedis);
this.#logger = loggerService.getLogger('streaming', 'coral');
@ -205,6 +207,7 @@ export class StreamingApiServerService implements OnApplicationShutdown {
this.notificationService,
this.cacheService,
this.channelFollowingService,
this.timeService,
this.loggerService,
user, app, requestIp,
rateLimiter,
@ -265,17 +268,17 @@ export class StreamingApiServerService implements OnApplicationShutdown {
await stream.listen(ev, connection);
this.#connections.set(connection, Date.now());
this.#connections.set(connection, this.timeService.now);
// TODO use collapsed queue
const userUpdateIntervalId = user ? setInterval(() => {
const userUpdateIntervalId = user ? this.timeService.startTimer(() => {
this.usersService.updateLastActiveDate(user);
}, 1000 * 60 * 5) : null;
}, 1000 * 60 * 5, { repeated: true }) : null;
if (user) {
this.usersService.updateLastActiveDate(user);
}
const pong = () => {
this.#connections.set(connection, Date.now());
this.#connections.set(connection, this.timeService.now);
};
connection.once('close', () => {
@ -285,7 +288,7 @@ export class StreamingApiServerService implements OnApplicationShutdown {
stream.dispose();
this.#globalEv.off('message', onRedisMessage);
this.#connections.delete(connection);
if (userUpdateIntervalId) clearInterval(userUpdateIntervalId);
if (userUpdateIntervalId) this.timeService.stopTimer(userUpdateIntervalId);
});
connection.on('error', this.onWsError);
@ -293,8 +296,8 @@ export class StreamingApiServerService implements OnApplicationShutdown {
});
// 一定期間通信が無いコネクションは実際には切断されている可能性があるため定期的にterminateする
this.#cleanConnectionsIntervalId = setInterval(() => {
const now = Date.now();
this.#cleanConnectionsIntervalId = this.timeService.startTimer(() => {
const now = this.timeService.now;
for (const [connection, lastActive] of this.#connections.entries()) {
if (now - lastActive > 1000 * 60 * 2) {
connection.terminate();
@ -303,13 +306,13 @@ export class StreamingApiServerService implements OnApplicationShutdown {
connection.ping();
}
}
}, 1000 * 60);
}, 1000 * 60, { repeated: true });
}
@bindThis
public async detach(): Promise<void> {
if (this.#cleanConnectionsIntervalId) {
clearInterval(this.#cleanConnectionsIntervalId);
this.timeService.stopTimer(this.#cleanConnectionsIntervalId);
this.#cleanConnectionsIntervalId = null;
}

View file

@ -8,6 +8,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
import type { AdsRepository } from '@/models/_.js';
import { QueryService } from '@/core/QueryService.js';
import { DI } from '@/di-symbols.js';
import { TimeService } from '@/core/TimeService.js';
export const meta = {
tags: ['admin'],
@ -46,13 +47,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private adsRepository: AdsRepository,
private queryService: QueryService,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async (ps, me) => {
const query = this.queryService.makePaginationQuery(this.adsRepository.createQueryBuilder('ad'), ps.sinceId, ps.untilId);
if (ps.publishing === true) {
query.andWhere('ad.expiresAt > :now', { now: new Date() }).andWhere('ad.startsAt <= :now', { now: new Date() });
query.andWhere('ad.expiresAt > :now', { now: this.timeService.date }).andWhere('ad.startsAt <= :now', { now: this.timeService.date });
} else if (ps.publishing === false) {
query.andWhere('ad.expiresAt <= :now', { now: new Date() }).orWhere('ad.startsAt > :now', { now: new Date() });
query.andWhere('ad.expiresAt <= :now', { now: this.timeService.date }).orWhere('ad.startsAt > :now', { now: this.timeService.date });
}
const ads = await query.limit(ps.limit).getMany();

View file

@ -9,6 +9,7 @@ import type { AnnouncementsRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js';
import { AnnouncementService } from '@/core/AnnouncementService.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import { TimeService } from '@/core/TimeService.js';
import { ApiError } from '../../../error.js';
export const meta = {
@ -58,6 +59,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private announcementsRepository: AnnouncementsRepository,
private announcementService: AnnouncementService,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async (ps, me) => {
const announcement = await this.announcementsRepository.findOneBy({ id: ps.id });
@ -66,7 +68,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
try {
await this.announcementService.update(announcement, {
updatedAt: new Date(),
updatedAt: this.timeService.date,
title: ps.title,
text: ps.text,
/* eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- 空の文字列の場合、nullを渡すようにするため */

View file

@ -8,6 +8,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
import type { RegistrationTicketsRepository } from '@/models/_.js';
import { InviteCodeEntityService } from '@/core/entities/InviteCodeEntityService.js';
import { DI } from '@/di-symbols.js';
import { TimeService } from '@/core/TimeService.js';
export const meta = {
tags: ['admin'],
@ -45,6 +46,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private registrationTicketsRepository: RegistrationTicketsRepository,
private inviteCodeEntityService: InviteCodeEntityService,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async (ps, me) => {
const query = this.registrationTicketsRepository.createQueryBuilder('ticket')
@ -54,7 +56,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
switch (ps.type) {
case 'unused': query.andWhere('ticket.usedBy IS NULL'); break;
case 'used': query.andWhere('ticket.usedBy IS NOT NULL'); break;
case 'expired': query.andWhere('ticket.expiresAt < :now', { now: new Date() }); break;
case 'expired': query.andWhere('ticket.expiresAt < :now', { now: this.timeService.date }); break;
}
switch (ps.sort) {

View file

@ -9,6 +9,7 @@ import type { RolesRepository, UsersRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js';
import { ApiError } from '@/server/api/error.js';
import { RoleService } from '@/core/RoleService.js';
import { TimeService } from '@/core/TimeService.js';
export const meta = {
tags: ['admin', 'role'],
@ -64,6 +65,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private rolesRepository: RolesRepository,
private roleService: RoleService,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async (ps, me) => {
const role = await this.rolesRepository.findOneBy({ id: ps.roleId });
@ -80,7 +82,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
throw new ApiError(meta.errors.noSuchUser);
}
if (ps.expiresAt && ps.expiresAt <= Date.now()) {
if (ps.expiresAt && ps.expiresAt <= this.timeService.now) {
return;
}

View file

@ -11,6 +11,7 @@ import { QueryService } from '@/core/QueryService.js';
import { DI } from '@/di-symbols.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { IdService } from '@/core/IdService.js';
import { TimeService } from '@/core/TimeService.js';
import { ApiError } from '../../../error.js';
export const meta = {
@ -71,6 +72,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private queryService: QueryService,
private userEntityService: UserEntityService,
private idService: IdService,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async (ps, me) => {
const role = await this.rolesRepository.findOneBy({
@ -86,7 +88,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.andWhere(new Brackets(qb => {
qb
.where('assign.expiresAt IS NULL')
.orWhere('assign.expiresAt > :now', { now: new Date() });
.orWhere('assign.expiresAt > :now', { now: this.timeService.date });
}))
.innerJoinAndSelect('assign.user', 'user');

View file

@ -10,6 +10,7 @@ import { DI } from '@/di-symbols.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { sqlLikeEscape } from '@/misc/sql-like-escape.js';
import { RoleService } from '@/core/RoleService.js';
import { TimeService } from '@/core/TimeService.js';
export const meta = {
tags: ['admin'],
@ -61,13 +62,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private userEntityService: UserEntityService,
private roleService: RoleService,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async (ps, me) => {
const query = this.usersRepository.createQueryBuilder('user');
switch (ps.state) {
case 'available': query.where('user.isSuspended = FALSE'); break;
case 'alive': query.where('user.updatedAt > :date', { date: new Date(Date.now() - 1000 * 60 * 60 * 24 * 5) }); break;
case 'alive': query.where('user.updatedAt > :date', { date: new Date(this.timeService.now - 1000 * 60 * 60 * 24 * 5) }); break;
case 'suspended': query.where('user.isSuspended = TRUE'); break;
case 'approved': query.where('user.approved = FALSE'); break;
case 'admin': {

View file

@ -11,6 +11,7 @@ import { GlobalEventService } from '@/core/GlobalEventService.js';
import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js';
import { DI } from '@/di-symbols.js';
import { RoleService } from '@/core/RoleService.js';
import { TimeService } from '@/core/TimeService.js';
import { ApiError } from '../../error.js';
export const meta = {
@ -97,6 +98,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private roleService: RoleService,
private idService: IdService,
private globalEventService: GlobalEventService,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async (ps, me) => {
if (ps.keywords.flat().every(x => x === '') && ps.excludeKeywords.flat().every(x => x === '')) {
@ -123,7 +125,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
}
const now = new Date();
const now = this.timeService.date;
const antenna = await this.antennasRepository.insertOne({
id: this.idService.gen(now.getTime()),

View file

@ -11,6 +11,7 @@ import { QueryService } from '@/core/QueryService.js';
import { DI } from '@/di-symbols.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { IdService } from '@/core/IdService.js';
import { TimeService } from '@/core/TimeService.js';
import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { trackPromise } from '@/misc/promise-tracker.js';
@ -77,6 +78,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private fanoutTimelineService: FanoutTimelineService,
private globalEventService: GlobalEventService,
private readonly activeUsersChart: ActiveUsersChart,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async (ps, me) => {
const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null);
@ -95,7 +97,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
const needPublishEvent = !antenna.isActive;
antenna.isActive = true;
antenna.lastUsedAt = new Date();
antenna.lastUsedAt = this.timeService.date;
trackPromise(this.antennasRepository.update(antenna.id, antenna));
if (needPublishEvent) {

View file

@ -8,6 +8,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
import type { AntennasRepository, UserListsRepository } from '@/models/_.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js';
import { TimeService } from '@/core/TimeService.js';
import { DI } from '@/di-symbols.js';
import { ApiError } from '../../error.js';
@ -94,6 +95,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private antennaEntityService: AntennaEntityService,
private globalEventService: GlobalEventService,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async (ps, me) => {
if (ps.keywords && ps.excludeKeywords) {
@ -138,7 +140,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
withFile: ps.withFile,
excludeNotesInSensitiveChannel: ps.excludeNotesInSensitiveChannel,
isActive: true,
lastUsedAt: new Date(),
lastUsedAt: this.timeService.date,
});
this.globalEventService.publishInternalEvent('antennaUpdated', await this.antennasRepository.findOneByOrFail({ id: antenna.id }));

View file

@ -8,6 +8,7 @@ import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import type { AuthSessionsRepository, AppsRepository, AccessTokensRepository } from '@/models/_.js';
import { IdService } from '@/core/IdService.js';
import { TimeService } from '@/core/TimeService.js';
import { secureRndstr } from '@/misc/secure-rndstr.js';
import { DI } from '@/di-symbols.js';
import { ApiError } from '../../error.js';
@ -55,6 +56,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private accessTokensRepository: AccessTokensRepository,
private idService: IdService,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async (ps, me) => {
// Fetch token
@ -83,7 +85,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
sha256.update(accessToken + app.secret);
const hash = sha256.digest('hex');
const now = new Date();
const now = this.timeService.date;
await this.accessTokensRepository.insert({
id: this.idService.gen(now.getTime()),

View file

@ -9,6 +9,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
import type { BubbleGameRecordsRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { TimeService } from '@/core/TimeService.js';
export const meta = {
allowGet: true,
@ -63,12 +64,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private bubbleGameRecordsRepository: BubbleGameRecordsRepository,
private userEntityService: UserEntityService,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async (ps, me) => {
const records = await this.bubbleGameRecordsRepository.find({
where: {
gameMode: ps.gameMode,
seededAt: MoreThan(new Date(Date.now() - 1000 * 60 * 60 * 24 * 7)),
seededAt: MoreThan(new Date(this.timeService.now - 1000 * 60 * 60 * 24 * 7)),
},
order: {
score: 'DESC',

View file

@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
import ms from 'ms';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { IdService } from '@/core/IdService.js';
import { TimeService } from '@/core/TimeService.js';
import type { BubbleGameRecordsRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js';
import { ApiError } from '../../error.js';
@ -58,10 +59,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private bubbleGameRecordsRepository: BubbleGameRecordsRepository,
private idService: IdService,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async (ps, me) => {
const seedDate = new Date(parseInt(ps.seed, 10));
const now = new Date();
const now = this.timeService.date;
// シードが未来なのは通常のプレイではありえないので弾く
if (seedDate.getTime() > now.getTime()) {

View file

@ -10,6 +10,7 @@ import { IdService } from '@/core/IdService.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { DI } from '@/di-symbols.js';
import { FlashEntityService } from '@/core/entities/FlashEntityService.js';
import { TimeService } from '@/core/TimeService.js';
export const meta = {
tags: ['flash'],
@ -57,12 +58,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private flashEntityService: FlashEntityService,
private idService: IdService,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async (ps, me) => {
const flash = await this.flashsRepository.insertOne({
id: this.idService.gen(),
userId: me.id,
updatedAt: new Date(),
updatedAt: this.timeService.date,
title: ps.title,
summary: ps.summary,
script: ps.script,

View file

@ -7,6 +7,7 @@ import ms from 'ms';
import { Inject, Injectable } from '@nestjs/common';
import type { FlashsRepository } from '@/models/_.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { TimeService } from '@/core/TimeService.js';
import { DI } from '@/di-symbols.js';
import { ApiError } from '../../error.js';
@ -59,6 +60,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
constructor(
@Inject(DI.flashsRepository)
private flashsRepository: FlashsRepository,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async (ps, me) => {
const flash = await this.flashsRepository.findOneBy({ id: ps.flashId });
@ -70,7 +73,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
await this.flashsRepository.update(flash.id, {
updatedAt: new Date(),
updatedAt: this.timeService.date,
...Object.fromEntries(
Object.entries(ps).filter(
([key, val]) => (key !== 'flashId') && Object.hasOwn(paramDef.properties, key)

View file

@ -9,6 +9,7 @@ import type { GalleryPostsRepository } from '@/models/_.js';
import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js';
import { DI } from '@/di-symbols.js';
import { FeaturedService } from '@/core/FeaturedService.js';
import { TimeService } from '@/core/TimeService.js';
export const meta = {
tags: ['gallery'],
@ -52,15 +53,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private galleryPostEntityService: GalleryPostEntityService,
private featuredService: FeaturedService,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async (ps, me) => {
let postIds: string[];
if (this.galleryPostsRankingCacheLastFetchedAt !== 0 && (Date.now() - this.galleryPostsRankingCacheLastFetchedAt < 1000 * 60 * 30)) {
if (this.galleryPostsRankingCacheLastFetchedAt !== 0 && (this.timeService.now - this.galleryPostsRankingCacheLastFetchedAt < 1000 * 60 * 30)) {
postIds = this.galleryPostsRankingCache;
} else {
postIds = await this.featuredService.getGalleryPostsRanking(100);
this.galleryPostsRankingCache = postIds;
this.galleryPostsRankingCacheLastFetchedAt = Date.now();
this.galleryPostsRankingCacheLastFetchedAt = this.timeService.now;
}
postIds.sort((a, b) => a > b ? -1 : 1);

View file

@ -12,6 +12,7 @@ import type { MiDriveFile } from '@/models/DriveFile.js';
import { IdService } from '@/core/IdService.js';
import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js';
import { DI } from '@/di-symbols.js';
import { TimeService } from '@/core/TimeService.js';
export const meta = {
tags: ['gallery'],
@ -62,6 +63,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private galleryPostEntityService: GalleryPostEntityService,
private idService: IdService,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async (ps, me) => {
const files = (await Promise.all(ps.fileIds.map(fileId =>
@ -77,7 +79,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
const post = await this.galleryPostsRepository.insertOne(new MiGalleryPost({
id: this.idService.gen(),
updatedAt: new Date(),
updatedAt: this.timeService.date,
title: ps.title,
description: ps.description,
userId: me.id,

View file

@ -8,6 +8,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
import type { GalleryLikesRepository, GalleryPostsRepository } from '@/models/_.js';
import { FeaturedService, GALLERY_POSTS_RANKING_WINDOW } from '@/core/FeaturedService.js';
import { IdService } from '@/core/IdService.js';
import { TimeService } from '@/core/TimeService.js';
import { DI } from '@/di-symbols.js';
import { ApiError } from '../../../error.js';
@ -66,6 +67,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private featuredService: FeaturedService,
private idService: IdService,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async (ps, me) => {
const post = await this.galleryPostsRepository.findOneBy({ id: ps.postId });
@ -97,7 +99,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
});
// ランキング更新
if (Date.now() - this.idService.parse(post.id).date.getTime() < GALLERY_POSTS_RANKING_WINDOW) {
if (this.timeService.now - this.idService.parse(post.id).date.getTime() < GALLERY_POSTS_RANKING_WINDOW) {
await this.featuredService.updateGalleryPostsRanking(post, 1);
}

View file

@ -8,6 +8,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
import type { GalleryPostsRepository, GalleryLikesRepository } from '@/models/_.js';
import { FeaturedService, GALLERY_POSTS_RANKING_WINDOW } from '@/core/FeaturedService.js';
import { IdService } from '@/core/IdService.js';
import { TimeService } from '@/core/TimeService.js';
import { DI } from '@/di-symbols.js';
import { ApiError } from '../../../error.js';
@ -60,6 +61,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private featuredService: FeaturedService,
private idService: IdService,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async (ps, me) => {
const post = await this.galleryPostsRepository.findOneBy({ id: ps.postId });
@ -80,7 +82,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
await this.galleryLikesRepository.delete(exist.id);
// ランキング更新
if (Date.now() - this.idService.parse(post.id).date.getTime() < GALLERY_POSTS_RANKING_WINDOW) {
if (this.timeService.now - this.idService.parse(post.id).date.getTime() < GALLERY_POSTS_RANKING_WINDOW) {
await this.featuredService.updateGalleryPostsRanking(post, -1);
}

View file

@ -10,6 +10,7 @@ import type { DriveFilesRepository, GalleryPostsRepository } from '@/models/_.js
import type { MiDriveFile } from '@/models/DriveFile.js';
import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js';
import { DI } from '@/di-symbols.js';
import { TimeService } from '@/core/TimeService.js';
export const meta = {
tags: ['gallery'],
@ -60,6 +61,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private driveFilesRepository: DriveFilesRepository,
private galleryPostEntityService: GalleryPostEntityService,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async (ps, me) => {
let files: Array<MiDriveFile> | undefined;
@ -81,7 +83,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
id: ps.postId,
userId: me.id,
}, {
updatedAt: new Date(),
updatedAt: this.timeService.date,
title: ps.title,
description: ps.description,
isSensitive: ps.isSensitive,

View file

@ -9,6 +9,7 @@ import { USER_ONLINE_THRESHOLD } from '@/const.js';
import type { UsersRepository } from '@/models/_.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { DI } from '@/di-symbols.js';
import { TimeService } from '@/core/TimeService.js';
export const meta = {
tags: ['meta'],
@ -45,10 +46,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
constructor(
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async () => {
const count = await this.usersRepository.countBy({
lastActiveDate: MoreThan(new Date(Date.now() - USER_ONLINE_THRESHOLD)),
lastActiveDate: MoreThan(new Date(this.timeService.now - USER_ONLINE_THRESHOLD)),
});
return {

View file

@ -11,6 +11,7 @@ import { normalizeForSearch } from '@/misc/normalize-for-search.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { DI } from '@/di-symbols.js';
import { RoleService } from '@/core/RoleService.js';
import { TimeService } from '@/core/TimeService.js';
export const meta = {
requireCredential: false,
@ -60,6 +61,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private userEntityService: UserEntityService,
private readonly roleService: RoleService,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async (ps, me) => {
if (!safeForSql(normalizeForSearch(ps.tag))) throw new Error('Injection');
@ -67,7 +69,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.where(':tag <@ user.tags', { tag: [normalizeForSearch(ps.tag)] })
.andWhere('user.isSuspended = FALSE');
const recent = new Date(Date.now() - (1000 * 60 * 60 * 24 * 5));
const recent = new Date(this.timeService.now - (1000 * 60 * 60 * 24 * 5));
if (ps.state === 'alive') {
query.andWhere('user.updatedAt > :date', { date: recent });

View file

@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
import type { UserProfilesRepository } from '@/models/_.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { TimeService } from '@/core/TimeService.js';
import { DI } from '@/di-symbols.js';
import { ApiError } from '../error.js';
@ -54,11 +55,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private userProfilesRepository: UserProfilesRepository,
private userEntityService: UserEntityService,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async (ps, user, token) => {
const isSecure = token == null;
const now = new Date();
const now = this.timeService.date;
const today = `${now.getFullYear()}/${now.getMonth() + 1}/${now.getDate()}`;
// 渡ってきている user はキャッシュされていて古い可能性があるので改めて取得

View file

@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { QueueService } from '@/core/QueueService.js';
import { AccountMoveService } from '@/core/AccountMoveService.js';
import { TimeService } from '@/core/TimeService.js';
import type { DriveFilesRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js';
import { ApiError } from '../../error.js';
@ -67,6 +68,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private queueService: QueueService,
private accountMoveService: AccountMoveService,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async (ps, me) => {
const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
@ -77,7 +79,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
const checkMoving = await this.accountMoveService.validateAlsoKnownAs(
me,
(old, src) => !!src.movedAt && src.movedAt.getTime() + 1000 * 60 * 60 * 2 > Date.now(),
(old, src) => !!src.movedAt && src.movedAt.getTime() + 1000 * 60 * 60 * 2 > this.timeService.now,
true,
);
if (checkMoving ? file.size > 32 * 1024 * 1024 : file.size > 64 * 1024) throw new ApiError(meta.errors.tooBigFile);

View file

@ -8,6 +8,7 @@ import ms from 'ms';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { QueueService } from '@/core/QueueService.js';
import { AccountMoveService } from '@/core/AccountMoveService.js';
import { TimeService } from '@/core/TimeService.js';
import type { DriveFilesRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js';
import { ApiError } from '../../error.js';
@ -69,6 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private queueService: QueueService,
private accountMoveService: AccountMoveService,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async (ps, me) => {
const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
@ -79,7 +81,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
const checkMoving = await this.accountMoveService.validateAlsoKnownAs(
me,
(old, src) => !!src.movedAt && src.movedAt.getTime() + 1000 * 60 * 60 * 2 > Date.now(),
(old, src) => !!src.movedAt && src.movedAt.getTime() + 1000 * 60 * 60 * 2 > this.timeService.now,
true,
);
if (checkMoving ? file.size > 32 * 1024 * 1024 : file.size > 64 * 1024) throw new ApiError(meta.errors.tooBigFile);

View file

@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { QueueService } from '@/core/QueueService.js';
import { AccountMoveService } from '@/core/AccountMoveService.js';
import { TimeService } from '@/core/TimeService.js';
import type { DriveFilesRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js';
import { ApiError } from '../../error.js';
@ -67,6 +68,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private queueService: QueueService,
private accountMoveService: AccountMoveService,
private readonly timeService: TimeService,
) {
super(meta, paramDef, async (ps, me) => {
const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
@ -77,7 +79,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
const checkMoving = await this.accountMoveService.validateAlsoKnownAs(
me,
(old, src) => !!src.movedAt && src.movedAt.getTime() + 1000 * 60 * 60 * 2 > Date.now(),
(old, src) => !!src.movedAt && src.movedAt.getTime() + 1000 * 60 * 60 * 2 > this.timeService.now,
true,
);
if (checkMoving ? file.size > 32 * 1024 * 1024 : file.size > 64 * 1024) throw new ApiError(meta.errors.tooBigFile);

Some files were not shown because too many files have changed in this diff Show more