Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 73b60f14a9 | |||
| b3f43c421f | |||
| a2339f7106 | |||
| e83a76f111 | |||
| 0096c18098 | |||
| 3284931f84 | |||
| 28517a3558 | |||
| 3b9f10ec98 | |||
| 65fd248993 | |||
| ebd9ab132c | |||
| ddaeb2c3ca | |||
| ad1a8c4fbf | |||
| 013b0259b2 | |||
| d5a9a3bce4 | |||
| b9fd583ac4 | |||
| bfdbaba0d0 | |||
| 4ea9cbc551 | |||
| d8c1a38c0d | |||
| b65b9a7fb2 | |||
| 858c7bbc39 | |||
| 149123ef90 | |||
| 6bc49d1c52 | |||
| 52ffe49019 | |||
| 73fa292528 | |||
| f2c0a4581c | |||
| 367c4d8404 | |||
| 587c88980f | |||
| fcdfdb4588 | |||
| 6bbaa8d105 | |||
| eccc084441 |
7
.changeset/resilient-build-scripts.md
Normal file
7
.changeset/resilient-build-scripts.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@mintel/monorepo": patch
|
||||
"acquisition-manager": patch
|
||||
"feedback-commander": patch
|
||||
---
|
||||
|
||||
fix: make directus extension build scripts more resilient
|
||||
@@ -1,4 +1,5 @@
|
||||
# Project
|
||||
IMAGE_TAG=v1.7.9
|
||||
PROJECT_NAME=sample-website
|
||||
PROJECT_COLOR=#82ed20
|
||||
|
||||
|
||||
44
.gitea/workflows/maintenance.yml
Normal file
44
.gitea/workflows/maintenance.yml
Normal 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
|
||||
@@ -12,8 +12,56 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
qa:
|
||||
name: 🧪 Quality Assurance
|
||||
prioritize:
|
||||
name: ⚡ Prioritize Release
|
||||
runs-on: docker
|
||||
container:
|
||||
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
|
||||
@@ -22,36 +70,66 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- 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
|
||||
|
||||
- name: 🏷️ Sync Versions (if Tagged)
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
run: pnpm sync-versions
|
||||
|
||||
run: pnpm install --frozen-lockfile --prefer-offline --ignore-scripts --no-color
|
||||
- name: 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
|
||||
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
|
||||
run: pnpm build
|
||||
|
||||
release:
|
||||
name: 🚀 Release
|
||||
needs: qa
|
||||
needs: [lint, test, build]
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: docker
|
||||
container:
|
||||
@@ -64,20 +142,16 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- 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
|
||||
|
||||
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)
|
||||
run: |
|
||||
echo "🏷️ Tag detected [${{ github.ref_name }}], performing sync release..."
|
||||
@@ -85,7 +159,7 @@ jobs:
|
||||
|
||||
build-images:
|
||||
name: 🐳 Build ${{ matrix.name }}
|
||||
needs: qa
|
||||
needs: [lint, test, build]
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: docker
|
||||
container:
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
|
||||
# Check if we are pushing a tag
|
||||
if echo "$*" | grep -q "refs/tags/v"; then
|
||||
echo "🏷️ Tag detected in push, syncing versions..."
|
||||
pnpm sync-versions
|
||||
|
||||
# Stage the changed package.json files
|
||||
git add "package.json" "packages/*/package.json" "apps/*/package.json"
|
||||
|
||||
# Amend the tag if it's on the current commit, but this is complex in pre-push.
|
||||
# Better: Just warn the user that they might need to update the tag if package.json changed.
|
||||
echo "⚠️ package.json files updated to match tag. Please ensure these changes are part of your tag/commit."
|
||||
fi
|
||||
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
56
Dockerfile.template
Normal 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"]
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sample-website",
|
||||
"version": "1.6.0",
|
||||
"version": "1.7.9",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -23,7 +23,7 @@ services:
|
||||
- "traefik.http.services.sample-website.loadbalancer.server.port=3000"
|
||||
|
||||
directus:
|
||||
image: registry.infra.mintel.me/mintel/directus:latest
|
||||
image: registry.infra.mintel.me/mintel/directus:${IMAGE_TAG:-latest}
|
||||
restart: always
|
||||
networks:
|
||||
- infra
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
"test": "pnpm -r test",
|
||||
"changeset": "changeset",
|
||||
"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:snapshot:infra": "./scripts/cms-snapshot.sh infra",
|
||||
"cms:schema:apply": "./scripts/cms-apply.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:tag": "pnpm build && pnpm -r publish --no-git-checks --access public",
|
||||
@@ -52,7 +52,7 @@
|
||||
"pino-pretty": "^13.1.3",
|
||||
"require-in-the-middle": "^8.0.1"
|
||||
},
|
||||
"version": "1.6.0",
|
||||
"version": "1.7.10",
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"next": "16.1.6",
|
||||
|
||||
1
packages/acquisition-manager/index.js
Normal file
1
packages/acquisition-manager/index.js
Normal 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};
|
||||
30
packages/acquisition-manager/package.json
Normal file
30
packages/acquisition-manager/package.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "acquisition-manager",
|
||||
"description": "Custom High-Fidelity Acquisition Management for Directus",
|
||||
"icon": "account_balance_wallet",
|
||||
"version": "1.7.9",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
14
packages/acquisition-manager/src/index.ts
Normal file
14
packages/acquisition-manager/src/index.ts
Normal 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,
|
||||
},
|
||||
],
|
||||
});
|
||||
18
packages/acquisition-manager/src/module.vue
Normal file
18
packages/acquisition-manager/src/module.vue
Normal 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>
|
||||
44
packages/acquisition/build.js
Normal file
44
packages/acquisition/build.js
Normal 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);
|
||||
});
|
||||
3845
packages/acquisition/index.js
Normal file
3845
packages/acquisition/index.js
Normal file
File diff suppressed because it is too large
Load Diff
25
packages/acquisition/package.json
Normal file
25
packages/acquisition/package.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "acquisition",
|
||||
"version": "1.7.9",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
5
packages/acquisition/src/index.ts
Normal file
5
packages/acquisition/src/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { defineEndpoint } from '@directus/extensions-sdk';
|
||||
|
||||
export default defineEndpoint((router) => {
|
||||
router.get('/ping', (req, res) => res.send('pong'));
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/cli",
|
||||
"version": "1.6.0",
|
||||
"version": "1.7.9",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
|
||||
Binary file not shown.
@@ -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};
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
3845
packages/cms-infra/extensions/acquisition/index.js
Normal file
3845
packages/cms-infra/extensions/acquisition/index.js
Normal file
File diff suppressed because it is too large
Load Diff
25
packages/cms-infra/extensions/acquisition/package.json
Normal file
25
packages/cms-infra/extensions/acquisition/package.json
Normal 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
@@ -1,29 +1,30 @@
|
||||
{
|
||||
"name": "customer-manager",
|
||||
"description": "Custom High-Fidelity Customer & Company Management for Directus",
|
||||
"icon": "supervisor_account",
|
||||
"version": "1.0.0",
|
||||
"keywords": [
|
||||
"directus",
|
||||
"directus-extension",
|
||||
"directus-extension-module"
|
||||
],
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"directus:extension": {
|
||||
"type": "module",
|
||||
"path": "index.js",
|
||||
"source": "src/index.ts",
|
||||
"host": "*",
|
||||
"name": "Customer Manager"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "directus-extension build",
|
||||
"dev": "directus-extension build -w"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@directus/extensions-sdk": "11.0.2",
|
||||
"vue": "^3.4.0"
|
||||
}
|
||||
"name": "customer-manager",
|
||||
"description": "Custom High-Fidelity Customer & Company Management for Directus",
|
||||
"icon": "supervisor_account",
|
||||
"version": "1.7.3",
|
||||
"type": "module",
|
||||
"keywords": [
|
||||
"directus",
|
||||
"directus-extension",
|
||||
"directus-extension-module"
|
||||
],
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"directus:extension": {
|
||||
"type": "module",
|
||||
"path": "index.js",
|
||||
"source": "src/index.ts",
|
||||
"host": "*",
|
||||
"name": "Customer 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"
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,30 @@
|
||||
{
|
||||
"name": "feedback-commander",
|
||||
"description": "Custom High-Fidelity Feedback Management Extension for Directus",
|
||||
"icon": "view_kanban",
|
||||
"version": "1.0.0",
|
||||
"keywords": [
|
||||
"directus",
|
||||
"directus-extension",
|
||||
"directus-extension-module"
|
||||
],
|
||||
"files": [
|
||||
"index.js"
|
||||
],
|
||||
"directus:extension": {
|
||||
"type": "module",
|
||||
"path": "index.js",
|
||||
"source": "src/index.ts",
|
||||
"host": "*",
|
||||
"name": "Feedback Commander"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "directus-extension build",
|
||||
"dev": "directus-extension build -w"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@directus/extensions-sdk": "11.0.2",
|
||||
"vue": "^3.4.0"
|
||||
}
|
||||
}
|
||||
"name": "feedback-commander",
|
||||
"description": "Custom High-Fidelity Feedback Management Extension for Directus",
|
||||
"icon": "view_kanban",
|
||||
"version": "1.7.3",
|
||||
"type": "module",
|
||||
"keywords": [
|
||||
"directus",
|
||||
"directus-extension",
|
||||
"directus-extension-module"
|
||||
],
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"directus:extension": {
|
||||
"type": "module",
|
||||
"path": "index.js",
|
||||
"source": "src/index.ts",
|
||||
"host": "*",
|
||||
"name": "Feedback Commander"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
||||
1
packages/cms-infra/extensions/people-manager/index.js
Normal file
1
packages/cms-infra/extensions/people-manager/index.js
Normal 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};
|
||||
30
packages/cms-infra/extensions/people-manager/package.json
Normal file
30
packages/cms-infra/extensions/people-manager/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"name": "@mintel/cms-infra",
|
||||
"version": "1.6.0",
|
||||
"version": "1.7.9",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"up": "docker compose up -d",
|
||||
"up": "npm run build:extensions && docker compose up -d",
|
||||
"down": "docker compose down",
|
||||
"logs": "docker compose logs -f"
|
||||
"logs": "docker compose logs -f",
|
||||
"build:extensions": "../../scripts/sync-extensions.sh"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,9 +126,9 @@ fields:
|
||||
schema:
|
||||
name: id
|
||||
table: client_users
|
||||
data_type: char
|
||||
data_type: uuid
|
||||
default_value: null
|
||||
max_length: 36
|
||||
max_length: null
|
||||
numeric_precision: null
|
||||
numeric_scale: null
|
||||
is_nullable: false
|
||||
@@ -304,7 +304,7 @@ fields:
|
||||
foreign_key_column: null
|
||||
- collection: client_users
|
||||
field: company
|
||||
type: string
|
||||
type: uuid
|
||||
meta:
|
||||
collection: client_users
|
||||
conditions: null
|
||||
@@ -328,9 +328,9 @@ fields:
|
||||
schema:
|
||||
name: company
|
||||
table: client_users
|
||||
data_type: char
|
||||
data_type: uuid
|
||||
default_value: null
|
||||
max_length: 36
|
||||
max_length: null
|
||||
numeric_precision: null
|
||||
numeric_scale: null
|
||||
is_nullable: true
|
||||
@@ -450,9 +450,9 @@ fields:
|
||||
schema:
|
||||
name: id
|
||||
table: companies
|
||||
data_type: char
|
||||
data_type: uuid
|
||||
default_value: null
|
||||
max_length: 36
|
||||
max_length: null
|
||||
numeric_precision: null
|
||||
numeric_scale: null
|
||||
is_nullable: false
|
||||
@@ -617,7 +617,7 @@ fields:
|
||||
width: full
|
||||
- collection: directus_users
|
||||
field: company
|
||||
type: string
|
||||
type: uuid
|
||||
meta:
|
||||
collection: directus_users
|
||||
conditions: null
|
||||
@@ -641,9 +641,9 @@ fields:
|
||||
schema:
|
||||
name: company
|
||||
table: directus_users
|
||||
data_type: char
|
||||
data_type: uuid
|
||||
default_value: null
|
||||
max_length: 36
|
||||
max_length: null
|
||||
numeric_precision: null
|
||||
numeric_scale: null
|
||||
is_nullable: true
|
||||
@@ -869,7 +869,7 @@ fields:
|
||||
foreign_key_column: null
|
||||
- collection: visual_feedback
|
||||
field: user_created
|
||||
type: string
|
||||
type: uuid
|
||||
meta:
|
||||
collection: visual_feedback
|
||||
conditions: null
|
||||
@@ -894,9 +894,9 @@ fields:
|
||||
schema:
|
||||
name: user_created
|
||||
table: visual_feedback
|
||||
data_type: char
|
||||
data_type: uuid
|
||||
default_value: null
|
||||
max_length: 36
|
||||
max_length: null
|
||||
numeric_precision: null
|
||||
numeric_scale: null
|
||||
is_nullable: true
|
||||
@@ -910,7 +910,7 @@ fields:
|
||||
foreign_key_column: null
|
||||
- collection: visual_feedback
|
||||
field: screenshot
|
||||
type: string
|
||||
type: uuid
|
||||
meta:
|
||||
collection: visual_feedback
|
||||
conditions: null
|
||||
@@ -934,9 +934,9 @@ fields:
|
||||
schema:
|
||||
name: screenshot
|
||||
table: visual_feedback
|
||||
data_type: char
|
||||
data_type: uuid
|
||||
default_value: null
|
||||
max_length: 36
|
||||
max_length: null
|
||||
numeric_precision: null
|
||||
numeric_scale: null
|
||||
is_nullable: true
|
||||
@@ -950,7 +950,7 @@ fields:
|
||||
foreign_key_column: null
|
||||
- collection: visual_feedback
|
||||
field: company
|
||||
type: string
|
||||
type: uuid
|
||||
meta:
|
||||
collection: visual_feedback
|
||||
conditions: null
|
||||
@@ -974,9 +974,9 @@ fields:
|
||||
schema:
|
||||
name: company
|
||||
table: visual_feedback
|
||||
data_type: char
|
||||
data_type: uuid
|
||||
default_value: null
|
||||
max_length: 36
|
||||
max_length: null
|
||||
numeric_precision: null
|
||||
numeric_scale: null
|
||||
is_nullable: true
|
||||
@@ -1072,7 +1072,7 @@ fields:
|
||||
foreign_key_column: null
|
||||
- collection: visual_feedback_comments
|
||||
field: user_created
|
||||
type: string
|
||||
type: uuid
|
||||
meta:
|
||||
collection: visual_feedback_comments
|
||||
conditions: null
|
||||
@@ -1097,9 +1097,9 @@ fields:
|
||||
schema:
|
||||
name: user_created
|
||||
table: visual_feedback_comments
|
||||
data_type: char
|
||||
data_type: uuid
|
||||
default_value: null
|
||||
max_length: 36
|
||||
max_length: null
|
||||
numeric_precision: null
|
||||
numeric_scale: null
|
||||
is_nullable: true
|
||||
@@ -1111,6 +1111,24 @@ fields:
|
||||
has_auto_increment: false
|
||||
foreign_key_table: null
|
||||
foreign_key_column: null
|
||||
- collection: visual_feedback_comments
|
||||
field: feedback_id
|
||||
type: integer
|
||||
meta:
|
||||
collection: visual_feedback_comments
|
||||
field: feedback_id
|
||||
interface: select-dropdown-m2o
|
||||
special: null
|
||||
width: full
|
||||
required: true
|
||||
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
|
||||
systemFields:
|
||||
- collection: directus_activity
|
||||
field: timestamp
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -2,7 +2,8 @@
|
||||
"name": "customer-manager",
|
||||
"description": "Custom High-Fidelity Customer & Company Management for Directus",
|
||||
"icon": "supervisor_account",
|
||||
"version": "1.6.0",
|
||||
"version": "1.7.9",
|
||||
"type": "module",
|
||||
"keywords": [
|
||||
"directus",
|
||||
"directus-extension",
|
||||
@@ -19,7 +20,7 @@
|
||||
"name": "Customer Manager"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "directus-extension build",
|
||||
"build": "directus-extension build && (cp -f dist/index.js index.js 2>/dev/null || true)",
|
||||
"dev": "directus-extension build -w"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/eslint-config",
|
||||
"version": "1.6.0",
|
||||
"version": "1.7.9",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
|
||||
1
packages/feedback-commander/index.js
Normal file
1
packages/feedback-commander/index.js
Normal file
File diff suppressed because one or more lines are too long
@@ -1,8 +1,9 @@
|
||||
{
|
||||
"name": "@mintel/extension-feedback-commander",
|
||||
"name": "feedback-commander",
|
||||
"description": "Custom High-Fidelity Feedback Management Extension for Directus",
|
||||
"icon": "view_kanban",
|
||||
"version": "1.6.0",
|
||||
"version": "1.7.9",
|
||||
"type": "module",
|
||||
"keywords": [
|
||||
"directus",
|
||||
"directus-extension",
|
||||
@@ -13,13 +14,13 @@
|
||||
],
|
||||
"directus:extension": {
|
||||
"type": "module",
|
||||
"path": "dist/index.js",
|
||||
"path": "index.js",
|
||||
"source": "src/index.ts",
|
||||
"host": "*",
|
||||
"name": "Feedback Commander"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "directus-extension build",
|
||||
"build": "directus-extension build && (cp -f dist/index.js index.js 2>/dev/null || true)",
|
||||
"dev": "directus-extension build -w"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/gatekeeper",
|
||||
"version": "1.6.0",
|
||||
"version": "1.7.9",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/husky-config",
|
||||
"version": "1.6.0",
|
||||
"version": "1.7.9",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
|
||||
@@ -1,38 +1,13 @@
|
||||
# Step 1: Builder image
|
||||
FROM node:20-alpine AS builder
|
||||
# Step 1: Base image for Next.js builds
|
||||
FROM node:20-alpine
|
||||
RUN apk add --no-cache libc6-compat curl
|
||||
|
||||
# Enable pnpm
|
||||
RUN corepack enable pnpm && \
|
||||
corepack prepare pnpm@10.2.0 --activate
|
||||
|
||||
WORKDIR /app
|
||||
RUN corepack enable pnpm
|
||||
|
||||
# Step 2: Install dependencies
|
||||
ENV NPM_TOKEN=placeholder
|
||||
# Copy manifest files specifically for better layer caching
|
||||
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json .npmrc ./
|
||||
# Copy package manifest files individually to preserve directory structure
|
||||
COPY packages/cli/package.json ./packages/cli/
|
||||
COPY packages/cms-infra/package.json ./packages/cms-infra/
|
||||
COPY packages/customer-manager/package.json ./packages/customer-manager/
|
||||
COPY packages/eslint-config/package.json ./packages/eslint-config/
|
||||
COPY packages/feedback-commander/package.json ./packages/feedback-commander/
|
||||
COPY packages/gatekeeper/package.json ./packages/gatekeeper/
|
||||
COPY packages/husky-config/package.json ./packages/husky-config/
|
||||
COPY packages/infra/package.json ./packages/infra/
|
||||
COPY packages/mail/package.json ./packages/mail/
|
||||
COPY packages/next-config/package.json ./packages/next-config/
|
||||
COPY packages/next-feedback/package.json ./packages/next-feedback/
|
||||
COPY packages/next-observability/package.json ./packages/next-observability/
|
||||
COPY packages/next-utils/package.json ./packages/next-utils/
|
||||
COPY packages/observability/package.json ./packages/observability/
|
||||
COPY packages/tsconfig/package.json ./packages/tsconfig/
|
||||
# packages/ui does not have a package.json
|
||||
|
||||
# Use a secret for NPM_TOKEN and a standardized 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) && \
|
||||
pnpm config set store-dir /pnpm/store && \
|
||||
pnpm i --frozen-lockfile
|
||||
|
||||
# Step 3: Build shared packages
|
||||
COPY . .
|
||||
RUN pnpm --filter "./packages/*" -r build
|
||||
# Final environment
|
||||
ENV NODE_ENV=production
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
|
||||
@@ -53,7 +53,7 @@ services:
|
||||
- "traefik.http.services.${PROJECT_NAME}-gatekeeper.loadbalancer.server.port=3000"
|
||||
|
||||
directus:
|
||||
image: registry.infra.mintel.me/mintel/directus:latest
|
||||
image: registry.infra.mintel.me/mintel/directus:${IMAGE_TAG:-latest}
|
||||
restart: always
|
||||
networks:
|
||||
- infra
|
||||
|
||||
@@ -275,6 +275,10 @@ jobs:
|
||||
docker system prune -f --filter "until=24h"
|
||||
EOF
|
||||
|
||||
- name: 🧹 Post-Deploy Cleanup (Runner)
|
||||
if: always()
|
||||
run: docker builder prune -f --filter "until=1h"
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# JOB 5: Notifications
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/infra",
|
||||
"version": "1.6.0",
|
||||
"version": "1.7.9",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
REGISTRY_DATA="/opt/infra/registry/data/docker/registry/v2"
|
||||
REGISTRY_DATA="/mnt/HC_Volume_104575103/registry-data/docker/registry/v2"
|
||||
KEEP_TAGS=3
|
||||
|
||||
echo "🏥 Starting Aggressive Registry & Docker Maintenance..."
|
||||
@@ -15,31 +15,26 @@ for repo_dir in "$REGISTRY_DATA/repositories/mintel/"*; do
|
||||
if [ -d "$tags_dir" ]; then
|
||||
echo "🔍 Processing repository: mintel/$repo_name"
|
||||
|
||||
# Prune main-* tags
|
||||
echo " 📦 Pruning main tags..."
|
||||
main_tags=$(ls -dt "$tags_dir"/main-* 2>/dev/null || true)
|
||||
count=0
|
||||
for tag_path in $main_tags; do
|
||||
((++count))
|
||||
if [ $count -gt $KEEP_TAGS ]; then
|
||||
echo " 🗑️ Deleting old main tag: $(basename "$tag_path")"
|
||||
rm -rf "$tag_path"
|
||||
fi
|
||||
# Prune various tag patterns
|
||||
PATTERNS=("main-*" "testing-*" "branch-*" "v*" "rc*" "[0-9a-f]*")
|
||||
|
||||
for pattern in "${PATTERNS[@]}"; do
|
||||
echo " 📦 Pruning $pattern tags..."
|
||||
tags=$(ls -dt "$tags_dir"/${pattern} 2>/dev/null || true)
|
||||
count=0
|
||||
for tag_path in $tags; do
|
||||
tag_name=$(basename "$tag_path")
|
||||
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
|
||||
|
||||
# Prune version tags (v* and rc*)
|
||||
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)
|
||||
# Always prune buildcache
|
||||
if [ -d "$tags_dir/buildcache" ]; then
|
||||
echo " 🧹 Deleting buildcache tag"
|
||||
rm -rf "$tags_dir/buildcache"
|
||||
@@ -49,7 +44,7 @@ done
|
||||
|
||||
# 2. Run 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)
|
||||
echo "🧹 Pruning Host Docker resources..."
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/mail",
|
||||
"version": "1.7.0",
|
||||
"version": "1.7.9",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
||||
@@ -6,6 +6,12 @@
|
||||
|
||||
- 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
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/next-config",
|
||||
"version": "1.6.0",
|
||||
"version": "1.7.9",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/next-feedback",
|
||||
"version": "1.6.0",
|
||||
"version": "1.7.9",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/next-observability",
|
||||
"version": "1.6.0",
|
||||
"version": "1.7.9",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
{
|
||||
"name": "@mintel/next-utils",
|
||||
"version": "1.6.0",
|
||||
"version": "1.7.9",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
},
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsup src/index.ts --format cjs,esm --dts",
|
||||
"dev": "tsup src/index.ts --format cjs,esm --watch --dts",
|
||||
"build": "tsup src/index.ts --format esm --dts --clean",
|
||||
"dev": "tsup src/index.ts --format esm --watch --dts",
|
||||
"lint": "eslint src/",
|
||||
"test": "vitest run"
|
||||
},
|
||||
|
||||
@@ -7,18 +7,43 @@ import {
|
||||
AuthenticationClient,
|
||||
} from "@directus/sdk";
|
||||
|
||||
export type MintelDirectusClient = DirectusClient<any> &
|
||||
RestClient<any> &
|
||||
AuthenticationClient<any>;
|
||||
export type MintelDirectusClient<Schema extends object = any> =
|
||||
DirectusClient<Schema> & RestClient<Schema> & AuthenticationClient<Schema>;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
const directusUrl =
|
||||
url || process.env.DIRECTUS_URL || "http://localhost:8055";
|
||||
export function createMintelDirectusClient<Schema extends object = any>(
|
||||
url?: string,
|
||||
): 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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,10 +4,17 @@ export const mintelEnvSchema = {
|
||||
NODE_ENV: z
|
||||
.enum(["development", "production", "test"])
|
||||
.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)
|
||||
UMAMI_WEBSITE_ID: z.string().optional(),
|
||||
NEXT_PUBLIC_UMAMI_WEBSITE_ID: z.string().optional(),
|
||||
UMAMI_API_ENDPOINT: z
|
||||
.string()
|
||||
.url()
|
||||
@@ -23,6 +30,8 @@ export const mintelEnvSchema = {
|
||||
LOG_LEVEL: z
|
||||
.enum(["trace", "debug", "info", "warn", "error", "fatal"])
|
||||
.default("info"),
|
||||
|
||||
// Mail
|
||||
MAIL_HOST: z.string().optional(),
|
||||
MAIL_PORT: z.coerce.number().default(587),
|
||||
MAIL_USERNAME: z.string().optional(),
|
||||
@@ -32,17 +41,60 @@ export const mintelEnvSchema = {
|
||||
(val) => (typeof val === "string" ? val.split(",").filter(Boolean) : val),
|
||||
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({
|
||||
...mintelEnvSchema,
|
||||
...schemaExtension,
|
||||
/**
|
||||
* Standard Mintel refinements for environment variables.
|
||||
* Enforces mandatory requirements for non-development environments.
|
||||
*/
|
||||
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 =
|
||||
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);
|
||||
|
||||
@@ -51,7 +103,7 @@ export function validateMintelEnv(schemaExtension = {}) {
|
||||
console.warn(
|
||||
"⚠️ 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>;
|
||||
}
|
||||
|
||||
@@ -62,5 +114,5 @@ export function validateMintelEnv(schemaExtension = {}) {
|
||||
throw new Error("Invalid environment variables");
|
||||
}
|
||||
|
||||
return result.data;
|
||||
return result.data as MintelEnv<T>;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/observability",
|
||||
"version": "1.6.0",
|
||||
"version": "1.7.9",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
|
||||
1
packages/people-manager/index.js
Normal file
1
packages/people-manager/index.js
Normal 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};
|
||||
30
packages/people-manager/package.json
Normal file
30
packages/people-manager/package.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "people-manager",
|
||||
"description": "Custom High-Fidelity People Management for Directus",
|
||||
"icon": "person",
|
||||
"version": "1.7.9",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
14
packages/people-manager/src/index.ts
Normal file
14
packages/people-manager/src/index.ts
Normal 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,
|
||||
},
|
||||
],
|
||||
});
|
||||
18
packages/people-manager/src/module.vue
Normal file
18
packages/people-manager/src/module.vue
Normal 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>
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mintel/tsconfig",
|
||||
"version": "1.6.0",
|
||||
"version": "1.7.9",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://npm.infra.mintel.me"
|
||||
|
||||
5013
pnpm-lock.yaml
generated
5013
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,3 @@
|
||||
packages:
|
||||
- 'packages/*'
|
||||
- 'apps/*'
|
||||
- '../klz-2026'
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
ENV=$1
|
||||
# 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
|
||||
@@ -11,38 +15,55 @@ fi
|
||||
|
||||
case $ENV in
|
||||
local)
|
||||
CONTAINER=$(docker compose ps -q directus)
|
||||
if [ -z "$CONTAINER" ]; then
|
||||
echo "❌ Local directus container not found."
|
||||
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 locally..."
|
||||
docker exec "$CONTAINER" npx directus schema apply /directus/schema/snapshot.yaml --yes
|
||||
|
||||
echo "🚀 Applying schema to LOCAL $PROJECT..."
|
||||
docker exec "$LOCAL_CONTAINER" npx directus schema apply -y /directus/schema/snapshot.yaml
|
||||
;;
|
||||
infra)
|
||||
PROJECT_NAME="directus"
|
||||
|
||||
echo "📤 Uploading snapshot to $ENV..."
|
||||
# Ensure remote directory exists
|
||||
ssh "$REMOTE_HOST" "mkdir -p $REMOTE_DIR/directus/schema"
|
||||
scp ./directus/schema/snapshot.yaml "$REMOTE_HOST:$REMOTE_DIR/directus/schema/snapshot.yaml"
|
||||
# 'infra' is the remote production server for at-mintel
|
||||
PROJECT="directus" # Remote project name
|
||||
|
||||
echo "🔍 Detecting remote container..."
|
||||
REMOTE_CONTAINER=$(ssh "$REMOTE_HOST" "cd $REMOTE_DIR && docker compose -p $PROJECT_NAME ps -q directus")
|
||||
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 "🚀 Applying schema to $ENV..."
|
||||
ssh "$REMOTE_HOST" "docker exec $REMOTE_CONTAINER npx directus schema apply /directus/schema/snapshot.yaml --yes"
|
||||
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 "🔄 Restarting Directus to clear cache..."
|
||||
ssh "$REMOTE_HOST" "cd $REMOTE_DIR && docker compose -p $PROJECT_NAME restart directus"
|
||||
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. Only 'local' and 'infra' are supported."
|
||||
echo "❌ Invalid environment: $ENV. Supported: local, infra."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -1,17 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Configuration
|
||||
PROJECT="directus"
|
||||
SCHEMA_PATH="./directus/schema/snapshot.yaml"
|
||||
CMD_PREFIX=""
|
||||
|
||||
if [ "$1" == "infra" ]; then
|
||||
PROJECT="infra-cms"
|
||||
SCHEMA_PATH="./packages/cms-infra/schema/snapshot.yaml"
|
||||
CMD_PREFIX="docker-compose -f packages/cms-infra/docker-compose.yml"
|
||||
else
|
||||
CMD_PREFIX="docker compose"
|
||||
fi
|
||||
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)
|
||||
@@ -21,8 +13,11 @@ if [ -z "$LOCAL_CONTAINER" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "📸 Creating schema snapshot for $PROJECT..."
|
||||
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 "✅ Snapshot saved to $SCHEMA_PATH"
|
||||
echo "🛠️ Repairing snapshot for Postgres compatibility..."
|
||||
python3 ./scripts/fix_snapshot_v3.py
|
||||
|
||||
echo "✅ Snapshot saved and repaired at $SCHEMA_PATH"
|
||||
|
||||
96
scripts/fix_snapshot_v3.py
Normal file
96
scripts/fix_snapshot_v3.py
Normal 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.")
|
||||
69
scripts/sync-extensions.sh
Executable file
69
scripts/sync-extensions.sh
Executable 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!"
|
||||
@@ -4,9 +4,15 @@ import * as path from "path";
|
||||
import { execSync } from "child_process";
|
||||
|
||||
/**
|
||||
* Gets the current version tag from environment or git.
|
||||
* Gets the current version tag from arguments, environment or git.
|
||||
*/
|
||||
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 &&
|
||||
@@ -67,20 +73,50 @@ function updatePkg(pkgPath: string) {
|
||||
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
|
||||
rootPkg.version = version;
|
||||
fs.writeFileSync("package.json", JSON.stringify(rootPkg, null, 2) + "\n");
|
||||
|
||||
// Update all packages
|
||||
const packages = fs.readdirSync(packagesDir);
|
||||
for (const p of packages) {
|
||||
updatePkg(path.join(packagesDir, p, "package.json"));
|
||||
if (fs.existsSync(packagesDir)) {
|
||||
const packages = fs.readdirSync(packagesDir);
|
||||
for (const p of packages) {
|
||||
updatePkg(path.join(packagesDir, p, "package.json"));
|
||||
}
|
||||
}
|
||||
|
||||
// Update all apps
|
||||
const apps = fs.readdirSync(appsDir);
|
||||
for (const a of apps) {
|
||||
updatePkg(path.join(appsDir, a, "package.json"));
|
||||
if (fs.existsSync(appsDir)) {
|
||||
const apps = fs.readdirSync(appsDir);
|
||||
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!");
|
||||
|
||||
Reference in New Issue
Block a user