feat(admin): POST /api/instances creates a Free cloud instance

Provisions the control-plane instance and its owner, records the
admin_instances row, issues and injects a Free licence, and emails the
customer where it is and when it expires.

Licence issuance and email cannot fail the request. The instance exists
and the customer can sign in; rolling back something they can already see
would be worse than shipping it unlicensed for staff to fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
mrhid6
2026-07-26 13:16:12 +01:00
co-authored by Claude Opus 5
parent 983655d2a1
commit 1836237f82
5 changed files with 169 additions and 1 deletions
+20
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"net/smtp"
"strings"
"time"
)
type Config struct {
@@ -53,3 +54,22 @@ func SendLicense(to, instanceName, blob string) error {
"Paste it into Settings → Licence on your Vantage install:\n\n%s\n",
instanceName, blob))
}
// SendInstanceReady tells a customer their cloud instance exists, where it is,
// and when its licence runs out.
//
// The expiry is stated here rather than only in a later reminder: a Free licence
// that quietly expires in a month is a surprise, and the first email is the one
// people keep.
func SendInstanceReady(to, instanceName, loginURL string, expires time.Time) error {
body := fmt.Sprintf("%s is ready.\n\n", instanceName)
if loginURL != "" {
body += "Sign in here:\n\n" + loginURL + "\n\n"
}
body += fmt.Sprintf(
"Your Free licence runs until %s. We will email you before then so you can renew it in one click.\n\n"+
"Sign in with the same email address and password you use for your Vantage account. "+
"Changing one does not change the other.\n",
expires.Format("2 January 2006"))
return send(to, instanceName+" is ready", body)
}