The flash-sale page loads active sessions and their items through the shared client with live sale price, remaining activity stock, sell-through, and a countdown, and adds the SKU to the existing cart so checkout resolves the authoritative price. Payment and order detail tag and render the snapshotted activity unit price. The flash-sale fixtures leave the page; the fixed-data adapter still serves the domain as the rollback path.
128 lines
3.8 KiB
TypeScript
128 lines
3.8 KiB
TypeScript
import { createApi } from "@vmall/shared";
|
|
import type { ApiClient } from "@vmall/shared";
|
|
import { createMockApi } from "~/mock/api";
|
|
|
|
/**
|
|
* Domains the live backend serves. Every other domain stays on the fixed-data
|
|
* adapter, so a domain can be migrated - or rolled back - by editing this list
|
|
* alone. See openspec/changes/replace-mock-api-wave-1/design.md.
|
|
*/
|
|
type LiveDomain =
|
|
| "auth"
|
|
| "account"
|
|
| "catalog"
|
|
| "currency"
|
|
| "content"
|
|
| "shops"
|
|
| "brands"
|
|
| "cart"
|
|
| "orders"
|
|
| "shipments"
|
|
| "invoices"
|
|
| "addresses"
|
|
| "coupons"
|
|
| "points"
|
|
| "flashSales";
|
|
|
|
/**
|
|
* Explicit per-domain method picks rather than a string allowlist: indexing
|
|
* `ApiClient` by a union of method names would need an unsafe cast, and this
|
|
* keeps the compiler checking that every picked key exists.
|
|
*/
|
|
const LIVE_PICKS = {
|
|
auth: (a: ApiClient) => ({ register: a.register, login: a.login, me: a.me }),
|
|
account: (a: ApiClient) => ({ getAccountSummary: a.getAccountSummary }),
|
|
catalog: (a: ApiClient) => ({
|
|
listProducts: a.listProducts,
|
|
getProduct: a.getProduct,
|
|
listCategories: a.listCategories,
|
|
}),
|
|
currency: (a: ApiClient) => ({ listCurrencies: a.listCurrencies, convert: a.convert }),
|
|
content: (a: ApiClient) => ({ getHomeContent: a.getHomeContent }),
|
|
shops: (a: ApiClient) => ({ listShops: a.listShops, getShop: a.getShop }),
|
|
brands: (a: ApiClient) => ({ listBrands: a.listBrands }),
|
|
cart: (a: ApiClient) => ({
|
|
getCart: a.getCart,
|
|
addCartItem: a.addCartItem,
|
|
updateCartItem: a.updateCartItem,
|
|
removeCartItem: a.removeCartItem,
|
|
}),
|
|
orders: (a: ApiClient) => ({
|
|
checkout: a.checkout,
|
|
listMyOrders: a.listMyOrders,
|
|
getOrder: a.getOrder,
|
|
cancelOrder: a.cancelOrder,
|
|
payOrder: a.payOrder,
|
|
}),
|
|
shipments: (a: ApiClient) => ({
|
|
confirmDelivered: a.confirmDelivered,
|
|
listMyShipments: a.listMyShipments,
|
|
}),
|
|
invoices: (a: ApiClient) => ({
|
|
requestInvoice: a.requestInvoice,
|
|
listMyInvoices: a.listMyInvoices,
|
|
}),
|
|
addresses: (a: ApiClient) => ({
|
|
listMyAddresses: a.listMyAddresses,
|
|
createAddress: a.createAddress,
|
|
updateAddress: a.updateAddress,
|
|
deleteAddress: a.deleteAddress,
|
|
setDefaultAddress: a.setDefaultAddress,
|
|
}),
|
|
coupons: (a: ApiClient) => ({
|
|
listShopCouponTemplates: a.listShopCouponTemplates,
|
|
listMyCoupons: a.listMyCoupons,
|
|
claimCoupon: a.claimCoupon,
|
|
}),
|
|
points: (a: ApiClient) => ({
|
|
listPointsProducts: a.listPointsProducts,
|
|
listMyRedemptions: a.listMyRedemptions,
|
|
redeemPoints: a.redeemPoints,
|
|
}),
|
|
flashSales: (a: ApiClient) => ({ listFlashSales: a.listFlashSales }),
|
|
} satisfies Record<LiveDomain, (a: ApiClient) => Partial<ApiClient>>;
|
|
|
|
const KNOWN_DOMAINS = Object.keys(LIVE_PICKS) as LiveDomain[];
|
|
|
|
/** Fallback when runtimeConfig supplies no list; keep in step with nuxt.config.ts. */
|
|
const DEFAULT_LIVE_DOMAINS: LiveDomain[] = [
|
|
"catalog",
|
|
"currency",
|
|
"content",
|
|
"shops",
|
|
"brands",
|
|
"auth",
|
|
"account",
|
|
"cart",
|
|
"orders",
|
|
"shipments",
|
|
"invoices",
|
|
"addresses",
|
|
"coupons",
|
|
"points",
|
|
"flashSales",
|
|
];
|
|
|
|
export default defineNuxtPlugin(() => {
|
|
const config = useRuntimeConfig();
|
|
const mock = createMockApi();
|
|
const live = createApi({
|
|
baseUrl: config.public.apiBase as string,
|
|
getToken: () => (import.meta.client ? localStorage.getItem("vmall.token") : null),
|
|
});
|
|
|
|
const configured = config.public.liveDomains as string[] | undefined;
|
|
const liveDomains = (configured ?? DEFAULT_LIVE_DOMAINS).filter((domain): domain is LiveDomain =>
|
|
KNOWN_DOMAINS.includes(domain as LiveDomain),
|
|
);
|
|
|
|
// The fixed-data adapter is the base object, so an unmigrated domain cannot
|
|
// regress and a live domain can be rolled back by removing one entry.
|
|
let api: ApiClient = mock;
|
|
for (const domain of liveDomains) {
|
|
api = { ...api, ...LIVE_PICKS[domain](live) };
|
|
}
|
|
|
|
return { provide: { api } };
|
|
});
|