Compare commits

...

52 Commits

Author SHA1 Message Date
316c03869a fix(gatekeeper): enhance logging and stabilize upstream polling
All checks were successful
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 2s
Monorepo Pipeline / 🧪 Test (push) Successful in 6m38s
Monorepo Pipeline / 🧹 Lint (push) Successful in 7m14s
Monorepo Pipeline / 🏗️ Build (push) Successful in 10m24s
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Successful in 1m39s
Monorepo Pipeline / 🐳 Build Build-Base (push) Successful in 2m7s
Monorepo Pipeline / 🐳 Build Production Runtime (push) Successful in 2m8s
Monorepo Pipeline / 🚀 Release (push) Successful in 2m18s
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Successful in 6m58s
2026-02-11 22:49:16 +01:00
63d2acfab5 feat(infra): add wait-for-upstream script for smart dependencies
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 2s
Monorepo Pipeline / 🧹 Lint (push) Successful in 47s
Monorepo Pipeline / 🧪 Test (push) Successful in 39s
Monorepo Pipeline / 🏗️ Build (push) Successful in 1m49s
Monorepo Pipeline / 🚀 Release (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been cancelled
2026-02-11 22:41:47 +01:00
bdeae0aca6 chore(gatekeeper): bump to 1.7.11 for fix
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 1s
Monorepo Pipeline / 🧹 Lint (push) Successful in 47s
Monorepo Pipeline / 🧪 Test (push) Successful in 40s
Monorepo Pipeline / 🏗️ Build (push) Successful in 1m51s
Monorepo Pipeline / 🚀 Release (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been cancelled
2026-02-11 22:35:04 +01:00
47c70a16f1 fix(gatekeeper): trim auth inputs and prioritize access code to prevent autofill traps
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 2s
Monorepo Pipeline / 🧹 Lint (push) Has been cancelled
Monorepo Pipeline / 🧪 Test (push) Has been cancelled
Monorepo Pipeline / 🏗️ Build (push) Has been cancelled
Monorepo Pipeline / 🚀 Release (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been cancelled
2026-02-11 22:32:17 +01:00
b96d44bf6d chore: finalize version updates for v1.7.10
All checks were successful
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 3s
Monorepo Pipeline / 🧪 Test (push) Successful in 45s
Monorepo Pipeline / 🧹 Lint (push) Successful in 1m12s
Monorepo Pipeline / 🏗️ Build (push) Successful in 1m52s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-11 16:55:17 +01:00
73b60f14a9 chore: release clean base image 1.7.10
All checks were successful
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 1s
Monorepo Pipeline / 🧹 Lint (push) Successful in 1m3s
Monorepo Pipeline / 🧪 Test (push) Successful in 41s
Monorepo Pipeline / 🏗️ Build (push) Successful in 2m19s
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Successful in 16s
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Successful in 2m14s
Monorepo Pipeline / 🚀 Release (push) Successful in 2m43s
Monorepo Pipeline / 🐳 Build Build-Base (push) Successful in 21s
Monorepo Pipeline / 🐳 Build Production Runtime (push) Successful in 32s
2026-02-11 16:32:16 +01:00
b3f43c421f chore: manual version bump to 1.7.9
All checks were successful
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 1s
Monorepo Pipeline / 🧹 Lint (push) Successful in 57s
Monorepo Pipeline / 🧪 Test (push) Successful in 50s
Monorepo Pipeline / 🏗️ Build (push) Successful in 1m49s
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Successful in 57s
Monorepo Pipeline / 🚀 Release (push) Successful in 2m7s
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Successful in 4m4s
Monorepo Pipeline / 🐳 Build Build-Base (push) Successful in 3m1s
Monorepo Pipeline / 🐳 Build Production Runtime (push) Successful in 34s
2026-02-11 16:02:51 +01:00
a2339f7106 fix: make directus extension build scripts more resilient
All checks were successful
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 2s
Monorepo Pipeline / 🧹 Lint (push) Successful in 45s
Monorepo Pipeline / 🧪 Test (push) Successful in 52s
Monorepo Pipeline / 🏗️ Build (push) Successful in 1m50s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-11 15:58:58 +01:00
e83a76f111 chore: trigger CI build after disk cleanup
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 1s
Monorepo Pipeline / 🧪 Test (push) Successful in 45s
Monorepo Pipeline / 🧹 Lint (push) Successful in 1m4s
Monorepo Pipeline / 🏗️ Build (push) Failing after 38s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-11 15:56:16 +01:00
0096c18098 fix(infra): correct registry data path and enable untagged manifest deletion
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 27s
Monorepo Pipeline / 🧪 Test (push) Failing after 24s
Monorepo Pipeline / 🏗️ Build (push) Failing after 22s
Monorepo Pipeline / 🧹 Lint (push) Successful in 1m4s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-11 12:39:50 +01:00
3284931f84 chore(next-utils): respect skip flags in refinements and publish v1.7.15
All checks were successful
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 2s
Monorepo Pipeline / 🧹 Lint (push) Successful in 46s
Monorepo Pipeline / 🧪 Test (push) Successful in 55s
Monorepo Pipeline / 🏗️ Build (push) Successful in 1m48s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-11 01:32:01 +01:00
28517a3558 chore(next-utils): introduce withMintelRefinements and publish v1.7.14
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 1s
Monorepo Pipeline / 🧹 Lint (push) Has started running
Monorepo Pipeline / 🏗️ Build (push) Has been cancelled
Monorepo Pipeline / 🚀 Release (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been cancelled
Monorepo Pipeline / 🧪 Test (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been cancelled
2026-02-11 01:31:16 +01:00
3b9f10ec98 chore(next-utils): convert to ESM-only and publish v1.7.13
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 1s
Monorepo Pipeline / 🧪 Test (push) Successful in 42s
Monorepo Pipeline / 🧹 Lint (push) Successful in 1m0s
Monorepo Pipeline / 🏗️ Build (push) Failing after 1m47s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-11 01:26:53 +01:00
65fd248993 chore(next-utils): fix generic propagation in createMintelDirectusClient and publish v1.7.12
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 1s
Monorepo Pipeline / 🧹 Lint (push) Successful in 45s
Monorepo Pipeline / 🧪 Test (push) Successful in 54s
Monorepo Pipeline / 🏗️ Build (push) Failing after 1m46s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-11 01:22:12 +01:00
ebd9ab132c chore(next-utils): add explicit return type to validateMintelEnv and publish v1.7.11
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 1s
Monorepo Pipeline / 🧹 Lint (push) Successful in 45s
Monorepo Pipeline / 🧪 Test (push) Successful in 58s
Monorepo Pipeline / 🚀 Release (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been cancelled
Monorepo Pipeline / 🏗️ Build (push) Has been cancelled
2026-02-11 01:20:57 +01:00
ddaeb2c3ca chore(next-utils): rebuild with generic types and publish v1.7.10
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 2s
Monorepo Pipeline / 🧪 Test (push) Successful in 42s
Monorepo Pipeline / 🚀 Release (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been cancelled
Monorepo Pipeline / 🏗️ Build (push) Has been cancelled
Monorepo Pipeline / 🧹 Lint (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been cancelled
2026-02-11 01:20:03 +01:00
ad1a8c4fbf feat(next-utils): support generic schema in directus client
All checks were successful
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 34s
Monorepo Pipeline / 🧪 Test (push) Successful in 43s
Monorepo Pipeline / 🧹 Lint (push) Successful in 1m6s
Monorepo Pipeline / 🏗️ Build (push) Successful in 1m50s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-11 01:00:34 +01:00
013b0259b2 fix(pipeline): use POSIX sh compatible logic for release prioritization
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 1s
Monorepo Pipeline / 🧹 Lint (push) Successful in 44s
Monorepo Pipeline / 🧪 Test (push) Successful in 40s
Monorepo Pipeline / 🏗️ Build (push) Successful in 1m47s
Monorepo Pipeline / 🚀 Release (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been cancelled
2026-02-11 00:33:06 +01:00
d5a9a3bce4 chore: refine release prioritization logic and bump v1.7.8
All checks were successful
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 2s
Monorepo Pipeline / 🧹 Lint (push) Successful in 1m0s
Monorepo Pipeline / 🧪 Test (push) Successful in 39s
Monorepo Pipeline / 🏗️ Build (push) Successful in 2m23s
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Successful in 19s
Monorepo Pipeline / 🚀 Release (push) Successful in 2m42s
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Successful in 2m17s
Monorepo Pipeline / 🐳 Build Production Runtime (push) Successful in 16s
Monorepo Pipeline / 🐳 Build Build-Base (push) Successful in 5m29s
2026-02-11 00:31:03 +01:00
b9fd583ac4 chore: fix pipeline hang by disabling broken caching and using corepack
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 1s
Monorepo Pipeline / 🧹 Lint (push) Successful in 57s
Monorepo Pipeline / 🚀 Release (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been cancelled
Monorepo Pipeline / 🧪 Test (push) Has been cancelled
Monorepo Pipeline / 🏗️ Build (push) Has been cancelled
2026-02-11 00:29:10 +01:00
bfdbaba0d0 chore: implement release prioritization and streamline setup for speed
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 2s
Monorepo Pipeline / 🧹 Lint (push) Successful in 1m2s
Monorepo Pipeline / 🧪 Test (push) Successful in 58s
Monorepo Pipeline / 🚀 Release (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been cancelled
Monorepo Pipeline / 🏗️ Build (push) Has been cancelled
2026-02-11 00:22:31 +01:00
4ea9cbc551 fix(next-utils): use natural type inference for validateMintelEnv to fix unknown type errors
Some checks failed
Monorepo Pipeline / 🚀 Release (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been cancelled
Monorepo Pipeline / 🏗️ Build (push) Has been cancelled
Monorepo Pipeline / 🧹 Lint (push) Has been cancelled
Monorepo Pipeline / 🧪 Test (push) Has been cancelled
Monorepo Pipeline / 📦 Install & Sync (push) Has been cancelled
2026-02-11 00:17:46 +01:00
d8c1a38c0d chore: optimize pipeline for speed and parallelize QA jobs
Some checks failed
Monorepo Pipeline / 🧹 Lint (push) Has been cancelled
Monorepo Pipeline / 🧪 Test (push) Has been cancelled
Monorepo Pipeline / 🏗️ Build (push) Has been cancelled
Monorepo Pipeline / 🚀 Release (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been cancelled
Monorepo Pipeline / 📦 Install & Sync (push) Has been cancelled
2026-02-11 00:14:55 +01:00
b65b9a7fb2 fix(next-utils): finalize type safety for validateMintelEnv and fix pre-push hook
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Failing after 2m7s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-11 00:10:38 +01:00
858c7bbc39 fix(next-utils): use z.extend() for robust type inference in validateMintelEnv
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Successful in 2m33s
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Successful in 33s
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been cancelled
Monorepo Pipeline / 🚀 Release (push) Has been cancelled
2026-02-11 00:04:14 +01:00
149123ef90 fix(next-utils): restore optional argument with robust types to satisfy linter
Some checks failed
Monorepo Pipeline / 🚀 Release (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been cancelled
Monorepo Pipeline / 🧪 Quality Assurance (push) Has been cancelled
2026-02-11 00:02:53 +01:00
6bc49d1c52 fix(next-utils): make validateMintelEnv generic for better type safety
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Failing after 2m58s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-10 23:57:18 +01:00
52ffe49019 feat(next-utils): make directus client environment-aware and standardize base env schema
All checks were successful
Monorepo Pipeline / 🧪 Quality Assurance (push) Successful in 2m10s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-10 23:44:27 +01:00
73fa292528 fix: remove klz from workspace 2026-02-10 21:39:48 +01:00
f2c0a4581c chore: sync versions
All checks were successful
Monorepo Pipeline / 🧪 Quality Assurance (push) Successful in 2m6s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-10 00:39:34 +01:00
367c4d8404 fix: cms schema 2026-02-10 00:35:26 +01:00
587c88980f chore: release next-config v1.7.0
All checks were successful
Monorepo Pipeline / 🧪 Quality Assurance (push) Successful in 2m8s
Monorepo Pipeline / 🚀 Release (push) Successful in 1m51s
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Successful in 17s
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Successful in 2m18s
Monorepo Pipeline / 🐳 Build Production Runtime (push) Successful in 16s
Monorepo Pipeline / 🐳 Build Build-Base (push) Successful in 5m34s
2026-02-10 00:29:02 +01:00
fcdfdb4588 chore: release next-config v1.6.1
All checks were successful
Monorepo Pipeline / 🧪 Quality Assurance (push) Successful in 2m12s
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Successful in 18s
Monorepo Pipeline / 🚀 Release (push) Successful in 2m17s
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Successful in 2m13s
Monorepo Pipeline / 🐳 Build Production Runtime (push) Successful in 17s
Monorepo Pipeline / 🐳 Build Build-Base (push) Successful in 5m27s
2026-02-10 00:27:59 +01:00
6bbaa8d105 chore: cms sync 2026-02-10 00:26:13 +01:00
eccc084441 chore: cms sync commands 2026-02-10 00:13:42 +01:00
da6b8aba64 fix: cms sync
All checks were successful
Monorepo Pipeline / 🧪 Quality Assurance (push) Successful in 2m43s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-10 00:03:27 +01:00
290097b4e6 chore: fix linter
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Has been cancelled
Monorepo Pipeline / 🚀 Release (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been cancelled
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been cancelled
2026-02-10 00:02:26 +01:00
45894cce34 chore: fix linter
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Failing after 57s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-09 23:59:22 +01:00
7195906da0 chore: fix linter
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Failing after 42s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-09 23:54:58 +01:00
dcb466f53b chore: fix husky
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Failing after 1m3s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-09 23:44:34 +01:00
14089766ea feat: infra cms
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Failing after 1m5s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-09 23:33:45 +01:00
6ecabe4a04 chore: sync lock file
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Failing after 9s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-09 23:26:38 +01:00
b205220bde fix: cli compatibility with nextjs 16
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Failing after 14s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-09 23:15:50 +01:00
3d5a802c6e chore: release packages
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Failing after 1m42s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-09 23:05:15 +01:00
b5d1272f85 fix: customer manager
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Failing after 2m15s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-09 23:04:21 +01:00
e152fb8171 fix: sync versions 2026-02-09 22:50:28 +01:00
d7cec1fa0e fix: docker images
All checks were successful
Monorepo Pipeline / 🧪 Quality Assurance (push) Successful in 4m4s
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Successful in 29s
Monorepo Pipeline / 🚀 Release (push) Successful in 3m44s
Monorepo Pipeline / 🐳 Build Build-Base (push) Successful in 4m40s
Monorepo Pipeline / 🐳 Build Production Runtime (push) Successful in 17s
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Successful in 7m15s
2026-02-09 22:37:19 +01:00
67c2af958a fix: docker images
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Successful in 4m1s
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Successful in 33s
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Failing after 21s
Monorepo Pipeline / 🐳 Build Build-Base (push) Failing after 45s
Monorepo Pipeline / 🐳 Build Production Runtime (push) Successful in 27s
Monorepo Pipeline / 🚀 Release (push) Successful in 3m43s
2026-02-09 22:26:16 +01:00
015e295370 fix: pipeline 2026-02-09 22:21:22 +01:00
c9952bfd1d fix: pipeline
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Successful in 6m14s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been cancelled
2026-02-09 22:16:07 +01:00
f9aaf3712e fix: pipeline
Some checks failed
Monorepo Pipeline / 🧪 Quality Assurance (push) Failing after 7m22s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
2026-02-09 22:02:58 +01:00
d2bbfe3b40 fix: pipeline 2026-02-09 21:58:04 +01:00
82 changed files with 11129 additions and 9426 deletions

View File

@@ -1,5 +0,0 @@
---
"@mintel/mail": minor
---
Initial release of the branded email system package.

View File

@@ -0,0 +1,7 @@
---
"@mintel/monorepo": patch
"acquisition-manager": patch
"feedback-commander": patch
---
fix: make directus extension build scripts more resilient

View File

@@ -1,7 +1,7 @@
node_modules node_modules
.next .next
.git .git
.npmrc # .npmrc is allowed as it contains the registry template
dist dist
build build
out out

View File

@@ -1,4 +1,5 @@
# Project # Project
IMAGE_TAG=v1.7.10
PROJECT_NAME=sample-website PROJECT_NAME=sample-website
PROJECT_COLOR=#82ed20 PROJECT_COLOR=#82ed20

View File

@@ -0,0 +1,44 @@
name: 🏥 Server Maintenance
on:
schedule:
- cron: '0 3 * * *' # Every day at 3:00 AM
workflow_dispatch: # Allow manual trigger
jobs:
maintenance:
name: 🧹 Prune & Clean
runs-on: docker
container:
image: catthehacker/ubuntu:act-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: 🚀 Execute Maintenance via SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
# Run the prune script on the host
# We transfer the script and execute it to ensure it matches the repo version
scp packages/infra/scripts/prune-registry.sh root@${{ secrets.SSH_HOST }}:/tmp/prune-registry.sh
ssh root@${{ secrets.SSH_HOST }} "bash /tmp/prune-registry.sh && rm /tmp/prune-registry.sh"
- name: 🔔 Notification - Success
if: success()
run: |
curl -s -k -X POST "${{ secrets.GOTIFY_URL }}/message?token=${{ secrets.GOTIFY_TOKEN }}" \
-F "title=🏥 Maintenance Complete" \
-F "message=Server-Wartung erfolgreich ausgeführt.\nRegistry & Docker Ressourcen bereinigt." \
-F "priority=2" || true
- name: 🔔 Notification - Failure
if: failure()
run: |
curl -s -k -X POST "${{ secrets.GOTIFY_URL }}/message?token=${{ secrets.GOTIFY_TOKEN }}" \
-F "title=❌ Maintenance FAILED" \
-F "message=Die automatische Server-Wartung ist fehlgeschlagen!\nBitte Logs prüfen." \
-F "priority=8" || true

View File

@@ -2,6 +2,8 @@ name: Monorepo Pipeline
on: on:
push: push:
branches:
- '**'
tags: tags:
- 'v*' - 'v*'
@@ -10,43 +12,124 @@ concurrency:
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:
qa: prioritize:
name: 🧪 Quality Assurance name: ⚡ Prioritize Release
runs-on: docker runs-on: docker
container: container:
image: catthehacker/ubuntu:act-latest image: catthehacker/ubuntu:act-latest
steps:
- name: 🛑 Cancel Redundant Runs
env:
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
RUN_ID: ${{ github.run_id }}
REF: ${{ github.ref }}
REF_NAME: ${{ github.ref_name }}
EVENT: ${{ github.event_name }}
run: |
echo "🔎 Debug: Event=$EVENT, Ref=$REF, RefName=$REF_NAME, RunId=$RUN_ID"
case "$REF" in
refs/tags/v*)
echo "🚀 Release detected ($REF_NAME). Cancelling non-tag runs..."
# Fetch all runs
RUNS=$(curl -s -H "Authorization: token $GITEA_TOKEN" "https://git.infra.mintel.me/api/v1/repos/$REPO/actions/runs")
# Identify runs to cancel: in_progress/queued, NOT this run, and NOT a tag run
echo "$RUNS" | jq -c '.workflow_runs[] | select(.status == "in_progress" or .status == "queued") | select(.id | tostring != "'$RUN_ID'")' | while read -r run; do
ID=$(echo "$run" | jq -r '.id')
RUN_REF=$(echo "$run" | jq -r '.ref')
TITLE=$(echo "$run" | jq -r '.display_title')
case "$RUN_REF" in
refs/tags/v*)
echo "⏭️ Skipping parallel release run $ID ($TITLE) on $RUN_REF"
;;
*)
echo "🛑 Cancelling redundant branch run $ID ($TITLE) on $RUN_REF..."
curl -X POST -s -H "Authorization: token $GITEA_TOKEN" "https://git.infra.mintel.me/api/v1/repos/$REPO/actions/runs/$ID/cancel"
;;
esac
done
;;
*)
echo " Regular push. No prioritization needed."
;;
esac
lint:
name: 🧹 Lint
needs: prioritize
if: always() && !cancelled() && (needs.prioritize.result == 'success' || needs.prioritize.result == 'skipped')
runs-on: docker
container:
image: catthehacker/ubuntu:act-latest
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Set up Node.js - name: Set up Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node_version: 20 node_version: 20
cache: 'pnpm' - name: Enable pnpm
run: corepack enable && corepack prepare pnpm@10.2.0 --activate
- name: Install dependencies - name: Install dependencies
run: pnpm install --frozen-lockfile run: pnpm install --frozen-lockfile --prefer-offline --ignore-scripts --no-color
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Lint - name: Lint
run: pnpm lint run: pnpm lint
test:
name: 🧪 Test
needs: prioritize
if: always() && !cancelled() && (needs.prioritize.result == 'success' || needs.prioritize.result == 'skipped')
runs-on: docker
container:
image: catthehacker/ubuntu:act-latest
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node_version: 20
- name: Enable pnpm
run: corepack enable && corepack prepare pnpm@10.2.0 --activate
- name: Install dependencies
run: pnpm install --frozen-lockfile --prefer-offline --ignore-scripts --no-color
- name: Test - name: Test
run: pnpm test run: pnpm test
build:
name: 🏗️ Build
needs: prioritize
if: always() && !cancelled() && (needs.prioritize.result == 'success' || needs.prioritize.result == 'skipped')
runs-on: docker
container:
image: catthehacker/ubuntu:act-latest
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node_version: 20
- name: Enable pnpm
run: corepack enable && corepack prepare pnpm@10.2.0 --activate
- name: Install dependencies
run: pnpm install --frozen-lockfile --prefer-offline --ignore-scripts --no-color
- name: Build - name: Build
run: pnpm build run: pnpm build
release: release:
name: 🚀 Release name: 🚀 Release
needs: qa needs: [lint, test, build]
if: startsWith(github.ref, 'refs/tags/v') if: startsWith(github.ref, 'refs/tags/v')
runs-on: docker runs-on: docker
container: container:
@@ -59,30 +142,24 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Set up Node.js - name: Set up Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node_version: 20 node_version: 20
cache: 'pnpm' - name: Enable pnpm
run: corepack enable && corepack prepare pnpm@10.2.0 --activate
- name: Install dependencies - name: Install dependencies
run: pnpm install --frozen-lockfile run: pnpm install --frozen-lockfile --prefer-offline --ignore-scripts --no-color
- name: 🏷️ Sync Versions (if Tagged)
run: pnpm sync-versions
- name: 🏷️ Release Packages (Tag-Driven) - name: 🏷️ Release Packages (Tag-Driven)
run: | run: |
echo "🏷️ Tag detected [${{ github.ref_name }}], performing sync release..." echo "🏷️ Tag detected [${{ github.ref_name }}], performing sync release..."
pnpm sync-versions
pnpm release:tag pnpm release:tag
build-images: build-images:
name: 🐳 Build ${{ matrix.name }} name: 🐳 Build ${{ matrix.name }}
needs: qa needs: [lint, test, build]
if: startsWith(github.ref, 'refs/tags/v') if: startsWith(github.ref, 'refs/tags/v')
runs-on: docker runs-on: docker
container: container:

16
.husky/pre-push Executable file
View File

@@ -0,0 +1,16 @@
# Check if we are pushing a tag
while read local_ref local_sha remote_ref remote_sha
do
if [[ "$remote_ref" == refs/tags/v* ]]; then
TAG=${remote_ref#refs/tags/}
echo "🏷️ Tag detected: $TAG, syncing versions..."
pnpm sync-versions "$TAG"
# Stage the changed files (excluding ignored files like .env)
git add package.json packages/*/package.json apps/*/package.json .env.example
echo "⚠️ package.json and .env files updated to match tag $TAG."
echo "⚠️ Note: You might need to push again if these changes were not already in your commit/tag."
fi
done

56
Dockerfile.template Normal file
View File

@@ -0,0 +1,56 @@
# Stage 1: Builder
FROM registry.infra.mintel.me/mintel/nextjs:latest AS builder
WORKDIR /app
# Clean the workspace in case the base image is dirty
RUN rm -rf ./*
# Arguments for build-time configuration
ARG NEXT_PUBLIC_BASE_URL
ARG NEXT_PUBLIC_TARGET
ARG DIRECTUS_URL
ARG NPM_TOKEN
# Environment variables for Next.js build
ENV NEXT_PUBLIC_BASE_URL=$NEXT_PUBLIC_BASE_URL
ENV NEXT_PUBLIC_TARGET=$NEXT_PUBLIC_TARGET
ENV DIRECTUS_URL=$DIRECTUS_URL
ENV SKIP_RUNTIME_ENV_VALIDATION=true
ENV CI=true
# Enable pnpm
RUN corepack enable
# Copy lockfile and manifest for dependency installation caching
COPY pnpm-lock.yaml package.json .npmrc* ./
# Install dependencies with cache mount
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
--mount=type=secret,id=NPM_TOKEN \
export NPM_TOKEN=$(cat /run/secrets/NPM_TOKEN 2>/dev/null || echo $NPM_TOKEN) && \
pnpm install --frozen-lockfile
# Copy source code
COPY . .
# Build application
RUN pnpm build
# Stage 2: Runner
FROM registry.infra.mintel.me/mintel/runtime:latest AS runner
WORKDIR /app
ENV HOSTNAME="0.0.0.0"
ENV PORT=3000
ENV NODE_ENV=production
# Copy standalone output and static files
# Adjust paths if using a monorepo structure (e.g., /app/apps/web/public)
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=builder --chown=nextjs:nodejs /app/.next/cache ./.next/cache
USER nextjs
CMD ["node", "server.js"]

View File

@@ -1,3 +0,0 @@
import { nextConfig } from "@mintel/eslint-config/next";
export default nextConfig;

View File

@@ -1,6 +1,6 @@
{ {
"name": "sample-website", "name": "sample-website",
"version": "0.1.1", "version": "1.7.10",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
@@ -8,15 +8,9 @@
"dev:local": "mintel dev --local", "dev:local": "mintel dev --local",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "next lint", "lint": "eslint src/",
"typecheck": "tsc --noEmit", "typecheck": "tsc --noEmit",
"test": "vitest run --passWithNoTests", "test": "vitest run --passWithNoTests",
"cms:bootstrap": "mintel directus bootstrap",
"cms:push:testing": "mintel directus sync push testing",
"cms:pull:testing": "mintel directus sync pull testing",
"cms:push:staging": "mintel directus sync push staging",
"cms:pull:staging": "mintel directus sync pull staging",
"cms:push:prod": "mintel directus sync push production",
"cms:pull:prod": "mintel directus sync pull production", "cms:pull:prod": "mintel directus sync pull production",
"pagespeed:test": "mintel pagespeed" "pagespeed:test": "mintel pagespeed"
}, },
@@ -24,8 +18,8 @@
"@mintel/next-utils": "workspace:*", "@mintel/next-utils": "workspace:*",
"@mintel/observability": "workspace:*", "@mintel/observability": "workspace:*",
"@mintel/next-observability": "workspace:*", "@mintel/next-observability": "workspace:*",
"@sentry/nextjs": "^8.55.0", "@sentry/nextjs": "10.38.0",
"next": "15.1.6", "next": "16.1.6",
"next-intl": "^4.8.2", "next-intl": "^4.8.2",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0" "react-dom": "^19.0.0"

View File

@@ -0,0 +1,19 @@
version: 1
directus: 11.15.1
vendor: postgres
collections: []
fields: []
systemFields:
- collection: directus_activity
field: timestamp
schema:
is_indexed: true
- collection: directus_revisions
field: activity
schema:
is_indexed: true
- collection: directus_revisions
field: parent
schema:
is_indexed: true
relations: []

View File

View File

@@ -1,17 +1,18 @@
services: services:
app: app:
build: build:
context: . context: ./apps/sample-website
dockerfile: Dockerfile dockerfile: Dockerfile
args: args:
NEXT_PUBLIC_BASE_URL: ${NEXT_PUBLIC_BASE_URL:-http://localhost:3000} NEXT_PUBLIC_BASE_URL: ${NEXT_PUBLIC_BASE_URL:-http://localhost:3000}
NEXT_PUBLIC_UMAMI_WEBSITE_ID: ${NEXT_PUBLIC_UMAMI_WEBSITE_ID} NEXT_PUBLIC_UMAMI_WEBSITE_ID: ${NEXT_PUBLIC_UMAMI_WEBSITE_ID}
NEXT_PUBLIC_UMAMI_SCRIPT_URL: ${NEXT_PUBLIC_UMAMI_SCRIPT_URL} NEXT_PUBLIC_UMAMI_SCRIPT_URL: ${NEXT_PUBLIC_UMAMI_SCRIPT_URL}
NEXT_PUBLIC_TARGET: ${TARGET:-development} NEXT_PUBLIC_TARGET: ${TARGET:-development}
DIRECTUS_URL: ${DIRECTUS_URL:-http://directus:8055}
restart: always restart: always
networks: networks:
- infra - infra
environment:
- DIRECTUS_URL=${DIRECTUS_URL:-http://directus:8055}
env_file: env_file:
- .env - .env
ports: ports:
@@ -22,7 +23,7 @@ services:
- "traefik.http.services.sample-website.loadbalancer.server.port=3000" - "traefik.http.services.sample-website.loadbalancer.server.port=3000"
directus: directus:
image: registry.infra.mintel.me/mintel/directus:latest image: registry.infra.mintel.me/mintel/directus:${IMAGE_TAG:-latest}
restart: always restart: always
networks: networks:
- infra - infra
@@ -46,6 +47,7 @@ services:
volumes: volumes:
- ./directus/uploads:/directus/uploads - ./directus/uploads:/directus/uploads
- ./directus/extensions:/directus/extensions - ./directus/extensions:/directus/extensions
- ./directus/schema:/directus/schema
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
- "traefik.http.routers.sample-website-directus.rule=Host(`${DIRECTUS_HOST:-cms.sample-website.localhost}`)" - "traefik.http.routers.sample-website-directus.rule=Host(`${DIRECTUS_HOST:-cms.sample-website.localhost}`)"

View File

@@ -8,6 +8,10 @@ export default [
"packages/customer-manager/index.js", "packages/customer-manager/index.js",
"**/*.db", "**/*.db",
"**/build/**", "**/build/**",
"**/data/**",
"**/reference/**",
"**/dist/**",
"**/.next/**",
], ],
}, },
...baseConfig, ...baseConfig,

View File

@@ -5,11 +5,17 @@
"scripts": { "scripts": {
"build": "pnpm -r build", "build": "pnpm -r build",
"dev": "pnpm -r dev", "dev": "pnpm -r dev",
"lint": "pnpm -r lint", "lint": "pnpm -r --filter='./packages/**' --filter='./apps/**' lint",
"test": "pnpm -r test", "test": "pnpm -r test",
"changeset": "changeset", "changeset": "changeset",
"version-packages": "changeset version", "version-packages": "changeset version",
"sync-versions": "tsx scripts/sync-versions.ts", "sync-versions": "tsx scripts/sync-versions.ts --",
"cms:push:infra": "./scripts/sync-directus.sh push infra",
"cms:pull:infra": "./scripts/sync-directus.sh pull infra",
"cms:schema:snapshot": "./scripts/cms-snapshot.sh",
"cms:schema:apply": "./scripts/cms-apply.sh local",
"cms:schema:apply:infra": "./scripts/cms-apply.sh infra",
"dev:infra": "docker-compose up -d directus directus-db",
"release": "pnpm build && changeset publish", "release": "pnpm build && changeset publish",
"release:tag": "pnpm build && pnpm -r publish --no-git-checks --access public", "release:tag": "pnpm build && pnpm -r publish --no-git-checks --access public",
"prepare": "husky" "prepare": "husky"
@@ -27,7 +33,7 @@
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.2", "@vitejs/plugin-react": "^5.1.2",
"eslint": "^9.39.2", "eslint": "^9.39.2",
"eslint-plugin-next": "^0.0.0", "@next/eslint-plugin-next": "16.1.6",
"eslint-plugin-react": "^7.37.5", "eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-hooks": "^7.0.1",
"happy-dom": "^20.4.0", "happy-dom": "^20.4.0",
@@ -45,5 +51,12 @@
"pino": "^10.3.1", "pino": "^10.3.1",
"pino-pretty": "^13.1.3", "pino-pretty": "^13.1.3",
"require-in-the-middle": "^8.0.1" "require-in-the-middle": "^8.0.1"
},
"version": "1.7.10",
"pnpm": {
"overrides": {
"next": "16.1.6",
"@sentry/nextjs": "10.38.0"
}
} }
} }

View File

@@ -0,0 +1 @@
import{defineModule as e}from"@directus/extensions-sdk";import{defineComponent as t,resolveComponent as n,openBlock as a,createBlock as i,withCtx as r,createElementVNode as o}from"vue";var s=t({__name:"module",setup:e=>(e,t)=>{const s=n("private-view");return a(),i(s,{title:"Acquisition Manager"},{default:r(()=>[...t[0]||(t[0]=[o("div",{class:"acquisition-manager"},[o("h1",null,"Acquisition Manager"),o("p",null,"Modern Industrial Acquisition Management Interface")],-1)])]),_:1})}}),u=[],c=[];!function(e,t){if(e&&"undefined"!=typeof document){var n,a=!0===t.prepend?"prepend":"append",i=!0===t.singleTag,r="string"==typeof t.container?document.querySelector(t.container):document.getElementsByTagName("head")[0];if(i){var o=u.indexOf(r);-1===o&&(o=u.push(r)-1,c[o]={}),n=c[o]&&c[o][a]?c[o][a]:c[o][a]=s()}else n=s();65279===e.charCodeAt(0)&&(e=e.substring(1)),n.styleSheet?n.styleSheet.cssText+=e:n.appendChild(document.createTextNode(e))}function s(){var e=document.createElement("style");if(e.setAttribute("type","text/css"),t.attributes)for(var n=Object.keys(t.attributes),i=0;i<n.length;i++)e.setAttribute(n[i],t.attributes[n[i]]);var o="prepend"===a?"afterbegin":"beforeend";return r.insertAdjacentElement(o,e),e}}("\n.acquisition-manager[data-v-19f4e937] {\n\tpadding: 20px;\n}\n",{});var d=e({id:"acquisition-manager",name:"Acquisition Manager",icon:"account_balance_wallet",routes:[{path:"",component:((e,t)=>{const n=e.__vccOpts||e;for(const[e,a]of t)n[e]=a;return n})(s,[["__scopeId","data-v-19f4e937"],["__file","module.vue"]])}]});export{d as default};

View File

@@ -0,0 +1,30 @@
{
"name": "acquisition-manager",
"description": "Custom High-Fidelity Acquisition Management for Directus",
"icon": "account_balance_wallet",
"version": "1.7.10",
"type": "module",
"keywords": [
"directus",
"directus-extension",
"directus-extension-module"
],
"files": [
"dist"
],
"directus:extension": {
"type": "module",
"path": "index.js",
"source": "src/index.ts",
"host": "*",
"name": "Acquisition Manager"
},
"scripts": {
"build": "directus-extension build && (cp -f dist/index.js index.js 2>/dev/null || true)",
"dev": "directus-extension build -w"
},
"devDependencies": {
"@directus/extensions-sdk": "11.0.2",
"vue": "^3.4.0"
}
}

View File

@@ -0,0 +1,14 @@
import { defineModule } from '@directus/extensions-sdk';
import ModuleComponent from './module.vue';
export default defineModule({
id: 'acquisition-manager',
name: 'Acquisition Manager',
icon: 'account_balance_wallet',
routes: [
{
path: '',
component: ModuleComponent,
},
],
});

View File

@@ -0,0 +1,18 @@
<template>
<private-view title="Acquisition Manager">
<div class="acquisition-manager">
<h1>Acquisition Manager</h1>
<p>Modern Industrial Acquisition Management Interface</p>
</div>
</private-view>
</template>
<script setup lang="ts">
// Logic will be added here
</script>
<style scoped>
.acquisition-manager {
padding: 20px;
}
</style>

View File

@@ -0,0 +1,44 @@
import { build } from 'esbuild';
import { resolve, dirname } from 'path';
import { mkdirSync } from 'fs';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const entryPoint = resolve(__dirname, 'src/index.ts');
const outfile = resolve(__dirname, 'index.js');
try {
mkdirSync(dirname(outfile), { recursive: true });
} catch (e) { }
console.log(`Building from ${entryPoint} to ${outfile}...`);
build({
entryPoints: [entryPoint],
bundle: true,
platform: 'node',
target: 'node18',
outfile: outfile,
format: 'esm',
external: [],
plugins: [{
name: 'mock-jquery',
setup(build) {
build.onResolve({ filter: /^jquery$/ }, args => ({ path: args.path, namespace: 'mock-jquery' }));
build.onLoad({ filter: /.*/, namespace: 'mock-jquery' }, () => ({ contents: 'export default {};', loader: 'js' }));
}
}, {
name: 'mock-canvas',
setup(build) {
build.onResolve({ filter: /^canvas$/ }, args => ({ path: args.path, namespace: 'mock-canvas' }));
build.onLoad({ filter: /.*/, namespace: 'mock-canvas' }, () => ({ contents: 'export default {};', loader: 'js' }));
}
}]
}).then(() => {
console.log("Build succeeded!");
}).catch((e) => {
console.error("Build failed:", e);
process.exit(1);
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
{
"name": "acquisition",
"version": "1.7.10",
"type": "module",
"directus:extension": {
"type": "endpoint",
"path": "index.js",
"source": "src/index.ts",
"host": "^11.0.0"
},
"scripts": {
"build": "node build.js",
"dev": "node build.js --watch"
},
"devDependencies": {
"@directus/extensions-sdk": "11.0.2",
"esbuild": "^0.25.0",
"typescript": "^5.6.3"
},
"dependencies": {
"jquery": "^3.7.1",
"react": "^19.2.4",
"react-dom": "^19.2.4"
}
}

View File

@@ -0,0 +1,5 @@
import { defineEndpoint } from '@directus/extensions-sdk';
export default defineEndpoint((router) => {
router.get('/ping', (req, res) => res.send('pong'));
});

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/cli", "name": "@mintel/cli",
"version": "1.0.1", "version": "1.7.10",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://npm.infra.mintel.me" "registry": "https://npm.infra.mintel.me"
@@ -28,4 +28,4 @@
"@types/prompts": "^2.4.4", "@types/prompts": "^2.4.4",
"@mintel/tsconfig": "workspace:*" "@mintel/tsconfig": "workspace:*"
} }
} }

View File

@@ -231,7 +231,7 @@ program
"pagespeed:test": "mintel pagespeed", "pagespeed:test": "mintel pagespeed",
}, },
dependencies: { dependencies: {
next: "15.1.6", next: "16.1.6",
react: "^19.0.0", react: "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"@mintel/next-utils": "workspace:*", "@mintel/next-utils": "workspace:*",

View File

Binary file not shown.

View File

@@ -0,0 +1 @@
import{defineModule as e}from"@directus/extensions-sdk";import{defineComponent as t,resolveComponent as n,openBlock as a,createBlock as i,withCtx as r,createElementVNode as o}from"vue";var s=t({__name:"module",setup:e=>(e,t)=>{const s=n("private-view");return a(),i(s,{title:"Acquisition Manager"},{default:r(()=>[...t[0]||(t[0]=[o("div",{class:"acquisition-manager"},[o("h1",null,"Acquisition Manager"),o("p",null,"Modern Industrial Acquisition Management Interface")],-1)])]),_:1})}}),u=[],c=[];!function(e,t){if(e&&"undefined"!=typeof document){var n,a=!0===t.prepend?"prepend":"append",i=!0===t.singleTag,r="string"==typeof t.container?document.querySelector(t.container):document.getElementsByTagName("head")[0];if(i){var o=u.indexOf(r);-1===o&&(o=u.push(r)-1,c[o]={}),n=c[o]&&c[o][a]?c[o][a]:c[o][a]=s()}else n=s();65279===e.charCodeAt(0)&&(e=e.substring(1)),n.styleSheet?n.styleSheet.cssText+=e:n.appendChild(document.createTextNode(e))}function s(){var e=document.createElement("style");if(e.setAttribute("type","text/css"),t.attributes)for(var n=Object.keys(t.attributes),i=0;i<n.length;i++)e.setAttribute(n[i],t.attributes[n[i]]);var o="prepend"===a?"afterbegin":"beforeend";return r.insertAdjacentElement(o,e),e}}("\n.acquisition-manager[data-v-19f4e937] {\n\tpadding: 20px;\n}\n",{});var d=e({id:"acquisition-manager",name:"Acquisition Manager",icon:"account_balance_wallet",routes:[{path:"",component:((e,t)=>{const n=e.__vccOpts||e;for(const[e,a]of t)n[e]=a;return n})(s,[["__scopeId","data-v-19f4e937"],["__file","module.vue"]])}]});export{d as default};

View File

@@ -0,0 +1,30 @@
{
"name": "acquisition-manager",
"description": "Custom High-Fidelity Acquisition Management for Directus",
"icon": "account_balance_wallet",
"version": "1.0.0",
"type": "module",
"keywords": [
"directus",
"directus-extension",
"directus-extension-module"
],
"files": [
"dist"
],
"directus:extension": {
"type": "module",
"path": "index.js",
"source": "src/index.ts",
"host": "*",
"name": "Acquisition Manager"
},
"scripts": {
"build": "directus-extension build && (cp -f dist/index.js index.js 2>/dev/null || true)",
"dev": "directus-extension build -w"
},
"devDependencies": {
"@directus/extensions-sdk": "11.0.2",
"vue": "^3.4.0"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
{
"name": "acquisition",
"version": "1.0.0",
"type": "module",
"directus:extension": {
"type": "endpoint",
"path": "index.js",
"source": "src/index.ts",
"host": "^11.0.0"
},
"scripts": {
"build": "node build.js",
"dev": "node build.js --watch"
},
"devDependencies": {
"@directus/extensions-sdk": "11.0.2",
"esbuild": "^0.25.0",
"typescript": "^5.6.3"
},
"dependencies": {
"jquery": "^3.7.1",
"react": "^19.2.4",
"react-dom": "^19.2.4"
}
}

File diff suppressed because one or more lines are too long

View File

@@ -1,29 +1,30 @@
{ {
"name": "customer-manager", "name": "customer-manager",
"description": "Custom High-Fidelity Customer & Company Management for Directus", "description": "Custom High-Fidelity Customer & Company Management for Directus",
"icon": "supervisor_account", "icon": "supervisor_account",
"version": "1.0.0", "version": "1.7.3",
"keywords": [ "type": "module",
"directus", "keywords": [
"directus-extension", "directus",
"directus-extension-module" "directus-extension",
], "directus-extension-module"
"files": [ ],
"dist" "files": [
], "dist"
"directus:extension": { ],
"type": "module", "directus:extension": {
"path": "index.js", "type": "module",
"source": "src/index.ts", "path": "index.js",
"host": "*", "source": "src/index.ts",
"name": "Customer Manager" "host": "*",
}, "name": "Customer Manager"
"scripts": { },
"build": "directus-extension build", "scripts": {
"dev": "directus-extension build -w" "build": "directus-extension build && (cp -f dist/index.js index.js 2>/dev/null || true)",
}, "dev": "directus-extension build -w"
"devDependencies": { },
"@directus/extensions-sdk": "11.0.2", "devDependencies": {
"vue": "^3.4.0" "@directus/extensions-sdk": "11.0.2",
} "vue": "^3.4.0"
}
} }

View File

@@ -1,29 +1,30 @@
{ {
"name": "feedback-commander", "name": "feedback-commander",
"description": "Custom High-Fidelity Feedback Management Extension for Directus", "description": "Custom High-Fidelity Feedback Management Extension for Directus",
"icon": "view_kanban", "icon": "view_kanban",
"version": "1.0.0", "version": "1.7.3",
"keywords": [ "type": "module",
"directus", "keywords": [
"directus-extension", "directus",
"directus-extension-module" "directus-extension",
], "directus-extension-module"
"files": [ ],
"index.js" "files": [
], "dist"
"directus:extension": { ],
"type": "module", "directus:extension": {
"path": "index.js", "type": "module",
"source": "src/index.ts", "path": "index.js",
"host": "*", "source": "src/index.ts",
"name": "Feedback Commander" "host": "*",
}, "name": "Feedback Commander"
"scripts": { },
"build": "directus-extension build", "scripts": {
"dev": "directus-extension build -w" "build": "directus-extension build && (cp -f dist/index.js index.js 2>/dev/null || true)",
}, "dev": "directus-extension build -w"
"devDependencies": { },
"@directus/extensions-sdk": "11.0.2", "devDependencies": {
"vue": "^3.4.0" "@directus/extensions-sdk": "11.0.2",
} "vue": "^3.4.0"
} }
}

View File

@@ -0,0 +1 @@
import{defineModule as e}from"@directus/extensions-sdk";import{defineComponent as t,resolveComponent as n,openBlock as a,createBlock as r,withCtx as o,createElementVNode as p}from"vue";var s=t({__name:"module",setup:e=>(e,t)=>{const s=n("private-view");return a(),r(s,{title:"People Manager"},{default:o(()=>[...t[0]||(t[0]=[p("div",{class:"people-manager"},[p("h1",null,"People Manager"),p("p",null,"Modern Industrial People Management Interface")],-1)])]),_:1})}}),d=[],i=[];!function(e,t){if(e&&"undefined"!=typeof document){var n,a=!0===t.prepend?"prepend":"append",r=!0===t.singleTag,o="string"==typeof t.container?document.querySelector(t.container):document.getElementsByTagName("head")[0];if(r){var p=d.indexOf(o);-1===p&&(p=d.push(o)-1,i[p]={}),n=i[p]&&i[p][a]?i[p][a]:i[p][a]=s()}else n=s();65279===e.charCodeAt(0)&&(e=e.substring(1)),n.styleSheet?n.styleSheet.cssText+=e:n.appendChild(document.createTextNode(e))}function s(){var e=document.createElement("style");if(e.setAttribute("type","text/css"),t.attributes)for(var n=Object.keys(t.attributes),r=0;r<n.length;r++)e.setAttribute(n[r],t.attributes[n[r]]);var p="prepend"===a?"afterbegin":"beforeend";return o.insertAdjacentElement(p,e),e}}("\n.people-manager[data-v-da2952f8] {\n\tpadding: 20px;\n}\n",{});var u=e({id:"people-manager",name:"People Manager",icon:"person",routes:[{path:"",component:((e,t)=>{const n=e.__vccOpts||e;for(const[e,a]of t)n[e]=a;return n})(s,[["__scopeId","data-v-da2952f8"],["__file","module.vue"]])}]});export{u as default};

View File

@@ -0,0 +1,30 @@
{
"name": "people-manager",
"description": "Custom High-Fidelity People Management for Directus",
"icon": "person",
"version": "1.0.0",
"type": "module",
"keywords": [
"directus",
"directus-extension",
"directus-extension-module"
],
"files": [
"dist"
],
"directus:extension": {
"type": "module",
"path": "index.js",
"source": "src/index.ts",
"host": "*",
"name": "People Manager"
},
"scripts": {
"build": "directus-extension build && (cp -f dist/index.js index.js 2>/dev/null || true)",
"dev": "directus-extension build -w"
},
"devDependencies": {
"@directus/extensions-sdk": "11.0.2",
"vue": "^3.4.0"
}
}

View File

@@ -1,11 +1,12 @@
{ {
"name": "@mintel/cms-infra", "name": "@mintel/cms-infra",
"version": "1.0.0", "version": "1.7.10",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
"up": "docker compose up -d", "up": "npm run build:extensions && docker compose up -d",
"down": "docker compose down", "down": "docker compose down",
"logs": "docker compose logs -f" "logs": "docker compose logs -f",
} "build:extensions": "../../scripts/sync-extensions.sh"
} }
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -1,29 +1,30 @@
{ {
"name": "customer-manager", "name": "customer-manager",
"description": "Custom High-Fidelity Customer & Company Management for Directus", "description": "Custom High-Fidelity Customer & Company Management for Directus",
"icon": "supervisor_account", "icon": "supervisor_account",
"version": "1.0.0", "version": "1.7.10",
"keywords": [ "type": "module",
"directus", "keywords": [
"directus-extension", "directus",
"directus-extension-module" "directus-extension",
], "directus-extension-module"
"files": [ ],
"dist" "files": [
], "dist"
"directus:extension": { ],
"type": "module", "directus:extension": {
"path": "index.js", "type": "module",
"source": "src/index.ts", "path": "index.js",
"host": "*", "source": "src/index.ts",
"name": "Customer Manager" "host": "*",
}, "name": "Customer Manager"
"scripts": { },
"build": "directus-extension build", "scripts": {
"dev": "directus-extension build -w" "build": "directus-extension build && (cp -f dist/index.js index.js 2>/dev/null || true)",
}, "dev": "directus-extension build -w"
"devDependencies": { },
"@directus/extensions-sdk": "11.0.2", "devDependencies": {
"vue": "^3.4.0" "@directus/extensions-sdk": "11.0.2",
} "vue": "^3.4.0"
} }
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/eslint-config", "name": "@mintel/eslint-config",
"version": "1.0.1", "version": "1.7.10",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://npm.infra.mintel.me" "registry": "https://npm.infra.mintel.me"
@@ -20,8 +20,8 @@
"dependencies": { "dependencies": {
"@eslint/eslintrc": "^3.0.0", "@eslint/eslintrc": "^3.0.0",
"@eslint/js": "^9.39.2", "@eslint/js": "^9.39.2",
"@next/eslint-plugin-next": "15.1.6", "@next/eslint-plugin-next": "16.1.6",
"eslint-config-next": "15.1.6", "eslint-config-next": "16.1.6",
"eslint-plugin-react": "^7.37.5", "eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-hooks": "^7.0.1",
"typescript-eslint": "^8.54.0" "typescript-eslint": "^8.54.0"

File diff suppressed because one or more lines are too long

View File

@@ -1,29 +1,30 @@
{ {
"name": "@mintel/extension-feedback-commander", "name": "feedback-commander",
"description": "Custom High-Fidelity Feedback Management Extension for Directus", "description": "Custom High-Fidelity Feedback Management Extension for Directus",
"icon": "view_kanban", "icon": "view_kanban",
"version": "1.0.0", "version": "1.7.10",
"keywords": [ "type": "module",
"directus", "keywords": [
"directus-extension", "directus",
"directus-extension-module" "directus-extension",
], "directus-extension-module"
"files": [ ],
"dist" "files": [
], "dist"
"directus:extension": { ],
"type": "module", "directus:extension": {
"path": "dist/index.js", "type": "module",
"source": "src/index.ts", "path": "index.js",
"host": "*", "source": "src/index.ts",
"name": "Feedback Commander" "host": "*",
}, "name": "Feedback Commander"
"scripts": { },
"build": "directus-extension build", "scripts": {
"dev": "directus-extension build -w" "build": "directus-extension build && (cp -f dist/index.js index.js 2>/dev/null || true)",
}, "dev": "directus-extension build -w"
"devDependencies": { },
"@directus/extensions-sdk": "11.0.2", "devDependencies": {
"vue": "^3.4.0" "@directus/extensions-sdk": "11.0.2",
} "vue": "^3.4.0"
} }
}

View File

@@ -1,20 +1,20 @@
{ {
"name": "@mintel/gatekeeper", "name": "@mintel/gatekeeper",
"version": "1.0.0", "version": "1.7.12",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "next lint", "lint": "eslint src/",
"test": "vitest run" "test": "vitest run"
}, },
"dependencies": { "dependencies": {
"@mintel/next-utils": "workspace:*", "@mintel/next-utils": "workspace:*",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"lucide-react": "^0.474.0", "lucide-react": "^0.474.0",
"next": "15.1.7", "next": "16.1.6",
"next-intl": "^4.8.2", "next-intl": "^4.8.2",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
@@ -33,4 +33,4 @@
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.17",
"typescript": "^5.0.0" "typescript": "^5.0.0"
} }
} }

View File

@@ -11,6 +11,8 @@ export async function GET(req: NextRequest) {
// 1. URL Parameter Bypass (for automated tests/staging) // 1. URL Parameter Bypass (for automated tests/staging)
const originalUrl = req.headers.get("x-forwarded-uri") || "/"; const originalUrl = req.headers.get("x-forwarded-uri") || "/";
console.log(`[Verify] Check: ${originalUrl} | Cookie: ${session ? "Found" : "Missing"}`);
const host = const host =
req.headers.get("x-forwarded-host") || req.headers.get("host") || ""; req.headers.get("x-forwarded-host") || req.headers.get("host") || "";
const proto = req.headers.get("x-forwarded-proto") || "https"; const proto = req.headers.get("x-forwarded-proto") || "https";
@@ -44,7 +46,7 @@ export async function GET(req: NextRequest) {
return response; return response;
} }
} catch (e) { } catch (_e) {
// URL parsing failed, proceed with normal logic // URL parsing failed, proceed with normal logic
} }
@@ -54,15 +56,17 @@ export async function GET(req: NextRequest) {
if (session?.value) { if (session?.value) {
if (session.value === password) { if (session.value === password) {
isAuthenticated = true; isAuthenticated = true;
console.log(`[Verify] Legacy password match`);
} else { } else {
try { try {
const payload = JSON.parse(session.value); const payload = JSON.parse(session.value);
if (payload.identity) { if (payload.identity) {
isAuthenticated = true; isAuthenticated = true;
identity = payload.identity; identity = payload.identity;
console.log(`[Verify] Identity verified: ${identity}`);
} }
} catch (e) { } catch (_e) {
// Fallback or old format console.log(`[Verify] JSON Parse failed for cookie: ${session.value.substring(0, 10)}...`);
} }
} }
} }

View File

@@ -1,7 +1,7 @@
import { NextRequest, NextResponse } from "next/server"; import { NextRequest, NextResponse } from "next/server";
import { cookies } from "next/headers"; import { cookies } from "next/headers";
export async function GET(req: NextRequest) { export async function GET(_req: NextRequest) {
const cookieStore = await cookies(); const cookieStore = await cookies();
const authCookieName = const authCookieName =
process.env.AUTH_COOKIE_NAME || "mintel_gatekeeper_session"; process.env.AUTH_COOKIE_NAME || "mintel_gatekeeper_session";
@@ -17,7 +17,7 @@ export async function GET(req: NextRequest) {
const payload = JSON.parse(session.value); const payload = JSON.parse(session.value);
identity = payload.identity || "Guest"; identity = payload.identity || "Guest";
company = payload.company || null; company = payload.company || null;
} catch (e) { } catch (_e) {
// Old format probably just the password // Old format probably just the password
} }

View File

@@ -17,8 +17,8 @@ export default async function LoginPage({ searchParams }: LoginPageProps) {
async function login(formData: FormData) { async function login(formData: FormData) {
"use server"; "use server";
const email = formData.get("email") as string; const email = (formData.get("email") as string || "").trim();
const password = formData.get("password") as string; const password = (formData.get("password") as string || "").trim();
const expectedCode = process.env.GATEKEEPER_PASSWORD || "mintel"; const expectedCode = process.env.GATEKEEPER_PASSWORD || "mintel";
const adminEmail = process.env.DIRECTUS_ADMIN_EMAIL; const adminEmail = process.env.DIRECTUS_ADMIN_EMAIL;
@@ -31,19 +31,19 @@ export default async function LoginPage({ searchParams }: LoginPageProps) {
let userIdentity = ""; let userIdentity = "";
let userCompany: any = null; let userCompany: any = null;
// 1. Check Global Admin (from ENV) // 1. Check Generic Code (Guest) - High Priority to prevent autofill traps
if ( if (password === expectedCode) {
userIdentity = "Guest";
}
// 2. Check Global Admin (from ENV)
else if (
adminEmail && adminEmail &&
adminPassword && adminPassword &&
email === adminEmail && email === adminEmail.trim() &&
password === adminPassword password === adminPassword.trim()
) { ) {
userIdentity = "Admin"; userIdentity = "Admin";
} }
// 2. Check Generic Code (Guest)
else if (!email && password === expectedCode) {
userIdentity = "Guest";
}
// 3. Check Lightweight Client Users (dedicated collection) // 3. Check Lightweight Client Users (dedicated collection)
if (email && password && process.env.INFRA_DIRECTUS_URL) { if (email && password && process.env.INFRA_DIRECTUS_URL) {
try { try {
@@ -116,6 +116,7 @@ export default async function LoginPage({ searchParams }: LoginPageProps) {
} }
if (userIdentity) { if (userIdentity) {
console.log(`[Login] Success: ${userIdentity} | Redirect: ${targetRedirect}`);
const cookieStore = await cookies(); const cookieStore = await cookies();
// Store identity in the cookie (simplified for now, ideally signed) // Store identity in the cookie (simplified for now, ideally signed)
const sessionValue = JSON.stringify({ const sessionValue = JSON.stringify({
@@ -126,6 +127,8 @@ export default async function LoginPage({ searchParams }: LoginPageProps) {
const isDev = process.env.NODE_ENV === "development"; const isDev = process.env.NODE_ENV === "development";
console.log(`[Login] Setting Cookie: ${authCookieName} | Domain: ${cookieDomain || "Default"}`);
cookieStore.set(authCookieName, sessionValue, { cookieStore.set(authCookieName, sessionValue, {
httpOnly: true, httpOnly: true,
secure: !isDev, secure: !isDev,
@@ -136,6 +139,7 @@ export default async function LoginPage({ searchParams }: LoginPageProps) {
}); });
redirect(targetRedirect); redirect(targetRedirect);
} else { } else {
console.log(`[Login] Failed for inputs. Redirecting back with error.`);
redirect(`/login?error=1&redirect=${encodeURIComponent(targetRedirect)}`); redirect(`/login?error=1&redirect=${encodeURIComponent(targetRedirect)}`);
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/husky-config", "name": "@mintel/husky-config",
"version": "1.0.0", "version": "1.7.10",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://npm.infra.mintel.me" "registry": "https://npm.infra.mintel.me"

View File

@@ -19,7 +19,6 @@ COPY packages/cli/package.json ./packages/cli/package.json
COPY packages/observability/package.json ./packages/observability/package.json COPY packages/observability/package.json ./packages/observability/package.json
COPY packages/next-observability/package.json ./packages/next-observability/package.json COPY packages/next-observability/package.json ./packages/next-observability/package.json
COPY packages/husky-config/package.json ./packages/husky-config/package.json COPY packages/husky-config/package.json ./packages/husky-config/package.json
COPY packages/ui/package.json ./packages/ui/package.json
# Use a secret for NPM_TOKEN and a cache mount for the pnpm store # Use a secret for NPM_TOKEN and a cache mount for the pnpm store
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \ RUN --mount=type=cache,id=pnpm,target=/pnpm/store \

View File

@@ -1,19 +1,13 @@
# Step 1: Builder image # Step 1: Base image for Next.js builds
FROM node:20-alpine AS builder FROM node:20-alpine
RUN apk add --no-cache libc6-compat curl RUN apk add --no-cache libc6-compat curl
# Enable pnpm
RUN corepack enable pnpm && \
corepack prepare pnpm@10.2.0 --activate
WORKDIR /app WORKDIR /app
RUN corepack enable pnpm
# Step 2: Install dependencies # Final environment
# We copy everything first because we have a .dockerignore ENV NODE_ENV=production
# and we need the workspace structure for pnpm to work correctly ENV NEXT_TELEMETRY_DISABLED=1
COPY . .
# Use a secret for NPM_TOKEN to authenticate with private registry
RUN --mount=type=cache,target=/root/.local/share/pnpm/store/v3 \
--mount=type=secret,id=NPM_TOKEN \
export NPM_TOKEN=$(cat /run/secrets/NPM_TOKEN) && \
pnpm i --frozen-lockfile
# Step 3: Build shared packages
RUN pnpm --filter "./packages/*" -r build

View File

@@ -1,19 +1,22 @@
FROM node:20-alpine FROM node:20-alpine AS runner
RUN apk add --no-cache libc6-compat curl
# Install essential production utilities WORKDIR /app
RUN apk add --no-cache curl libc6-compat
# Set standard production environment
ENV NODE_ENV=production ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1 ENV NEXT_TELEMETRY_DISABLED=1
ENV PORT=3000 ENV PORT=3000
ENV HOSTNAME="0.0.0.0" ENV HOSTNAME="0.0.0.0"
WORKDIR /app
# Create non-root user for security # Create non-root user for security
RUN addgroup --system --gid 1001 nodejs && \ RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs adduser --system --uid 1001 nextjs
# Expose the default Next.js port # Set correct permissions
RUN chown -R nextjs:nodejs /app
USER nextjs
EXPOSE 3000 EXPOSE 3000
CMD ["node", "server.js"]

View File

@@ -53,7 +53,7 @@ services:
- "traefik.http.services.${PROJECT_NAME}-gatekeeper.loadbalancer.server.port=3000" - "traefik.http.services.${PROJECT_NAME}-gatekeeper.loadbalancer.server.port=3000"
directus: directus:
image: registry.infra.mintel.me/mintel/directus:latest image: registry.infra.mintel.me/mintel/directus:${IMAGE_TAG:-latest}
restart: always restart: always
networks: networks:
- infra - infra

View File

@@ -275,6 +275,10 @@ jobs:
docker system prune -f --filter "until=24h" docker system prune -f --filter "until=24h"
EOF EOF
- name: 🧹 Post-Deploy Cleanup (Runner)
if: always()
run: docker builder prune -f --filter "until=1h"
# ────────────────────────────────────────────────────────────────────────────── # ──────────────────────────────────────────────────────────────────────────────
# JOB 5: Notifications # JOB 5: Notifications
# ────────────────────────────────────────────────────────────────────────────── # ──────────────────────────────────────────────────────────────────────────────

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/infra", "name": "@mintel/infra",
"version": "1.0.1", "version": "1.7.10",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://npm.infra.mintel.me" "registry": "https://npm.infra.mintel.me"

View File

@@ -2,7 +2,7 @@
set -e set -e
# Configuration # Configuration
REGISTRY_DATA="/opt/infra/registry/data/docker/registry/v2" REGISTRY_DATA="/mnt/HC_Volume_104575103/registry-data/docker/registry/v2"
KEEP_TAGS=3 KEEP_TAGS=3
echo "🏥 Starting Aggressive Registry & Docker Maintenance..." echo "🏥 Starting Aggressive Registry & Docker Maintenance..."
@@ -15,31 +15,26 @@ for repo_dir in "$REGISTRY_DATA/repositories/mintel/"*; do
if [ -d "$tags_dir" ]; then if [ -d "$tags_dir" ]; then
echo "🔍 Processing repository: mintel/$repo_name" echo "🔍 Processing repository: mintel/$repo_name"
# Prune main-* tags # Prune various tag patterns
echo " 📦 Pruning main tags..." PATTERNS=("main-*" "testing-*" "branch-*" "v*" "rc*" "[0-9a-f]*")
main_tags=$(ls -dt "$tags_dir"/main-* 2>/dev/null || true)
count=0 for pattern in "${PATTERNS[@]}"; do
for tag_path in $main_tags; do echo " 📦 Pruning $pattern tags..."
((++count)) tags=$(ls -dt "$tags_dir"/${pattern} 2>/dev/null || true)
if [ $count -gt $KEEP_TAGS ]; then count=0
echo " 🗑️ Deleting old main tag: $(basename "$tag_path")" for tag_path in $tags; do
rm -rf "$tag_path" tag_name=$(basename "$tag_path")
fi if [[ "$tag_name" == "latest" ]]; then continue; fi
((++count))
if [ $count -gt $KEEP_TAGS ]; then
echo " 🗑️ Deleting old tag: $tag_name"
rm -rf "$tag_path"
fi
done
done done
# Prune version tags (v* and rc*) # Always prune buildcache
echo " 🏷️ Pruning version tags..."
version_tags=$(ls -dt "$tags_dir"/v1* 2>/dev/null || true)
count=0
for tag_path in $version_tags; do
((++count))
if [ $count -gt $KEEP_TAGS ]; then
echo " 🗑️ Deleting old version tag: $(basename "$tag_path")"
rm -rf "$tag_path"
fi
done
# Always prune buildcache (as it rebuilds quickly)
if [ -d "$tags_dir/buildcache" ]; then if [ -d "$tags_dir/buildcache" ]; then
echo " 🧹 Deleting buildcache tag" echo " 🧹 Deleting buildcache tag"
rm -rf "$tags_dir/buildcache" rm -rf "$tags_dir/buildcache"
@@ -49,7 +44,7 @@ done
# 2. Run Garbage Collection # 2. Run Garbage Collection
echo "♻️ Running Registry Garbage Collection..." echo "♻️ Running Registry Garbage Collection..."
docker exec registry-registry-1 bin/registry garbage-collect /etc/docker/registry/config.yml docker exec registry-registry-1 bin/registry garbage-collect /etc/docker/registry/config.yml --delete-untagged
# 3. Prune Host Docker resources (Shorter window: 24h) # 3. Prune Host Docker resources (Shorter window: 24h)
echo "🧹 Pruning Host Docker resources..." echo "🧹 Pruning Host Docker resources..."

View File

@@ -0,0 +1,90 @@
#!/bin/bash
set -e
# wait-for-upstream.sh
# Usage: ./wait-for-upstream.sh <org/repo> <version_tag> [poll_interval_sec]
REPO=$1
TAG=$2
INTERVAL=${3:-30}
MAX_RETRIES=40 # ~20 minutes default
if [[ -z "$REPO" || -z "$TAG" ]]; then
echo "❌ Error: REPO and TAG are required."
echo "Usage: $0 <org/repo> <version_tag>"
exit 1
fi
if [[ -z "$GITEA_TOKEN" ]]; then
echo "❌ Error: GITEA_TOKEN is not set."
exit 1
fi
GITEA_API="https://git.infra.mintel.me/api/v1"
echo "🔎 Searching for upstream release $TAG in $REPO..."
# 1. Find the run for the specific tag
# We look for runs on the specific ref (refs/tags/vX.Y.Z)
RUN_QUERY=$(curl -s -H "Authorization: token $GITEA_TOKEN" "$GITEA_API/repos/$REPO/actions/runs?ref=refs/tags/$TAG")
# Gitea returns a list of runs. We take the latest one by creation date.
RUN_ID=$(echo "$RUN_QUERY" | jq -r '.workflow_runs | sort_by(.created_at) | last | .id // empty')
if [[ -z "$RUN_ID" || "$RUN_ID" == "null" ]]; then
echo " No recent action run found for tag $TAG in $REPO."
echo "🔎 Checking if tag $TAG exists in the repository..."
TAG_EXISTS=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token $GITEA_TOKEN" "$GITEA_API/repos/$REPO/tags/$TAG")
if [[ "$TAG_EXISTS" == "200" ]]; then
echo "✅ Tag $TAG exists. Assuming it was released successfully in the past."
exit 0
fi
echo "⚠️ Warning: Tag $TAG not found either. Upstream might be lagging or the version is invalid."
echo " Waiting 15s to see if it appears..."
sleep 15
RUN_QUERY=$(curl -s -H "Authorization: token $GITEA_TOKEN" "$GITEA_API/repos/$REPO/actions/runs?ref=refs/tags/$TAG")
RUN_ID=$(echo "$RUN_QUERY" | jq -r '.workflow_runs[0].id // empty')
if [[ -z "$RUN_ID" || "$RUN_ID" == "null" ]]; then
# Final check for tag
TAG_EXISTS=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token $GITEA_TOKEN" "$GITEA_API/repos/$REPO/tags/$TAG")
if [[ "$TAG_EXISTS" == "200" ]]; then
echo "✅ Tag $TAG finally detected. Proceeding."
exit 0
fi
echo "❌ Error: Could not find any action run OR tag for $TAG in $REPO."
exit 1
fi
fi
echo "⏳ Waiting for upstream run $RUN_ID status..."
RETRY_COUNT=0
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
STATUS_QUERY=$(curl -s -H "Authorization: token $GITEA_TOKEN" "$GITEA_API/repos/$REPO/actions/runs/$RUN_ID")
STATUS=$(echo "$STATUS_QUERY" | jq -r '.status')
CONCLUSION=$(echo "$STATUS_QUERY" | jq -r '.conclusion')
echo " - Current Status: $STATUS (Conclusion: $CONCLUSION)"
if [[ "$STATUS" == "success" || "$CONCLUSION" == "success" ]]; then
echo "✅ Upstream release $TAG is READY."
exit 0
fi
if [[ "$STATUS" == "failure" || "$CONCLUSION" == "failure" || "$CONCLUSION" == "cancelled" ]]; then
echo "❌ Error: Upstream release $TAG FAILED or was CANCELLED."
exit 1
fi
echo " - Still working... waiting $INTERVAL seconds (Attempt $((RETRY_COUNT+1))/$MAX_RETRIES)"
sleep $INTERVAL
RETRY_COUNT=$((RETRY_COUNT+1))
done
echo "❌ Error: Timeout waiting for upstream release $TAG."
exit 1

View File

@@ -0,0 +1,7 @@
# @mintel/mail
## 1.7.0
### Minor Changes
- 96ec2c7: Initial release of the branded email system package.

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/mail", "name": "@mintel/mail",
"version": "1.2.0", "version": "1.7.10",
"private": false, "private": false,
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
@@ -38,6 +38,7 @@
"@mintel/tsconfig": "workspace:*", "@mintel/tsconfig": "workspace:*",
"@types/react": "^19.0.0", "@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0", "@types/react-dom": "^19.0.0",
"prettier": "^3.8.1",
"tsup": "^8.3.5", "tsup": "^8.3.5",
"typescript": "^5.0.0", "typescript": "^5.0.0",
"vitest": "^3.0.4" "vitest": "^3.0.4"

View File

@@ -0,0 +1,23 @@
import { defineConfig } from "vitest/config";
import path from "path";
export default defineConfig({
test: {
environment: "node",
alias: {
"prettier/plugins/html": path.resolve(
process.cwd(),
"../../node_modules/prettier/plugins/html.js",
),
"prettier/parser-html": path.resolve(
process.cwd(),
"../../node_modules/prettier/plugins/html.js",
),
},
server: {
deps: {
inline: [/@react-email/],
},
},
},
});

View File

@@ -1,5 +1,17 @@
# @mintel/next-config # @mintel/next-config
## 1.6.1
### Patch Changes
- Add `turbopack: {}` to support Next.js 16 default Turbopack behavior when a webpack config is present.
## 1.6.1
### Patch Changes
- Add `turbopack: {}` to support Next.js 16 default Turbopack behavior when a webpack config is present.
## 1.0.1 ## 1.0.1
### Patch Changes ### Patch Changes

View File

@@ -7,6 +7,7 @@ import path from "node:path";
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
export const baseNextConfig = { export const baseNextConfig = {
output: "standalone", output: "standalone",
turbopack: {},
images: { images: {
dangerouslyAllowSVG: true, dangerouslyAllowSVG: true,
contentDispositionType: "attachment", contentDispositionType: "attachment",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/next-config", "name": "@mintel/next-config",
"version": "1.0.1", "version": "1.7.10",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://npm.infra.mintel.me" "registry": "https://npm.infra.mintel.me"
@@ -16,6 +16,7 @@
}, },
"dependencies": { "dependencies": {
"next-intl": "^4.8.2", "next-intl": "^4.8.2",
"@sentry/nextjs": "^8.0.0" "@sentry/nextjs": "^10.38.0",
"next": "16.1.6"
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/next-feedback", "name": "@mintel/next-feedback",
"version": "1.0.0", "version": "1.7.10",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://npm.infra.mintel.me" "registry": "https://npm.infra.mintel.me"
@@ -23,8 +23,7 @@
"scripts": { "scripts": {
"build": "tsup", "build": "tsup",
"dev": "tsup --watch", "dev": "tsup --watch",
"lint": "eslint src/", "lint": "eslint src/"
"test": "vitest run"
}, },
"dependencies": { "dependencies": {
"@directus/sdk": "^21.0.0", "@directus/sdk": "^21.0.0",
@@ -32,7 +31,7 @@
"framer-motion": "^11.5.4", "framer-motion": "^11.5.4",
"html2canvas": "^1.4.1", "html2canvas": "^1.4.1",
"lucide-react": "^0.441.0", "lucide-react": "^0.441.0",
"next": "15.1.7", "next": "16.1.6",
"tailwind-merge": "^2.5.2" "tailwind-merge": "^2.5.2"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/next-observability", "name": "@mintel/next-observability",
"version": "1.0.0", "version": "1.7.10",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://npm.infra.mintel.me" "registry": "https://npm.infra.mintel.me"
@@ -28,8 +28,8 @@
}, },
"dependencies": { "dependencies": {
"@mintel/observability": "workspace:*", "@mintel/observability": "workspace:*",
"@sentry/nextjs": "^8.55.0", "@sentry/nextjs": "^10.38.0",
"next": "15.1.7" "next": "16.1.6"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0",

View File

@@ -1,22 +1,22 @@
{ {
"name": "@mintel/next-utils", "name": "@mintel/next-utils",
"version": "1.0.1", "version": "1.7.10",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://npm.infra.mintel.me" "registry": "https://npm.infra.mintel.me"
}, },
"type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
"scripts": { "scripts": {
"build": "tsup src/index.ts --format cjs,esm --dts", "build": "tsup src/index.ts --format esm --dts --clean",
"dev": "tsup src/index.ts --format cjs,esm --watch --dts", "dev": "tsup src/index.ts --format esm --watch --dts",
"lint": "eslint src/", "lint": "eslint src/",
"test": "vitest run" "test": "vitest run"
}, },
"dependencies": { "dependencies": {
"@directus/sdk": "^21.0.0", "@directus/sdk": "^21.0.0",
"next": "15.1.7", "next": "16.1.6",
"next-intl": "^4.8.2", "next-intl": "^4.8.2",
"zod": "^3.0.0" "zod": "^3.0.0"
}, },

View File

@@ -7,18 +7,43 @@ import {
AuthenticationClient, AuthenticationClient,
} from "@directus/sdk"; } from "@directus/sdk";
export type MintelDirectusClient = DirectusClient<any> & export type MintelDirectusClient<Schema extends object = any> =
RestClient<any> & DirectusClient<Schema> & RestClient<Schema> & AuthenticationClient<Schema>;
AuthenticationClient<any>;
/** /**
* Creates a Directus client configured with Mintel standards * Creates a Directus client configured with Mintel standards.
* Automatically handles internal vs. external URLs based on environment.
*/ */
export function createMintelDirectusClient(url?: string): MintelDirectusClient { export function createMintelDirectusClient<Schema extends object = any>(
const directusUrl = url?: string,
url || process.env.DIRECTUS_URL || "http://localhost:8055"; ): MintelDirectusClient<Schema> {
const isServer = typeof window === "undefined";
return createDirectus(directusUrl).with(rest()).with(authentication()); // 1. If an explicit URL is provided, use it.
if (url) {
return createDirectus<Schema>(url).with(rest()).with(authentication());
}
// 2. On server: Prioritize INTERNAL_DIRECTUS_URL, fallback to DIRECTUS_URL
if (isServer) {
const directusUrl =
process.env.INTERNAL_DIRECTUS_URL ||
process.env.DIRECTUS_URL ||
"http://localhost:8055";
return createDirectus<Schema>(directusUrl)
.with(rest())
.with(authentication());
}
// 3. In browser: Use a proxy path if we are on a different origin,
// or use the current origin if no DIRECTUS_URL is set.
const proxyPath = "/api/directus"; // Standard Mintel proxy path
const browserUrl =
typeof window !== "undefined"
? `${window.location.origin}${proxyPath}`
: proxyPath;
return createDirectus<Schema>(browserUrl).with(rest()).with(authentication());
} }
/** /**

View File

@@ -4,10 +4,17 @@ export const mintelEnvSchema = {
NODE_ENV: z NODE_ENV: z
.enum(["development", "production", "test"]) .enum(["development", "production", "test"])
.default("development"), .default("development"),
NEXT_PUBLIC_BASE_URL: z.string().url(), NEXT_PUBLIC_BASE_URL: z.string().url().optional(),
NEXT_PUBLIC_TARGET: z
.enum(["development", "testing", "staging", "production"])
.optional(),
TARGET: z
.enum(["development", "testing", "staging", "production"])
.optional(),
// Analytics (Proxy Pattern) // Analytics (Proxy Pattern)
UMAMI_WEBSITE_ID: z.string().optional(), UMAMI_WEBSITE_ID: z.string().optional(),
NEXT_PUBLIC_UMAMI_WEBSITE_ID: z.string().optional(),
UMAMI_API_ENDPOINT: z UMAMI_API_ENDPOINT: z
.string() .string()
.url() .url()
@@ -23,6 +30,8 @@ export const mintelEnvSchema = {
LOG_LEVEL: z LOG_LEVEL: z
.enum(["trace", "debug", "info", "warn", "error", "fatal"]) .enum(["trace", "debug", "info", "warn", "error", "fatal"])
.default("info"), .default("info"),
// Mail
MAIL_HOST: z.string().optional(), MAIL_HOST: z.string().optional(),
MAIL_PORT: z.coerce.number().default(587), MAIL_PORT: z.coerce.number().default(587),
MAIL_USERNAME: z.string().optional(), MAIL_USERNAME: z.string().optional(),
@@ -32,17 +41,60 @@ export const mintelEnvSchema = {
(val) => (typeof val === "string" ? val.split(",").filter(Boolean) : val), (val) => (typeof val === "string" ? val.split(",").filter(Boolean) : val),
z.array(z.string()).default([]), z.array(z.string()).default([]),
), ),
// Directus
DIRECTUS_URL: z.string().url().default("http://localhost:8055"),
DIRECTUS_ADMIN_EMAIL: z.string().optional(),
DIRECTUS_ADMIN_PASSWORD: z.string().optional(),
DIRECTUS_API_TOKEN: z.string().optional(),
INTERNAL_DIRECTUS_URL: z.string().url().optional(),
}; };
export function validateMintelEnv(schemaExtension = {}) { /**
const fullSchema = z.object({ * Standard Mintel refinements for environment variables.
...mintelEnvSchema, * Enforces mandatory requirements for non-development environments.
...schemaExtension, */
export const withMintelRefinements = <T extends z.ZodTypeAny>(schema: T) => {
return schema.superRefine((data: any, ctx) => {
const skipValidation =
process.env.SKIP_ENV_VALIDATION === "true" ||
process.env.SKIP_RUNTIME_ENV_VALIDATION === "true";
if (skipValidation) return;
const target = data.TARGET || data.NEXT_PUBLIC_TARGET || "development";
// Strict validation for non-development environments
if (target !== "development") {
if (!data.MAIL_HOST) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "MAIL_HOST is required in non-development environments",
path: ["MAIL_HOST"],
});
}
}
}); });
};
export type MintelEnv<T extends z.ZodRawShape = Record<string, never>> =
z.infer<
ReturnType<
typeof withMintelRefinements<z.ZodObject<typeof mintelEnvSchema & T>>
>
>;
export function validateMintelEnv<
T extends z.ZodRawShape = Record<string, never>,
>(schemaExtension: T = {} as T): MintelEnv<T> {
const fullSchema = withMintelRefinements(
z.object(mintelEnvSchema).extend(schemaExtension),
);
const isBuildTime = const isBuildTime =
process.env.NEXT_PHASE === "phase-production-build" || process.env.NEXT_PHASE === "phase-production-build" ||
process.env.SKIP_ENV_VALIDATION === "true"; process.env.SKIP_ENV_VALIDATION === "true" ||
process.env.SKIP_RUNTIME_ENV_VALIDATION === "true";
const result = fullSchema.safeParse(process.env); const result = fullSchema.safeParse(process.env);
@@ -51,7 +103,7 @@ export function validateMintelEnv(schemaExtension = {}) {
console.warn( console.warn(
"⚠️ Some environment variables are missing during build, but skipping strict validation.", "⚠️ Some environment variables are missing during build, but skipping strict validation.",
); );
// Return partial data to allow build to continue // Return process.env casted to the full schema type to unblock builds
return process.env as unknown as z.infer<typeof fullSchema>; return process.env as unknown as z.infer<typeof fullSchema>;
} }
@@ -62,5 +114,5 @@ export function validateMintelEnv(schemaExtension = {}) {
throw new Error("Invalid environment variables"); throw new Error("Invalid environment variables");
} }
return result.data; return result.data as MintelEnv<T>;
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/observability", "name": "@mintel/observability",
"version": "1.0.0", "version": "1.7.10",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://npm.infra.mintel.me" "registry": "https://npm.infra.mintel.me"

View File

@@ -0,0 +1 @@
import{defineModule as e}from"@directus/extensions-sdk";import{defineComponent as t,resolveComponent as n,openBlock as a,createBlock as r,withCtx as o,createElementVNode as p}from"vue";var s=t({__name:"module",setup:e=>(e,t)=>{const s=n("private-view");return a(),r(s,{title:"People Manager"},{default:o(()=>[...t[0]||(t[0]=[p("div",{class:"people-manager"},[p("h1",null,"People Manager"),p("p",null,"Modern Industrial People Management Interface")],-1)])]),_:1})}}),d=[],i=[];!function(e,t){if(e&&"undefined"!=typeof document){var n,a=!0===t.prepend?"prepend":"append",r=!0===t.singleTag,o="string"==typeof t.container?document.querySelector(t.container):document.getElementsByTagName("head")[0];if(r){var p=d.indexOf(o);-1===p&&(p=d.push(o)-1,i[p]={}),n=i[p]&&i[p][a]?i[p][a]:i[p][a]=s()}else n=s();65279===e.charCodeAt(0)&&(e=e.substring(1)),n.styleSheet?n.styleSheet.cssText+=e:n.appendChild(document.createTextNode(e))}function s(){var e=document.createElement("style");if(e.setAttribute("type","text/css"),t.attributes)for(var n=Object.keys(t.attributes),r=0;r<n.length;r++)e.setAttribute(n[r],t.attributes[n[r]]);var p="prepend"===a?"afterbegin":"beforeend";return o.insertAdjacentElement(p,e),e}}("\n.people-manager[data-v-da2952f8] {\n\tpadding: 20px;\n}\n",{});var u=e({id:"people-manager",name:"People Manager",icon:"person",routes:[{path:"",component:((e,t)=>{const n=e.__vccOpts||e;for(const[e,a]of t)n[e]=a;return n})(s,[["__scopeId","data-v-da2952f8"],["__file","module.vue"]])}]});export{u as default};

View File

@@ -0,0 +1,30 @@
{
"name": "people-manager",
"description": "Custom High-Fidelity People Management for Directus",
"icon": "person",
"version": "1.7.10",
"type": "module",
"keywords": [
"directus",
"directus-extension",
"directus-extension-module"
],
"files": [
"dist"
],
"directus:extension": {
"type": "module",
"path": "index.js",
"source": "src/index.ts",
"host": "*",
"name": "People Manager"
},
"scripts": {
"build": "directus-extension build && (cp -f dist/index.js index.js 2>/dev/null || true)",
"dev": "directus-extension build -w"
},
"devDependencies": {
"@directus/extensions-sdk": "11.0.2",
"vue": "^3.4.0"
}
}

View File

@@ -0,0 +1,14 @@
import { defineModule } from '@directus/extensions-sdk';
import ModuleComponent from './module.vue';
export default defineModule({
id: 'people-manager',
name: 'People Manager',
icon: 'person',
routes: [
{
path: '',
component: ModuleComponent,
},
],
});

View File

@@ -0,0 +1,18 @@
<template>
<private-view title="People Manager">
<div class="people-manager">
<h1>People Manager</h1>
<p>Modern Industrial People Management Interface</p>
</div>
</private-view>
</template>
<script setup lang="ts">
// Logic will be added here
</script>
<style scoped>
.people-manager {
padding: 20px;
}
</style>

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/tsconfig", "name": "@mintel/tsconfig",
"version": "1.0.1", "version": "1.7.10",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://npm.infra.mintel.me" "registry": "https://npm.infra.mintel.me"

8169
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,3 @@
packages: packages:
- 'packages/*' - 'packages/*'
- 'apps/*' - 'apps/*'
- '../klz-2026'

71
scripts/cms-apply.sh Executable file
View File

@@ -0,0 +1,71 @@
#!/bin/bash
# Configuration
PROJECT="infra-cms"
LOCAL_SCHEMA_PATH="./packages/cms-infra/schema/snapshot.yaml"
REMOTE_HOST="root@infra.mintel.me"
REMOTE_DIR="/opt/infra/directus"
ENV=$1
if [ -z "$ENV" ]; then
echo "Usage: ./scripts/cms-apply.sh [local|infra]"
exit 1
fi
case $ENV in
local)
PROJECT="infra-cms"
CMD_PREFIX="docker-compose -f packages/cms-infra/docker-compose.yml"
LOCAL_CONTAINER=$($CMD_PREFIX ps -q $PROJECT)
if [ -z "$LOCAL_CONTAINER" ]; then
echo "❌ Local $PROJECT container not found. Is it running?"
exit 1
fi
echo "🚀 Applying schema to LOCAL $PROJECT..."
docker exec "$LOCAL_CONTAINER" npx directus schema apply -y /directus/schema/snapshot.yaml
;;
infra)
# 'infra' is the remote production server for at-mintel
PROJECT="directus" # Remote project name
echo "🔍 Detecting remote container..."
REMOTE_CONTAINER=$(ssh "$REMOTE_HOST" "docker ps --filter label=com.docker.compose.project=$PROJECT --filter label=com.docker.compose.service=directus -q")
if [ -z "$REMOTE_CONTAINER" ]; then
# Fallback to older name if labels fail
REMOTE_CONTAINER=$(ssh "$REMOTE_HOST" "docker ps -f name=directus-directus-1 -q")
fi
if [ -z "$REMOTE_CONTAINER" ]; then
echo "❌ Remote container for $ENV not found."
exit 1
fi
echo "📦 Syncing extensions to REMOTE $ENV..."
# Ensure remote directory exists
ssh "$REMOTE_HOST" "mkdir -p $REMOTE_DIR/extensions"
rsync -avz --delete ./packages/cms-infra/extensions/ "$REMOTE_HOST:$REMOTE_DIR/extensions/"
echo "📤 Injecting snapshot directly into container $REMOTE_CONTAINER..."
# Inject file via stdin to avoid needing a host-side mount or scp path matching
ssh "$REMOTE_HOST" "docker exec -i $REMOTE_CONTAINER sh -c 'cat > /tmp/snapshot.yaml'" < "$LOCAL_SCHEMA_PATH"
echo "🚀 Applying schema to REMOTE $ENV..."
ssh "$REMOTE_HOST" "docker exec $REMOTE_CONTAINER npx directus schema apply -y /tmp/snapshot.yaml"
echo "🔄 Restarting remote Directus to clear cache..."
ssh "$REMOTE_HOST" "cd $REMOTE_DIR && docker compose restart directus"
# Cleanup
ssh "$REMOTE_HOST" "docker exec $REMOTE_CONTAINER rm /tmp/snapshot.yaml"
;;
*)
echo "❌ Invalid environment: $ENV. Supported: local, infra."
exit 1
;;
esac
echo "✨ Schema apply complete!"

23
scripts/cms-snapshot.sh Executable file
View File

@@ -0,0 +1,23 @@
#!/bin/bash
# Configuration
PROJECT="infra-cms"
SCHEMA_PATH="./packages/cms-infra/schema/snapshot.yaml"
CMD_PREFIX="docker-compose -f packages/cms-infra/docker-compose.yml"
# Detect local container
LOCAL_CONTAINER=$($CMD_PREFIX ps -q $PROJECT)
if [ -z "$LOCAL_CONTAINER" ]; then
echo "❌ Local $PROJECT container not found. Is it running?"
exit 1
fi
echo "📸 Creating schema snapshot for local $PROJECT..."
# Note: we save it to the mounted volume path inside the container
docker exec "$LOCAL_CONTAINER" npx directus schema snapshot -y /directus/schema/snapshot.yaml
echo "🛠️ Repairing snapshot for Postgres compatibility..."
python3 ./scripts/fix_snapshot_v3.py
echo "✅ Snapshot saved and repaired at $SCHEMA_PATH"

View File

@@ -0,0 +1,96 @@
import sys
import os
path = '/Users/marcmintel/Projects/at-mintel/packages/cms-infra/schema/snapshot.yaml'
if not os.path.exists(path):
print(f"File not found: {path}")
sys.exit(1)
with open(path, 'r') as f:
lines = f.readlines()
new_lines = []
current_collection = None
current_field = None
in_schema = False
fix_fields = {'id', 'company', 'user_created', 'user_updated', 'screenshot', 'logo', 'feedback_id'}
uuid_fields = {'id', 'company', 'user_created', 'user_updated'}
# For multi-pass logic
snapshot_has_feedback_id = False
for line in lines:
stripped = line.strip()
if stripped.startswith('- collection:'):
current_collection = stripped.split(':')[-1].strip()
in_schema = False
elif stripped.startswith('field:'):
current_field = stripped.split(':')[-1].strip()
if current_collection == 'visual_feedback_comments' and current_field == 'feedback_id':
snapshot_has_feedback_id = True
elif stripped == 'schema:':
in_schema = True
elif stripped == 'meta:' or stripped.startswith('- collection:') or (not line.startswith(' ') and line.strip() and not line.startswith('-')):
in_schema = False
# Top-level field type
if not in_schema and stripped.startswith('type:') and current_field in uuid_fields:
line = line.replace('type: string', 'type: uuid')
# Schema data type
if in_schema and current_field in fix_fields:
if 'data_type: char' in line or 'data_type: varchar' in line:
line = line.replace('data_type: char', 'data_type: uuid').replace('data_type: varchar', 'data_type: uuid')
if 'max_length:' in line:
line = ' max_length: null\n'
new_lines.append(line)
# Handle Missing feedback_id Injection
if not snapshot_has_feedback_id:
# We find systemFields and inject before it
injected = False
final_lines = []
feedback_id_block = """ - collection: visual_feedback_comments
field: feedback_id
type: integer
meta:
collection: visual_feedback_comments
field: feedback_id
interface: select-dropdown-m2o
required: true
sort: 4
width: full
schema:
name: feedback_id
table: visual_feedback_comments
data_type: integer
is_nullable: false
is_indexed: true
foreign_key_table: visual_feedback
foreign_key_column: id
"""
for line in new_lines:
if 'systemFields:' in line and not injected:
final_lines.append(feedback_id_block)
injected = True
final_lines.append(line)
new_lines = final_lines
# Second pass for primary key nullability
final_lines = []
for i in range(len(new_lines)):
line = new_lines[i]
if 'is_primary_key: true' in line:
# Search backwards and forwards
for j in range(max(0, i-10), min(len(new_lines), i+10)):
if 'is_nullable: true' in new_lines[j]:
new_lines[j] = new_lines[j].replace('is_nullable: true', 'is_nullable: false')
final_lines.append(line)
with open(path, 'w') as f:
f.writelines(new_lines)
print("SUCCESS: Full normalization and field injection complete.")

123
scripts/sync-directus.sh Executable file
View File

@@ -0,0 +1,123 @@
#!/bin/bash
# Configuration
REMOTE_HOST="root@infra.mintel.me"
REMOTE_DIR="/opt/infra/directus"
# DB Details (matching docker-compose defaults)
DB_USER="directus"
DB_NAME="directus"
ACTION=$1
ENV=$2
# Help
if [ -z "$ACTION" ] || [ -z "$ENV" ]; then
echo "Usage: ./scripts/sync-directus.sh [push|pull] [infra|testing|staging|production]"
echo ""
echo "Commands:"
echo " push Sync LOCAL data -> REMOTE"
echo " pull Sync REMOTE data -> LOCAL"
echo ""
echo "Environments:"
echo " infra (infra.mintel.me)"
exit 1
fi
# Map Environment
case $ENV in
infra)
PROJECT_NAME="directus"
;;
*)
echo "❌ Invalid environment: $ENV. Only 'infra' is currently configured for monorepo sync."
exit 1
;;
esac
# Detect local containers
echo "🔍 Detecting local database..."
LOCAL_DB_CONTAINER=$(docker compose ps -q directus-db)
if [ -z "$LOCAL_DB_CONTAINER" ]; then
echo "❌ Local directus-db container not found. Is it running? (npm run dev)"
exit 1
fi
if [ "$ACTION" == "push" ]; then
echo "🚀 Pushing Local Data to $ENV..."
# 1. DB Dump
echo "📦 Dumping local database..."
docker exec "$LOCAL_DB_CONTAINER" pg_dump -U "$DB_USER" --clean --if-exists --no-owner --no-privileges "$DB_NAME" > dump.sql
# 2. Upload Dump
echo "📤 Uploading dump to remote server..."
scp dump.sql "$REMOTE_HOST:$REMOTE_DIR/dump.sql"
# 3. Restore on Remote
echo "🔄 Restoring dump on $ENV..."
REMOTE_DB_CONTAINER=$(ssh "$REMOTE_HOST" "cd $REMOTE_DIR && docker compose -p $PROJECT_NAME ps -q directus-postgres")
if [ -z "$REMOTE_DB_CONTAINER" ]; then
echo "❌ Remote $ENV-db container not found!"
exit 1
fi
# Wipe remote DB clean before restore to avoid constraint errors
echo "🧹 Wiping remote database schema..."
ssh "$REMOTE_HOST" "docker exec $REMOTE_DB_CONTAINER psql -U $DB_USER $DB_NAME -c 'DROP SCHEMA public CASCADE; CREATE SCHEMA public;'"
echo "⚡ Restoring database..."
ssh "$REMOTE_HOST" "docker exec -i $REMOTE_DB_CONTAINER psql -U $DB_USER $DB_NAME < $REMOTE_DIR/dump.sql"
# 4. Sync Uploads
echo "📁 Syncing uploads (Local -> $ENV)..."
rsync -avz --progress ./directus/uploads/ "$REMOTE_HOST:$REMOTE_DIR/uploads/"
# Clean up
rm dump.sql
ssh "$REMOTE_HOST" "rm $REMOTE_DIR/dump.sql"
# 5. Restart Directus to trigger migrations and refresh schema cache
echo "🔄 Restarting remote Directus to apply migrations..."
ssh "$REMOTE_HOST" "cd $REMOTE_DIR && docker compose -p $PROJECT_NAME restart directus"
echo "✨ Push to $ENV complete!"
elif [ "$ACTION" == "pull" ]; then
echo "📥 Pulling $ENV Data to Local..."
# 1. DB Dump on Remote
echo "📦 Dumping remote database ($ENV)..."
REMOTE_DB_CONTAINER=$(ssh "$REMOTE_HOST" "cd $REMOTE_DIR && docker compose -p $PROJECT_NAME ps -q directus-postgres")
if [ -z "$REMOTE_DB_CONTAINER" ]; then
echo "❌ Remote $ENV-db container not found!"
exit 1
fi
ssh "$REMOTE_HOST" "docker exec $REMOTE_DB_CONTAINER pg_dump -U $DB_USER --clean --if-exists --no-owner --no-privileges $DB_NAME > $REMOTE_DIR/dump.sql"
# 2. Download Dump
echo "📥 Downloading dump..."
scp "$REMOTE_HOST:$REMOTE_DIR/dump.sql" dump.sql
# Wipe local DB clean before restore to avoid constraint errors
echo "🧹 Wiping local database schema..."
docker exec "$LOCAL_DB_CONTAINER" psql -U "$DB_USER" "$DB_NAME" -c 'DROP SCHEMA public CASCADE; CREATE SCHEMA public;'
echo "⚡ Restoring database locally..."
docker exec -i "$LOCAL_DB_CONTAINER" psql -U "$DB_USER" "$DB_NAME" < dump.sql
# 4. Sync Uploads
echo "📁 Syncing uploads ($ENV -> Local)..."
rsync -avz --progress "$REMOTE_HOST:$REMOTE_DIR/uploads/" ./directus/uploads/
# Clean up
rm dump.sql
ssh "$REMOTE_HOST" "rm $REMOTE_DIR/dump.sql"
echo "✨ Pull to Local complete!"
else
echo "Invalid action: $ACTION. Use push or pull."
exit 1
fi

69
scripts/sync-extensions.sh Executable file
View File

@@ -0,0 +1,69 @@
#!/bin/bash
# Configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
EXTENSIONS_ROOT="$REPO_ROOT/packages"
TARGET_DIR="$REPO_ROOT/packages/cms-infra/extensions"
# List of extensions to sync - including modules and endpoints
EXTENSIONS=(
"acquisition"
"acquisition-manager"
"customer-manager"
"feedback-commander"
"people-manager"
)
echo "🚀 Starting extension sync..."
# Ensure target directory exists
mkdir -p "$TARGET_DIR"
for EXT in "${EXTENSIONS[@]}"; do
EXT_PATH="$EXTENSIONS_ROOT/$EXT"
if [ -d "$EXT_PATH" ]; then
echo "📦 Building $EXT..."
# Build the extension
# We use --if-present to avoid errors if build script is missing
(cd "$EXT_PATH" && pnpm build)
# Create target directory for this extension
# Directus expects extensions to be in subdirectories matching their name
mkdir -p "$TARGET_DIR/$EXT"
echo "🚚 Syncing $EXT to $TARGET_DIR/$EXT..."
# Clean target first to avoid ghost files
rm -rf "${TARGET_DIR:?}/$EXT"/*
# Copy build artifacts and package metadata
# Some extensions have index.js in root after build, some use dist/
# We check for index.js and package.json
if [ -f "$EXT_PATH/index.js" ]; then
cp "$EXT_PATH/index.js" "$TARGET_DIR/$EXT/"
fi
if [ -f "$EXT_PATH/package.json" ]; then
cp "$EXT_PATH/package.json" "$TARGET_DIR/$EXT/"
fi
if [ -d "$EXT_PATH/dist" ]; then
cp -r "$EXT_PATH/dist" "$TARGET_DIR/$EXT/"
fi
# Sync node_modules if they exist (sometimes needed if not everything is bundled)
if [ -d "$EXT_PATH/node_modules" ]; then
echo "📚 Syncing node_modules for $EXT..."
rsync -a --delete "$EXT_PATH/node_modules/" "$TARGET_DIR/$EXT/node_modules/"
fi
echo "$EXT synced."
else
echo "❌ Extension source not found: $EXT_PATH"
fi
done
echo "✨ Extension sync complete!"

View File

@@ -1,11 +1,51 @@
import * as fs from "fs"; import * as fs from "fs";
import * as path from "path"; import * as path from "path";
const tag = process.env.GITHUB_REF_NAME || process.env.TAG; import { execSync } from "child_process";
if (!tag || !tag.startsWith("v")) { /**
console.error("❌ No valid tag found (must start with v, e.g., v1.0.0)"); * Gets the current version tag from arguments, environment or git.
process.exit(1); */
function getVersionTag() {
// 0. Check arguments (passed from husky hook or manual run)
const argTag = process.argv.slice(2).find((arg) => arg.startsWith("v"));
if (argTag) {
return argTag;
}
// 1. Check CI environment variables
if (
process.env.GITHUB_REF_NAME &&
process.env.GITHUB_REF_NAME.startsWith("v")
) {
return process.env.GITHUB_REF_NAME;
}
if (process.env.TAG && process.env.TAG.startsWith("v")) {
return process.env.TAG;
}
// 2. Try to get it from local git
try {
const gitTag = execSync("git describe --tags --abbrev=0", {
encoding: "utf8",
}).trim();
if (gitTag && gitTag.startsWith("v")) {
return gitTag;
}
} catch (e) {
// Fallback or silence
}
return null;
}
const tag = getVersionTag();
if (!tag) {
console.log(
" No version tag found (starting with v). Skipping version sync.",
);
process.exit(0); // Exit gracefully if no tag is present
} }
const version = tag.replace(/^v/, ""); const version = tag.replace(/^v/, "");
@@ -33,20 +73,50 @@ function updatePkg(pkgPath: string) {
console.log(`✅ Updated ${pkg.name} to ${version}`); console.log(`✅ Updated ${pkg.name} to ${version}`);
} }
/**
* Updates the IMAGE_TAG in .env files.
*/
function updateEnv(envPath: string) {
if (!fs.existsSync(envPath)) return;
let content = fs.readFileSync(envPath, "utf-8");
if (content.includes("IMAGE_TAG=")) {
content = content.replace(/IMAGE_TAG=.*/g, `IMAGE_TAG=${tag}`);
} else {
// Proactively add it if missing
if (content.includes("# Project")) {
content = content.replace("# Project", `# Project\nIMAGE_TAG=${tag}`);
} else {
content = `IMAGE_TAG=${tag}\n${content}`;
}
}
fs.writeFileSync(envPath, content);
console.log(`✅ Updated IMAGE_TAG in ${envPath} to ${tag}`);
}
// Update root // Update root
rootPkg.version = version; rootPkg.version = version;
fs.writeFileSync("package.json", JSON.stringify(rootPkg, null, 2) + "\n"); fs.writeFileSync("package.json", JSON.stringify(rootPkg, null, 2) + "\n");
// Update all packages // Update all packages
const packages = fs.readdirSync(packagesDir); if (fs.existsSync(packagesDir)) {
for (const p of packages) { const packages = fs.readdirSync(packagesDir);
updatePkg(path.join(packagesDir, p, "package.json")); for (const p of packages) {
updatePkg(path.join(packagesDir, p, "package.json"));
}
} }
// Update all apps // Update all apps
const apps = fs.readdirSync(appsDir); if (fs.existsSync(appsDir)) {
for (const a of apps) { const apps = fs.readdirSync(appsDir);
updatePkg(path.join(appsDir, a, "package.json")); for (const a of apps) {
updatePkg(path.join(appsDir, a, "package.json"));
}
} }
// Update .env files
updateEnv(".env");
updateEnv(".env.example");
console.log("✨ All versions synced!"); console.log("✨ All versions synced!");