mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-31 20:24:27 -04:00
Validate language tags
This commit is contained in:
parent
cdceb1a734
commit
0c0628529c
@ -1,7 +1,7 @@
|
|||||||
import Elysia from "elysia";
|
import Elysia from "elysia";
|
||||||
import { Movie, SeedMovie } from "~/models/movie";
|
import { Movie, SeedMovie } from "~/models/movie";
|
||||||
import { seedMovie, SeedMovieResponse } from "./movies";
|
import { seedMovie, SeedMovieResponse } from "./movies";
|
||||||
import { Resource } from "~/models/utils";
|
import { Resource, validateTranslations } from "~/models/utils";
|
||||||
import { comment } from "~/utils";
|
import { comment } from "~/utils";
|
||||||
import { KError } from "~/models/error";
|
import { KError } from "~/models/error";
|
||||||
|
|
||||||
@ -14,6 +14,9 @@ export const seed = new Elysia()
|
|||||||
.post(
|
.post(
|
||||||
"/movies",
|
"/movies",
|
||||||
async ({ body, error }) => {
|
async ({ body, error }) => {
|
||||||
|
const err = validateTranslations(body.translations);
|
||||||
|
if (err) return error(400, err);
|
||||||
|
|
||||||
const { status, ...ret } = await seedMovie(body);
|
const { status, ...ret } = await seedMovie(body);
|
||||||
return error(status, ret);
|
return error(status, ret);
|
||||||
},
|
},
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { sql } from "drizzle-orm";
|
import { relations, sql } from "drizzle-orm";
|
||||||
import {
|
import {
|
||||||
check,
|
check,
|
||||||
date,
|
date,
|
||||||
@ -108,3 +108,14 @@ export const showTranslations = schema.table(
|
|||||||
},
|
},
|
||||||
(t) => [primaryKey({ columns: [t.pk, t.language] })],
|
(t) => [primaryKey({ columns: [t.pk, t.language] })],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const showsRelations = relations(shows, ({ many }) => ({
|
||||||
|
translations: many(showTranslations, { relationName: "showTranslations" }),
|
||||||
|
}));
|
||||||
|
export const showsTrRelations = relations(showTranslations, ({ one }) => ({
|
||||||
|
show: one(shows, {
|
||||||
|
relationName: "showTranslations",
|
||||||
|
fields: [showTranslations.pk],
|
||||||
|
references: [shows.pk],
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
@ -12,6 +12,7 @@ import { videos } from "./controllers/videos";
|
|||||||
import { db } from "./db";
|
import { db } from "./db";
|
||||||
import { Image } from "./models/utils";
|
import { Image } from "./models/utils";
|
||||||
import { comment } from "./utils";
|
import { comment } from "./utils";
|
||||||
|
import { base } from "./base";
|
||||||
|
|
||||||
await migrate(db, { migrationsSchema: "kyoo", migrationsFolder: "./drizzle" });
|
await migrate(db, { migrationsSchema: "kyoo", migrationsFolder: "./drizzle" });
|
||||||
|
|
||||||
@ -37,6 +38,7 @@ if (!secret) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const app = new Elysia()
|
const app = new Elysia()
|
||||||
|
.use(base)
|
||||||
.use(jwt({ secret }))
|
.use(jwt({ secret }))
|
||||||
.use(
|
.use(
|
||||||
swagger({
|
swagger({
|
||||||
|
@ -1,6 +1,28 @@
|
|||||||
import { FormatRegistry } from "@sinclair/typebox";
|
import { FormatRegistry } from "@sinclair/typebox";
|
||||||
import { t } from "elysia";
|
import { t } from "elysia";
|
||||||
import { comment } from "../../utils";
|
import { comment } from "../../utils";
|
||||||
|
import type { KError } from "../error";
|
||||||
|
|
||||||
|
export const validateTranslations = <T extends object>(
|
||||||
|
translations: Record<string, T>,
|
||||||
|
): KError | null => {
|
||||||
|
for (const lang of Object.keys(translations)) {
|
||||||
|
try {
|
||||||
|
const valid = new Intl.Locale(lang).baseName;
|
||||||
|
if (lang !== valid) {
|
||||||
|
translations[valid] = translations[lang];
|
||||||
|
delete translations[lang];
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
status: 400,
|
||||||
|
message: `Invalid translation name: '${lang}'.`,
|
||||||
|
details: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
FormatRegistry.Set("language", (lang) => {
|
FormatRegistry.Set("language", (lang) => {
|
||||||
try {
|
try {
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
import { afterAll, beforeAll, describe, expect, it, test } from "bun:test";
|
import { afterAll, beforeAll, describe, expect, it, test } from "bun:test";
|
||||||
import { eq, inArray } from "drizzle-orm";
|
import { eq, inArray } from "drizzle-orm";
|
||||||
import Elysia from "elysia";
|
import Elysia from "elysia";
|
||||||
|
import { base } from "~/base";
|
||||||
import { seed } from "~/controllers/seed";
|
import { seed } from "~/controllers/seed";
|
||||||
import { db } from "~/db";
|
import { db } from "~/db";
|
||||||
import { shows, showTranslations, videos } from "~/db/schema";
|
import { shows, showTranslations, videos } from "~/db/schema";
|
||||||
|
import { bubble } from "~/models/examples";
|
||||||
import { dune, duneVideo } from "~/models/examples/dune-2021";
|
import { dune, duneVideo } from "~/models/examples/dune-2021";
|
||||||
import type { SeedMovie } from "~/models/movie";
|
import type { SeedMovie } from "~/models/movie";
|
||||||
|
|
||||||
const app = new Elysia().use(seed);
|
const app = new Elysia().use(base).use(seed);
|
||||||
const createMovie = async (movie: SeedMovie) => {
|
const createMovie = async (movie: SeedMovie) => {
|
||||||
const resp = await app.handle(
|
const resp = await app.handle(
|
||||||
new Request("http://localhost/movies", {
|
new Request("http://localhost/movies", {
|
||||||
@ -157,6 +159,65 @@ describe("Movie seeding", () => {
|
|||||||
expect(body.details).toBeObject();
|
expect(body.details).toBeObject();
|
||||||
// TODO: handle additional fields too
|
// TODO: handle additional fields too
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Invalid translation name", async () => {
|
||||||
|
const [resp, body] = await createMovie({
|
||||||
|
...dune,
|
||||||
|
translations: {
|
||||||
|
...dune.translations,
|
||||||
|
test: {
|
||||||
|
name: "foo",
|
||||||
|
description: "bar",
|
||||||
|
tags: [],
|
||||||
|
aliases: [],
|
||||||
|
tagline: "toto",
|
||||||
|
banner: null,
|
||||||
|
poster: null,
|
||||||
|
thumbnail: null,
|
||||||
|
logo: null,
|
||||||
|
trailerUrl: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expectStatus(resp, body).toBe(400);
|
||||||
|
expect(body.status).toBe(400);
|
||||||
|
expect(body.message).toBe("Invalid translation name: 'test'.");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Correct translations casing.", async () => {
|
||||||
|
const [resp, body] = await createMovie({
|
||||||
|
...bubble,
|
||||||
|
slug: "casing-test",
|
||||||
|
translations: {
|
||||||
|
"en-us": {
|
||||||
|
name: "foo",
|
||||||
|
description: "bar",
|
||||||
|
tags: [],
|
||||||
|
aliases: [],
|
||||||
|
tagline: "toto",
|
||||||
|
banner: null,
|
||||||
|
poster: null,
|
||||||
|
thumbnail: null,
|
||||||
|
logo: null,
|
||||||
|
trailerUrl: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(resp.status).toBeWithin(200, 299);
|
||||||
|
expect(body.id).toBeString();
|
||||||
|
const ret = await db.query.shows.findFirst({
|
||||||
|
where: eq(shows.id, body.id),
|
||||||
|
with: { translations: true },
|
||||||
|
});
|
||||||
|
expect(ret!.translations).toBeArrayOfSize(1);
|
||||||
|
expect(ret!.translations[0]).toMatchObject({
|
||||||
|
language: "en-US",
|
||||||
|
name: "foo",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test.todo("Create correct video slug (version)", async () => {});
|
test.todo("Create correct video slug (version)", async () => {});
|
||||||
test.todo("Create correct video slug (part)", async () => {});
|
test.todo("Create correct video slug (part)", async () => {});
|
||||||
test.todo("Create correct video slug (rendering)", async () => {});
|
test.todo("Create correct video slug (rendering)", async () => {});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user