Fixed 267U.pre2

This commit is contained in:
2026-07-26 18:25:37 +09:00
parent 50bfaeafdf
commit 317d00a284
1286 changed files with 80222 additions and 1 deletions
@@ -0,0 +1,42 @@
import { MemorietArchives, Memoriets, Notes, Users } from "../../../models/index.js";
import deleteNote from "../../../services/note/delete.js";
import { queueLogger } from "../../logger.js";
import { genId } from "../../../misc/gen-id.js";
const logger = queueLogger.createSubLogger("check-expired-memoriets");
export async function checkExpiredMemoriets() {
logger.info("Checking expired Memoriets...");
const expired = await Memoriets.createQueryBuilder("memoriet").where("memoriet.expiresAt IS NOT NULL").andWhere("memoriet.expiresAt < :now", {
now: new Date()
}).getMany();
for (const memoriet of expired){
const note = await Notes.findOneBy({
id: memoriet.noteId
});
if (note != null) {
const user = await Users.findOneBy({
id: note.userId
});
if (user != null) {
await MemorietArchives.save({
id: genId(),
createdAt: memoriet.createdAt,
deletedAt: new Date(),
userId: memoriet.userId,
text: note.text?.replace(/\n?#Memoriet\b/gi, "").trim() || null,
cw: note.cw,
fileIds: note.fileIds,
textLayers: memoriet.textLayers,
visibility: note.visibility
}).catch((err)=>{
logger.warn(`Failed to archive expired Memoriet ${memoriet.id}: ${err}`);
});
await deleteNote(user, note, true).catch((err)=>{
logger.warn(`Failed to delete expired Memoriet note ${note.id}: ${err}`);
});
}
}
await Memoriets.delete(memoriet.id);
}
logger.succ("All expired Memoriets checked.");
return `done: ${expired.length}`;
}
@@ -0,0 +1,21 @@
import { In } from "typeorm";
import { Mutings } from "../../../models/index.js";
import { queueLogger } from "../../logger.js";
import { publishUserEvent } from "../../../services/stream.js";
const logger = queueLogger.createSubLogger("check-expired-mutings");
export async function checkExpiredMutings() {
logger.info("Checking expired mutings...");
const expired = await Mutings.createQueryBuilder("muting").where("muting.expiresAt IS NOT NULL").andWhere("muting.expiresAt < :now", {
now: new Date()
}).innerJoinAndSelect("muting.mutee", "mutee").getMany();
if (expired.length > 0) {
await Mutings.delete({
id: In(expired.map((m)=>m.id))
});
for (const m of expired){
publishUserEvent(m.muterId, "unmute", m.mutee);
}
}
logger.succ("All expired mutings checked.");
return "Success";
}
@@ -0,0 +1,22 @@
import { queueLogger } from "../../logger.js";
import { activeUsersChart, driveChart, federationChart, hashtagChart, instanceChart, notesChart, perUserDriveChart, perUserFollowingChart, perUserNotesChart, perUserReactionsChart, usersChart, apRequestChart } from "../../../services/chart/index.js";
const logger = queueLogger.createSubLogger("clean-charts");
export async function cleanCharts() {
logger.info("Clean charts...");
await Promise.all([
federationChart.clean(),
notesChart.clean(),
usersChart.clean(),
activeUsersChart.clean(),
instanceChart.clean(),
perUserNotesChart.clean(),
driveChart.clean(),
perUserReactionsChart.clean(),
hashtagChart.clean(),
perUserFollowingChart.clean(),
perUserDriveChart.clean(),
apRequestChart.clean()
]);
logger.succ("All charts successfully cleaned.");
return "Success";
}
@@ -0,0 +1,12 @@
import { LessThan } from "typeorm";
import { UserIps } from "../../../models/index.js";
import { queueLogger } from "../../logger.js";
const logger = queueLogger.createSubLogger("clean");
export async function clean() {
logger.info("Cleaning...");
await UserIps.delete({
createdAt: LessThan(new Date(Date.now() - 1000 * 60 * 60 * 24 * 90))
});
logger.succ("Cleaned.");
return "Success";
}
@@ -0,0 +1,80 @@
import { ScheduledNotes } from "../../../models/index.js";
import { createQueue, defaultJobOpts } from "../index.js";
import { clean } from "./clean.js";
import { cleanCharts } from "./clean-charts.js";
import { checkExpiredMutings } from "./check-expired-mutings.js";
import { checkExpiredMemoriets } from "./check-expired-memoriets.js";
import { resyncCharts } from "./resync-charts.js";
import { tickCharts } from "./tick-charts.js";
import { verifyLinks } from "./verify-links.js";
import { publishScheduledNoteJob } from "./publish-scheduled-note.js";
import { enqueueScheduledNote } from "./scheduled-note.js";
const processors = {
clean,
cleanCharts,
checkExpiredMutings,
checkExpiredMemoriets,
resyncCharts,
tickCharts,
verifyLinks,
publishScheduledNote: publishScheduledNoteJob
};
async function process(job) {
const processor = processors[job.name];
if (processor === undefined) return "skip: unknown job name";
return await processor(job);
}
const [systemQueue_, systemInitQueue] = createQueue("system", process, {
concurrency: Object.keys(processors).length
});
export const systemQueue = systemQueue_;
async function enqueuePendingScheduledNotes() {
const scheduledNotes = await ScheduledNotes.find({
where: {
status: "scheduled"
}
});
for (const scheduledNote of scheduledNotes){
await enqueueScheduledNote(scheduledNote);
}
}
export async function systemInit() {
await systemInitQueue();
for (const { name, seconds } of [
{
name: "clean",
seconds: 60 * 60
},
{
name: "cleanCharts",
seconds: 60 * 60
},
{
name: "checkExpiredMutings",
seconds: 5 * 60
},
{
name: "checkExpiredMemoriets",
seconds: 60
},
{
name: "resyncCharts",
seconds: 60 * 60
},
{
name: "tickCharts",
seconds: 60
},
{
name: "verifyLinks",
seconds: 60 * 60 * 24
}
]){
await systemQueue.upsertJobScheduler(name, {
every: seconds * 1000
}, {
opts: defaultJobOpts
});
}
await enqueuePendingScheduledNotes();
}
@@ -0,0 +1,4 @@
import { publishScheduledNote } from "../../../services/note/scheduled.js";
export async function publishScheduledNoteJob(job) {
return await publishScheduledNote(job.data.scheduledNoteId);
}
@@ -0,0 +1,15 @@
import { queueLogger } from "../../logger.js";
import { driveChart, notesChart, usersChart } from "../../../services/chart/index.js";
const logger = queueLogger.createSubLogger("resync-charts");
export async function resyncCharts() {
logger.info("Resync charts...");
// TODO: ユーザーごとのチャートも更新する
// TODO: インスタンスごとのチャートも更新する
await Promise.all([
driveChart.resync(),
notesChart.resync(),
usersChart.resync()
]);
logger.succ("All charts successfully resynced.");
return "Success";
}
@@ -0,0 +1,11 @@
import { createQueueProducer, defaultJobOpts } from "../index.js";
const scheduledNoteSystemQueue = createQueueProducer("system");
export async function enqueueScheduledNote(scheduledNote) {
await scheduledNoteSystemQueue.add("publishScheduledNote", {
scheduledNoteId: scheduledNote.id
}, {
...defaultJobOpts,
delay: Math.max(0, scheduledNote.scheduledAt.getTime() - Date.now()),
jobId: `scheduledNote:${scheduledNote.id}`
});
}
@@ -0,0 +1,22 @@
import { queueLogger } from "../../logger.js";
import { activeUsersChart, driveChart, federationChart, hashtagChart, instanceChart, notesChart, perUserDriveChart, perUserFollowingChart, perUserNotesChart, perUserReactionsChart, usersChart, apRequestChart } from "../../../services/chart/index.js";
const logger = queueLogger.createSubLogger("tick-charts");
export async function tickCharts() {
logger.info("Tick charts...");
await Promise.all([
federationChart.tick(false),
notesChart.tick(false),
usersChart.tick(false),
activeUsersChart.tick(false),
instanceChart.tick(false),
perUserNotesChart.tick(false),
driveChart.tick(false),
perUserReactionsChart.tick(false),
hashtagChart.tick(false),
perUserFollowingChart.tick(false),
perUserDriveChart.tick(false),
apRequestChart.tick(false)
]);
logger.succ("All charts successfully ticked.");
return "Success";
}
@@ -0,0 +1,34 @@
import { UserProfiles } from "../../../models/index.js";
import { Not } from "typeorm";
import { queueLogger } from "../../logger.js";
import { verifyLink } from "../../../services/fetch-rel-me.js";
const logger = queueLogger.createSubLogger("verify-links");
export async function verifyLinks() {
logger.info("Verifying links...");
const usersToVerify = await UserProfiles.findBy({
fields: Not(null),
userHost: ""
});
for (const user of usersToVerify){
for (const field of user.fields){
if (!field || field.name === "" || field.value === "") {
continue;
}
if (field.value.startsWith("http") && user.user?.username) {
field.verified = await verifyLink(field.value, user.user.username);
}
}
if (user.fields.length > 0) {
try {
await UserProfiles.update(user.userId, {
fields: user.fields
});
} catch (e) {
logger.error(`Failed to update user ${user.userId} ${e}`);
throw e;
}
}
}
logger.succ("All links successfully verified.");
return "Success";
}