Skip to content

Commit

Permalink
[apps] Add missing locales (#84)
Browse files Browse the repository at this point in the history
  • Loading branch information
54nd10 authored Dec 6, 2023
1 parent c6ec093 commit 68c8ae3
Show file tree
Hide file tree
Showing 45 changed files with 7,257 additions and 13,148 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,4 @@ public/blog
.vercel
.env
.vscode
*.dump
2 changes: 2 additions & 0 deletions apps/cms/.strapi/client/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
import i18N from "@strapi/plugin-i18n/strapi-admin";
import usersPermissions from "@strapi/plugin-users-permissions/strapi-admin";
import multiSelect from "strapi-plugin-multi-select/strapi-admin";
import translate from "strapi-plugin-translate/strapi-admin";
import { renderAdmin } from "@strapi/strapi/admin";

renderAdmin(document.getElementById("strapi"), {
plugins: {
i18n: i18N,
"users-permissions": usersPermissions,
"multi-select": multiSelect,
translate: translate,
},
});
30 changes: 28 additions & 2 deletions apps/cms/config/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ export default ({env}) => ({
provider: 'aws-s3',
providerOptions: {
s3Options: {
accessKeyId: env('AWS_ACCESS_KEY_ID'),
secretAccessKey: env('AWS_ACCESS_SECRET'),
credentials: {
accessKeyId: env('AWS_ACCESS_KEY_ID'),
secretAccessKey: env('AWS_ACCESS_SECRET')
},
region: env('AWS_REGION'),
params: {
ACL: env('AWS_ACL', 'public-read'),
Expand All @@ -26,5 +28,29 @@ export default ({env}) => ({
config: {
size: 10
}
},
translate: {
enabled: true,
config: {
provider: 'deepl',
providerOptions: {
apiKey: env('DEEPL_API_KEY'),
apiUrl: 'https://api.deepl.com',
localeMap: {
EN: 'EN-US'
},
apiOptions: {
formality: 'default'
}
},
translatedFieldTypes: [
'string',
{type: 'text', format: 'plain'},
{type: 'richtext', format: 'markdown'},
'component',
'dynamiczone'
],
translateRelations: true
}
}
})
4 changes: 4 additions & 0 deletions apps/cms/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@
"@strapi/provider-upload-aws-s3": "^4.14.5",
"@strapi/strapi": "4.14.5",
"better-sqlite3": "9.1.1",
"glob": "^10.3.10",
"pg": "^8.11.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^5.2.0",
"strapi-plugin-multi-select": "^1.2.2",
"strapi-plugin-placeholder": "^4.4.0",
"strapi-plugin-populate-deep": "^3.0.1",
"strapi-plugin-slugify": "^2.3.8",
"strapi-plugin-translate": "^1.2.4",
"strapi-provider-translate-deepl": "^1.1.12",
"styled-components": "^5.2.1"
},
"devDependencies": {
Expand Down
188 changes: 188 additions & 0 deletions apps/cms/scripts/add-relations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import 'dotenv/config'
import {pool, query} from './share/db-connect'

function camelToSnake(str: string) {
return str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`)
}

function cleanUpObject(object: object) {
const keysToDelete = ['id', 'created_at', 'updated_at', 'published_at']
keysToDelete.forEach((key) => {
delete object[key]
})
Object.keys(object).forEach((key) => {
if (object[key] === null) {
delete object[key]
}
})
return object
}
async function insert(tableName, object) {
const cleanedObject = cleanUpObject({...object})
const keysToString = Object.keys(cleanedObject).map(key => JSON.stringify(key)).join(',')
const valueIndexes = Object.keys(cleanedObject).map((_, index) => `$${index+1}`).join(',')

if (keysToString.length === 0) {
return (await query(`INSERT INTO ${tableName} DEFAULT VALUES RETURNING *`))[0]
} else {
return (await query(
`INSERT INTO ${tableName}(${keysToString}) VALUES(${valueIndexes}) RETURNING *`,
Object.values(cleanedObject)
))[0]
}
}

async function checkIfAlreadyExists(tableName, object) {
const cleanedObject = cleanUpObject({...object})
const keysToString = Object.keys(cleanedObject).map(key => key)
return ((await query(
`SELECT * FROM ${tableName} WHERE ${keysToString.map((key) => `${key} = ${cleanedObject[key]}`).join(' AND ')}`
)) as unknown as Array<any>).length > 0
}

async function getAllContentTypeItems(contentType: string) {
const items = await query(`
SELECT * FROM ${contentType}s
`) as unknown as any[]
return items
}

async function getAllContentTypeLocalizationsLinks(contentType: string) {
const items = await query(`
SELECT * FROM ${contentType}s_localizations_links
`) as unknown as any[]
return items
}
async function getItemComponents(contentType: string, entityId: number) {
return await query(`
SELECT * FROM ${contentType}s_components
WHERE entity_id = ${entityId}
`) as unknown as any[]
}

function schemaWithRelationAttributes(schema) {
const attributes = Object.keys(schema.attributes)
return attributes.some((attr: string) => schema.attributes[attr].type === 'relation')
}

async function filterByComponentsThatHaveRelations(components: any[]) {
return (await Promise.all(components.map(async comp => {
const name = comp.component_type.split('.')
const schema = await import(`../src/components/${name[0]}/${name[1]}.json`)
return schemaWithRelationAttributes(schema) ? {
schema,
comp
} : null
}))).filter(comp => comp !== null) as Array<{schema: any, comp: any}>
}

async function getGroupedItems(contentType: string) {
contentType = contentType.replaceAll('-', '_')
const items = await getAllContentTypeItems(contentType)
// console.log(items)
const localizationsLinks = await getAllContentTypeLocalizationsLinks(contentType)
const groupedItems = items.filter(item => item.locale === 'en').map(item => {
return {
default: item,
localizations: localizationsLinks
.filter(link => link[`${contentType}_id`] === item.id)
.map(link => items.find(item => link[`inv_${contentType}_id`] === item.id))
}
})
return groupedItems
}

async function addMissingRelations(contentType: string) {
const groupedItems = await getGroupedItems(contentType)
await Promise.all([groupedItems[0]].map(async item => {
await handleDirectRelations(contentType, item)
await handleComponentsRelations(contentType, item)
}))

}

async function handleDirectRelations(contentType: string, item: any) {
const schema = await import(`../src/api/${contentType}/content-types/${contentType}/schema.json`)
contentType = contentType.replaceAll('-', '_')
const relations = Object.keys(schema.attributes)
.filter((attr: string) => schema.attributes[attr].type === 'relation')
await Promise.all(relations.map(async relation => {
const target = schema.attributes[relation].target.split('.')[1].replaceAll('-', '_')
relation = camelToSnake(relation)
const relationGI = (await getGroupedItems(`${target}`))
const defaultLinks = await query(`
SELECT * FROM ${contentType}s_${relation}_links
WHERE ${contentType}_id = ${item.default.id}
`) as unknown as any[]
console.log(defaultLinks)
if (defaultLinks) {
await Promise.all(defaultLinks?.map(async link => {
const linkTargetId = `${contentType === target ? 'inv_' : ''}${target}_id`
link[`${contentType}_id`] = item.localizations[0]?.id
link[linkTargetId] = relationGI.find(
item => item.default.id === link[linkTargetId]
)?.localizations[0]?.id || undefined
const tableLinks = `${contentType}s_${relation}_links`
console.log(link, tableLinks)
if (Object.values(link).some(value => value === undefined)) {
console.log(`${contentType} ${item.default.id} link not created because of undefined values!`)
} else {
if (await checkIfAlreadyExists(tableLinks, link)) {
console.log(`${contentType} ${item.default.id} link already exists.`)
} else {
await insert(tableLinks, link)
console.log(`${contentType} ${item.default.id} link created!`)
}
}
}))
}
}))
}

async function handleComponentsRelations(contentType: string, item: any) {
const componentWithRelations = await filterByComponentsThatHaveRelations(
await getItemComponents(contentType, item.default.id)
)
await Promise.all(componentWithRelations.map(async component => {
const defaultComponentId = (await query(`
SELECT * FROM ${contentType}s_components
WHERE entity_id = ${item.default.id}
AND component_type = '${component.comp.component_type}'
`) as unknown as any[])[0]
await Promise.all(item.localizations.map(async localization => {
const localeComponentId = (await query(`
SELECT * FROM ${contentType}s_components
WHERE entity_id = ${localization.id}
AND component_type = '${component.comp.component_type}'
`) as unknown as any[])[0]
const relations = Object.keys(component.schema.attributes)
.filter((attr: string) => component.schema.attributes[attr].type === 'relation')
const target = component.schema.attributes[relations[0]].target.split('.')[1]
const relationGI = await getGroupedItems(`${target}`)
const compToSnake = component.comp.component_type.split('.')[1].replaceAll('-', '_')
const defaultLinks = await query(`
SELECT * FROM ${compToSnake}_${relations[0]}_links
WHERE ${compToSnake}_id = ${defaultComponentId.component_id}
`) as unknown as any[]
await Promise.all(defaultLinks.map(async link => {
link[`${compToSnake}_id`] = localeComponentId.component_id
link[`${target}_id`] = relationGI.find(item => item.default.id === link[`${target}_id`])?.localizations[0].id
const tableLinks = `${compToSnake}_${relations[0]}_links`
if (await checkIfAlreadyExists(tableLinks, link)) {
console.log(`${contentType} ${item.default.id} component link already exists.`)
} else {
await insert(tableLinks, link)
console.log(`${contentType} ${item.default.id} component link created!`)
}
}))
}))
}))
}

(async () => {
const client = await pool.connect()
await addMissingRelations('service')
await client.release()
await pool.end()
})()
26 changes: 3 additions & 23 deletions apps/cms/scripts/fields-size.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,10 @@
import 'dotenv/config'
import {Pool, QueryResult} from 'pg'
import {pool, query} from './share/db-connect'

const size = 12
const pool = new Pool({
user: process.env.DATABASE_USERNAME,
host: process.env.DATABASE_HOST,
database: process.env.DATABASE_NAME,
password: process.env.DATABASE_PASSWORD,
port: Number(process.env.DATABASE_PORT),
ssl: (process.env.DATABASE_USE_SSL ? JSON.parse(process.env.DATABASE_USE_SSL) : null) ?? true
? {rejectUnauthorized: false}
: false
})

export const sampleQuery = async (query: string): Promise<QueryResult> => {
try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const result = await pool.query(query) as Record<string, any>
return result.rows
} catch (error) {
console.error('Error executing query', error)
}
}

async function updateConfigs(size: number) {
const configs = await sampleQuery('SELECT * FROM strapi_core_store_settings \
const configs = await query('SELECT * FROM strapi_core_store_settings \
WHERE key LIKE \'plugin_content_manager_configuration%\';')
if (Array.isArray(configs) && configs.length > 0) {
await Promise.all(configs.map(async (config) => {
Expand All @@ -36,7 +16,7 @@ async function updateConfigs(size: number) {
})
return edit
}))
await sampleQuery(`UPDATE strapi_core_store_settings SET value = '${JSON.stringify(value)}' \
await query(`UPDATE strapi_core_store_settings SET value = '${JSON.stringify(value)}' \
WHERE id = ${config.id};`)
}))
console.log(`${configs.length} config sizes have been updated to ${size}! 🎉`)
Expand Down
22 changes: 22 additions & 0 deletions apps/cms/scripts/share/db-connect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {Pool, QueryResult} from 'pg'

export const pool = new Pool({
user: process.env.DATABASE_USERNAME,
host: process.env.DATABASE_HOST,
database: process.env.DATABASE_NAME,
password: process.env.DATABASE_PASSWORD,
port: Number(process.env.DATABASE_PORT),
ssl: (process.env.DATABASE_USE_SSL ? JSON.parse(process.env.DATABASE_USE_SSL) : null) ?? true
? {rejectUnauthorized: false}
: false
})

export const query = async (query, values?): Promise<QueryResult> => {
try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const result = await pool.query(query, values) as Record<string, any>
return result.rows
} catch (error) {
console.error('Error executing query', error)
}
}
20 changes: 17 additions & 3 deletions apps/cms/src/api/about-page/content-types/about-page/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@
"options": {
"draftAndPublish": true
},
"pluginOptions": {},
"pluginOptions": {
"i18n": {
"localized": true
}
},
"attributes": {
"body": {
"type": "dynamiczone",
Expand All @@ -22,13 +26,23 @@
"team-sections.team-three-column",
"cta-sections.join-our-team",
"contact-sections.contact-split-with-pattern"
]
],
"pluginOptions": {
"i18n": {
"localized": true
}
}
},
"seo": {
"type": "component",
"repeatable": false,
"component": "seo.seo",
"required": true
"required": true,
"pluginOptions": {
"i18n": {
"localized": true
}
}
}
}
}
6 changes: 3 additions & 3 deletions apps/cms/src/api/article/content-types/article/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
"options": {
"draftAndPublish": true
},
"pluginOptions": {},
"attributes": {
"title": {
"type": "string",
Expand All @@ -29,8 +28,9 @@
"type": "date"
},
"slug": {
"type": "text",
"required": true
"type": "string",
"required": true,
"regex": "^[a-zA-Z0-9-]+$"
},
"image": {
"type": "media",
Expand Down
Loading

0 comments on commit 68c8ae3

Please sign in to comment.