panel-redesign: the application form is one POST, under its domain
Stage 1, step 4 (plan § Routes).
Service (internal/app)
- Settings{Mode, Addresses, AuthIPRestrict, AuthAllowedIPs, Limit} is what
the form holds besides the login. CreateWithSettings creates the
application with all of it and removes it again if a later write fails;
SaveSettings validates the sender rule first and, if a later write
fails, puts back what it had already changed. The form produces a whole
application or leaves things as they were.
- Limit.Mode "" is "use the domain limit"; auto takes its ceiling from a
recalculation the caller supplies.
Routes and handlers
- GET, POST /outbound/domains/{id}/applications/new
GET, POST /outbound/domains/{id}/applications/{aid}
POST .../{aid}/password, .../{aid}/ratelimit/recalc, .../{aid}/delete
replace POST /domains/{id}/applications and the six /applications/{aid}/*
routes. lookupDomainApplication answers 404 when the application is not
in the domain named by the path - also when the caller may open that
domain.
- A generated password is the response to its POST only (no GET path) and
is sent with Cache-Control: no-store.
- The login is not renamed by the form; a changed login is a 400.
Old domain page: the three per-application forms (mode, IP restriction,
limit) are one form with one Save; "Remove limit" became the "Use the domain
limit" choice. The e2e client follows (edited, not run here).
Tests: service - create and save in every limit mode, and all-or-nothing on
a refused sender rule, a failed recalculation and a failed sender-map
rebuild; handlers - the form's parsing, one POST creating sender + IPs +
limit, four kinds of refused form creating nothing, save, the no-store
header on both password pages, and five application routes against another
tenant's application addressed through its own domain and through the
caller's.
With @routes removed by hand TestRoutesFollowNavigation now reports only
GET /server/health (step 5).
go test ./... green except the two Windows-only failures in internal/domain
and internal/logtail.
Roadmap 14/27.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -172,7 +172,7 @@ func (c *panelClient) dkimRecord(domainID string) (name, value string, err error
|
||||
// login/password the panel renders inline (security.md — never recoverable
|
||||
// later, so this is the only place to read it).
|
||||
func (c *panelClient) addApplication(domainID, login, mode, addresses string) (appLogin, password string, err error) {
|
||||
resp, body, err := c.postForm("/outbound/domains/"+domainID+"/applications", url.Values{
|
||||
resp, body, err := c.postForm("/outbound/domains/"+domainID+"/applications/new", url.Values{
|
||||
"login": {login},
|
||||
"mode": {mode},
|
||||
"addresses": {addresses},
|
||||
@@ -194,14 +194,17 @@ func (c *panelClient) addApplication(domainID, login, mode, addresses string) (a
|
||||
return appLogin, password, nil
|
||||
}
|
||||
|
||||
// setRateLimit saves a level-2 limit (guide § Rate limiting) on an application
|
||||
// or domain (/applications/{id}/ratelimit or /outbound/domains/{id}/settings/ratelimit).
|
||||
func (c *panelClient) setRateLimit(path string, maxMessages, windowSeconds int) error {
|
||||
// setApplicationLimit saves a manual level-2 limit (guide § Rate limiting) on
|
||||
// an application. The application form is one POST, so the sender rule goes
|
||||
// with it: the application stays on the domain wildcard it was created with.
|
||||
func (c *panelClient) setApplicationLimit(domainID, appID string, maxMessages, windowSeconds int) error {
|
||||
vals := url.Values{
|
||||
"mode": {"wildcard"},
|
||||
"rl_mode": {"manual"},
|
||||
"max_messages": {fmt.Sprintf("%d", maxMessages)},
|
||||
"window_seconds": {fmt.Sprintf("%d", windowSeconds)},
|
||||
}
|
||||
resp, body, err := c.postForm(path, vals)
|
||||
resp, body, err := c.postForm("/outbound/domains/"+domainID+"/applications/"+appID, vals)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user