feat: complete TASK-WP4-04 color and dynamic stickers
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
export interface AmapAdapter {
|
||||
reverseGeocode(coordinates: { latitude: number; longitude: number }): Promise<{ formattedValue: string; serviceMode: "mock" }>;
|
||||
}
|
||||
|
||||
export class MockAmapAdapter implements AmapAdapter {
|
||||
readonly calls: Array<{ latitude: number; longitude: number }> = [];
|
||||
|
||||
async reverseGeocode(coordinates: { latitude: number; longitude: number }) {
|
||||
this.calls.push({ ...coordinates });
|
||||
return {
|
||||
formattedValue: `模拟地点 ${coordinates.latitude.toFixed(4)}, ${coordinates.longitude.toFixed(4)}`,
|
||||
serviceMode: "mock" as const,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -93,6 +93,8 @@ import {
|
||||
RecentAssetQuerySchema,
|
||||
RecentAssetRecordRequestSchema,
|
||||
RecentAssetRecordResponseSchema,
|
||||
ReverseGeocodeRequestSchema,
|
||||
ReverseGeocodeResponseSchema,
|
||||
RegistrationCompleteHeadersSchema,
|
||||
RegistrationCompleteRequestSchema,
|
||||
RegistrationCompleteResponseSchema,
|
||||
@@ -128,6 +130,7 @@ import {
|
||||
type ProjectStateSaveHeaders,
|
||||
type RecentAssetQuery,
|
||||
type RecentAssetRecordRequest,
|
||||
type ReverseGeocodeRequest,
|
||||
type RegistrationCompleteRequest,
|
||||
type RegistrationSendRequest,
|
||||
} from "@dada/shared-contracts";
|
||||
@@ -171,6 +174,7 @@ import {
|
||||
} from "./registration-errors.js";
|
||||
import type { RegistrationService } from "./registration.js";
|
||||
import type { RecentAssetService } from "./recent-assets.js";
|
||||
import type { AmapAdapter } from "./amap-adapter.js";
|
||||
import { ModelConfigurationError } from "./model-configuration.js";
|
||||
import type { ModelConfigurationService } from "./model-configuration.js";
|
||||
|
||||
@@ -187,6 +191,7 @@ const defaultBootstrap: BootstrapResponse = {
|
||||
};
|
||||
|
||||
export interface CreateAppOptions {
|
||||
amap?: AmapAdapter;
|
||||
bootstrap?: () => BootstrapResponse | Promise<BootstrapResponse>;
|
||||
browserGate?: boolean;
|
||||
browserSupportRelease?: BrowserSupportRelease;
|
||||
@@ -742,6 +747,8 @@ export async function createApp(options: CreateAppOptions = {}) {
|
||||
RecentAssetListResponseSchema,
|
||||
RecentAssetRecordRequestSchema,
|
||||
RecentAssetRecordResponseSchema,
|
||||
ReverseGeocodeRequestSchema,
|
||||
ReverseGeocodeResponseSchema,
|
||||
FailedEmptyTrashRequestSchema,
|
||||
FailedEmptyTrashResponseSchema,
|
||||
ModelIdSchema,
|
||||
@@ -896,6 +903,40 @@ export async function createApp(options: CreateAppOptions = {}) {
|
||||
},
|
||||
);
|
||||
|
||||
app.post(
|
||||
"/api/v1/location/reverse-geocode",
|
||||
{
|
||||
attachValidation: true,
|
||||
schema: {
|
||||
body: Type.Ref(ReverseGeocodeRequestSchema),
|
||||
headers: Type.Ref(CsrfHeadersSchema),
|
||||
operationId: "reverseGeocodeLocation",
|
||||
response: {
|
||||
200: Type.Ref(ReverseGeocodeResponseSchema),
|
||||
400: Type.Null(),
|
||||
401: Type.Ref(ErrorEnvelopeSchema),
|
||||
503: Type.Null(),
|
||||
},
|
||||
tags: ["Location"],
|
||||
},
|
||||
},
|
||||
async (request, reply) => {
|
||||
if (request.validationError) return reply.code(400).send(null);
|
||||
if (!options.registration || !options.amap) return reply.code(503).send(null);
|
||||
const token = cookieValue(headerValue(request.headers.cookie), userSessionCookieName);
|
||||
const csrfToken = headerValue(request.headers["x-csrf-token"]);
|
||||
if (!token || !csrfToken) return reply.code(401).send(createErrorEnvelope({ code: "AUTH_SESSION_INVALID", correlationId: request.id }));
|
||||
try {
|
||||
options.registration.authorizeUserMutation({ csrfToken, sessionToken: token });
|
||||
const result = await options.amap.reverseGeocode(request.body as ReverseGeocodeRequest);
|
||||
return { formatted_value: result.formattedValue, service_mode: result.serviceMode, status: "resolved" as const };
|
||||
} catch (error) {
|
||||
if (error instanceof RegistrationError) return registrationFailure(reply, request.id, error);
|
||||
return reply.code(503).send(null);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
app.post(
|
||||
"/api/v1/admin-auth/login/send",
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@ import { readSecureConfigCandidate } from "./secure-config.js";
|
||||
import { StructuredJsonlLogger } from "./structured-log.js";
|
||||
import { attachApiSupervisorControl, initializeApiCredentialClients, receiveApiCredentials } from "./supervisor-channel.js";
|
||||
import { ModelConfigurationService } from "./model-configuration.js";
|
||||
import { MockAmapAdapter } from "./amap-adapter.js";
|
||||
|
||||
const credentialChannelEnabled = process.argv.includes("--dada-credential-stdin");
|
||||
let registration: RegistrationService | undefined;
|
||||
@@ -70,6 +71,7 @@ if (credentialChannelEnabled) {
|
||||
|
||||
const browserSupportRelease = readBrowserSupportRelease(resolve("RELEASE.json"));
|
||||
const app = await createApp({
|
||||
amap: new MockAmapAdapter(),
|
||||
...(browserSupportRelease ? { browserSupportRelease } : {}),
|
||||
...(credits ? { credits } : {}),
|
||||
...(latestExports ? { latestExports } : {}),
|
||||
|
||||
Reference in New Issue
Block a user