refactor(private-zone): use canonical product name
Docker Image / Build and Push Docker Image (push) Successful in 2m7s
Docker Image / Build and Push Docker Image (push) Successful in 2m7s
This commit is contained in:
@@ -71,7 +71,7 @@ describe("local character catalog", () => {
|
||||
expect(snapshot.catalog.getById("maya-tan")).toMatchObject({
|
||||
displayName: "Maya Backend",
|
||||
assets: { avatar: "/images/avatar/maya.png" },
|
||||
capabilities: { chat: true, privateZoom: false, tip: true },
|
||||
capabilities: { chat: true, privateZone: false, tip: true },
|
||||
});
|
||||
expect(snapshot.defaultCharacter.id).toBe("elio");
|
||||
});
|
||||
@@ -101,7 +101,7 @@ describe("local character catalog", () => {
|
||||
for (const character of CHARACTERS) {
|
||||
expect(character.capabilities).toEqual({
|
||||
chat: true,
|
||||
privateZoom: true,
|
||||
privateZone: true,
|
||||
tip: true,
|
||||
});
|
||||
expect(character.tagline.length).toBeGreaterThan(0);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export interface CharacterCapabilities {
|
||||
readonly chat: boolean;
|
||||
readonly privateZoom: boolean;
|
||||
readonly privateZone: boolean;
|
||||
readonly tip: boolean;
|
||||
}
|
||||
|
||||
@@ -17,12 +17,12 @@ export interface CharacterProfile {
|
||||
readonly avatar: string;
|
||||
readonly cover: string;
|
||||
readonly chatBackground: string;
|
||||
readonly privateZoomBanner: string;
|
||||
readonly privateZoneBanner: string;
|
||||
};
|
||||
readonly copy: {
|
||||
readonly splashRelationship: string;
|
||||
readonly privateZoomTitle: string;
|
||||
readonly privateZoomSubtitle: string;
|
||||
readonly privateZoneTitle: string;
|
||||
readonly privateZoneSubtitle: string;
|
||||
readonly tipHeader: string;
|
||||
readonly tipTitle: string;
|
||||
};
|
||||
@@ -62,19 +62,19 @@ const CHARACTER_PROFILES: readonly CharacterProfile[] = Object.freeze([
|
||||
"Finally I can relax. How was your day out?",
|
||||
capabilities: {
|
||||
chat: true,
|
||||
privateZoom: true,
|
||||
privateZone: true,
|
||||
tip: true,
|
||||
},
|
||||
assets: {
|
||||
avatar: "/images/avatar/elio.png",
|
||||
cover: "/images/cover/elio.png",
|
||||
chatBackground: "/images/chat/bg-chatpage.png",
|
||||
privateZoomBanner: "/images/private-zoom/banner/elio.png",
|
||||
privateZoneBanner: "/images/private-zone/banner/elio.png",
|
||||
},
|
||||
copy: {
|
||||
splashRelationship: "Your exclusive AI boyfriend",
|
||||
privateZoomTitle: "Elio Private Zoom",
|
||||
privateZoomSubtitle: "Join me, unlock my private zoom",
|
||||
privateZoneTitle: "Elio Private Zone",
|
||||
privateZoneSubtitle: "Join me, unlock my private zone",
|
||||
tipHeader: "Tip Elio",
|
||||
tipTitle: "Buy Elio a coffee",
|
||||
},
|
||||
@@ -91,19 +91,19 @@ const CHARACTER_PROFILES: readonly CharacterProfile[] = Object.freeze([
|
||||
"How was your day?",
|
||||
capabilities: {
|
||||
chat: true,
|
||||
privateZoom: true,
|
||||
privateZone: true,
|
||||
tip: true,
|
||||
},
|
||||
assets: {
|
||||
avatar: "/images/avatar/maya.png",
|
||||
cover: "/images/cover/maya.webp",
|
||||
chatBackground: "/images/chat/bg-chatpage.png",
|
||||
privateZoomBanner: "/images/private-zoom/banner/maya.png",
|
||||
privateZoneBanner: "/images/private-zone/banner/maya.png",
|
||||
},
|
||||
copy: {
|
||||
splashRelationship: "Your exclusive AI girlfriend",
|
||||
privateZoomTitle: "Maya Private Zoom",
|
||||
privateZoomSubtitle: "Join me, unlock my private zoom",
|
||||
privateZoneTitle: "Maya Private Zone",
|
||||
privateZoneSubtitle: "Join me, unlock my private zone",
|
||||
tipHeader: "Tip Maya",
|
||||
tipTitle: "Buy Maya a coffee",
|
||||
},
|
||||
@@ -120,19 +120,19 @@ const CHARACTER_PROFILES: readonly CharacterProfile[] = Object.freeze([
|
||||
"Tell me how your day went.",
|
||||
capabilities: {
|
||||
chat: true,
|
||||
privateZoom: true,
|
||||
privateZone: true,
|
||||
tip: true,
|
||||
},
|
||||
assets: {
|
||||
avatar: "/images/avatar/nayeli.png",
|
||||
cover: "/images/cover/nayeli.webp",
|
||||
chatBackground: "/images/chat/bg-chatpage.png",
|
||||
privateZoomBanner: "/images/private-zoom/banner/nayeli.png",
|
||||
privateZoneBanner: "/images/private-zone/banner/nayeli.png",
|
||||
},
|
||||
copy: {
|
||||
splashRelationship: "Your exclusive AI girlfriend",
|
||||
privateZoomTitle: "Nayeli Private Zoom",
|
||||
privateZoomSubtitle: "Join me, unlock my private zoom",
|
||||
privateZoneTitle: "Nayeli Private Zone",
|
||||
privateZoneSubtitle: "Join me, unlock my private zone",
|
||||
tipHeader: "Tip Nayeli",
|
||||
tipTitle: "Buy Nayeli a coffee",
|
||||
},
|
||||
@@ -186,8 +186,8 @@ export function mergeRemoteCharacterCatalog(
|
||||
sortOrder: item.sortOrder,
|
||||
capabilities: {
|
||||
chat: item.capabilities.chat,
|
||||
privateZoom:
|
||||
local.capabilities.privateZoom && item.capabilities.privateContent,
|
||||
privateZone:
|
||||
local.capabilities.privateZone && item.capabilities.privateContent,
|
||||
tip: local.capabilities.tip,
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -8,7 +8,7 @@ export * from "./character_repository";
|
||||
export * from "./feedback_repository";
|
||||
export * from "./metrics_repository";
|
||||
export * from "./payment_repository";
|
||||
export * from "./private_zoom_repository";
|
||||
export * from "./private_zone_repository";
|
||||
export * from "./user_repository";
|
||||
export * from "./interfaces/iauth_repository";
|
||||
export * from "./interfaces/ichat_repository";
|
||||
@@ -16,5 +16,5 @@ export * from "./interfaces/icharacter_repository";
|
||||
export * from "./interfaces/ifeedback_repository";
|
||||
export * from "./interfaces/imetrics_repository";
|
||||
export * from "./interfaces/ipayment_repository";
|
||||
export * from "./interfaces/iprivate_zoom_repository";
|
||||
export * from "./interfaces/iprivate_zone_repository";
|
||||
export * from "./interfaces/iuser_repository";
|
||||
|
||||
@@ -8,5 +8,5 @@ export * from "./icharacter_repository";
|
||||
export * from "./ifeedback_repository";
|
||||
export * from "./imetrics_repository";
|
||||
export * from "./ipayment_repository";
|
||||
export * from "./iprivate_zoom_repository";
|
||||
export * from "./iprivate_zone_repository";
|
||||
export * from "./iuser_repository";
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import type {
|
||||
PrivateAlbumsResponse,
|
||||
PrivateAlbumUnlockResponse,
|
||||
} from "@/data/schemas/private-zoom";
|
||||
} from "@/data/schemas/private-zone";
|
||||
import type { Result } from "@/utils/result";
|
||||
|
||||
export interface GetPrivateAlbumsInput {
|
||||
@@ -9,7 +9,7 @@ export interface GetPrivateAlbumsInput {
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export interface IPrivateZoomRepository {
|
||||
export interface IPrivateZoneRepository {
|
||||
getAlbums(
|
||||
input: GetPrivateAlbumsInput,
|
||||
): Promise<Result<PrivateAlbumsResponse>>;
|
||||
+10
-10
@@ -1,22 +1,22 @@
|
||||
import type {
|
||||
GetPrivateAlbumsInput,
|
||||
IPrivateZoomRepository,
|
||||
IPrivateZoneRepository,
|
||||
} from "@/data/repositories/interfaces";
|
||||
import {
|
||||
type PrivateAlbumsResponse,
|
||||
type PrivateAlbumUnlockResponse,
|
||||
UnlockPrivateAlbumRequestSchema,
|
||||
} from "@/data/schemas/private-zoom";
|
||||
} from "@/data/schemas/private-zone";
|
||||
import {
|
||||
PrivateZoomApi,
|
||||
privateZoomApi,
|
||||
} from "@/data/services/api/private_zoom_api";
|
||||
PrivateZoneApi,
|
||||
privateZoneApi,
|
||||
} from "@/data/services/api/private_zone_api";
|
||||
import { Result } from "@/utils/result";
|
||||
|
||||
import { createLazySingleton } from "./lazy_singleton";
|
||||
|
||||
export class PrivateZoomRepository implements IPrivateZoomRepository {
|
||||
constructor(private readonly api: PrivateZoomApi) {}
|
||||
export class PrivateZoneRepository implements IPrivateZoneRepository {
|
||||
constructor(private readonly api: PrivateZoneApi) {}
|
||||
|
||||
getAlbums(
|
||||
input: GetPrivateAlbumsInput,
|
||||
@@ -37,7 +37,7 @@ export class PrivateZoomRepository implements IPrivateZoomRepository {
|
||||
}
|
||||
}
|
||||
|
||||
export const getPrivateZoomRepository =
|
||||
createLazySingleton<IPrivateZoomRepository>(
|
||||
() => new PrivateZoomRepository(privateZoomApi),
|
||||
export const getPrivateZoneRepository =
|
||||
createLazySingleton<IPrivateZoneRepository>(
|
||||
() => new PrivateZoneRepository(privateZoneApi),
|
||||
);
|
||||
@@ -3,7 +3,7 @@ import { describe, expect, it } from "vitest";
|
||||
import { FacebookIdentityResponseSchema } from "@/data/schemas/auth";
|
||||
import { ChatHistoryResponseSchema } from "@/data/schemas/chat";
|
||||
import { PaymentPlansResponseSchema } from "@/data/schemas/payment";
|
||||
import { PrivateAlbumsResponseSchema } from "@/data/schemas/private-zoom";
|
||||
import { PrivateAlbumsResponseSchema } from "@/data/schemas/private-zone";
|
||||
import { UserSchema } from "@/data/schemas/user";
|
||||
|
||||
describe("immutable schemas", () => {
|
||||
@@ -33,7 +33,7 @@ describe("immutable schemas", () => {
|
||||
expectDeepFrozen(response);
|
||||
});
|
||||
|
||||
it("freezes payment and private-zoom collections", () => {
|
||||
it("freezes payment and private-zone collections", () => {
|
||||
const plans = PaymentPlansResponseSchema.parse({
|
||||
firstRechargeOffer: {
|
||||
enabled: true,
|
||||
|
||||
+3
-3
@@ -4,7 +4,7 @@ import {
|
||||
PrivateAlbumUnlockResponseSchema,
|
||||
PrivateAlbumsResponseSchema,
|
||||
UnlockPrivateAlbumRequestSchema,
|
||||
} from "@/data/schemas/private-zoom";
|
||||
} from "@/data/schemas/private-zone";
|
||||
import { ApiPath } from "@/data/services/api";
|
||||
|
||||
const ALBUM_ID = "a1b2c3d4-0000-0000-0000-000000000000";
|
||||
@@ -96,8 +96,8 @@ describe("Private Album schema models", () => {
|
||||
});
|
||||
|
||||
it("encodes the album id in the unlock path", () => {
|
||||
expect(ApiPath.privateZoomAlbumUnlock("album:91")).toBe(
|
||||
"/api/private-zoom/albums/album%3A91/unlock",
|
||||
expect(ApiPath.privateZoneAlbumUnlock("album:91")).toBe(
|
||||
"/api/private-zone/albums/album%3A91/unlock",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -6,7 +6,7 @@ import { ApiPath } from "../api_path";
|
||||
describe("ApiPath contract source", () => {
|
||||
it("uses the shared contract path for every static operation", () => {
|
||||
const staticOperations = Object.entries(apiContract).filter(
|
||||
([operationId]) => operationId !== "privateZoomAlbumUnlock",
|
||||
([operationId]) => operationId !== "privateZoneAlbumUnlock",
|
||||
);
|
||||
|
||||
for (const [operationId, operation] of staticOperations) {
|
||||
@@ -17,8 +17,8 @@ describe("ApiPath contract source", () => {
|
||||
});
|
||||
|
||||
it("fills and encodes the private album path parameter", () => {
|
||||
expect(ApiPath.privateZoomAlbumUnlock("album/id 1")).toBe(
|
||||
"/api/private-zoom/albums/album%2Fid%201/unlock",
|
||||
expect(ApiPath.privateZoneAlbumUnlock("album/id 1")).toBe(
|
||||
"/api/private-zone/albums/album%2Fid%201/unlock",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@ vi.mock("../http_client", () => ({
|
||||
|
||||
import { ChatApi } from "../chat_api";
|
||||
import { CharacterApi } from "../character_api";
|
||||
import { PrivateZoomApi } from "../private_zoom_api";
|
||||
import { PrivateZoneApi } from "../private_zone_api";
|
||||
|
||||
const CHARACTER_ID = "elio";
|
||||
|
||||
@@ -146,18 +146,18 @@ describe("multi-character API contract", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("scopes private-zoom albums to characterId", async () => {
|
||||
it("scopes private-zone albums to characterId", async () => {
|
||||
httpClientMock.mockResolvedValue({
|
||||
success: true,
|
||||
data: { items: [], creditBalance: 0 },
|
||||
});
|
||||
|
||||
await new PrivateZoomApi().getAlbums({
|
||||
await new PrivateZoneApi().getAlbums({
|
||||
characterId: CHARACTER_ID,
|
||||
limit: 20,
|
||||
});
|
||||
|
||||
expect(httpClientMock).toHaveBeenCalledWith("/api/private-zoom/albums", {
|
||||
expect(httpClientMock).toHaveBeenCalledWith("/api/private-zone/albums", {
|
||||
query: { characterId: CHARACTER_ID, limit: 20 },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
"chatHistory": { "method": "get", "path": "/api/chat/history" },
|
||||
"chatUnlockPrivate": { "method": "post", "path": "/api/chat/unlock-private" },
|
||||
"chatUnlockHistory": { "method": "post", "path": "/api/chat/unlock-history" },
|
||||
"privateZoomAlbums": { "method": "get", "path": "/api/private-zoom/albums" },
|
||||
"privateZoomAlbumUnlock": { "method": "post", "path": "/api/private-zoom/albums/{albumId}/unlock" },
|
||||
"privateZoneAlbums": { "method": "get", "path": "/api/private-zone/albums" },
|
||||
"privateZoneAlbumUnlock": { "method": "post", "path": "/api/private-zone/albums/{albumId}/unlock" },
|
||||
"metricsPwaEvent": { "method": "post", "path": "/api/metrics/pwa/event" },
|
||||
"reportUserInfo": { "method": "post", "path": "/api/data/report-user-info" },
|
||||
"feedback": { "method": "post", "path": "/api/feedback" },
|
||||
|
||||
@@ -81,11 +81,11 @@ export class ApiPath {
|
||||
|
||||
// ============ 私密空间相关 ============
|
||||
/** 获取私密图片包列表 */
|
||||
static readonly privateZoomAlbums = apiContract.privateZoomAlbums.path;
|
||||
static readonly privateZoneAlbums = apiContract.privateZoneAlbums.path;
|
||||
|
||||
/** 解锁私密图片包 */
|
||||
static privateZoomAlbumUnlock(albumId: string): string {
|
||||
return apiContract.privateZoomAlbumUnlock.path.replace(
|
||||
static privateZoneAlbumUnlock(albumId: string): string {
|
||||
return apiContract.privateZoneAlbumUnlock.path.replace(
|
||||
"{albumId}",
|
||||
encodeURIComponent(albumId),
|
||||
);
|
||||
|
||||
@@ -12,6 +12,6 @@ export * from "./feedback_api";
|
||||
export * from "./http_client";
|
||||
export * from "./metrics_api";
|
||||
export * from "./payment_api";
|
||||
export * from "./private_zoom_api";
|
||||
export * from "./private_zone_api";
|
||||
export * from "./response_helper";
|
||||
export * from "./user_api";
|
||||
|
||||
+5
-5
@@ -4,7 +4,7 @@ import {
|
||||
PrivateAlbumUnlockResponse,
|
||||
PrivateAlbumUnlockResponseSchema,
|
||||
UnlockPrivateAlbumRequest,
|
||||
} from "@/data/schemas/private-zoom";
|
||||
} from "@/data/schemas/private-zone";
|
||||
|
||||
import { ApiPath } from "./api_path";
|
||||
import { httpClient } from "./http_client";
|
||||
@@ -15,12 +15,12 @@ export interface GetPrivateAlbumsInput {
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export class PrivateZoomApi {
|
||||
export class PrivateZoneApi {
|
||||
async getAlbums(
|
||||
input: GetPrivateAlbumsInput,
|
||||
): Promise<PrivateAlbumsResponse> {
|
||||
const env = await httpClient<ApiEnvelope<unknown>>(
|
||||
ApiPath.privateZoomAlbums,
|
||||
ApiPath.privateZoneAlbums,
|
||||
{
|
||||
query: {
|
||||
characterId: input.characterId,
|
||||
@@ -36,7 +36,7 @@ export class PrivateZoomApi {
|
||||
body: UnlockPrivateAlbumRequest,
|
||||
): Promise<PrivateAlbumUnlockResponse> {
|
||||
const env = await httpClient<ApiEnvelope<unknown>>(
|
||||
ApiPath.privateZoomAlbumUnlock(albumId),
|
||||
ApiPath.privateZoneAlbumUnlock(albumId),
|
||||
{
|
||||
method: "POST",
|
||||
body,
|
||||
@@ -46,4 +46,4 @@ export class PrivateZoomApi {
|
||||
}
|
||||
}
|
||||
|
||||
export const privateZoomApi = new PrivateZoomApi();
|
||||
export const privateZoneApi = new PrivateZoneApi();
|
||||
@@ -14,7 +14,7 @@ const PendingPaymentOrderSchema = z
|
||||
subscriptionType: z.enum(["vip", "topup", "tip"]),
|
||||
giftCategory: z.string().min(1).nullable().default(null),
|
||||
giftPlanId: z.string().min(1).nullable().default(null),
|
||||
returnTo: z.enum(["chat", "private-zoom", "profile"]).optional(),
|
||||
returnTo: z.enum(["chat", "private-zone", "profile"]).optional(),
|
||||
characterSlug: z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/).optional(),
|
||||
createdAt: z.number(),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user