From 6311f8a553cc9e4b086aa1ca160d8229fd31fb67 Mon Sep 17 00:00:00 2001 From: chyoo Date: Fri, 10 Jul 2026 11:11:32 +0900 Subject: [PATCH] show notice creators with admin avatars --- prisma/launcher.schema.prisma | 4 ++ .../migration.sql | 16 +++++ src/notices/notices.controller.ts | 8 ++- src/notices/notices.module.ts | 3 +- src/notices/notices.service.ts | 63 +++++++++++++++++-- 5 files changed, 86 insertions(+), 8 deletions(-) create mode 100644 prisma/migrations/20260710102000_add_launcher_notice_creator/migration.sql diff --git a/prisma/launcher.schema.prisma b/prisma/launcher.schema.prisma index fd9cfac..d7d04c0 100644 --- a/prisma/launcher.schema.prisma +++ b/prisma/launcher.schema.prisma @@ -19,10 +19,14 @@ model LauncherNotice { startsAt DateTime? @map("starts_at") @db.Timestamptz(3) endsAt DateTime? @map("ends_at") @db.Timestamptz(3) priority Int @default(0) + createdById String? @map("created_by_id") @db.Uuid + createdByName String? @map("created_by_name") + createdByEmail String? @map("created_by_email") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3) updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3) @@index([appKey, isActive, priority]) + @@index([createdByEmail]) @@map("launcher_notices") } diff --git a/prisma/migrations/20260710102000_add_launcher_notice_creator/migration.sql b/prisma/migrations/20260710102000_add_launcher_notice_creator/migration.sql new file mode 100644 index 0000000..ccaf59f --- /dev/null +++ b/prisma/migrations/20260710102000_add_launcher_notice_creator/migration.sql @@ -0,0 +1,16 @@ +DO $$ +BEGIN + IF to_regclass('public.launcher_notices') IS NOT NULL THEN + ALTER TABLE "launcher_notices" + ADD COLUMN IF NOT EXISTS "created_by_id" UUID, + ADD COLUMN IF NOT EXISTS "created_by_name" TEXT, + ADD COLUMN IF NOT EXISTS "created_by_email" TEXT; + END IF; +END $$; + +DO $$ +BEGIN + IF to_regclass('public.launcher_notices') IS NOT NULL THEN + CREATE INDEX IF NOT EXISTS "launcher_notices_created_by_email_idx" ON "launcher_notices"("created_by_email"); + END IF; +END $$; diff --git a/src/notices/notices.controller.ts b/src/notices/notices.controller.ts index 13d0008..9be840d 100644 --- a/src/notices/notices.controller.ts +++ b/src/notices/notices.controller.ts @@ -7,6 +7,7 @@ import { Patch, Post, Query, + Req, } from '@nestjs/common'; import { @@ -31,8 +32,11 @@ export class NoticesController { } @Post() - create(@Body() body: CreateLauncherNoticeDto) { - return this.noticesService.create(body); + create( + @Body() body: CreateLauncherNoticeDto, + @Req() request: { admin?: { id: string; name: string; email: string } }, + ) { + return this.noticesService.create(body, request.admin); } @Patch(':id') diff --git a/src/notices/notices.module.ts b/src/notices/notices.module.ts index 4d1b9c8..d3b6286 100644 --- a/src/notices/notices.module.ts +++ b/src/notices/notices.module.ts @@ -1,11 +1,12 @@ import { Module } from '@nestjs/common'; +import { AuthPrismaModule } from '../auth-prisma/auth-prisma.module'; import { LauncherPrismaModule } from '../launcher-prisma/launcher-prisma.module'; import { NoticesController } from './notices.controller'; import { NoticesService } from './notices.service'; @Module({ - imports: [LauncherPrismaModule], + imports: [AuthPrismaModule, LauncherPrismaModule], controllers: [NoticesController], providers: [NoticesService], }) diff --git a/src/notices/notices.service.ts b/src/notices/notices.service.ts index 37c0e19..db57ae3 100644 --- a/src/notices/notices.service.ts +++ b/src/notices/notices.service.ts @@ -1,16 +1,66 @@ import { Injectable, NotFoundException } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; import { Prisma } from '@prisma/launcher-client'; +import { AuthPrismaService } from '../auth-prisma/auth-prisma.service'; import { LauncherPrismaService } from '../launcher-prisma/launcher-prisma.service'; +import { getUserAvatarUrl } from '../users/users.service'; import { CreateLauncherNoticeDto, ListLauncherNoticesQuery, UpdateLauncherNoticeDto, } from './dto'; +type AdminRequester = { + id: string; + name: string; + email: string; +}; + @Injectable() export class NoticesService { - constructor(private readonly launcherPrisma: LauncherPrismaService) {} + constructor( + private readonly authPrisma: AuthPrismaService, + private readonly configService: ConfigService, + private readonly launcherPrisma: LauncherPrismaService, + ) {} + + private creatorData(requester?: AdminRequester) { + return requester + ? { + createdById: requester.id, + createdByName: requester.name, + createdByEmail: requester.email, + } + : {}; + } + + private async withCreatorAvatars(items: T[]) { + const emails = [...new Set(items.map((item) => item.createdByEmail).filter((email): email is string => Boolean(email)))]; + if (emails.length === 0) return items.map((item) => ({ ...item, createdByAvatarUrl: null })); + + const users = await this.authPrisma.user.findMany({ + where: { email: { in: emails } }, + select: { + id: true, + email: true, + profileImageMime: true, + }, + }); + const userByEmail = new Map(users.map((user) => [user.email, user])); + const authBaseUrl = this.configService.get('AUTH_PUBLIC_URL') + ?? this.configService.get('ATNMY_AUTH_URL') + ?? this.configService.get('AUTH_EXTERNAL_URL') + ?? 'https://auth.autonomystudio.net'; + + return items.map((item) => { + const user = item.createdByEmail ? userByEmail.get(item.createdByEmail) : null; + return { + ...item, + createdByAvatarUrl: getUserAvatarUrl(user?.id ?? '', user?.profileImageMime, authBaseUrl), + }; + }); + } async list(query: ListLauncherNoticesQuery) { const keyword = query.keyword?.trim(); @@ -37,11 +87,11 @@ export class NoticesService { }); return { - notices: notices.map((notice, index) => ({ + notices: await this.withCreatorAvatars(notices.map((notice, index) => ({ ...notice, no: notices.length - index, status: getNoticeStatus(notice, now), - })), + }))), }; } @@ -57,9 +107,12 @@ export class NoticesService { return { notice }; } - async create(body: CreateLauncherNoticeDto) { + async create(body: CreateLauncherNoticeDto, requester?: AdminRequester) { const notice = await this.launcherPrisma.launcherNotice.create({ - data: toCreateNoticeData(body), + data: { + ...toCreateNoticeData(body), + ...this.creatorData(requester), + }, }); return { notice };