Much stricter ts
This commit is contained in:
parent
f56ba4675f
commit
f28a928442
|
@ -0,0 +1,9 @@
|
||||||
|
import { Access } from "payload/config";
|
||||||
|
import { RecordersRoles } from "../../constants";
|
||||||
|
import { Recorder } from "../../types/collections";
|
||||||
|
import { isDefined, isUndefined } from "../../utils/asserts";
|
||||||
|
|
||||||
|
export const mustBeAdmin: Access<unknown, Recorder> = ({ req: { user } }): boolean => {
|
||||||
|
if (isUndefined(user)) return false;
|
||||||
|
return isDefined(user.role) && user.role.includes(RecordersRoles.Admin);
|
||||||
|
};
|
|
@ -1,11 +1,10 @@
|
||||||
import { Access } from "payload/config";
|
import { Access } from "payload/config";
|
||||||
import { Recorder } from "../../types/collections";
|
|
||||||
import { RecordersRoles } from "../../constants";
|
import { RecordersRoles } from "../../constants";
|
||||||
|
import { Recorder } from "../../types/collections";
|
||||||
import { isUndefined } from "../../utils/asserts";
|
import { isUndefined } from "../../utils/asserts";
|
||||||
|
|
||||||
export const mustBeAdminOrSelf: Access = ({ req }) => {
|
export const mustBeAdminOrSelf: Access<unknown, Recorder> = ({ req: { user } }) => {
|
||||||
const user = req.user as Recorder | undefined;
|
|
||||||
if (isUndefined(user)) return false;
|
if (isUndefined(user)) return false;
|
||||||
if (user.role.includes(RecordersRoles.Admin)) return true;
|
if (user.role?.includes(RecordersRoles.Admin)) return true;
|
||||||
return { id: { equals: user.id } };
|
return { id: { equals: user.id } };
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
|
import { Access } from "payload/config";
|
||||||
import { Recorder } from "../../types/collections";
|
import { Recorder } from "../../types/collections";
|
||||||
import { isUndefined } from "../../utils/asserts";
|
import { isDefined, isUndefined } from "../../utils/asserts";
|
||||||
|
|
||||||
export const mustHaveAtLeastOneRole = ({ req }): boolean => {
|
export const mustHaveAtLeastOneRole: Access<unknown, Recorder> = ({ req: { user } }): boolean => {
|
||||||
const user = req.user as Recorder | undefined;
|
|
||||||
if (isUndefined(user)) return false;
|
if (isUndefined(user)) return false;
|
||||||
return user.role.length > 0;
|
return isDefined(user.role) && user.role.length > 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { FieldAccess } from "payload/types";
|
||||||
|
import { RecordersRoles } from "../../constants";
|
||||||
|
import { Recorder } from "../../types/collections";
|
||||||
|
import { isDefined, isUndefined } from "../../utils/asserts";
|
||||||
|
|
||||||
|
export const mustBeAdmin: FieldAccess<any, any, Recorder> = ({ req: { user } }): boolean => {
|
||||||
|
if (isUndefined(user)) return false;
|
||||||
|
return isDefined(user.role) && user.role.includes(RecordersRoles.Admin);
|
||||||
|
};
|
|
@ -1,9 +0,0 @@
|
||||||
import { Recorder } from "../types/collections";
|
|
||||||
import { RecordersRoles } from "../constants";
|
|
||||||
import { isUndefined } from "../utils/asserts";
|
|
||||||
|
|
||||||
export const mustBeAdmin = ({ req }): boolean => {
|
|
||||||
const user = req.user as Recorder | undefined;
|
|
||||||
if (isUndefined(user)) return false;
|
|
||||||
return user.role.includes(RecordersRoles.Admin);
|
|
||||||
};
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { CollectionConfig } from "payload/types";
|
import { CollectionConfig } from "payload/types";
|
||||||
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin";
|
||||||
import { CollectionGroups, Collections } from "../../constants";
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
import { backPropagationField } from "../../fields/backPropagationField/backPropagationField";
|
import { backPropagationField } from "../../fields/backPropagationField/backPropagationField";
|
||||||
import { slugField } from "../../fields/slugField/slugField";
|
import { slugField } from "../../fields/slugField/slugField";
|
||||||
|
|
|
@ -1,8 +1,17 @@
|
||||||
import { Collections } from "../../../constants";
|
import { Collections } from "../../../constants";
|
||||||
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||||
import { ChronologyEra } from "../../../types/collections";
|
import { ChronologyEra } from "../../../types/collections";
|
||||||
|
import { StrapiLanguage } from "../../../types/strapi";
|
||||||
|
import { isUndefined } from "../../../utils/asserts";
|
||||||
|
|
||||||
export const importFromStrapi = createStrapiImportEndpoint<ChronologyEra>({
|
type StrapiChronologyEra = {
|
||||||
|
slug: string;
|
||||||
|
starting_year: number;
|
||||||
|
ending_year: number;
|
||||||
|
title: { title: string; language: StrapiLanguage; description?: string }[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const importFromStrapi = createStrapiImportEndpoint<ChronologyEra, StrapiChronologyEra>({
|
||||||
strapi: {
|
strapi: {
|
||||||
collection: "chronology-eras",
|
collection: "chronology-eras",
|
||||||
params: {
|
params: {
|
||||||
|
@ -15,11 +24,15 @@ export const importFromStrapi = createStrapiImportEndpoint<ChronologyEra>({
|
||||||
slug,
|
slug,
|
||||||
startingYear: starting_year,
|
startingYear: starting_year,
|
||||||
endingYear: ending_year,
|
endingYear: ending_year,
|
||||||
translations: titles.map(({ language, title, description }) => ({
|
translations: titles.map(({ language, title, description }) => {
|
||||||
language: language.data.attributes.code,
|
if (isUndefined(language.data))
|
||||||
title,
|
throw new Error("Language is undefined for one of the translations");
|
||||||
description,
|
return {
|
||||||
})),
|
language: language.data?.attributes.code,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
};
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
import { CollectionBeforeValidateHook } from "payload/types";
|
import { CollectionBeforeValidateHook } from "payload/types";
|
||||||
import { ChronologyEra } from "../../../types/collections";
|
import { ChronologyEra } from "../../../types/collections";
|
||||||
|
import { isUndefined } from "../../../utils/asserts";
|
||||||
|
|
||||||
export const beforeValidateEndingGreaterThanStarting: CollectionBeforeValidateHook<
|
export const beforeValidateEndingGreaterThanStarting: CollectionBeforeValidateHook<
|
||||||
ChronologyEra
|
ChronologyEra
|
||||||
> = async ({ data: { startingYear, endingYear } }) => {
|
> = async ({ data }) => {
|
||||||
|
if (isUndefined(data)) throw new Error("The data is undefined");
|
||||||
|
const { startingYear, endingYear } = data;
|
||||||
|
if (isUndefined(endingYear)) throw new Error("Ending year is undefined");
|
||||||
|
if (isUndefined(startingYear)) throw new Error("Starting year is undefined");
|
||||||
if (endingYear < startingYear) {
|
if (endingYear < startingYear) {
|
||||||
throw new Error("The ending year cannot be before the starting year.");
|
throw new Error("The ending year cannot be before the starting year.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,16 @@ import payload from "payload";
|
||||||
import { CollectionBeforeValidateHook } from "payload/types";
|
import { CollectionBeforeValidateHook } from "payload/types";
|
||||||
import { Collections } from "../../../constants";
|
import { Collections } from "../../../constants";
|
||||||
import { ChronologyEra } from "../../../types/collections";
|
import { ChronologyEra } from "../../../types/collections";
|
||||||
import { hasIntersection } from "../../../utils/asserts";
|
import { hasIntersection, isUndefined } from "../../../utils/asserts";
|
||||||
|
|
||||||
export const beforeValidateNoIntersection: CollectionBeforeValidateHook<ChronologyEra> = async ({
|
export const beforeValidateNoIntersection: CollectionBeforeValidateHook<ChronologyEra> = async ({
|
||||||
data: { startingYear, endingYear },
|
data,
|
||||||
}) => {
|
}) => {
|
||||||
|
if (isUndefined(data)) throw new Error("The data is undefined");
|
||||||
|
const { startingYear, endingYear } = data;
|
||||||
|
if (isUndefined(endingYear)) throw new Error("Ending year is undefined");
|
||||||
|
if (isUndefined(startingYear)) throw new Error("Starting year is undefined");
|
||||||
|
|
||||||
const otherEras = await payload.find({
|
const otherEras = await payload.find({
|
||||||
collection: Collections.ChronologyEras,
|
collection: Collections.ChronologyEras,
|
||||||
limit: 100,
|
limit: 100,
|
||||||
|
|
|
@ -72,6 +72,7 @@ export const ChronologyItems: CollectionConfig = buildVersionedCollectionConfig(
|
||||||
if (!DateTime.fromObject({ year, month, day }).isValid) {
|
if (!DateTime.fromObject({ year, month, day }).isValid) {
|
||||||
return `The given date (${stringDate}) is not a valid date.`;
|
return `The given date (${stringDate}) is not a valid date.`;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,8 +1,24 @@
|
||||||
import { Collections } from "../../../constants";
|
import { Collections } from "../../../constants";
|
||||||
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||||
import { ChronologyItem } from "../../../types/collections";
|
import { ChronologyItem } from "../../../types/collections";
|
||||||
|
import { StrapiLanguage } from "../../../types/strapi";
|
||||||
|
import { isUndefined } from "../../../utils/asserts";
|
||||||
|
|
||||||
export const importFromStrapi = createStrapiImportEndpoint<ChronologyItem>({
|
type StrapiChronologyItem = {
|
||||||
|
year: number;
|
||||||
|
month?: number;
|
||||||
|
day?: number;
|
||||||
|
events: {
|
||||||
|
translations: {
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
note?: string;
|
||||||
|
language: StrapiLanguage;
|
||||||
|
}[];
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const importFromStrapi = createStrapiImportEndpoint<ChronologyItem, StrapiChronologyItem>({
|
||||||
strapi: {
|
strapi: {
|
||||||
collection: "chronology-items",
|
collection: "chronology-items",
|
||||||
params: {
|
params: {
|
||||||
|
@ -14,16 +30,20 @@ export const importFromStrapi = createStrapiImportEndpoint<ChronologyItem>({
|
||||||
convert: ({ year, month, day, events }, user) => ({
|
convert: ({ year, month, day, events }, user) => ({
|
||||||
date: { year, month, day },
|
date: { year, month, day },
|
||||||
events: events.map((event) => ({
|
events: events.map((event) => ({
|
||||||
translations: event.translations.map(({ title, description, note, language }) => ({
|
translations: event.translations.map(({ title, description, note, language }) => {
|
||||||
title,
|
if (isUndefined(language.data))
|
||||||
description,
|
throw new Error("A language is required for a chronology item event translation");
|
||||||
note,
|
return {
|
||||||
language: language.data.attributes.code,
|
title,
|
||||||
sourceLanguage: "en",
|
description,
|
||||||
...(language.data.attributes.code === "en"
|
note,
|
||||||
? { transcribers: [user.id] }
|
language: language.data.attributes.code,
|
||||||
: { translators: [user.id] }),
|
sourceLanguage: "en",
|
||||||
})),
|
...(language.data.attributes.code === "en"
|
||||||
|
? { transcribers: [user.id] }
|
||||||
|
: { translators: [user.id] }),
|
||||||
|
};
|
||||||
|
}),
|
||||||
})),
|
})),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,8 +6,12 @@ export const beforeValidatePopulateNameField: FieldHook<
|
||||||
ChronologyItem,
|
ChronologyItem,
|
||||||
ChronologyItem["name"],
|
ChronologyItem["name"],
|
||||||
ChronologyItem
|
ChronologyItem
|
||||||
> = ({ data: { date } }) => {
|
> = ({ data }) => {
|
||||||
if (isUndefined(date?.year)) return "????-??-??";
|
if (isUndefined(data)) {
|
||||||
|
return "????-??-??";
|
||||||
|
}
|
||||||
|
const { date } = data;
|
||||||
|
if (isUndefined(date) || isUndefined(date?.year)) return "????-??-??";
|
||||||
const { year, month, day } = date;
|
const { year, month, day } = date;
|
||||||
let result = String(year).padStart(5, " ");
|
let result = String(year).padStart(5, " ");
|
||||||
if (isDefined(month)) {
|
if (isDefined(month)) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { text } from "payload/dist/fields/validations";
|
import { text } from "payload/dist/fields/validations";
|
||||||
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin";
|
||||||
import { CollectionGroups, Collections } from "../../constants";
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||||
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
||||||
|
|
|
@ -2,7 +2,12 @@ import { Collections } from "../../../constants";
|
||||||
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||||
import { Language } from "../../../types/collections";
|
import { Language } from "../../../types/collections";
|
||||||
|
|
||||||
export const importFromStrapi = createStrapiImportEndpoint<Language>({
|
type StrapiLanguage = {
|
||||||
|
code: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const importFromStrapi = createStrapiImportEndpoint<Language, StrapiLanguage>({
|
||||||
strapi: {
|
strapi: {
|
||||||
collection: "currencies",
|
collection: "currencies",
|
||||||
params: {},
|
params: {},
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import payload from "payload";
|
import payload from "payload";
|
||||||
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin";
|
||||||
import { QuickFilters } from "../../components/QuickFilters";
|
import { QuickFilters } from "../../components/QuickFilters";
|
||||||
import { CollectionGroups, Collections, KeysTypes, LanguageCodes } from "../../constants";
|
import { CollectionGroups, Collections, KeysTypes, LanguageCodes } from "../../constants";
|
||||||
import { translatedFields } from "../../fields/translatedFields/translatedFields";
|
import { translatedFields } from "../../fields/translatedFields/translatedFields";
|
||||||
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
|
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
|
||||||
import { Key } from "../../types/collections";
|
import { Key } from "../../types/collections";
|
||||||
import { isDefined } from "../../utils/asserts";
|
import { isDefined, isUndefined } from "../../utils/asserts";
|
||||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||||
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
||||||
|
|
||||||
|
@ -58,15 +58,15 @@ export const Keys = buildCollectionConfig({
|
||||||
},
|
},
|
||||||
hooks: {
|
hooks: {
|
||||||
beforeValidate: [
|
beforeValidate: [
|
||||||
async ({ data: { name, type } }) => {
|
async ({ data }) => {
|
||||||
|
if (isUndefined(data)) return;
|
||||||
|
const { name, type } = data;
|
||||||
const result = await payload.find({
|
const result = await payload.find({
|
||||||
collection: Collections.Keys,
|
collection: Collections.Keys,
|
||||||
where: { name: { equals: name }, type: { equals: type } },
|
where: { name: { equals: name }, type: { equals: type } },
|
||||||
});
|
});
|
||||||
if (result.docs.length > 0) {
|
if (result.docs.length > 0) {
|
||||||
throw new Error(
|
throw new Error(`A Key of type "${type}" already exists with the name "${name}"`);
|
||||||
`A Key of type "${KeysTypes[type]}" already exists with the name "${name}"`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import payload from "payload";
|
import payload from "payload";
|
||||||
import { CollectionConfig } from "payload/types";
|
|
||||||
import { Collections } from "../../../constants";
|
import { Collections } from "../../../constants";
|
||||||
import {
|
import {
|
||||||
getAllStrapiEntries,
|
getAllStrapiEntries,
|
||||||
importStrapiEntries,
|
importStrapiEntries,
|
||||||
} from "../../../endpoints/createStrapiImportEndpoint";
|
} from "../../../endpoints/createStrapiImportEndpoint";
|
||||||
import { Key } from "../../../types/collections";
|
import { Key } from "../../../types/collections";
|
||||||
import { isDefined } from "../../../utils/asserts";
|
import { CollectionEndpoint, PayloadCreateData } from "../../../types/payload";
|
||||||
|
import { StrapiLanguage } from "../../../types/strapi";
|
||||||
|
import { isDefined, isUndefined } from "../../../utils/asserts";
|
||||||
import { formatToCamelCase } from "../../../utils/string";
|
import { formatToCamelCase } from "../../../utils/string";
|
||||||
import { PayloadCreateData } from "../../../utils/types";
|
|
||||||
|
|
||||||
const importStrapiWordings: typeof importStrapiEntries = async ({
|
const importStrapiWordings: typeof importStrapiEntries = async ({
|
||||||
payload: payloadParams,
|
payload: payloadParams,
|
||||||
|
@ -30,7 +30,7 @@ const importStrapiWordings: typeof importStrapiEntries = async ({
|
||||||
.filter(({ name }) => isDefined(name) && name !== ""),
|
.filter(({ name }) => isDefined(name) && name !== ""),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const errors = [];
|
const errors: string[] = [];
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
entries.map(async (entry) => {
|
entries.map(async (entry) => {
|
||||||
|
@ -42,7 +42,9 @@ const importStrapiWordings: typeof importStrapiEntries = async ({
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(e);
|
console.warn(e);
|
||||||
errors.push(`${e.name} with ${entry.name}`);
|
if (typeof e === "object" && isDefined(e) && "name" in e) {
|
||||||
|
errors.push(`${e.name} with ${entry.name}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -50,7 +52,7 @@ const importStrapiWordings: typeof importStrapiEntries = async ({
|
||||||
return { count: entries.length, errors };
|
return { count: entries.length, errors };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const importFromStrapi: CollectionConfig["endpoints"][number] = {
|
export const importFromStrapi: CollectionEndpoint = {
|
||||||
method: "get",
|
method: "get",
|
||||||
path: "/strapi",
|
path: "/strapi",
|
||||||
handler: async (req, res) => {
|
handler: async (req, res) => {
|
||||||
|
@ -64,7 +66,15 @@ export const importFromStrapi: CollectionConfig["endpoints"][number] = {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { count: categoriesCount, errors: categoriesErrors } = await importStrapiEntries<Key>({
|
type StrapiCategories = {
|
||||||
|
slug: string;
|
||||||
|
titles: { title?: string; short?: string; language: StrapiLanguage }[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const { count: categoriesCount, errors: categoriesErrors } = await importStrapiEntries<
|
||||||
|
Key,
|
||||||
|
StrapiCategories
|
||||||
|
>({
|
||||||
strapi: {
|
strapi: {
|
||||||
collection: "categories",
|
collection: "categories",
|
||||||
params: { populate: { titles: { populate: "language" } } },
|
params: { populate: { titles: { populate: "language" } } },
|
||||||
|
@ -74,59 +84,96 @@ export const importFromStrapi: CollectionConfig["endpoints"][number] = {
|
||||||
convert: ({ slug, titles }) => ({
|
convert: ({ slug, titles }) => ({
|
||||||
name: slug,
|
name: slug,
|
||||||
type: "Categories",
|
type: "Categories",
|
||||||
translations: titles.map(({ title, short, language }) => ({
|
translations: titles.map(({ title, short, language }) => {
|
||||||
name: title,
|
if (isUndefined(language.data))
|
||||||
short,
|
throw new Error("A language is required for a Keys title translation");
|
||||||
language: language.data.attributes.code,
|
if (isUndefined(title))
|
||||||
})),
|
throw new Error("A title is required for a Keys title translation");
|
||||||
|
return {
|
||||||
|
name: title,
|
||||||
|
short,
|
||||||
|
language: language.data.attributes.code,
|
||||||
|
};
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
user: req.user,
|
user: req.user,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { count: contentTypesCount, errors: contentTypesErrors } = await importStrapiEntries<Key>(
|
type StrapiContentType = {
|
||||||
{
|
slug: string;
|
||||||
strapi: {
|
titles: { title: string; language: StrapiLanguage }[];
|
||||||
collection: "content-types",
|
};
|
||||||
params: { populate: { titles: { populate: "language" } } },
|
|
||||||
},
|
const { count: contentTypesCount, errors: contentTypesErrors } = await importStrapiEntries<
|
||||||
payload: {
|
Key,
|
||||||
collection: Collections.Keys,
|
StrapiContentType
|
||||||
convert: ({ slug, titles }) => ({
|
>({
|
||||||
name: slug,
|
strapi: {
|
||||||
type: "Contents",
|
collection: "content-types",
|
||||||
translations: titles.map(({ title, language }) => ({
|
params: { populate: { titles: { populate: "language" } } },
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
collection: Collections.Keys,
|
||||||
|
convert: ({ slug, titles }) => ({
|
||||||
|
name: slug,
|
||||||
|
type: "Contents",
|
||||||
|
translations: titles.map(({ title, language }) => {
|
||||||
|
if (isUndefined(language.data))
|
||||||
|
throw new Error("A language is required for a Keys title translation");
|
||||||
|
return {
|
||||||
name: title,
|
name: title,
|
||||||
language: language.data.attributes.code,
|
language: language.data.attributes.code,
|
||||||
})),
|
};
|
||||||
}),
|
}),
|
||||||
},
|
}),
|
||||||
user: req.user,
|
},
|
||||||
}
|
user: req.user,
|
||||||
);
|
});
|
||||||
|
|
||||||
const { count: gamePlatformsCount, errors: gamePlatformsErrors } =
|
type StrapiGamePlatform = {
|
||||||
await importStrapiEntries<Key>({
|
slug: string;
|
||||||
strapi: {
|
titles: { title?: string; short?: string; language: StrapiLanguage }[];
|
||||||
collection: "game-platforms",
|
};
|
||||||
params: { populate: { titles: { populate: "language" } } },
|
|
||||||
},
|
const { count: gamePlatformsCount, errors: gamePlatformsErrors } = await importStrapiEntries<
|
||||||
payload: {
|
Key,
|
||||||
collection: Collections.Keys,
|
StrapiGamePlatform
|
||||||
convert: ({ slug, titles }) => ({
|
>({
|
||||||
name: slug,
|
strapi: {
|
||||||
type: "GamePlatforms",
|
collection: "game-platforms",
|
||||||
translations: titles.map(({ title, short, language }) => ({
|
params: { populate: { titles: { populate: "language" } } },
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
collection: Collections.Keys,
|
||||||
|
convert: ({ slug, titles }) => ({
|
||||||
|
name: slug,
|
||||||
|
type: "GamePlatforms",
|
||||||
|
translations: titles.map(({ title, short, language }) => {
|
||||||
|
if (isUndefined(language.data))
|
||||||
|
throw new Error("A language is required for a Keys title translation");
|
||||||
|
if (isUndefined(title))
|
||||||
|
throw new Error("A title is required for a Keys title translation");
|
||||||
|
return {
|
||||||
name: title,
|
name: title,
|
||||||
short,
|
short,
|
||||||
language: language.data.attributes.code,
|
language: language.data.attributes.code,
|
||||||
})),
|
};
|
||||||
}),
|
}),
|
||||||
},
|
}),
|
||||||
user: req.user,
|
},
|
||||||
});
|
user: req.user,
|
||||||
|
});
|
||||||
|
|
||||||
const { count: libraryCount, errors: libraryErrors } = await importStrapiEntries<Key>({
|
type StrapiMetadataTypes = {
|
||||||
|
slug: string;
|
||||||
|
titles: { title: string; language: StrapiLanguage }[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const { count: libraryCount, errors: libraryErrors } = await importStrapiEntries<
|
||||||
|
Key,
|
||||||
|
StrapiMetadataTypes
|
||||||
|
>({
|
||||||
strapi: {
|
strapi: {
|
||||||
collection: "metadata-types",
|
collection: "metadata-types",
|
||||||
params: { populate: { titles: { populate: "language" } } },
|
params: { populate: { titles: { populate: "language" } } },
|
||||||
|
@ -136,99 +183,152 @@ export const importFromStrapi: CollectionConfig["endpoints"][number] = {
|
||||||
convert: ({ slug, titles }) => ({
|
convert: ({ slug, titles }) => ({
|
||||||
name: slug,
|
name: slug,
|
||||||
type: "Library",
|
type: "Library",
|
||||||
translations: titles.map(({ title, language }) => ({
|
translations: titles.map(({ title, language }) => {
|
||||||
name: title,
|
if (isUndefined(language.data))
|
||||||
language: language.data.attributes.code,
|
throw new Error("A language is required for a Keys title translation");
|
||||||
})),
|
return {
|
||||||
|
name: title,
|
||||||
|
language: language.data.attributes.code,
|
||||||
|
};
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
user: req.user,
|
user: req.user,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { count: libraryAudioCount, errors: libraryAudioErrors } = await importStrapiEntries<Key>(
|
type StrapiAudioSubtypes = {
|
||||||
{
|
slug: string;
|
||||||
strapi: {
|
titles: { title: string; language: StrapiLanguage }[];
|
||||||
collection: "audio-subtypes",
|
};
|
||||||
params: { populate: { titles: { populate: "language" } } },
|
|
||||||
},
|
const { count: libraryAudioCount, errors: libraryAudioErrors } = await importStrapiEntries<
|
||||||
payload: {
|
Key,
|
||||||
collection: Collections.Keys,
|
StrapiAudioSubtypes
|
||||||
convert: ({ slug, titles }) => ({
|
>({
|
||||||
name: slug,
|
strapi: {
|
||||||
type: "LibraryAudio",
|
collection: "audio-subtypes",
|
||||||
translations: titles.map(({ title, language }) => ({
|
params: { populate: { titles: { populate: "language" } } },
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
collection: Collections.Keys,
|
||||||
|
convert: ({ slug, titles }) => ({
|
||||||
|
name: slug,
|
||||||
|
type: "LibraryAudio",
|
||||||
|
translations: titles.map(({ title, language }) => {
|
||||||
|
if (isUndefined(language.data))
|
||||||
|
throw new Error("A language is required for a Keys title translation");
|
||||||
|
return {
|
||||||
name: title,
|
name: title,
|
||||||
language: language.data.attributes.code,
|
language: language.data.attributes.code,
|
||||||
})),
|
};
|
||||||
}),
|
}),
|
||||||
},
|
}),
|
||||||
user: req.user,
|
},
|
||||||
}
|
user: req.user,
|
||||||
);
|
});
|
||||||
|
|
||||||
const { count: libraryGroupCount, errors: libraryGroupErrors } = await importStrapiEntries<Key>(
|
type StrapiGroupSubtypes = {
|
||||||
{
|
slug: string;
|
||||||
strapi: {
|
titles: { title: string; language: StrapiLanguage }[];
|
||||||
collection: "group-subtypes",
|
};
|
||||||
params: { populate: { titles: { populate: "language" } } },
|
|
||||||
},
|
const { count: libraryGroupCount, errors: libraryGroupErrors } = await importStrapiEntries<
|
||||||
payload: {
|
Key,
|
||||||
collection: Collections.Keys,
|
StrapiGroupSubtypes
|
||||||
convert: ({ slug, titles }) => ({
|
>({
|
||||||
name: slug,
|
strapi: {
|
||||||
type: "LibraryGroup",
|
collection: "group-subtypes",
|
||||||
translations: titles.map(({ title, language }) => ({
|
params: { populate: { titles: { populate: "language" } } },
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
collection: Collections.Keys,
|
||||||
|
convert: ({ slug, titles }) => ({
|
||||||
|
name: slug,
|
||||||
|
type: "LibraryGroup",
|
||||||
|
translations: titles.map(({ title, language }) => {
|
||||||
|
if (isUndefined(language.data))
|
||||||
|
throw new Error("A language is required for a Keys title translation");
|
||||||
|
return {
|
||||||
name: title,
|
name: title,
|
||||||
language: language.data.attributes.code,
|
language: language.data.attributes.code,
|
||||||
})),
|
};
|
||||||
}),
|
}),
|
||||||
},
|
}),
|
||||||
user: req.user,
|
},
|
||||||
}
|
user: req.user,
|
||||||
);
|
});
|
||||||
|
|
||||||
const { count: libraryTextualCount, errors: libraryTextualErrors } =
|
type StrapiTextualSubtypes = {
|
||||||
await importStrapiEntries<Key>({
|
slug: string;
|
||||||
strapi: {
|
titles: { title: string; language: StrapiLanguage }[];
|
||||||
collection: "textual-subtypes",
|
};
|
||||||
params: { populate: { titles: { populate: "language" } } },
|
|
||||||
},
|
const { count: libraryTextualCount, errors: libraryTextualErrors } = await importStrapiEntries<
|
||||||
payload: {
|
Key,
|
||||||
collection: Collections.Keys,
|
StrapiTextualSubtypes
|
||||||
convert: ({ slug, titles }) => ({
|
>({
|
||||||
name: slug,
|
strapi: {
|
||||||
type: "LibraryTextual",
|
collection: "textual-subtypes",
|
||||||
translations: titles.map(({ title, language }) => ({
|
params: { populate: { titles: { populate: "language" } } },
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
collection: Collections.Keys,
|
||||||
|
convert: ({ slug, titles }) => ({
|
||||||
|
name: slug,
|
||||||
|
type: "LibraryTextual",
|
||||||
|
translations: titles.map(({ title, language }) => {
|
||||||
|
if (isUndefined(language.data))
|
||||||
|
throw new Error("A language is required for a Keys title translation");
|
||||||
|
return {
|
||||||
name: title,
|
name: title,
|
||||||
language: language.data.attributes.code,
|
language: language.data.attributes.code,
|
||||||
})),
|
};
|
||||||
}),
|
}),
|
||||||
},
|
}),
|
||||||
user: req.user,
|
},
|
||||||
});
|
user: req.user,
|
||||||
|
});
|
||||||
|
|
||||||
const { count: libraryVideoCount, errors: libraryVideoErrors } = await importStrapiEntries<Key>(
|
type StrapiVideoSubtypes = {
|
||||||
{
|
slug: string;
|
||||||
strapi: {
|
titles: { title: string; language: StrapiLanguage }[];
|
||||||
collection: "video-subtypes",
|
};
|
||||||
params: { populate: { titles: { populate: "language" } } },
|
|
||||||
},
|
const { count: libraryVideoCount, errors: libraryVideoErrors } = await importStrapiEntries<
|
||||||
payload: {
|
Key,
|
||||||
collection: Collections.Keys,
|
StrapiVideoSubtypes
|
||||||
convert: ({ slug, titles }) => ({
|
>({
|
||||||
name: slug,
|
strapi: {
|
||||||
type: "LibraryVideo",
|
collection: "video-subtypes",
|
||||||
translations: titles.map(({ title, language }) => ({
|
params: { populate: { titles: { populate: "language" } } },
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
collection: Collections.Keys,
|
||||||
|
convert: ({ slug, titles }) => ({
|
||||||
|
name: slug,
|
||||||
|
type: "LibraryVideo",
|
||||||
|
translations: titles.map(({ title, language }) => {
|
||||||
|
if (isUndefined(language.data))
|
||||||
|
throw new Error("A language is required for a Keys title translation");
|
||||||
|
return {
|
||||||
name: title,
|
name: title,
|
||||||
language: language.data.attributes.code,
|
language: language.data.attributes.code,
|
||||||
})),
|
};
|
||||||
}),
|
}),
|
||||||
},
|
}),
|
||||||
user: req.user,
|
},
|
||||||
}
|
user: req.user,
|
||||||
);
|
});
|
||||||
|
|
||||||
const { count: weaponsCount, errors: weaponsErrors } = await importStrapiEntries<Key>({
|
type StrapiWeaponTypes = {
|
||||||
|
slug: string;
|
||||||
|
translations: { name?: string; language: StrapiLanguage }[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const { count: weaponsCount, errors: weaponsErrors } = await importStrapiEntries<
|
||||||
|
Key,
|
||||||
|
StrapiWeaponTypes
|
||||||
|
>({
|
||||||
strapi: {
|
strapi: {
|
||||||
collection: "weapon-story-types",
|
collection: "weapon-story-types",
|
||||||
params: { populate: { translations: { populate: "language" } } },
|
params: { populate: { translations: { populate: "language" } } },
|
||||||
|
@ -238,16 +338,22 @@ export const importFromStrapi: CollectionConfig["endpoints"][number] = {
|
||||||
convert: ({ slug, translations }) => ({
|
convert: ({ slug, translations }) => ({
|
||||||
name: slug,
|
name: slug,
|
||||||
type: "Weapons",
|
type: "Weapons",
|
||||||
translations: translations.map(({ name, language }) => ({
|
translations: translations.map(({ name, language }) => {
|
||||||
name,
|
if (isUndefined(language.data))
|
||||||
language: language.data.attributes.code,
|
throw new Error("A language is required for a Keys title translation");
|
||||||
})),
|
if (isUndefined(name))
|
||||||
|
throw new Error("A name is required for a Keys title translation");
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
language: language.data.attributes.code,
|
||||||
|
};
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
user: req.user,
|
user: req.user,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { count: wordingsCount, errors: wordingsErrors } = await importStrapiWordings({
|
const { count: wordingsCount, errors: wordingsErrors } = await importStrapiWordings<Key, Key>({
|
||||||
strapi: { collection: "website-interfaces", params: { populate: "ui_language" } },
|
strapi: { collection: "website-interfaces", params: { populate: "ui_language" } },
|
||||||
payload: { collection: Collections.Keys, convert: (strapiObject) => strapiObject },
|
payload: { collection: Collections.Keys, convert: (strapiObject) => strapiObject },
|
||||||
user: req.user,
|
user: req.user,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { text } from "payload/dist/fields/validations";
|
import { text } from "payload/dist/fields/validations";
|
||||||
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin";
|
||||||
import { publicAccess } from "../../accesses/publicAccess";
|
import { publicAccess } from "../../accesses/publicAccess";
|
||||||
import { CollectionGroups, Collections } from "../../constants";
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||||
|
|
|
@ -2,7 +2,12 @@ import { Collections } from "../../../constants";
|
||||||
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||||
import { Language } from "../../../types/collections";
|
import { Language } from "../../../types/collections";
|
||||||
|
|
||||||
export const importFromStrapi = createStrapiImportEndpoint<Language>({
|
type StrapiLanguage = {
|
||||||
|
name: string;
|
||||||
|
code: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const importFromStrapi = createStrapiImportEndpoint<Language, StrapiLanguage>({
|
||||||
strapi: {
|
strapi: {
|
||||||
collection: "languages",
|
collection: "languages",
|
||||||
params: {},
|
params: {},
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { RowLabelArgs } from "payload/dist/admin/components/forms/RowLabel/types";
|
||||||
import {
|
import {
|
||||||
CollectionGroups,
|
CollectionGroups,
|
||||||
Collections,
|
Collections,
|
||||||
|
@ -18,7 +19,6 @@ import { LibraryItem } from "../../types/collections";
|
||||||
import { isDefined } from "../../utils/asserts";
|
import { isDefined } from "../../utils/asserts";
|
||||||
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
||||||
import { RowLabel } from "./components/RowLabel";
|
import { RowLabel } from "./components/RowLabel";
|
||||||
import { getBySlug } from "./endpoints/getBySlug";
|
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
status: "_status",
|
status: "_status",
|
||||||
|
@ -139,7 +139,6 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
||||||
},
|
},
|
||||||
preview: (doc) => `https://accords-library.com/library/${doc.slug}`,
|
preview: (doc) => `https://accords-library.com/library/${doc.slug}`,
|
||||||
},
|
},
|
||||||
endpoints: [getBySlug],
|
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: fields.itemType,
|
name: fields.itemType,
|
||||||
|
@ -480,7 +479,7 @@ export const LibraryItems = buildVersionedCollectionConfig({
|
||||||
"Make sure the page number corresponds to the page number written on \
|
"Make sure the page number corresponds to the page number written on \
|
||||||
the scan. You can use negative page numbers if necessary.",
|
the scan. You can use negative page numbers if necessary.",
|
||||||
components: {
|
components: {
|
||||||
RowLabel: ({ data }) => RowLabel(data),
|
RowLabel: ({ data }: RowLabelArgs) => RowLabel(data),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
fields: [
|
fields: [
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
import cleanDeep from "clean-deep";
|
|
||||||
import { Collections } from "../../../constants";
|
|
||||||
import { createGetByEndpoint } from "../../../endpoints/createByEndpoint";
|
|
||||||
import { LibraryItem } from "../../../types/collections";
|
|
||||||
|
|
||||||
type ProcessedLibraryItem = Omit<LibraryItem, "size" | "price" | "scans" | "id"> & {
|
|
||||||
size?: Omit<LibraryItem["size"][number], "id">;
|
|
||||||
price?: Omit<LibraryItem["price"][number], "id" | "currency"> & { currency: string };
|
|
||||||
scans?: Omit<LibraryItem["scans"][number], "id" | "obi" | "cover" | "dustjacket"> & {
|
|
||||||
obi: Omit<LibraryItem["scans"][number]["obi"][number], "id">;
|
|
||||||
cover: Omit<LibraryItem["scans"][number]["obi"][number], "id">;
|
|
||||||
dustjacket: Omit<LibraryItem["scans"][number]["obi"][number], "id">;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getBySlug = createGetByEndpoint<LibraryItem, Partial<ProcessedLibraryItem>>(
|
|
||||||
Collections.LibraryItems,
|
|
||||||
"slug",
|
|
||||||
async ({ id, size, price, scans, ...otherProps }) => {
|
|
||||||
const processedLibraryItem: ProcessedLibraryItem = {
|
|
||||||
size: processOptionalGroup(size),
|
|
||||||
price: processPrice(price),
|
|
||||||
scans: processScans(scans),
|
|
||||||
...otherProps,
|
|
||||||
};
|
|
||||||
|
|
||||||
return cleanDeep(processedLibraryItem, {
|
|
||||||
emptyStrings: false,
|
|
||||||
emptyArrays: false,
|
|
||||||
emptyObjects: false,
|
|
||||||
nullValues: true,
|
|
||||||
undefinedValues: true,
|
|
||||||
NaNValues: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const processScans = (scans: LibraryItem["scans"]): ProcessedLibraryItem["scans"] => {
|
|
||||||
if (!scans || scans.length === 0) return undefined;
|
|
||||||
const { cover, dustjacket, id, obi, ...otherProps } = scans[0];
|
|
||||||
return {
|
|
||||||
cover: processOptionalGroup(cover),
|
|
||||||
dustjacket: processOptionalGroup(dustjacket),
|
|
||||||
obi: processOptionalGroup(obi),
|
|
||||||
...otherProps,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const processPrice = (price: LibraryItem["price"]): ProcessedLibraryItem["price"] => {
|
|
||||||
if (!price || price.length === 0) return undefined;
|
|
||||||
const { currency, ...otherProps } = processOptionalGroup(price);
|
|
||||||
return { ...otherProps, currency: typeof currency === "string" ? currency : currency.id };
|
|
||||||
};
|
|
||||||
|
|
||||||
const processOptionalGroup = <T extends { id?: string }>(group: T[] | null | undefined) => {
|
|
||||||
if (!group || group.length === 0) return undefined;
|
|
||||||
const { id, ...otherProps } = group[0];
|
|
||||||
return otherProps;
|
|
||||||
};
|
|
|
@ -8,7 +8,6 @@ import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping";
|
||||||
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
|
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
|
||||||
import { isDefined, isUndefined } from "../../utils/asserts";
|
import { isDefined, isUndefined } from "../../utils/asserts";
|
||||||
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
|
||||||
import { removeTranslatorsForTranscripts } from "./hooks/beforeValidate";
|
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
slug: "slug",
|
slug: "slug",
|
||||||
|
@ -57,9 +56,6 @@ export const Posts = buildVersionedCollectionConfig({
|
||||||
},
|
},
|
||||||
preview: (doc) => `https://accords-library.com/news/${doc.slug}`,
|
preview: (doc) => `https://accords-library.com/news/${doc.slug}`,
|
||||||
},
|
},
|
||||||
hooks: {
|
|
||||||
beforeValidate: [removeTranslatorsForTranscripts],
|
|
||||||
},
|
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
type: "row",
|
type: "row",
|
||||||
|
@ -110,6 +106,15 @@ export const Posts = buildVersionedCollectionConfig({
|
||||||
type: "relationship",
|
type: "relationship",
|
||||||
relationTo: Collections.Recorders,
|
relationTo: Collections.Recorders,
|
||||||
hasMany: true,
|
hasMany: true,
|
||||||
|
hooks: {
|
||||||
|
beforeChange: [
|
||||||
|
({ siblingData }) => {
|
||||||
|
if (siblingData.language === siblingData.sourceLanguage) {
|
||||||
|
delete siblingData.translators;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
admin: {
|
admin: {
|
||||||
condition: (_, siblingData) => {
|
condition: (_, siblingData) => {
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
import { CollectionBeforeValidateHook } from "payload/types";
|
|
||||||
import { Post } from "../../../types/collections";
|
|
||||||
|
|
||||||
export const removeTranslatorsForTranscripts: CollectionBeforeValidateHook<Post> = async ({
|
|
||||||
data: { translations, ...data },
|
|
||||||
}) => ({
|
|
||||||
...data,
|
|
||||||
translations: translations?.map(({ translators, ...translation }) => {
|
|
||||||
if (translation.language === translation.sourceLanguage) {
|
|
||||||
return { ...translation, translators: [] };
|
|
||||||
}
|
|
||||||
return { ...translation, translators };
|
|
||||||
}),
|
|
||||||
});
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
import { mustBeAdmin as mustBeAdminForCollections } from "../../accesses/collections/mustBeAdmin";
|
||||||
import { mustBeAdminOrSelf } from "../../accesses/collections/mustBeAdminOrSelf";
|
import { mustBeAdminOrSelf } from "../../accesses/collections/mustBeAdminOrSelf";
|
||||||
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
import { mustBeAdmin as mustBeAdminForFields } from "../../accesses/fields/mustBeAdmin";
|
||||||
import { QuickFilters } from "../../components/QuickFilters";
|
import { QuickFilters } from "../../components/QuickFilters";
|
||||||
import { CollectionGroups, Collections, RecordersRoles } from "../../constants";
|
import { CollectionGroups, Collections, RecordersRoles } from "../../constants";
|
||||||
import { imageField } from "../../fields/imageField/imageField";
|
import { imageField } from "../../fields/imageField/imageField";
|
||||||
|
@ -55,7 +56,6 @@ export const Recorders = buildCollectionConfig({
|
||||||
label: "∅ Role",
|
label: "∅ Role",
|
||||||
filter: { where: { role: { not_in: Object.keys(RecordersRoles).join(",") } } },
|
filter: { where: { role: { not_in: Object.keys(RecordersRoles).join(",") } } },
|
||||||
},
|
},
|
||||||
,
|
|
||||||
],
|
],
|
||||||
[{ label: "Anonymized", filter: { where: { anonymize: { equals: true } } } }],
|
[{ label: "Anonymized", filter: { where: { anonymize: { equals: true } } } }],
|
||||||
],
|
],
|
||||||
|
@ -65,10 +65,10 @@ export const Recorders = buildCollectionConfig({
|
||||||
},
|
},
|
||||||
auth: true,
|
auth: true,
|
||||||
access: {
|
access: {
|
||||||
unlock: mustBeAdmin,
|
unlock: mustBeAdminForCollections,
|
||||||
update: mustBeAdminOrSelf,
|
update: mustBeAdminOrSelf,
|
||||||
delete: mustBeAdmin,
|
delete: mustBeAdminForCollections,
|
||||||
create: mustBeAdmin,
|
create: mustBeAdminForCollections,
|
||||||
},
|
},
|
||||||
hooks: {
|
hooks: {
|
||||||
beforeLogin: [beforeLoginMustHaveAtLeastOneRole],
|
beforeLogin: [beforeLoginMustHaveAtLeastOneRole],
|
||||||
|
@ -111,14 +111,14 @@ export const Recorders = buildCollectionConfig({
|
||||||
description:
|
description:
|
||||||
"A short personal description about you or your involvement with this project or the franchise",
|
"A short personal description about you or your involvement with this project or the franchise",
|
||||||
},
|
},
|
||||||
fields: [{ name: fields.biography, type: "textarea" }],
|
fields: [{ name: fields.biography, required: true, type: "textarea" }],
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: fields.role,
|
name: fields.role,
|
||||||
type: "select",
|
type: "select",
|
||||||
access: {
|
access: {
|
||||||
update: mustBeAdmin,
|
update: mustBeAdminForFields,
|
||||||
create: mustBeAdmin,
|
create: mustBeAdminForFields,
|
||||||
},
|
},
|
||||||
hasMany: true,
|
hasMany: true,
|
||||||
options: Object.entries(RecordersRoles).map(([value, label]) => ({
|
options: Object.entries(RecordersRoles).map(([value, label]) => ({
|
||||||
|
|
|
@ -2,10 +2,21 @@ import payload from "payload";
|
||||||
import { Collections } from "../../../constants";
|
import { Collections } from "../../../constants";
|
||||||
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||||
import { Recorder } from "../../../types/collections";
|
import { Recorder } from "../../../types/collections";
|
||||||
|
import { PayloadCreateData } from "../../../types/payload";
|
||||||
|
import { StrapiImage, StrapiLanguage } from "../../../types/strapi";
|
||||||
|
import { isUndefined } from "../../../utils/asserts";
|
||||||
import { uploadStrapiImage } from "../../../utils/localApi";
|
import { uploadStrapiImage } from "../../../utils/localApi";
|
||||||
import { PayloadCreateData } from "../../../utils/types";
|
|
||||||
|
|
||||||
export const importFromStrapi = createStrapiImportEndpoint<Recorder>({
|
type StrapiRecorder = {
|
||||||
|
username: string;
|
||||||
|
anonymize: boolean;
|
||||||
|
anonymous_code: number;
|
||||||
|
languages: { data: { attributes: { code: string } }[] };
|
||||||
|
avatar: StrapiImage;
|
||||||
|
bio: { language: StrapiLanguage; bio?: string }[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const importFromStrapi = createStrapiImportEndpoint<Recorder, StrapiRecorder>({
|
||||||
strapi: {
|
strapi: {
|
||||||
collection: "recorders",
|
collection: "recorders",
|
||||||
params: {
|
params: {
|
||||||
|
@ -27,10 +38,15 @@ export const importFromStrapi = createStrapiImportEndpoint<Recorder>({
|
||||||
anonymize,
|
anonymize,
|
||||||
languages: languages.data?.map((language) => language.attributes.code),
|
languages: languages.data?.map((language) => language.attributes.code),
|
||||||
avatar: avatarId,
|
avatar: avatarId,
|
||||||
biographies: bios?.map(({ language, bio }) => ({
|
biographies: bios?.map(({ language, bio }) => {
|
||||||
language: language.data.attributes.code,
|
if (isUndefined(language.data))
|
||||||
biography: bio,
|
throw new Error("A language is required for a Recorder biography");
|
||||||
})),
|
if (isUndefined(bio)) throw new Error("A bio is required for a Recorder biography");
|
||||||
|
return {
|
||||||
|
language: language.data.attributes.code,
|
||||||
|
biography: bio,
|
||||||
|
};
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
await payload.create({ collection: Collections.Recorders, data, user });
|
await payload.create({ collection: Collections.Recorders, data, user });
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { CollectionConfig } from "payload/types";
|
import { CollectionConfig } from "payload/types";
|
||||||
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin";
|
||||||
import { CollectionGroups, Collections, VideoSources } from "../../constants";
|
import { CollectionGroups, Collections, VideoSources } from "../../constants";
|
||||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||||
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
||||||
|
|
|
@ -2,9 +2,26 @@ import payload from "payload";
|
||||||
import { Collections } from "../../../constants";
|
import { Collections } from "../../../constants";
|
||||||
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||||
import { Video, VideosChannel } from "../../../types/collections";
|
import { Video, VideosChannel } from "../../../types/collections";
|
||||||
import { PayloadCreateData } from "../../../utils/types";
|
import { PayloadCreateData } from "../../../types/payload";
|
||||||
|
import { isUndefined } from "../../../utils/asserts";
|
||||||
|
|
||||||
export const importFromStrapi = createStrapiImportEndpoint<Video>({
|
type StapiVideo = {
|
||||||
|
uid: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
published_date: {
|
||||||
|
year?: number;
|
||||||
|
month?: number;
|
||||||
|
day?: number;
|
||||||
|
};
|
||||||
|
views: number;
|
||||||
|
likes: number;
|
||||||
|
source?: "YouTube" | "NicoNico" | "Tumblr";
|
||||||
|
gone: boolean;
|
||||||
|
channel: { data?: { attributes: { uid: string; title: string; subscribers: number } } };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const importFromStrapi = createStrapiImportEndpoint<Video, StapiVideo>({
|
||||||
strapi: {
|
strapi: {
|
||||||
collection: "videos",
|
collection: "videos",
|
||||||
params: { populate: "published_date,channel" },
|
params: { populate: "published_date,channel" },
|
||||||
|
@ -25,6 +42,9 @@ export const importFromStrapi = createStrapiImportEndpoint<Video>({
|
||||||
},
|
},
|
||||||
user
|
user
|
||||||
) => {
|
) => {
|
||||||
|
if (isUndefined(source)) throw new Error("A source is required to create a Video");
|
||||||
|
if (isUndefined(channel.data)) throw new Error("A channel is required to create a Video");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const videoChannel: PayloadCreateData<VideosChannel> = {
|
const videoChannel: PayloadCreateData<VideosChannel> = {
|
||||||
uid: channel.data.attributes.uid,
|
uid: channel.data.attributes.uid,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { CollectionConfig } from "payload/types";
|
import { CollectionConfig } from "payload/types";
|
||||||
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
|
import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin";
|
||||||
import { CollectionGroups, Collections } from "../../constants";
|
import { CollectionGroups, Collections } from "../../constants";
|
||||||
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
import { buildCollectionConfig } from "../../utils/collectionConfig";
|
||||||
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
import { importFromStrapi } from "./endpoints/importFromStrapi";
|
||||||
|
|
|
@ -2,7 +2,13 @@ import { Collections } from "../../../constants";
|
||||||
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||||
import { VideosChannel } from "../../../types/collections";
|
import { VideosChannel } from "../../../types/collections";
|
||||||
|
|
||||||
export const importFromStrapi = createStrapiImportEndpoint<VideosChannel>({
|
type StrapiVideoChannel = {
|
||||||
|
uid: string;
|
||||||
|
title: string;
|
||||||
|
subscribers: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const importFromStrapi = createStrapiImportEndpoint<VideosChannel, StrapiVideoChannel>({
|
||||||
strapi: {
|
strapi: {
|
||||||
collection: "video-channels",
|
collection: "video-channels",
|
||||||
params: {},
|
params: {},
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { RowLabelArgs } from "payload/dist/admin/components/forms/RowLabel/types";
|
||||||
import { CollectionGroups, Collections, KeysTypes } from "../../constants";
|
import { CollectionGroups, Collections, KeysTypes } from "../../constants";
|
||||||
import { imageField } from "../../fields/imageField/imageField";
|
import { imageField } from "../../fields/imageField/imageField";
|
||||||
import { keysField } from "../../fields/keysField/keysField";
|
import { keysField } from "../../fields/keysField/keysField";
|
||||||
|
@ -78,7 +79,7 @@ export const Weapons = buildVersionedCollectionConfig({
|
||||||
admin: {
|
admin: {
|
||||||
initCollapsed: true,
|
initCollapsed: true,
|
||||||
components: {
|
components: {
|
||||||
RowLabel: ({ data }) =>
|
RowLabel: ({ data }: RowLabelArgs) =>
|
||||||
AppearanceRowLabel({ keyIds: data[fields.appearancesCategories] ?? [] }),
|
AppearanceRowLabel({ keyIds: data[fields.appearancesCategories] ?? [] }),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,11 +2,31 @@ import payload from "payload";
|
||||||
import { Collections } from "../../../constants";
|
import { Collections } from "../../../constants";
|
||||||
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
|
||||||
import { Weapon, WeaponsGroup } from "../../../types/collections";
|
import { Weapon, WeaponsGroup } from "../../../types/collections";
|
||||||
import { isDefined } from "../../../utils/asserts";
|
import { PayloadCreateData } from "../../../types/payload";
|
||||||
|
import { StrapiImage, StrapiLanguage } from "../../../types/strapi";
|
||||||
|
import { isDefined, isUndefined } from "../../../utils/asserts";
|
||||||
import { findCategory, findWeaponType, uploadStrapiImage } from "../../../utils/localApi";
|
import { findCategory, findWeaponType, uploadStrapiImage } from "../../../utils/localApi";
|
||||||
import { PayloadCreateData } from "../../../utils/types";
|
|
||||||
|
|
||||||
export const importFromStrapi = createStrapiImportEndpoint<Weapon>({
|
type StrapiWeapon = {
|
||||||
|
slug: string;
|
||||||
|
name: { name: string; language: StrapiLanguage }[];
|
||||||
|
weapon_group: { data?: { attributes: { slug: string } } };
|
||||||
|
thumbnail: StrapiImage;
|
||||||
|
type: { data?: { attributes: { slug: string } } };
|
||||||
|
stories: {
|
||||||
|
categories: { data: { attributes: { slug: string } }[] };
|
||||||
|
translations: {
|
||||||
|
language: StrapiLanguage;
|
||||||
|
description?: string;
|
||||||
|
level_1?: string;
|
||||||
|
level_2?: string;
|
||||||
|
level_3?: string;
|
||||||
|
level_4?: string;
|
||||||
|
}[];
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const importFromStrapi = createStrapiImportEndpoint<Weapon, StrapiWeapon>({
|
||||||
strapi: {
|
strapi: {
|
||||||
collection: "weapon-stories",
|
collection: "weapon-stories",
|
||||||
params: {
|
params: {
|
||||||
|
@ -23,7 +43,7 @@ export const importFromStrapi = createStrapiImportEndpoint<Weapon>({
|
||||||
payload: {
|
payload: {
|
||||||
collection: Collections.Weapons,
|
collection: Collections.Weapons,
|
||||||
import: async ({ slug, type, stories, name: names, weapon_group, thumbnail }, user) => {
|
import: async ({ slug, type, stories, name: names, weapon_group, thumbnail }, user) => {
|
||||||
let groupId: string;
|
let groupId: string | undefined;
|
||||||
if (isDefined(weapon_group.data)) {
|
if (isDefined(weapon_group.data)) {
|
||||||
try {
|
try {
|
||||||
const groupData: PayloadCreateData<WeaponsGroup> = {
|
const groupData: PayloadCreateData<WeaponsGroup> = {
|
||||||
|
@ -51,6 +71,8 @@ export const importFromStrapi = createStrapiImportEndpoint<Weapon>({
|
||||||
image: thumbnail,
|
image: thumbnail,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (isUndefined(type.data)) throw new Error("A type is required to create a Weapon");
|
||||||
|
|
||||||
const data: PayloadCreateData<Weapon> = {
|
const data: PayloadCreateData<Weapon> = {
|
||||||
slug,
|
slug,
|
||||||
type: await findWeaponType(type.data.attributes.slug),
|
type: await findWeaponType(type.data.attributes.slug),
|
||||||
|
@ -62,19 +84,26 @@ export const importFromStrapi = createStrapiImportEndpoint<Weapon>({
|
||||||
categories.data.map(({ attributes }) => findCategory(attributes.slug))
|
categories.data.map(({ attributes }) => findCategory(attributes.slug))
|
||||||
),
|
),
|
||||||
translations: translations.map(
|
translations: translations.map(
|
||||||
({ language, description, level_1, level_2, level_3, level_4 }) => ({
|
({ language, description, level_1, level_2, level_3, level_4 }) => {
|
||||||
language: language.data?.attributes.code,
|
if (isUndefined(language.data))
|
||||||
sourceLanguage: language.data?.attributes.code,
|
throw new Error("A language is required to create a Weapon translation");
|
||||||
name: names.find(
|
const name = names.find(
|
||||||
(name) => name.language.data?.attributes.code === language.data?.attributes.code
|
(name) => name.language.data?.attributes.code === language.data?.attributes.code
|
||||||
)?.name,
|
)?.name;
|
||||||
description,
|
if (isUndefined(name))
|
||||||
level1: level_1,
|
throw new Error("A name is required to create a Weapon translation");
|
||||||
level2: level_2,
|
return {
|
||||||
level3: level_3,
|
language: language.data?.attributes.code,
|
||||||
level4: level_4,
|
sourceLanguage: language.data?.attributes.code,
|
||||||
transcribers: [user.id],
|
name,
|
||||||
})
|
description,
|
||||||
|
level1: level_1,
|
||||||
|
level2: level_2,
|
||||||
|
level3: level_3,
|
||||||
|
level4: level_4,
|
||||||
|
transcribers: [user.id],
|
||||||
|
};
|
||||||
|
}
|
||||||
),
|
),
|
||||||
}))
|
}))
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import payload from "payload";
|
import payload from "payload";
|
||||||
import { CollectionConfig } from "payload/types";
|
import { CollectionEndpoint } from "../types/payload";
|
||||||
|
|
||||||
export const createGetByEndpoint = <T, R>(
|
export const createGetByEndpoint = <T, R>(
|
||||||
collection: string,
|
collection: string,
|
||||||
attribute: string,
|
attribute: string,
|
||||||
handler: (doc: T) => Promise<R>
|
handler: (doc: T) => Promise<R>
|
||||||
): CollectionConfig["endpoints"][number] => ({
|
): CollectionEndpoint => ({
|
||||||
path: `/${attribute}/:${attribute}`,
|
path: `/${attribute}/:${attribute}`,
|
||||||
method: "get",
|
method: "get",
|
||||||
handler: async (req, res) => {
|
handler: async (req, res) => {
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import payload from "payload";
|
import payload from "payload";
|
||||||
import { CollectionConfig } from "payload/types";
|
|
||||||
import QueryString from "qs";
|
import QueryString from "qs";
|
||||||
import { Recorder } from "../types/collections";
|
import { Recorder } from "../types/collections";
|
||||||
|
import { CollectionEndpoint, PayloadCreateData } from "../types/payload";
|
||||||
import { isDefined } from "../utils/asserts";
|
import { isDefined } from "../utils/asserts";
|
||||||
import { PayloadCreateData } from "../utils/types";
|
|
||||||
|
|
||||||
export const getAllStrapiEntries = async <T>(
|
export const getAllStrapiEntries = async <T>(
|
||||||
collectionSlug: string,
|
collectionSlug: string,
|
||||||
|
@ -31,26 +30,26 @@ export const getAllStrapiEntries = async <T>(
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Params<T> = {
|
type Params<T, S> = {
|
||||||
strapi: {
|
strapi: {
|
||||||
collection: string;
|
collection: string;
|
||||||
params: any;
|
params: any;
|
||||||
};
|
};
|
||||||
payload: {
|
payload: {
|
||||||
collection: string;
|
collection: string;
|
||||||
import?: (strapiObject: any, user: any) => Promise<void>;
|
import?: (strapiObject: S, user: any) => Promise<void>;
|
||||||
convert?: (strapiObject: any, user: any) => PayloadCreateData<T>;
|
convert?: (strapiObject: S, user: any) => PayloadCreateData<T>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const importStrapiEntries = async <T>({
|
export const importStrapiEntries = async <T, S>({
|
||||||
strapi: strapiParams,
|
strapi: strapiParams,
|
||||||
payload: payloadParams,
|
payload: payloadParams,
|
||||||
user,
|
user,
|
||||||
}: Params<T> & { user: Recorder }) => {
|
}: Params<T, S> & { user: Recorder }) => {
|
||||||
const entries = await getAllStrapiEntries<any>(strapiParams.collection, strapiParams.params);
|
const entries = await getAllStrapiEntries<any>(strapiParams.collection, strapiParams.params);
|
||||||
|
|
||||||
const errors = [];
|
const errors: string[] = [];
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
entries.map(async ({ attributes, id }) => {
|
entries.map(async ({ attributes, id }) => {
|
||||||
|
@ -68,7 +67,9 @@ export const importStrapiEntries = async <T>({
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(e);
|
console.warn(e);
|
||||||
errors.push(`${e.name} with ${id}`);
|
if (typeof e === "object" && isDefined(e) && "name" in e) {
|
||||||
|
errors.push(`${e.name} with ${id}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -76,9 +77,7 @@ export const importStrapiEntries = async <T>({
|
||||||
return { count: entries.length, errors };
|
return { count: entries.length, errors };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createStrapiImportEndpoint = <T>(
|
export const createStrapiImportEndpoint = <T, S>(params: Params<T, S>): CollectionEndpoint => ({
|
||||||
params: Params<T>
|
|
||||||
): CollectionConfig["endpoints"][number] => ({
|
|
||||||
method: "get",
|
method: "get",
|
||||||
path: "/strapi",
|
path: "/strapi",
|
||||||
handler: async (req, res) => {
|
handler: async (req, res) => {
|
||||||
|
|
|
@ -30,7 +30,7 @@ export const backPropagationField = ({
|
||||||
afterRead: [
|
afterRead: [
|
||||||
...afterRead,
|
...afterRead,
|
||||||
async ({ data }) => {
|
async ({ data }) => {
|
||||||
if (isNotEmpty(data.id)) {
|
if (isNotEmpty(data?.id)) {
|
||||||
const result = await payload.find({
|
const result = await payload.find({
|
||||||
collection: params.relationTo,
|
collection: params.relationTo,
|
||||||
where: where(data),
|
where: where(data),
|
||||||
|
|
|
@ -19,5 +19,5 @@ export const keysField = ({
|
||||||
filterOptions: { type: { equals: getKeysTypesKey(relationTo) } },
|
filterOptions: { type: { equals: getKeysTypesKey(relationTo) } },
|
||||||
});
|
});
|
||||||
|
|
||||||
const getKeysTypesKey = (keyType: KeysTypes): string =>
|
const getKeysTypesKey = (keyType: KeysTypes): string | undefined =>
|
||||||
Object.entries(KeysTypes).find(([, value]) => value === keyType)[0];
|
Object.entries(KeysTypes).find(([, value]) => value === keyType)?.[0];
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { RowLabelArgs } from "payload/dist/admin/components/forms/RowLabel/types";
|
||||||
import { array } from "payload/dist/fields/validations";
|
import { array } from "payload/dist/fields/validations";
|
||||||
import { ArrayField, Field } from "payload/types";
|
import { ArrayField, Field } from "payload/types";
|
||||||
import { Collections } from "../../constants";
|
import { Collections } from "../../constants";
|
||||||
|
@ -118,6 +119,8 @@ const creditFields: Field = {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type FieldData = Record<string, any> & { [fieldsNames.language]: string };
|
||||||
|
|
||||||
export const translatedFields = ({
|
export const translatedFields = ({
|
||||||
fields,
|
fields,
|
||||||
validate,
|
validate,
|
||||||
|
@ -129,7 +132,7 @@ export const translatedFields = ({
|
||||||
admin: {
|
admin: {
|
||||||
initCollapsed: true,
|
initCollapsed: true,
|
||||||
components: {
|
components: {
|
||||||
Cell: ({ cellData }) =>
|
Cell: ({ cellData }: { cellData: FieldData[] }) =>
|
||||||
Cell({
|
Cell({
|
||||||
cellData:
|
cellData:
|
||||||
cellData?.map((row) => ({
|
cellData?.map((row) => ({
|
||||||
|
@ -137,7 +140,7 @@ export const translatedFields = ({
|
||||||
title: isDefined(useAsTitle) ? row[useAsTitle] : undefined,
|
title: isDefined(useAsTitle) ? row[useAsTitle] : undefined,
|
||||||
})) ?? [],
|
})) ?? [],
|
||||||
}),
|
}),
|
||||||
RowLabel: ({ data }) =>
|
RowLabel: ({ data }: RowLabelArgs) =>
|
||||||
RowLabel({
|
RowLabel({
|
||||||
language: data[fieldsNames.language],
|
language: data[fieldsNames.language],
|
||||||
title: isDefined(useAsTitle) ? data[useAsTitle] : undefined,
|
title: isDefined(useAsTitle) ? data[useAsTitle] : undefined,
|
||||||
|
@ -162,6 +165,8 @@ export const translatedFields = ({
|
||||||
if (hasDuplicates(languages)) {
|
if (hasDuplicates(languages)) {
|
||||||
return `There cannot be multiple ${otherProps.name} with the same ${fieldsNames.language}`;
|
return `There cannot be multiple ${otherProps.name} with the same ${fieldsNames.language}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
fields: [
|
fields: [
|
||||||
hasSourceLanguage
|
hasSourceLanguage
|
||||||
|
|
|
@ -4,7 +4,8 @@ import path from "path";
|
||||||
import payload from "payload";
|
import payload from "payload";
|
||||||
import { Collections, RecordersRoles } from "./constants";
|
import { Collections, RecordersRoles } from "./constants";
|
||||||
import { Recorder } from "./types/collections";
|
import { Recorder } from "./types/collections";
|
||||||
import { PayloadCreateData } from "./utils/types";
|
import { PayloadCreateData } from "./types/payload";
|
||||||
|
import { isDefined, isUndefined } from "./utils/asserts";
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
|
@ -15,6 +16,15 @@ app.get("/", (_, res) => {
|
||||||
|
|
||||||
const start = async () => {
|
const start = async () => {
|
||||||
// Initialize Payload
|
// Initialize Payload
|
||||||
|
|
||||||
|
if (isUndefined(process.env.PAYLOAD_SECRET)) {
|
||||||
|
throw new Error("Missing required env variable: PAYLOAD_SECRET");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isUndefined(process.env.MONGODB_URI)) {
|
||||||
|
throw new Error("Missing required env variable: MONGODB_URI");
|
||||||
|
}
|
||||||
|
|
||||||
await payload.init({
|
await payload.init({
|
||||||
secret: process.env.PAYLOAD_SECRET,
|
secret: process.env.PAYLOAD_SECRET,
|
||||||
mongoURL: process.env.MONGODB_URI,
|
mongoURL: process.env.MONGODB_URI,
|
||||||
|
@ -24,19 +34,26 @@ const start = async () => {
|
||||||
const recorders = await payload.find({ collection: Collections.Recorders });
|
const recorders = await payload.find({ collection: Collections.Recorders });
|
||||||
|
|
||||||
// If no recorders, we seed some initial data
|
// If no recorders, we seed some initial data
|
||||||
if (recorders.docs.length === 0) {
|
if (
|
||||||
payload.logger.info("Seeding some initial data");
|
isDefined(process.env.SEEDING_ADMIN_EMAIL) &&
|
||||||
const recorder: PayloadCreateData<Recorder> = {
|
isDefined(process.env.SEEDING_ADMIN_PASSWORD) &&
|
||||||
email: process.env.SEEDING_ADMIN_EMAIL,
|
isDefined(process.env.SEEDING_ADMIN_USERNAME)
|
||||||
password: process.env.SEEDING_ADMIN_PASSWORD,
|
) {
|
||||||
username: process.env.SEEDING_ADMIN_USERNAME,
|
if (recorders.docs.length === 0) {
|
||||||
role: [RecordersRoles.Admin],
|
payload.logger.info("Seeding some initial data");
|
||||||
anonymize: false,
|
|
||||||
};
|
const recorder: PayloadCreateData<Recorder> = {
|
||||||
await payload.create({
|
email: process.env.SEEDING_ADMIN_EMAIL,
|
||||||
collection: Collections.Recorders,
|
password: process.env.SEEDING_ADMIN_PASSWORD,
|
||||||
data: recorder,
|
username: process.env.SEEDING_ADMIN_USERNAME,
|
||||||
});
|
role: [RecordersRoles.Admin],
|
||||||
|
anonymize: false,
|
||||||
|
};
|
||||||
|
await payload.create({
|
||||||
|
collection: Collections.Recorders,
|
||||||
|
data: recorder,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,7 +14,7 @@ export type CategoryTranslations = {
|
||||||
}[];
|
}[];
|
||||||
export type RecorderBiographies = {
|
export type RecorderBiographies = {
|
||||||
language: string | Language;
|
language: string | Language;
|
||||||
biography?: string;
|
biography: string;
|
||||||
id?: string;
|
id?: string;
|
||||||
}[];
|
}[];
|
||||||
export type ContentFoldersTranslation = {
|
export type ContentFoldersTranslation = {
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { CollectionConfig } from "payload/types";
|
||||||
|
|
||||||
|
export type PayloadCreateData<T> = Omit<
|
||||||
|
T,
|
||||||
|
"id" | "updatedAt" | "createdAt" | "sizes" | "updatedBy"
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type CollectionEndpoint = NonNullable<CollectionConfig["endpoints"]>[number];
|
|
@ -0,0 +1,14 @@
|
||||||
|
export type StrapiLanguage = {
|
||||||
|
data?: { attributes: { code: string } };
|
||||||
|
};
|
||||||
|
|
||||||
|
export type StrapiImage = {
|
||||||
|
data?: {
|
||||||
|
attributes: {
|
||||||
|
url: string;
|
||||||
|
mime: string;
|
||||||
|
name: string;
|
||||||
|
size: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,5 +1,6 @@
|
||||||
import payload from "payload";
|
import payload from "payload";
|
||||||
import { Collections, KeysTypes } from "../constants";
|
import { Collections, KeysTypes } from "../constants";
|
||||||
|
import { StrapiImage } from "../types/strapi";
|
||||||
import { isDefined } from "./asserts";
|
import { isDefined } from "./asserts";
|
||||||
|
|
||||||
export const findWeaponType = async (name: string): Promise<string> => {
|
export const findWeaponType = async (name: string): Promise<string> => {
|
||||||
|
@ -23,20 +24,10 @@ type UploadStrapiImage = {
|
||||||
collection: Collections;
|
collection: Collections;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StrapiImage = {
|
|
||||||
data?: {
|
|
||||||
attributes: {
|
|
||||||
url: string;
|
|
||||||
mime: string;
|
|
||||||
name: string;
|
|
||||||
size: number;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
export const uploadStrapiImage = async ({
|
export const uploadStrapiImage = async ({
|
||||||
collection,
|
collection,
|
||||||
image,
|
image,
|
||||||
}: UploadStrapiImage): Promise<string> => {
|
}: UploadStrapiImage): Promise<string | undefined> => {
|
||||||
if (isDefined(image.data)) {
|
if (isDefined(image.data)) {
|
||||||
const url = `${process.env.STRAPI_URI}${image.data.attributes.url}`;
|
const url = `${process.env.STRAPI_URI}${image.data.attributes.url}`;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Block, BlockField } from "payload/types";
|
import { Block, BlockField } from "payload/types";
|
||||||
|
import { capitalize } from "./string";
|
||||||
|
|
||||||
const isDefined = <T>(value: T | null | undefined): value is T =>
|
const isDefined = <T>(value: T | null | undefined): value is T =>
|
||||||
value !== null && value !== undefined;
|
value !== null && value !== undefined;
|
||||||
|
@ -26,11 +27,6 @@ export const generateBlocks = <T extends string>(blocksConfig: BlocksConfig<T>):
|
||||||
recursionFieldName in block;
|
recursionFieldName in block;
|
||||||
|
|
||||||
const getInterfaceName = (parents: T[], currentBlockName: T): string => {
|
const getInterfaceName = (parents: T[], currentBlockName: T): string => {
|
||||||
const capitalize = (text: string): string => {
|
|
||||||
if (text.length === 0) return text;
|
|
||||||
const [firstLetter, ...rest] = text;
|
|
||||||
return [firstLetter.toUpperCase(), ...rest].join("");
|
|
||||||
};
|
|
||||||
return [...parents, currentBlockName]
|
return [...parents, currentBlockName]
|
||||||
.map((blockName) => blocksConfig[blockName].block.slug)
|
.map((blockName) => blocksConfig[blockName].block.slug)
|
||||||
.map(capitalize)
|
.map(capitalize)
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
import tags from "language-tags";
|
import tags from "language-tags";
|
||||||
|
import { isUndefined } from "./asserts";
|
||||||
|
|
||||||
export const shortenEllipsis = (text: string, length: number): string =>
|
export const shortenEllipsis = (text: string, length: number): string =>
|
||||||
text.length - 3 > length ? `${text.substring(0, length)}...` : text;
|
text.length - 3 > length ? `${text.substring(0, length)}...` : text;
|
||||||
|
|
||||||
export const formatLanguageCode = (code: string): string =>
|
export const formatLanguageCode = (code: string): string =>
|
||||||
tags(code).valid() ? tags(code).language().descriptions()[0] : code;
|
tags(code).valid() ? tags(code).language()?.descriptions()[0] ?? code : code;
|
||||||
|
|
||||||
export const capitalize = (string: string): string => {
|
export const capitalize = (string: string): string => {
|
||||||
const [firstLetter, ...otherLetters] = string;
|
const [firstLetter, ...otherLetters] = string;
|
||||||
|
if (isUndefined(firstLetter)) return "";
|
||||||
return [firstLetter.toUpperCase(), ...otherLetters].join("");
|
return [firstLetter.toUpperCase(), ...otherLetters].join("");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
export type PayloadCreateData<T> = Omit<
|
|
||||||
T,
|
|
||||||
"id" | "updatedAt" | "createdAt" | "sizes" | "updatedBy"
|
|
||||||
>;
|
|
120
tsconfig.json
120
tsconfig.json
|
@ -1,18 +1,116 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2022",
|
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||||
"moduleResolution": "NodeNext",
|
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
/* Projects */
|
||||||
"allowJs": true,
|
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||||
"strict": false,
|
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||||
"esModuleInterop": true,
|
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||||
"skipLibCheck": true,
|
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||||
"outDir": "./dist",
|
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||||
"rootDir": "./src",
|
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||||
"jsx": "react",
|
|
||||||
|
/* Language and Environment */
|
||||||
|
"target": "ES2022" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
||||||
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"esnext"
|
||||||
|
] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
|
||||||
|
"jsx": "react" /* Specify what JSX code is generated. */,
|
||||||
|
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
||||||
|
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||||
|
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||||
|
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||||
|
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||||
|
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||||
|
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||||
|
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||||
|
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||||
|
|
||||||
|
/* Modules */
|
||||||
|
"module": "commonjs" /* Specify what module code is generated. */,
|
||||||
|
"rootDir": "./src" /* Specify the root folder within your source files. */,
|
||||||
|
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||||
|
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||||
"paths": {
|
"paths": {
|
||||||
"payload/generated-types": ["./src/payload-types.ts"]
|
"payload/generated-types": ["./src/payload-types.ts"]
|
||||||
}
|
} /* Specify a set of entries that re-map imports to additional lookup locations. */,
|
||||||
|
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||||
|
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||||
|
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||||
|
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||||
|
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||||
|
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
|
||||||
|
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
||||||
|
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
||||||
|
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
||||||
|
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||||
|
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
||||||
|
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||||
|
|
||||||
|
/* JavaScript Support */
|
||||||
|
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||||
|
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||||
|
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||||
|
|
||||||
|
/* Emit */
|
||||||
|
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||||
|
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||||
|
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||||
|
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||||
|
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||||
|
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||||
|
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
|
||||||
|
// "removeComments": true, /* Disable emitting comments. */
|
||||||
|
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||||
|
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||||
|
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
||||||
|
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||||
|
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||||
|
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||||
|
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||||
|
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||||
|
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||||
|
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||||
|
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||||
|
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||||
|
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||||
|
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||||
|
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||||
|
|
||||||
|
/* Interop Constraints */
|
||||||
|
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||||
|
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
|
||||||
|
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||||
|
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
|
||||||
|
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||||
|
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
||||||
|
|
||||||
|
/* Type Checking */
|
||||||
|
"strict": true /* Enable all strict type-checking options. */,
|
||||||
|
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||||
|
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||||
|
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||||
|
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||||
|
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||||
|
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||||
|
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||||
|
"alwaysStrict": true /* Ensure 'use strict' is always emitted. */,
|
||||||
|
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||||
|
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||||
|
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||||
|
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||||
|
"noFallthroughCasesInSwitch": true /* Enable error reporting for fallthrough cases in switch statements. */,
|
||||||
|
"noUncheckedIndexedAccess": true /* Add 'undefined' to a type when accessed using an index. */,
|
||||||
|
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||||
|
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||||
|
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||||
|
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||||
|
|
||||||
|
/* Completeness */
|
||||||
|
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||||
|
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
"include": ["src"],
|
||||||
"exclude": ["node_modules", "dist", "build"],
|
"exclude": ["node_modules", "dist", "build"],
|
||||||
|
|
Loading…
Reference in New Issue