Files
cozsweet-frontend-nextjs/src/data/repositories/chat_cache_identity.ts
T

73 lines
2.7 KiB
TypeScript

import { LoginStatus } from "@/data/dto/auth";
import { AuthStorage, type IAuthStorage } from "@/data/storage/auth";
import { UserStorage, type IUserStorage } from "@/data/storage/user";
import { Result, type Result as ResultT } from "@/utils/result";
export type ChatCacheIdentityResolver = () => Promise<ResultT<string>>;
export type ChatConversationKeyResolver = (
characterId: string,
) => Promise<ResultT<string>>;
type ChatCacheAuthStorage = Pick<
IAuthStorage,
"getLoginProvider" | "getDeviceId"
>;
type ChatCacheUserStorage = Pick<IUserStorage, "getUserId">;
/**
* Resolve the only identity allowed to access the current chat cache.
*
* A user id is shared by neither account switches nor guest/account changes,
* so it is the preferred namespace for both authenticated and guest users.
* The device id is only a safe fallback for an active guest session. Real
* accounts without a persisted user id must skip the cache instead of falling
* back to a device-wide or anonymous namespace.
*/
export async function resolveChatCacheOwnerKey(
authStorage: ChatCacheAuthStorage = AuthStorage.getInstance(),
userStorage: ChatCacheUserStorage = UserStorage.getInstance(),
): Promise<ResultT<string>> {
const providerResult = await authStorage.getLoginProvider();
if (Result.isErr(providerResult)) return providerResult;
if (
providerResult.data === null ||
providerResult.data === LoginStatus.NotLoggedIn
) {
return Result.err(new Error("Chat cache identity is unavailable."));
}
const userIdResult = await userStorage.getUserId();
if (Result.isErr(userIdResult)) return userIdResult;
if (userIdResult.data && userIdResult.data.length > 0) {
return Result.ok(`user:${userIdResult.data}`);
}
if (providerResult.data === LoginStatus.Guest) {
const deviceIdResult = await authStorage.getDeviceId();
if (Result.isErr(deviceIdResult)) return deviceIdResult;
if (deviceIdResult.data && deviceIdResult.data.length > 0) {
return Result.ok(`device:${deviceIdResult.data}`);
}
}
return Result.err(new Error("Chat cache identity is unavailable."));
}
export function buildChatConversationKey(
ownerKey: string,
characterId: string,
): string {
if (ownerKey.trim().length === 0 || characterId.trim().length === 0) {
throw new Error("Chat owner and character identities must not be empty.");
}
return `${ownerKey}::character:${encodeURIComponent(characterId)}`;
}
export async function resolveChatConversationKey(
characterId: string,
): Promise<ResultT<string>> {
const ownerResult = await resolveChatCacheOwnerKey();
if (Result.isErr(ownerResult)) return ownerResult;
return Result.ok(buildChatConversationKey(ownerResult.data, characterId));
}