Skip to content

feat: Full-stack SSL certificate management app (ZeroSSL + PHP + React) - #1

Draft
ilBiondojs with Copilot wants to merge 24 commits into
masterfrom
copilot/create-website-certificate-management-app
Draft

feat: Full-stack SSL certificate management app (ZeroSSL + PHP + React)#1
ilBiondojs with Copilot wants to merge 24 commits into
masterfrom
copilot/create-website-certificate-management-app

Conversation

Copilot AI commented Mar 30, 2026

Copy link
Copy Markdown
  • Understand current split-files implementation
  • Update CertManager::installCertificateSplit() — write cert+CA together as fullchain, key separately (removed caFilePath param)
  • Update cert.php actionInstall — only require key_path, no ca_path in split mode
  • Update services.php path validation — direct key_path check (no foreach), remove ca_path checks from split mode
  • Update ServiceFormScreen.tsx — split mode now shows "Fullchain File" and "Private Key File" fields only (removed CA Bundle field)
  • Update services.api.ts — remove unused caPath param from checkPaths()
  • Build verified (no errors in changed files)
Original prompt

Fai un applicazione sito web per la gestione dei certificati ecco le funzionalità:

Puoi creare un nuovo servizio alla quale è associato una path che contiene il certiciato. La path motlo spesso è una sola con il file.pem. Es. Sito web che ha il certiicato sotto /path/to/cert

voglio che puoi creare questi servizi poi fare richiesta di un certiicato e il codice inautomatico crea un certificato per il dominio o i domini richiesti di 30 giorni e poi sceglie l'email il file come verifica crea il file lo carica verifica scarica il certificato lo mette tutto in un file unico e poi lo aggiorna nella path desidereata. una volta fatto questo esegue il comando di riavvio sul server di destinazione di caso. Tutti questi dati vanno inseriti alla creazione del servizio. Per la gestione dei servizi fallo in un json locale per il backend usa php e per il resto fai tuo. Voglio in parica che quando creo un servizio tipo web metto i domini metto la path dove andrà il certificato e il resto fa da solo. Poi prima della scadenza visto che mi arriva l'email da zero ssl posso fare renew e lui sovrascrive il vecchio certificato e riavvia il servizio


📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.

Copilot AI changed the title [WIP] Add website certificate management application feat: Full-stack SSL certificate management app (ZeroSSL + PHP + React) Mar 30, 2026
Copilot AI requested a review from ilBiondojs March 30, 2026 10:33
@ilBiondojs

Copy link
Copy Markdown
Member

@copilot aggiungi le varie verifiche alla creazione del servizio deve verificare se può scrivere nelel vaire path e se esiste la path

Copilot AI commented Mar 30, 2026

Copy link
Copy Markdown
Author

@copilot aggiungi le varie verifiche alla creazione del servizio deve verificare se può scrivere nelel vaire path e se esiste la path

Done in commit c0ae056. Path validation now happens on both layers:

PHP backend (backend/api/services.php) — both create and update actions check paths before saving:

  • cert_path: if it already exists, must be a writable directory; if it doesn't exist, its parent must exist and be writable (the directory will be created automatically at install time)
  • webroot_path (HTTP verification only): must already exist and be writable
  • A new validate_paths action is also available for the frontend to check paths independently

Frontend (ServiceFormScreen.tsx) — live validation badge appears next to each path field on blur:

  • Accessible (green) – directory exists and is writable
  • ⚠️ Will be created (yellow) – doesn't exist yet but parent is writable
  • Error (red, with message) – doesn't exist, not writable, or parent is missing — submit is blocked

Path validation UI

@ilBiondojs

Copy link
Copy Markdown
Member

Putroppo non funziona con il file perché lo faccio su piu domini conteporaneamente. Quindi dobbiamo cambiare apporccio. Facciamo che viene mandata un'email a webmaster@dominion e per i sottodomini viene sempre mandata a webmaster@dominio. Poi ti passo le credenziali di una mailbox che si chiamerà crt@jawsdevelopers.ch che ricceverà le emial. Facciamo così

@ilBiondojs

Copy link
Copy Markdown
Member

@copilot Putroppo non funziona con il file perché lo faccio su piu domini conteporaneamente. Quindi dobbiamo cambiare apporccio. Facciamo che viene mandata un'email a webmaster@dominion e per i sottodomini viene sempre mandata a webmaster@dominio. Poi ti passo le credenziali di una mailbox che si chiamerà crt@jawsdevelopers.ch che ricceverà le emial. Facciamo così

Copilot AI commented Mar 30, 2026

Copy link
Copy Markdown
Author

@copilot Putroppo non funziona con il file perché lo faccio su piu domini conteporaneamente. Quindi dobbiamo cambiare apporccio. Facciamo che viene mandata un'email a webmaster@dominion e per i sotto...

Done in commit 037b56c. The verification approach has been switched to email by default:

  • Default method is now Email (HTTP File is still available as a fallback option)
  • Auto-derived email: as soon as the first domain is entered, the verification email is automatically set to webmaster@<root-domain>. Subdomains are always stripped — e.g. sub.app.example.comwebmaster@example.com
  • The field remains editable in case you need to use a different address (admin@, hostmaster@, etc.)
  • If you manually customise the email and then change a domain, the auto-fill won't override your value

Email verification UI

Once you have the crt@jawsdevelopers.ch mailbox credentials ready, let me know and I can integrate automatic polling/reading of that mailbox to complete the verification flow end-to-end.

@ilBiondojs

Copy link
Copy Markdown
Member

@copilot ma non posso darti le credenziali di una mailbox dove arrivano solo le email di validazione e lo validi in automatico ?

Copilot AI commented Mar 30, 2026

Copy link
Copy Markdown
Author

@copilot ma non posso darti le credenziali di una mailbox dove arrivano solo le email di validazione e lo validi in automatico ?

Yes, done in commit a62f667. You can enter the crt@jawsdevelopers.ch credentials in Settings → Verification Inbox (IMAP) and CertManager will handle everything automatically.

How it works:

  1. You enter the IMAP host, port, encryption, username and password — there's a Test Connection button to verify before saving
  2. When a certificate is pending email validation, a new "Auto-Verify from Inbox" button appears on the service detail page
  3. Clicking it connects to the mailbox, finds all unseen ZeroSSL verification emails, extracts and clicks the verification links (via HTTPS), marks the emails as read, then immediately refreshes the cert status from ZeroSSL

The password is never returned by the API (only •••••••• is shown) and is stored server-side in backend/data/settings.json.

IMAP settings + Auto-Verify button

@ilBiondojs

Copy link
Copy Markdown
Member

non me la vede guarda i cambiamenti che ho fatto prima e poi guarda l'email che arriva e cerca di capire perché non la vede. Poi guarda che all'emissione del cerificato non viene inviata l'eamil l'ho fatto io a mano cerca di capire perché

@copilot
Verify Domains (Reference #46052).eml

ilBiondo.js and others added 2 commits March 31, 2026 00:10

Copilot AI commented Mar 30, 2026

Copy link
Copy Markdown
Author

non me la vede guarda i cambiamenti che ho fatto prima e poi guarda l'email che arriva e cerca di capire perché non la vede. Poi guarda che all'emissione del cerificato non viene inviata l'eamil l'ho...

Fixed in commit 0ffc785. Two root causes found from the .eml you shared:

1 — IMAP wasn't seeing the email
ZeroSSL sends from noreply@trust-provider.com (Comodo/Sectigo infrastructure), not from zerossl.com. The verification link is https://secure.trust-provider.com/products/EnterDCVCode?orderNumber=…, not app.zerossl.com. Every pattern in the previous code was wrong. Changes:

  • IMAP search now looks for FROM "trust-provider.com" OR FROM "zerossl.com" (catches both known sender domains)
  • Link extraction now finds the real EnterDCVCode URLs on secure.trust-provider.com
  • RejectDCVCode links are hard-blocked — clicking those would cancel the certificate
  • Allow-list updated to accept both trust-provider.com and zerossl.com
  • Emails with no actionable links are no longer marked as seen

2 — Verification email not sent on "Request Certificate"
The ZeroSSL API requires a per-domain key format for multi-domain certs: validation_email[domain.com]=email@address. The previous code sent a flat validation_email=email which ZeroSSL silently ignored. Changes:

  • ZeroSSL::initiateVerification now builds validation_email[domain]=email for every domain in the certificate
  • The POST body is built manually (not via http_build_query) so the literal brackets in the key names are preserved
  • The challenge API response is now checked for errors — if ZeroSSL rejects the call, the frontend now shows the error instead of a false "email sent" success

Copilot AI commented Mar 31, 2026

Copy link
Copy Markdown
Author

si ma nell'email c'è un pezzo così:

Verification Key

<div style="color: rgb(...

Fixed in commit dde2cbf. The backend now extracts the DCV code directly from the email HTML body (the code shown in the <div> block), not just from the URL query parameter. Two extraction strategies are used in order:

  1. Block-element scan — finds any <div>, <td>, <p>, <span>, etc. whose entire text content is a 25–80 character uppercase-alphanumeric string (the code as displayed in the email)
  2. Label scan — finds text immediately following phrases like "validation code" or "dcv code" in the plain-text body

The URL dcvCode param is still kept as a final fallback. So the dcv_code returned to the frontend — and copied to clipboard when you click "Open Verification & Copy Code" — is now the exact code shown in the email <div> that you need to paste on the verification page.

@ilBiondojs

Copy link
Copy Markdown
Member

@copilot ho corretto la regex ora ho bisogno che mi cancelli l'email una volta tornata alla webgui e che aggiungi un tasto per permettere di rimandare l'email nel caso si è cancellata per sbaglio

Copilot AI commented Mar 31, 2026

Copy link
Copy Markdown
Author

@copilot ho corretto la regex ora ho bisogno che mi cancelli l'email una volta tornata alla webgui e che aggiungi un tasto per permettere di rimandare l'email nel caso si è cancellata per sbaglio

Done in commit e1aa62d. Two changes:

1 — Emails deleted automatically after reading
When you click 📋 Show Links & Codes, the backend now deletes those emails from the IMAP inbox immediately after fetching them (via imap_delete + imap_expunge). The inbox panel shows a small note: "ℹ️ These emails have been removed from the inbox."

2 — 📨 Resend Email button
A new 📨 Resend Email button appears alongside "Show Links & Codes" whenever a certificate is pending email verification. Clicking it calls the ZeroSSL /challenges endpoint again to re-send the verification email to the configured address — useful if the original was accidentally deleted before you could use it.

…start credentials

Agent-Logs-Url: https://github.com/JAWS-Developers/CertManager/sessions/0a9d1acc-887b-4fbe-99d4-623913ff031c

Co-authored-by: ilBiondojs <86325934+ilBiondojs@users.noreply.github.com>
@ilBiondojs

Copy link
Copy Markdown
Member

@copilot
{
"success": true,
"message": "Certificate installed successfully",
"cert_expiry": "2026-06-29 23:59:59",
"restart_output": "SSHPASS: Failed to run command: No such file or directory",
"restart_exit_code": 3
}

… php-ssh2 extension fallback

Agent-Logs-Url: https://github.com/JAWS-Developers/CertManager/sessions/3b2fc37c-b141-4f6e-a45b-6fbcaef1d9d8

Co-authored-by: ilBiondojs <86325934+ilBiondojs@users.noreply.github.com>

Copilot AI commented Mar 31, 2026

Copy link
Copy Markdown
Author

@copilot
{
"success": true,
...

Fixed in commit 6fa4bb1. The SSHPASS: Failed to run command: No such file or directory error was caused by a hardcoded /bin/sshpass path that didn't exist on your server.

executeCommand now tries three strategies in order:

  1. PHP ssh2 extension (php-ssh2) — no external binary needed at all; works with both password and key-based auth
  2. sshpass + ssh — searched dynamically across /usr/bin, /bin, /usr/local/bin, /usr/sbin, and which — no longer hardcoded to /bin/sshpass
  3. Plain ssh — used when no password is set (key / agent-based auth)

If none of those are available and a password was supplied, you get a clear error: "SSH with password requires either the PHP ssh2 extension (php-ssh2) or the sshpass utility to be installed on the server."

The local sudo path now uses proc_open to pipe the password directly to stdin instead of echo pass | sudo, which is more reliable and doesn't expose the password in the process list.

…ns to prevent known_hosts permission errors

Agent-Logs-Url: https://github.com/JAWS-Developers/CertManager/sessions/352b0f15-136b-4a6c-916a-d16d1f6a4c0c

Co-authored-by: ilBiondojs <86325934+ilBiondojs@users.noreply.github.com>
@ilBiondojs

Copy link
Copy Markdown
Member

@copilot
Fammi un aleggera modifica. Voglio che metti un checkbox che si puo mettere anche due file distinti per cert e ca e key c'è voglio che rimanga il modo pem ma se schiaccio il check box posso mettere l apath per il certificato il ca e poi la key in un altro file

…te key file paths

Agent-Logs-Url: https://github.com/JAWS-Developers/CertManager/sessions/f03de3ee-f19d-45b3-88d7-d307e6afc9c7

Co-authored-by: ilBiondojs <86325934+ilBiondojs@users.noreply.github.com>

Copilot AI commented Apr 27, 2026

Copy link
Copy Markdown
Author

@copilot
Fammi un aleggera modifica. Voglio che metti un checkbox che si puo mettere anche due file distinti per cert e ca e key c'è voglio che rimanga il modo pem ma se schiaccio il check box poss...

Done in commit 0fe2720. A "Use separate files" checkbox now appears at the top of the Certificate Settings card.

When unchecked (default — combined PEM):

  • Single "Certificate File Path" field as before (cert + CA + key written to one file)

When checked (split mode):

  • Certificate File — leaf cert only (certificate.crt)
  • CA Bundle File — chain/CA bundle (ca_bundle.crt)
  • Private Key File — key written with 0600 permissions

All three fields get the same live path-validation badges (✅ Accessible / ⚠️ Will be created / ❌ Error) as the combined field. The backend validates and writes each file independently via a new installCertificateSplit() method.

Copilot AI and others added 2 commits April 27, 2026 17:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants