5 Commits (v1.3.42)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
e376b571be |
Read the support mailbox into the console
tests / pest (push) Failing after 7m59s
Details
tests / assets (push) Successful in 20s
Details
tests / release (push) Has been skipped
Details
No, not an API token: mailcow's API administers domains and mailboxes — it does not read mail. Reading a mailbox is IMAP whoever runs the server, so that is what this is. Host, user and folder are console settings; the password is a vault entry, like every other credential that opens something. IMAP by hand rather than a dependency. Four verbs are needed — LOGIN, SELECT, SEARCH UNSEEN, FETCH — and the parsing that follows is the real work either way. It is bounded by what the console shows: a sender, a subject, the words a person wrote, and the NAMES of anything attached. Real mail from real people arrives encoded, so it handles RFC 2047 subjects (every German subject with an umlaut in it), quoted-printable and base64 bodies, Windows charsets, and multipart — text wins over HTML, because a console is not a mail client and the words are in the text part. A message it cannot make sense of is skipped and logged, never guessed at: a garbled question in front of an operator is worse than one they go and read in the mailbox, where it still is. ATTACHMENTS ARE NOT STORED. Only their names and sizes, so an operator knows something was attached and can go and look if it matters. Keeping whatever a stranger chooses to send would make this application a malware store with a web interface in front of it. Whose a mail is, is decided by the sender's ADDRESS and by nothing else — not by a name in the subject, not by anything in the body. A mail is easy to write and this decision attaches a stranger's words to a real customer's file; there is a test that forges the name and the subject and still gets nothing. An address nobody recognises stays unassigned, shown FIRST and never hidden: that is a new enquiry, or a customer writing from their private account, and it is the mail that must not be missed. An operator can place it by hand from the same row. Nothing is deleted on the mail server, ever. Messages are flagged seen once the row is safely written — and only then, because flagging first and failing after would lose the message with no second copy anywhere. Filing one away is this console's own state; the mailbox is untouched. Two fixes on the way past: the customer page printed "customers.status.active" and "support.status.open" — a lang key with no file behind it renders as itself and Laravel says nothing — and the same page is now covered by a test proving each list is scoped to the customer in the URL, which was reported as "unterhalb steht immer das selbe". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|
|
|
f85f57152e |
Let the customer buy it themselves, and tab the integrations page
tests / pest (push) Failing after 21m20s
Details
tests / assets (push) Successful in 20s
Details
tests / release (push) Has been skipped
Details
Every path on the site ended in "anfragen": a mailto: link and a promise to answer the same working day. That is an afternoon of somebody's time per customer, for a product whose whole point is that the machine does the work — and it scales exactly as far as one inbox does. Now: sign in, pick a package, pay, and provisioning starts by itself. The pieces were already there — Stripe holds a Price per plan version (the catalogue sync writes the ids), the webhook opens the contract, the pipeline builds the VM — and what was missing was the checkout session between them. CheckoutController opens one and gets out of the way; the purchase becomes real on the webhook, which stays the only place allowed to believe a payment happened. The success URL is a redirect target and therefore a URL anybody can type, so it says "we are building it", not "you have paid". Nothing in the request may decide the price. The plan key arrives from a form and everything else is re-derived from the catalogue, including the plan VERSION, so a version published while the customer types their card number cannot change what they were quoted. Two cases that are not errors: no capacity is a parked order (already built), and an existing customer is sent to the plan change rather than sold a second, empty cloud that bills twice. The site says what it can keep. "danach wissen Sie … wie Ihre Daten sicher umziehen" was a promise made before seeing the thing it is about: we do not know where a visitor's data is, what shape it is in, or whether it can be moved at all. Migration is named as its own question, looked at first and quoted afterwards. The integrations page gets the settings page's treatment for the same reason: it was one narrow column of six cards, so an operator connecting DNS scrolled past Stripe to reach it and the raw .env editor sat at the bottom of everything. Three tabs by what a section is FOR — services we buy from outside, our own machines, the file underneath — the same axis the page was rebuilt around when it stopped being split by storage mechanism. The tab is in the query string; unlocking survives a switch, because a confirmed password is a session fact and not a property of a tab. The .env tab is Owner-only, like the editor it holds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|
|
|
33e5b15099 |
Restart the workers automatically after saving .env, instead of handing the operator back to the shell
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|
|
|
90131f088a |
Add a raw .env editor to the Integrations page, with a net under it
Everything the console has no field for is still real — MAIL_*, DB_*, APP_KEY, whichever key nobody has built a form for yet — and without a way to touch it from here, the operator needs a shell and the console page misses its own point. EnvFileEditor is the net, not just the warning: 1. Validates before writing. A line that is neither blank, nor a comment, nor KEY=value is rejected outright, and an empty file (syntactically "valid" by that rule, but not survivable) is refused too. write() never touches the file before checking the new content. 2. Backs up before every write that actually happens — a timestamped copy beside .env, before the new content lands. Never pruned automatically; the page says so, next to where it says where they land. 3. Names the keys a mistake here can lock an operator out with — APP_KEY, DB_*, REDIS_*, SESSION_* — rather than a blanket warning nobody reads. 4. Gated by secrets.manage and the same confirmed password the vault entries use — this is the one place on the page that can reach every credential the vault otherwise keeps write-only. 5. Says plainly what saving does not do: queue, queue-provisioning, scheduler and reverb only read .env at their own startup, and names the restart command plus config:clear. 6. Marks which .env keys SecretVault currently overrides, so editing a line that a stored vault value already shadows does not look broken. Two issues surfaced by Codex review and fixed before this commit: the raw file content stayed in the Livewire component snapshot after the password confirmation window expired on its own (not only on an explicit re-lock), and the first save on an installation with no .env yet failed trying to back up a file that was never there. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|
|
|
3fadaa14b0 |
Merge credentials and infrastructure into one Integrations page
Zugangsdaten and Infrastruktur split the same subject by storage mechanism — SecretVault vs App\Support\Settings — instead of by what each field configures, which is not a distinction an operator setting up Stripe or DNS should have to know or care about. One page now, grouped by integration: Zahlungen (Stripe), DNS (Hetzner), Monitoring, VPN/WireGuard, SSH-Identität. A secret and a plain setting sit side by side within a section; the difference stays visible — a vault entry is write-only and shows only an outline, a setting shows its value in full — as a property of the field, not a reason for a separate page. Both storage mechanisms are unchanged underneath: this is a presentation change, not a data migration. The vault keeps its own gate (secrets.manage + a recently confirmed password); plain settings keep theirs (hosts.manage alone). Reachable with either capability, since the two are no longer two pages a role happens to see one, both, or neither of — every section and every save action still checks its own capability server-side. admin.secrets and admin.infrastructure redirect here permanently rather than 404ing a bookmark. The nav entry that replaces both is visible to either capability — Navigation's capability field now accepts an array meaning "any of these", not only a single ability. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |