release: 1.1.0: Domain-admin role and internal/web package split

The first MINOR after 1.0.0: send-only DMARC guidance in the panel, AGPL packaging on every page, and an internal split of `internal/web` ahead of domain-admin work. Upgrading runs one SQLite migration (empty defaults; existing DNS guidance is unchanged until you set a report address).
Changed:
  - `internal/web` split into subpackages (`web/view`, `web/auth`, `web/validate`,
Added:
  - panel: DMARC guidance for send-only relays — the suggested `_dmarc` record
  - AGPL packaging hygiene: [NOTICE](NOTICE) names the copyright holder and the
  - [docs/roadmap.md](docs/roadmap.md) — candidate item **visual-style** (panel

Full detail: CHANGELOG.md (1.1.0).
This commit is contained in:
mix
2026-09-22 15:24:52 +03:00
parent 75207f0d1e
commit f62fe23826
93 changed files with 3256 additions and 1770 deletions
+14
View File
@@ -1,6 +1,7 @@
package domain
import (
"database/sql"
"fmt"
"github.com/mixeme/selfpost/internal/buildinfo"
@@ -23,6 +24,7 @@ type DomainExport struct {
Domain string `json:"domain"`
DKIMSelector string `json:"dkim_selector"`
DKIMPrivateKey string `json:"dkim_private_key"` // PKCS#1 PEM
DMARCRua *string `json:"dmarc_rua,omitempty"` // nil = inherit profile; set = override ("" = none)
Applications []AppExport `json:"applications"`
}
@@ -59,6 +61,10 @@ func (s *Service) Export(id int64) (DomainExport, error) {
DKIMPrivateKey: string(pem),
Applications: make([]AppExport, 0, len(apps)),
}
if d.DMARCRua.Valid {
s := d.DMARCRua.String
exp.DMARCRua = &s
}
for _, a := range apps {
password, err := s.apps.Secret(a.Login)
if err != nil {
@@ -110,6 +116,14 @@ func (s *Service) Import(exp DomainExport) (store.Domain, error) {
return store.Domain{}, err
}
if exp.DMARCRua != nil {
if err := s.store.UpdateDomainDMARCRua(d.ID, sql.NullString{Valid: true, String: *exp.DMARCRua}); err != nil {
s.importRollback(d.ID)
return store.Domain{}, err
}
d.DMARCRua = sql.NullString{Valid: true, String: *exp.DMARCRua}
}
for _, a := range exp.Applications {
if err := s.apps.ImportApplication(d.ID, a.Login, a.AddressMode, a.Addresses, a.Password); err != nil {
s.importRollback(d.ID)