Piattaforma ITI TECHNOLOGY: monorepo, Medusa, API, sito, area riservata e deploy
Deploy stack su Proxmox / deploy (push) Failing after 3s
Deploy stack su Proxmox / deploy (push) Failing after 3s
Monorepo npm con apps/public (Angular SSR), apps/private (Angular SPA), apps/api (Fastify), packages/shared e packages/styles; Medusa 2.21 in medusa/; infrastruttura Docker (Postgres PostGIS+pgvector, Redis, Caddy, backup) e workflow Gitea Actions per il deploy via SSH a ogni push su master. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018jQyT6dR9czzGwqb7FkG6u
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
// Genera l'indice dei file di contenuto in src/content/generated/ (gli import li scrive lui, non a mano).
|
||||
// Uso: node scripts/content-index.mjs → (ri)scrive l'indice
|
||||
// node scripts/content-index.mjs --check → exit 1 se l'indice non è aggiornato (usato dai test)
|
||||
// Parte da solo prima di dev, build, test e typecheck (script "pre*" in package.json).
|
||||
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const root = join(dirname(fileURLToPath(import.meta.url)), '..', 'src', 'content')
|
||||
const outDir = join(root, 'generated')
|
||||
const check = process.argv.includes('--check')
|
||||
|
||||
const SLUG = /^[a-z0-9]+(-[a-z0-9]+)*$/
|
||||
|
||||
/** File .ts di una cartella (slug = nome senza estensione), ordinati. */
|
||||
function slugsIn(dir) {
|
||||
if (!existsSync(dir)) return []
|
||||
return readdirSync(dir)
|
||||
.filter((f) => f.endsWith('.ts') && !f.endsWith('.spec.ts'))
|
||||
.map((f) => f.slice(0, -3))
|
||||
.sort()
|
||||
.map((slug) => {
|
||||
if (!SLUG.test(slug)) throw new Error(`Nome file non valido: ${join(dir, slug)}.ts (solo a-z, 0-9 e "-")`)
|
||||
return slug
|
||||
})
|
||||
}
|
||||
|
||||
const ident = (...parts) => parts.join('_').replace(/-/g, '_')
|
||||
|
||||
const areas = slugsIn(join(root, 'areas'))
|
||||
const solutionsDir = join(root, 'solutions')
|
||||
const solutionAreas = existsSync(solutionsDir)
|
||||
? readdirSync(solutionsDir).filter((d) => statSync(join(solutionsDir, d)).isDirectory()).sort()
|
||||
: []
|
||||
const solutions = solutionAreas.flatMap((area) => {
|
||||
if (!areas.includes(area)) throw new Error(`src/content/solutions/${area}/: non esiste la macro-area src/content/areas/${area}.ts`)
|
||||
return slugsIn(join(solutionsDir, area)).map((slug) => ({ area, slug }))
|
||||
})
|
||||
const companyPages = slugsIn(join(root, 'company'))
|
||||
|
||||
const header = '// File generato da scripts/content-index.mjs: non modificare a mano.\n'
|
||||
|
||||
const files = {
|
||||
'paths.ts':
|
||||
header +
|
||||
'// Solo slug (dai nomi dei file), senza importare i contenuti: per le route e la sitemap.\n\n' +
|
||||
`export const AREA_SLUGS: readonly string[] = ${JSON.stringify(areas)}\n\n` +
|
||||
`export const SOLUTION_PATHS: readonly { area: string; slug: string }[] = ${JSON.stringify(solutions)}\n\n` +
|
||||
`export const COMPANY_PAGE_SLUGS: readonly string[] = ${JSON.stringify(companyPages)}\n`,
|
||||
'areas.ts':
|
||||
header +
|
||||
"import type { AreaContent } from '../types'\n" +
|
||||
areas.map((s) => `import ${ident('area', s)} from '../areas/${s}'\n`).join('') +
|
||||
`\nexport const AREA_FILES: readonly AreaContent[] = [${areas.map((s) => ident('area', s)).join(', ')}]\n`,
|
||||
'solutions.ts':
|
||||
header +
|
||||
"import type { SolutionContent } from '../types'\n" +
|
||||
solutions.map(({ area, slug }) => `import ${ident('solution', area, slug)} from '../solutions/${area}/${slug}'\n`).join('') +
|
||||
`\nexport const SOLUTION_FILES: readonly SolutionContent[] = [\n${solutions
|
||||
.map(({ area, slug }) => ` ${ident('solution', area, slug)},\n`)
|
||||
.join('')}]\n`,
|
||||
'company.ts':
|
||||
header +
|
||||
"import type { CompanyPageContent } from '../types'\n" +
|
||||
companyPages.map((s) => `import ${ident('company', s)} from '../company/${s}'\n`).join('') +
|
||||
`\nexport const COMPANY_PAGE_FILES: readonly CompanyPageContent[] = [${companyPages.map((s) => ident('company', s)).join(', ')}]\n`,
|
||||
}
|
||||
|
||||
let stale = []
|
||||
if (!check) mkdirSync(outDir, { recursive: true })
|
||||
for (const [name, content] of Object.entries(files)) {
|
||||
const path = join(outDir, name)
|
||||
const current = existsSync(path) ? readFileSync(path, 'utf8') : null
|
||||
if (current === content) continue
|
||||
if (check) stale.push(name)
|
||||
else writeFileSync(path, content)
|
||||
}
|
||||
|
||||
if (check && stale.length) {
|
||||
console.error(`Indice dei contenuti non aggiornato (${stale.join(', ')}): esegui "npm run content -w apps/public"`)
|
||||
process.exit(1)
|
||||
}
|
||||
console.log(
|
||||
`Contenuti: ${areas.length} aree, ${solutions.length} soluzioni, ${companyPages.length} pagine azienda${check ? ' (indice aggiornato)' : ''}`,
|
||||
)
|
||||
Reference in New Issue
Block a user