Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"@elysiajs/eden": "^1.4.9",
"@elysiajs/openapi": "^1.4.14",
"@elysiajs/opentelemetry": "^1.4.10",
"@fanee/runtime-node": "^0.2.1",
"@fanee/core": "^0.8.1",
"@fanee/node": "^0.8.1",
"@opentelemetry/api": "^1.9.1",
"@opentelemetry/exporter-trace-otlp-proto": "^0.214.0",
"@opentelemetry/sdk-trace-node": "^2.6.1",
Expand Down
2 changes: 1 addition & 1 deletion apps/backend/src/common/error/getErrorResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const getErrorResponse = <T extends StatusFunc>(
const traceId = getTraceId();
const { message } = data;
const translatedMessages = message ? i18nRuntime.tAll(message) : undefined;
const translatedMessage = message ? i18nRuntime.t({ locale })(message) : undefined;
const translatedMessage = message ? i18nRuntime.getT({ locale })(message) : undefined;
return statusFunc(statusCode, {
...data,
message: translatedMessage,
Expand Down
17 changes: 10 additions & 7 deletions apps/backend/src/common/i18n.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { env } from "@cvsa/env";
import { FaneeRuntime } from "@fanee/runtime-node";
import { i18n } from "@fanee/core";
import path from "node:path";
import { initFaneeNode } from "@fanee/node";

const getBundleLocation = () => {
if (env.NODE_ENV !== "production") {
Expand All @@ -10,10 +11,12 @@ const getBundleLocation = () => {
}
};

export const i18nRuntime: FaneeRuntime = new FaneeRuntime({
bundlePath: getBundleLocation(),
defaultLocale: "zh",
namespace: "backend",
});
i18n.use(
initFaneeNode({
bundlePath: getBundleLocation(),
})
);

await i18nRuntime.load();
export const i18nRuntime = i18n;

await i18n.ready();
38 changes: 38 additions & 0 deletions apps/backend/src/handlers/catalog/song/externalLink/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Elysia } from "elysia";
import {
ErrorResponseSchema,
SongExternalLinkCreateRequestSchema,
SongExternalLinkResponseSchema,
songService,
} from "@cvsa/core";
import { authMiddleware } from "@/middlewares";
import { traceTask } from "@/common/trace";
import z from "zod";

export const songExternalLinkCreateHandler = new Elysia({ name: "songExternalLinkCreateHandler" })
.use(authMiddleware)
.post(
"/song/:id/external-link",
async ({ body, params, status }) => {
const link = await traceTask("songService.createExternalLink", async () => {
return await songService.createExternalLink(params.id, body);
});
return status(201, link);
},
{
body: SongExternalLinkCreateRequestSchema,
detail: {
summary: "Create Song External Link",
description: "Add a new external link for a song. Requires authentication.",
},
response: {
201: SongExternalLinkResponseSchema,
400: ErrorResponseSchema,
401: ErrorResponseSchema,
404: ErrorResponseSchema,
},
params: z.object({
id: z.coerce.number().int().positive(),
}),
}
);
34 changes: 34 additions & 0 deletions apps/backend/src/handlers/catalog/song/externalLink/delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Elysia } from "elysia";
import { ErrorResponseSchema, songService } from "@cvsa/core";
import { authMiddleware } from "@/middlewares";
import { traceTask } from "@/common/trace";
import z from "zod";

export const songExternalLinkDeleteHandler = new Elysia({ name: "songExternalLinkDeleteHandler" })
.use(authMiddleware)
.delete(
"/song/:id/external-link/:linkId",
async ({ params, set }) => {
await traceTask("songService.deleteExternalLink", async () => {
return await songService.deleteExternalLink(params.id, params.linkId);
});
set.status = 204;
return null;
},
{
detail: {
summary: "Delete Song External Link",
description: "Delete a specific external link for a song. Requires authentication.",
},
response: {
204: z.null(),
400: ErrorResponseSchema,
401: ErrorResponseSchema,
404: ErrorResponseSchema,
},
params: z.object({
id: z.coerce.number().int().positive(),
linkId: z.coerce.number().int().positive(),
}),
}
);
9 changes: 9 additions & 0 deletions apps/backend/src/handlers/catalog/song/externalLink/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Elysia } from "elysia";
import { songExternalLinkCreateHandler } from "./create";
import { songExternalLinkUpdateHandler } from "./update";
import { songExternalLinkDeleteHandler } from "./delete";

export const songExternalLinkHandler = new Elysia({ name: "songExternalLinkHandler" })
.use(songExternalLinkCreateHandler)
.use(songExternalLinkUpdateHandler)
.use(songExternalLinkDeleteHandler);
39 changes: 39 additions & 0 deletions apps/backend/src/handlers/catalog/song/externalLink/update.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Elysia } from "elysia";
import {
ErrorResponseSchema,
SongExternalLinkUpdateRequestSchema,
SongExternalLinkResponseSchema,
songService,
} from "@cvsa/core";
import { authMiddleware } from "@/middlewares";
import { traceTask } from "@/common/trace";
import z from "zod";

export const songExternalLinkUpdateHandler = new Elysia({ name: "songExternalLinkUpdateHandler" })
.use(authMiddleware)
.patch(
"/song/:id/external-link/:linkId",
async ({ body, params, status }) => {
const link = await traceTask("songService.updateExternalLink", async () => {
return await songService.updateExternalLink(params.id, params.linkId, body);
});
return status(200, link);
},
{
body: SongExternalLinkUpdateRequestSchema,
detail: {
summary: "Update Song External Link",
description: "Update a specific external link for a song. Requires authentication.",
},
response: {
200: SongExternalLinkResponseSchema,
400: ErrorResponseSchema,
401: ErrorResponseSchema,
404: ErrorResponseSchema,
},
params: z.object({
id: z.coerce.number().int().positive(),
linkId: z.coerce.number().int().positive(),
}),
}
);
4 changes: 3 additions & 1 deletion apps/backend/src/handlers/catalog/song/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import { songDeleteHandler } from "./delete";
import { songDetailsHandler } from "./details";
import { songSearchHandler } from "./search";
import { songLyricsHandler } from "./lyrics";
import { songExternalLinkHandler } from "./externalLink";

export const songHandler = new Elysia({ name: "songHandler" })
.use(songDetailsHandler)
.use(songCreateHandler)
.use(songUpdateHandler)
.use(songDeleteHandler)
.use(songSearchHandler)
.use(songLyricsHandler);
.use(songLyricsHandler)
.use(songExternalLinkHandler);
1 change: 0 additions & 1 deletion apps/backend/src/middlewares/auth.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Elysia } from "elysia";
import { AppError, auth, betterAuthUserToEntity, toBetterAuthHeaders } from "@cvsa/core";
import { appLogger } from "@cvsa/logger";

export const authMiddleware = new Elysia({ name: "authMiddleware" }).derive(
{ as: "scoped" },
Expand Down
Loading