계정 아이콘 등록과 목록 내리기 제한을 조정한다

This commit is contained in:
2026-09-24 15:46:11 +00:00
parent 4bf812b601
commit 8823434610
9 changed files with 138 additions and 153 deletions
@@ -434,13 +434,14 @@ export const createInMemoryUserRepository = (hasher: PasswordHasher = createPass
updatedAt: Date,
dayStart: Date,
consumeDailyQuota: boolean,
allowCutoffEquality = false
allowCutoffEquality = false,
enforceCooldown = true
): Promise<string | null> {
for (const user of usersByName.values()) {
if (user.id !== userId) {
continue;
}
if (user.picture !== 'default.jpg' && user.iconUpdatedAt) {
if (enforceCooldown && user.picture !== 'default.jpg' && user.iconUpdatedAt) {
const previousUpdate = new Date(user.iconUpdatedAt);
if (allowCutoffEquality ? previousUpdate > dayStart : previousUpdate >= dayStart) {
return null;
@@ -468,12 +469,9 @@ export const createInMemoryUserRepository = (hasher: PasswordHasher = createPass
.filter((icon) => icon.userId === userId && (includeRetired || !icon.retiredAt))
.sort((a, b) => a.createdAt.localeCompare(b.createdAt) || a.id.localeCompare(b.id));
},
async addIconForWindow(userId, picture, imageServer, now, uploadCutoff, maxActive) {
async addIconForWindow(userId, picture, imageServer, now, maxActive) {
const user = [...usersByName.values()].find((candidate) => candidate.id === userId);
if (!user) return { ok: false, reason: 'NOT_FOUND' };
if (user.iconUpdatedAt && new Date(user.iconUpdatedAt) > uploadCutoff) {
return { ok: false, reason: 'COOLDOWN' };
}
const active = [...iconsById.values()].filter((icon) => icon.userId === userId && !icon.retiredAt);
if (active.length >= maxActive) return { ok: false, reason: 'LIMIT' };
const revision = nextRevision(user, now);
@@ -607,7 +607,8 @@ export const createPostgresUserRepository = (
updatedAt: Date,
dayStart: Date,
consumeDailyQuota: boolean,
allowCutoffEquality = false
allowCutoffEquality = false,
enforceCooldown = true
): Promise<string | null> {
const rows = await prisma.$queryRaw<Array<{ iconRevision: Date }>>(GatewayPrisma.sql`
UPDATE "app_user"
@@ -623,7 +624,7 @@ export const createPostgresUserRepository = (
COALESCE("icon_revision", "icon_updated_at", "created_at") + INTERVAL '1 millisecond'
)
WHERE "id" = ${userId}
AND (
AND (NOT ${enforceCooldown} OR
"picture" = 'default.jpg'
OR "icon_updated_at" IS NULL
OR "icon_updated_at" < ${dayStart}
@@ -640,20 +641,16 @@ export const createPostgresUserRepository = (
});
return rows.map(mapIcon);
},
async addIconForWindow(userId, picture, imageServer, now, uploadCutoff, maxActive) {
async addIconForWindow(userId, picture, imageServer, now, maxActive) {
return prisma.$transaction(async (tx) => {
const users = await tx.$queryRaw<
Array<{ createdAt: Date; iconUpdatedAt: Date | null; iconRevision: Date | null }>
Array<{ createdAt: Date; iconRevision: Date | null }>
>(GatewayPrisma.sql`
SELECT "created_at" AS "createdAt", "icon_updated_at" AS "iconUpdatedAt",
"icon_revision" AS "iconRevision"
SELECT "created_at" AS "createdAt", "icon_revision" AS "iconRevision"
FROM "app_user" WHERE "id" = ${userId} FOR UPDATE
`);
const user = users[0];
if (!user) return { ok: false as const, reason: 'NOT_FOUND' as const };
if (user.iconUpdatedAt && user.iconUpdatedAt > uploadCutoff) {
return { ok: false as const, reason: 'COOLDOWN' as const };
}
const activeCount = await tx.userIcon.count({ where: { userId, retiredAt: null } });
if (activeCount >= maxActive) return { ok: false as const, reason: 'LIMIT' as const };
const revision = new Date(
+3 -3
View File
@@ -63,7 +63,7 @@ export interface SpecialAccountAccessGrantRecord {
}
export type AddUserIconResult =
{ ok: true; icon: UserIconRecord; revision: string } | { ok: false; reason: 'COOLDOWN' | 'LIMIT' | 'NOT_FOUND' };
{ ok: true; icon: UserIconRecord; revision: string } | { ok: false; reason: 'LIMIT' | 'NOT_FOUND' };
export type RetireUserIconResult =
| { ok: true; icon: UserIconRecord; revision: string; preferredChanged: boolean }
@@ -233,7 +233,8 @@ export interface UserRepository {
updatedAt: Date,
dayStart: Date,
consumeDailyQuota: boolean,
allowCutoffEquality?: boolean
allowCutoffEquality?: boolean,
enforceCooldown?: boolean
): Promise<string | null>;
listIcons(userId: string, includeRetired?: boolean): Promise<UserIconRecord[]>;
addIconForWindow(
@@ -241,7 +242,6 @@ export interface UserRepository {
picture: string,
imageServer: number,
now: Date,
uploadCutoff: Date,
maxActive: number
): Promise<AddUserIconResult>;
setPreferredIcon(userId: string, iconId: string, now: Date): Promise<string | null>;