41 Commits

Author SHA1 Message Date
chaim 770ae1104f fix(publish): ship only the installable payload in release assets
build_and_publish copied the entire tag archive (minus top dir + .git) into
the release zip, leaking dev/docs files to clients: ARCHITECTURE.md, README.md,
CHANGELOG.md, build.sh, .env.example, .taskmaster/, .claude/, the repo's own
composer.json. This raced n8n IcF30's clean upload under the same asset name,
producing two same-named assets per release (one clean, one junk).

Filter the copy loop to manifest.json + files/ + scripts/ only (composer.json
is generated), matching n8n IcF30 and build.sh. The /publish output is now the
clean installable package.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-21 18:22:34 +00:00
chaim c2b88c5d33 ci: re-trigger build with rotated REGISTRY_PASSWORD secret 2026-04-25 21:58:29 +00:00
chaim 686a07e088 fix(security): require DB_PASS and GITEA_TOKEN env vars
Removed hardcoded fallback secrets in app_server.py. Both env vars
are now required at startup — fail fast instead of silently using
production credentials baked into the source.

Why: discovered while mapping the leak radius of a separate Gitea
token during the LegalCrm 2026-04-25 security audit. Per the
Infisical-only secrets policy, hardcoded credentials in any source
file are Critical regardless of intent.

Note: deployment must set both env vars before restarting the
service; missing either now produces KeyError on startup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 21:49:23 +00:00
chaim 740866eb1c feat: separate manifest.displayLabel for catalog UI from manifest.description
manifest.description is for composer / dev consumption (often a long
English text). The admin catalog needs short Hebrew labels. Introduces
optional manifest.displayLabel field — only that field is synced into
extensions.description (the column the admin UI reads). When
displayLabel is missing the DB row is left untouched, so curated
labels never get clobbered by long English manifest descriptions.

Adds POST /admin/set-labels for bulk backfilling labels in one call.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 17:55:59 +00:00
chaim 536b03b34d fix: don't reactivate extensions with zero versions in sync-registry
After /release-deleted deactivates an extension (when the last release
is removed from Gitea), running /sync-registry would reactivate it
because the repo still exists. Now sync-registry skips reactivation
when an extension has zero rows in extension_versions — those were
intentionally retired and must stay deactivated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 17:49:16 +00:00
chaim 18d54e1403 feat: sync extensions.description from manifest.json
Manifest is now the source of truth for the catalog label shown in the
admin UI. build_and_publish updates description on every release; the
new _sync_description_from_manifest helper is also called from
sync_extension_registry for backfill / drift correction (runs every 6h
via the auto-add-webhook workflow, or on manual /sync-registry).

This replaces the hardcoded HEBREW_LABELS map in KlearBranding's
admin/index.js — new extensions inherit their label from their manifest
without code changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 17:47:21 +00:00
chaim 50c408c4c4 feat: add /release-deleted endpoint for Gitea release deletion handler
When a release is deleted in Gitea (e.g., to retire an extension), the
platform DB still showed the extension as installable. This endpoint
removes the version row, recomputes latest_version, and deactivates
the extension if no versions remain — making it disappear from the
install catalog without touching the repo.

Called by n8n workflow NwrRbHNAXFVOLHCQ on Gitea release.deleted event.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 17:02:27 +00:00
chaim c2269f8d58 Add CI/CD workflow for pre-built Docker image deployment 2026-04-13 20:41:59 +00:00
chaim d0b87b16dc fix: use semver comparison for extension version upgrades
Previously the system used simple string inequality to detect available
updates, causing downgrades to appear as upgrades (e.g. 2.5.1 → 2.5.0).
Now both backend (SQL) and frontend (JS) compare version tuples properly.
Also fixes latest_version in DB on registry sync startup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 15:05:15 +00:00
chaim 3cee3abfeb fix: add curl for healthcheck, PYTHONUNBUFFERED for logging
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 14:28:01 +00:00
chaim 7595763711 fix: restore server.py content destroyed by bad Gitea API commit fed6ca3
Commit fed6ca3 replaced the entire server.py (1017 lines) with a single
${FILE:/tmp/ext-platform-server.py} placeholder via Gitea API, breaking
the application. This restores the correct code and adds /api/health.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 14:24:44 +00:00
chaim c70c4e71e6 fix: rename server.py to app_server.py to bypass stale Coolify file mount
A Coolify persistent storage entry was overriding /app/server.py with an
unresolved ${FILE:} placeholder, causing SyntaxError on startup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 14:21:32 +00:00
chaim fed6ca30f6 feat: add /api/health endpoint with CORS headers 2026-04-06 13:39:27 +00:00
chaim 9aaee609cb fix: hide deploy button for extensions without a published version
Extensions like LegalAssistance that are in the registry but haven't
been published yet (latest_version is null) now show "אין גרסה"
instead of a deploy button. Batch deploy also skips them.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 07:17:59 +00:00
chaim 1e5f220c97 fix: filter inactive extensions from dashboard API responses
/api/extensions and customer extension list now only return
is_active extensions, preventing deleted extensions from showing
in the dashboard and causing deploy errors with null versions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 07:11:32 +00:00
chaim d2a2d28b01 feat: auto-sync extension registry from Gitea on startup
Every time the server starts (including after deploy), it syncs
the extension registry from Gitea before serving requests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 06:58:03 +00:00
chaim 69fd5ae0c4 feat: auto-discover extensions from Gitea, sync registry with DB
MODULES was a static dict that got out of sync with Gitea repos —
LegalAssistance was missing, 5 deleted extensions were still listed.

Now MODULES loads from DB on startup with static fallback.
New POST /sync-registry endpoint scans espocrm-extensions org on Gitea,
adds new extensions, deactivates removed ones, and reloads MODULES.
INSTALL_ORDER is now computed dynamically via topological sort.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 06:49:57 +00:00
chaim 9bbac18b1a fix: send both Espo-Authorization and X-Api-Key for opcache clear
The platform stores API keys as base64-encoded Espo-Authorization,
not raw API keys. Send both headers so opcache-clear.php can validate.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 12:18:26 +00:00
chaim 08995b9027 feat: clear PHP opcache after extension deploy
Add multi-tier opcache clearing after extension install:
- Tier 1: HTTP call to opcache-clear.php endpoint
- Tier 2: SSH + docker exec apachectl graceful
- Tier 3: Log warning

Add ssh_host, ssh_user, ssh_key, docker_container fields
to customers table for SSH fallback.

Fixes production issue where new PHP files weren't loaded
after extension upgrade due to stale opcache.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 11:51:49 +00:00
chaim f8026afed4 fix: improved error display - show step, HTTP code, Hebrew messages; sync errors shown to user; deployment log shows error details 2026-03-30 21:00:43 +00:00
chaim 9380364376 fix: stale deploy check only looks at latest deploy per extension+customer
Was showing all historical deploys causing noise. Now uses DISTINCT ON
to get only the most recent deploy for each extension+customer pair.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:44:26 +00:00
chaim 0c77056422 feat: add health check endpoint + twice-daily monitoring workflow
New /health-check endpoint verifies:
- Gitea release integrity (manifest version matches tag)
- Duplicate ZIP assets in releases
- EspoCRM API reachability per customer
- Extension load status on each instance
- Stale deployments (logged ok but version not updated)

New n8n workflow runs at 07:00 & 19:00, combines health check
with drift sync, and posts combined report to Mattermost.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:38:36 +00:00
chaim f546b7d660 fix: handle duplicate ZIP assets in Gitea releases
- When re-publishing, delete old ZIP assets before uploading new one
- Take last ZIP asset when duplicates exist in deploy flow
- Handle empty response body from DELETE API calls

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:34:38 +00:00
chaim fc28ef2d7c fix: sync auto-assigns all active extensions to customer
Ensures every sync call first fills in any missing extension
assignments, so new extensions are automatically available to
all customers without manual intervention.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 10:25:38 +00:00
chaim 2cbb9aaa65 feat: auto-assign extensions + form validation
- Backend: new customers auto-get all active extensions assigned
- Frontend: required field validation (name, URL, credentials)
- Frontend: removed manual "add extension" dropdown (auto-assigned)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 10:02:05 +00:00
chaim 09ef629b8d fix: customer form auth fields layout + sticky header opacity
- Auth fields: use grid-cols-2 with dir=ltr for proper username/password
  layout, better spacing and visual hierarchy with key icon
- Header: increase opacity to 0.95 with subtle shadow to prevent
  content showing through when scrolling

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 09:34:27 +00:00
chaim 7aca2abf78 feat: add delete customer + fix auth form UX
- Backend: DELETE /api/customers/:id removes customer + extensions + logs
- Frontend: delete button on customer detail with confirmation
- Auth form: only sends espocrm_api_key when credentials are entered

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 09:27:19 +00:00
chaim ad386f5cef feat: add username/password fields to customer form for Espo auth
Instead of manually entering base64, users can now type username and
password which auto-generates the Espo-Authorization header value.
Empty credentials on edit won't overwrite existing API key.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 09:20:40 +00:00
chaim a11ddb82a6 fix: sync now resets installed_version when extension removed from server
- Backend: sync_customer_extensions now sets installed_version=NULL when
  extension is missing_on_server (previously only detected drift without
  updating DB)
- Frontend: refresh button now calls /sync endpoint before reloading,
  ensuring the dashboard reflects actual server state

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 09:02:11 +00:00
chaim 36812af6e7 feat: add POST /sync endpoint + daily n8n drift check workflow
Adds extension sync that queries each customer's EspoCRM to compare
actually-installed extensions with what the platform DB tracks.
Updates platform DB on version mismatches and reports drifts to Mattermost.

n8n workflow ID: NXxBHm3qhWL3yHza (daily at 07:00)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 21:26:01 +00:00
chaim f028299693 feat: add DataMigration to MODULES, DEPENDENCIES, and INSTALL_ORDER 2026-03-28 19:59:16 +00:00
chaim 98db6899db feat: UI improvements - default to customers tab, refresh button, installed extensions first 2026-03-28 19:30:40 +00:00
chaim 814e5a182c fix: return 404 for missing assets instead of index.html fallback
Prevents MIME type mismatch when asset filenames change after rebuild.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 18:46:35 +00:00
chaim 22b05617d4 feat: install order, dependency blocking, batch install
- Sort extensions by install order (HebrewLanguage first, then deps)
- Grey out extensions with missing dependencies (show lock icon + "נדרש: X")
- Checkbox selection for batch install with progress indicator
- "בחר הכל" button to select all installable extensions
- Server returns dependencies + install_order in customer API

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 18:44:56 +00:00
chaim eea011d68a fix: cancel button on form, edit customer, show installed/total count 2026-03-26 08:20:51 +00:00
chaim 72c3087dd0 redesign: apply Stitch design system — Heebo font, glass header, Material icons, editorial tables 2026-03-26 07:56:11 +00:00
chaim ccb467ebaa fix: show update button only when newer version available 2026-03-26 06:57:48 +00:00
chaim 4638763ee1 feat: structured extensions table in customer detail view
4 columns: name, installed version, available update, actions
Actions: install (if not installed), update + remove (if installed)
2026-03-26 06:50:35 +00:00
chaim 188c5fb878 fix: use multipart upload for Composer Registry (fixes 500 error) 2026-03-26 06:22:27 +00:00
chaim 8c6cd8a3ef fix: handle 409 Conflict in gitea_upload as 'exists' not error 2026-03-26 06:19:13 +00:00
chaim 5e0e5f1066 Extension Platform: API server + React dashboard
- Python API server with Gitea-based build (no local files needed)
- PostgreSQL on Coolify internal network
- React + Vite + Tailwind dashboard
- Endpoints: /publish, /deploy, /deploy-all, /api/*
- Dockerfile: multi-stage (Node build + Python runtime)
2026-03-26 04:53:01 +00:00