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' :