feat: add user icon library and in-game selection
This commit is contained in:
@@ -32,6 +32,14 @@ export interface GatewayUserInfo {
|
||||
canUseGeneralPicture?: boolean;
|
||||
createdAt?: string;
|
||||
legacyMemberNo?: number;
|
||||
icons?: GatewayUserIconInfo[];
|
||||
}
|
||||
|
||||
export interface GatewayUserIconInfo {
|
||||
id: string;
|
||||
picture: string;
|
||||
imageServer: number;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface GameSessionTokenPayload {
|
||||
@@ -98,6 +106,20 @@ export const parseGameSessionTokenPayload = (value: unknown): GameSessionTokenPa
|
||||
(user.profileIconResetAt !== undefined &&
|
||||
(typeof user.profileIconResetAt !== 'string' || !isCanonicalIsoTimestamp(user.profileIconResetAt))) ||
|
||||
(user.canUseGeneralPicture !== undefined && typeof user.canUseGeneralPicture !== 'boolean') ||
|
||||
(user.icons !== undefined &&
|
||||
(!Array.isArray(user.icons) ||
|
||||
user.icons.length > 5 ||
|
||||
user.icons.some(
|
||||
(icon) =>
|
||||
!icon ||
|
||||
typeof icon !== 'object' ||
|
||||
typeof icon.id !== 'string' ||
|
||||
typeof icon.picture !== 'string' ||
|
||||
!Number.isSafeInteger(icon.imageServer) ||
|
||||
icon.imageServer < 0 ||
|
||||
typeof icon.createdAt !== 'string' ||
|
||||
!isCanonicalIsoTimestamp(icon.createdAt)
|
||||
))) ||
|
||||
(user.legacyMemberNo !== undefined && (!Number.isSafeInteger(user.legacyMemberNo) || user.legacyMemberNo <= 0))
|
||||
) {
|
||||
return null;
|
||||
|
||||
@@ -212,6 +212,7 @@ export type TurnDaemonCommand =
|
||||
picture: string;
|
||||
imageServer: number;
|
||||
iconRevision: string;
|
||||
enforceCooldown?: boolean;
|
||||
}
|
||||
| {
|
||||
type: 'joinCreateGeneral';
|
||||
@@ -254,6 +255,9 @@ export type TurnDaemonCommand =
|
||||
uniqueName: string;
|
||||
personality: string;
|
||||
seedOwnerIdentity: string | number;
|
||||
ownerPicture?: string;
|
||||
ownerImageServer?: number;
|
||||
ownerIconRevision?: string;
|
||||
}
|
||||
| {
|
||||
type: 'selectPoolReselect';
|
||||
@@ -534,8 +538,9 @@ export type TurnDaemonCommandResult =
|
||||
| {
|
||||
type: 'adjustGeneralIcon';
|
||||
ok: false;
|
||||
code: 'CONFLICT' | 'PRECONDITION_FAILED';
|
||||
code: 'CONFLICT' | 'PRECONDITION_FAILED' | 'TOO_MANY_REQUESTS';
|
||||
reason: string;
|
||||
availableAt?: string;
|
||||
}
|
||||
| {
|
||||
type: 'joinCreateGeneral';
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
ALTER TABLE "app_user"
|
||||
ADD COLUMN "icon_retired_at" TIMESTAMP(3);
|
||||
|
||||
CREATE TABLE "user_icon" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"user_id" TEXT NOT NULL,
|
||||
"picture" TEXT NOT NULL,
|
||||
"image_server" INTEGER NOT NULL DEFAULT 1,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"retired_at" TIMESTAMP(3),
|
||||
CONSTRAINT "user_icon_pkey" PRIMARY KEY ("id"),
|
||||
CONSTRAINT "user_icon_user_id_fkey" FOREIGN KEY ("user_id")
|
||||
REFERENCES "app_user"("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX "user_icon_picture_key" ON "user_icon"("picture");
|
||||
CREATE INDEX "user_icon_user_id_retired_at_created_at_idx"
|
||||
ON "user_icon"("user_id", "retired_at", "created_at");
|
||||
|
||||
-- 기존 전콘의 파일명과 URL을 바꾸지 않고 활성 목록의 첫 항목으로 올립니다.
|
||||
INSERT INTO "user_icon" ("user_id", "picture", "image_server", "created_at")
|
||||
SELECT
|
||||
"id",
|
||||
"picture",
|
||||
"image_server",
|
||||
COALESCE("icon_updated_at", "created_at")
|
||||
FROM "app_user"
|
||||
WHERE "picture" <> 'default.jpg' AND "image_server" > 0
|
||||
ON CONFLICT ("picture") DO NOTHING;
|
||||
@@ -59,36 +59,51 @@ enum GatewaySourceMode {
|
||||
}
|
||||
|
||||
model AppUser {
|
||||
id String @id @default(uuid())
|
||||
loginId String @unique @map("login_id")
|
||||
displayName String @unique @map("display_name")
|
||||
passwordHash String @map("password_hash")
|
||||
passwordSalt String @map("password_salt")
|
||||
roles Json @default(dbgenerated("'[]'::jsonb"))
|
||||
sanctions Json @default(dbgenerated("'{}'::jsonb"))
|
||||
oauthType OAuthType @default(NONE) @map("oauth_type")
|
||||
oauthId String? @unique @map("oauth_id")
|
||||
email String? @unique
|
||||
oauthInfo Json @default(dbgenerated("'{}'::jsonb")) @map("oauth_info")
|
||||
picture String @default("default.jpg")
|
||||
imageServer Int @default(0) @map("image_server")
|
||||
iconUpdatedAt DateTime? @map("icon_updated_at")
|
||||
iconRevision DateTime? @map("icon_revision")
|
||||
profileIconResetAt DateTime? @map("profile_icon_reset_at")
|
||||
thirdPartyUse Boolean @default(true) @map("third_party_use")
|
||||
termsAcceptedAt DateTime? @map("terms_accepted_at")
|
||||
privacyAcceptedAt DateTime? @map("privacy_accepted_at")
|
||||
kakaoVerifiedAt DateTime? @map("kakao_verified_at")
|
||||
kakaoGraceStartedAt DateTime @default(now()) @map("kakao_grace_started_at")
|
||||
deleteAfter DateTime? @map("delete_after")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
lastLoginAt DateTime? @map("last_login_at")
|
||||
legacyData Json @default(dbgenerated("'{}'::jsonb")) @map("legacy_data")
|
||||
id String @id @default(uuid())
|
||||
loginId String @unique @map("login_id")
|
||||
displayName String @unique @map("display_name")
|
||||
passwordHash String @map("password_hash")
|
||||
passwordSalt String @map("password_salt")
|
||||
roles Json @default(dbgenerated("'[]'::jsonb"))
|
||||
sanctions Json @default(dbgenerated("'{}'::jsonb"))
|
||||
oauthType OAuthType @default(NONE) @map("oauth_type")
|
||||
oauthId String? @unique @map("oauth_id")
|
||||
email String? @unique
|
||||
oauthInfo Json @default(dbgenerated("'{}'::jsonb")) @map("oauth_info")
|
||||
picture String @default("default.jpg")
|
||||
imageServer Int @default(0) @map("image_server")
|
||||
iconUpdatedAt DateTime? @map("icon_updated_at")
|
||||
iconRevision DateTime? @map("icon_revision")
|
||||
profileIconResetAt DateTime? @map("profile_icon_reset_at")
|
||||
iconRetiredAt DateTime? @map("icon_retired_at")
|
||||
thirdPartyUse Boolean @default(true) @map("third_party_use")
|
||||
termsAcceptedAt DateTime? @map("terms_accepted_at")
|
||||
privacyAcceptedAt DateTime? @map("privacy_accepted_at")
|
||||
kakaoVerifiedAt DateTime? @map("kakao_verified_at")
|
||||
kakaoGraceStartedAt DateTime @default(now()) @map("kakao_grace_started_at")
|
||||
deleteAfter DateTime? @map("delete_after")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
lastLoginAt DateTime? @map("last_login_at")
|
||||
legacyData Json @default(dbgenerated("'{}'::jsonb")) @map("legacy_data")
|
||||
icons UserIcon[]
|
||||
|
||||
@@map("app_user")
|
||||
}
|
||||
|
||||
model UserIcon {
|
||||
id String @id @default(uuid())
|
||||
userId String @map("user_id")
|
||||
user AppUser @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
picture String @unique
|
||||
imageServer Int @default(1) @map("image_server")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
retiredAt DateTime? @map("retired_at")
|
||||
|
||||
@@index([userId, retiredAt, createdAt])
|
||||
@@map("user_icon")
|
||||
}
|
||||
|
||||
model LegacyMemberLog {
|
||||
id BigInt @id
|
||||
memberNo Int @map("member_no")
|
||||
|
||||
Reference in New Issue
Block a user