diff --git a/backend/Dockerfile b/backend/Dockerfile index 2897fe2f6..e481da5a3 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -22,6 +22,8 @@ FROM base AS build COPY . . ONBUILD COPY ./branding/constants/ src/config/tmp ONBUILD RUN tools/replace-constants.sh +# copy categories to brand them (use yarn prod:db:data:categories) +ONBUILD COPY branding/constants/ src/constants/ ONBUILD COPY ./branding/email/ src/middleware/helpers/email/ ONBUILD COPY ./branding/middlewares/ src/middleware/branding/ ONBUILD COPY ./branding/data/ src/db/data diff --git a/backend/package.json b/backend/package.json index 38bf966ac..0cfa5a080 100644 --- a/backend/package.json +++ b/backend/package.json @@ -24,7 +24,8 @@ "db:migrate": "migrate --compiler 'ts:./src/db/compiler.ts' --migrations-dir ./src/db/migrations --store ./src/db/migrate/store.ts", "db:migrate:create": "migrate --compiler 'ts:./src/db/compiler.ts' --migrations-dir ./src/db/migrations --template-file ./src/db/migrate/template.ts --date-format 'yyyymmddHHmmss' create", "prod:migrate": "migrate --migrations-dir ./build/src/db/migrations --store ./build/src/db/migrate/store.js", - "prod:db:data:branding": "node build/src/db/data-branding.js" + "prod:db:data:branding": "node build/src/db/data-branding.js", + "prod:db:data:categories": "node build/src/db/categories.js" }, "dependencies": { "@sentry/node": "^5.15.4", diff --git a/backend/src/constants/categories.ts b/backend/src/constants/categories.ts index 6365d268a..b6fce03ca 100644 --- a/backend/src/constants/categories.ts +++ b/backend/src/constants/categories.ts @@ -5,98 +5,116 @@ export const CATEGORIES_MAX = 3 export const categories = [ { icon: 'networking', + id: 'cat0', + slug: 'networking', name: 'networking', - description: 'Kooperation, Aktionsbündnisse, Solidarität, Hilfe', }, { icon: 'home', + id: 'cat1', + slug: 'home', name: 'home', - description: 'Bauen, Lebensgemeinschaften, Tiny Houses, Gemüsegarten', }, { icon: 'energy', + id: 'cat2', + slug: 'energy', name: 'energy', - description: 'Öl, Gas, Kohle, Wind, Wasserkraft, Biogas, Atomenergie, ...', }, { icon: 'psyche', + id: 'cat3', + slug: 'psyche', name: 'psyche', - description: 'Seele, Gefühle, Glück', }, { icon: 'movement', + id: 'cat4', + slug: 'body-and-excercise', name: 'body-and-excercise', - description: 'Sport, Yoga, Massage, Tanzen, Entspannung', }, { icon: 'balance-scale', + id: 'cat5', + slug: 'law', name: 'law', - description: 'Menschenrechte, Gesetze, Verordnungen', }, { icon: 'finance', + id: 'cat6', + slug: 'finance', name: 'finance', - description: 'Geld, Finanzsystem, Alternativwährungen, ...', }, { icon: 'child', + id: 'cat7', + slug: 'children', name: 'children', - description: 'Familie, Pädagogik, Schule, Prägung', }, { icon: 'mobility', + id: 'cat8', + slug: 'mobility', name: 'mobility', - description: 'Reise, Verkehr, Elektromobilität', }, { icon: 'shopping-cart', + id: 'cat9', + slug: 'economy', name: 'economy', - description: 'Handel, Konsum, Marketing, Lebensmittel, Lieferketten, ...', }, { icon: 'peace', + id: 'cat10', + slug: 'peace', name: 'peace', - description: 'Krieg, Militär, soziale Verteidigung, Waffen, Cyberattacken', }, { icon: 'politics', + id: 'cat11', + slug: 'politics', name: 'politics', - description: 'Demokratie, Mitbestimmung, Wahlen, Korruption, Parteien', }, { icon: 'nature', + id: 'cat12', + slug: 'nature', name: 'nature', - description: 'Tiere, Pflanzen, Landwirtschaft, Ökologie, Artenvielfalt', }, { icon: 'science', + id: 'cat13', + slug: 'science', name: 'science', - description: 'Bildung, Hochschule, Publikationen, ...', }, { icon: 'health', + id: 'cat14', + slug: 'health', name: 'health', - description: 'Medizin, Ernährung, WHO, Impfungen, Schadstoffe, ...', }, { icon: 'media', + id: 'cat15', + slug: 'it-and-media', name: 'it-and-media', - description: - 'Nachrichten, Manipulation, Datenschutz, Überwachung, Datenkraken, AI, Software, Apps', }, { icon: 'spirituality', + id: 'cat16', + slug: 'spirituality', name: 'spirituality', - description: 'Religion, Werte, Ethik', }, { icon: 'culture', + id: 'cat17', + slug: 'culture', name: 'culture', - description: 'Kunst, Theater, Musik, Fotografie, Film', }, { icon: 'miscellaneous', + id: 'cat18', + slug: 'miscellaneous', name: 'miscellaneous', - description: '', }, ] diff --git a/backend/src/context/database.ts b/backend/src/context/database.ts index c1dc244d9..dc623470d 100644 --- a/backend/src/context/database.ts +++ b/backend/src/context/database.ts @@ -4,7 +4,7 @@ import type { Driver } from 'neo4j-driver' export const query = (driver: Driver) => - async ({ query, variables = {} }: { query: string; variables: object }) => { + async ({ query, variables = {} }: { query: string; variables?: object }) => { const session = driver.session() const result = session.readTransaction(async (transaction) => { @@ -21,7 +21,7 @@ export const query = export const write = (driver: Driver) => - async ({ query, variables = {} }: { query: string; variables: object }) => { + async ({ query, variables = {} }: { query: string; variables?: object }) => { const session = driver.session() const result = session.writeTransaction(async (transaction) => { diff --git a/backend/src/db/categories.ts b/backend/src/db/categories.ts index a007b25ae..24421a400 100644 --- a/backend/src/db/categories.ts +++ b/backend/src/db/categories.ts @@ -1,38 +1,44 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ -/* eslint-disable @typescript-eslint/restrict-template-expressions */ -/* eslint-disable @typescript-eslint/require-await */ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { categories } from '@constants/categories' +import databaseContext from '@context/database' -import { getDriver } from './neo4j' +const { query, write, driver } = databaseContext() const createCategories = async () => { - const driver = getDriver() - const session = driver.session() - const createCategoriesTxResultPromise = session.writeTransaction(async (txc) => { - categories.forEach(({ icon, name }, index) => { - const id = `cat${index + 1}` - txc.run( - `MERGE (c:Category { - icon: "${icon}", - slug: "${name}", - name: "${name}", - id: "${id}", - createdAt: toString(datetime()) - })`, - ) - }) + const result = await query({ + query: 'MATCH (category:Category) RETURN category { .* }', }) - try { - await createCategoriesTxResultPromise - console.log('Successfully created categories!') // eslint-disable-line no-console - // eslint-disable-next-line no-catch-all/no-catch-all - } catch (error) { - console.log(`Error creating categories: ${error}`) // eslint-disable-line no-console - } finally { - session.close() - driver.close() - } + + const existingCategories = result.records.map((r) => r.get('category')) + const existingCategoryIds = existingCategories.map((c) => c.id) + + const newCategories = categories.filter((c) => !existingCategoryIds.includes(c.id)) + + await write({ + query: `UNWIND $newCategories AS map + CREATE (category:Category) + SET category = map + SET category.createdAt = toString(datetime())`, + variables: { + newCategories, + }, + }) + + const categoryIds = categories.map((c) => c.id) + await write({ + query: `MATCH (category:Category) + WHERE NOT category.id IN $categoryIds + DETACH DELETE category`, + variables: { + categoryIds, + }, + }) + // eslint-disable-next-line no-console + console.log('Successfully created categories!') + await driver.close() } ;(async function () {