feat: auto-refresh non-IP module updates and add full project manual
CI / changes (push) Successful in 7s
CI / openapi (push) Has been skipped
CI / go (push) Successful in 38s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, , evobgp-web) (push) Successful in 1m4s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, evobgp-all, evobgp-web-all) (push) Successful in 1m3s
CI / docker-bird (push) Has been skipped
CI / bird2 (push) Successful in 16s
CI / docker-go-prime (push) Successful in 23s
CI / docker-go (deploy/docker/evobgp-agent/Dockerfile, , evobgp-agent) (push) Successful in 1m1s
CI / docker-go (evobgp-all, 1, deploy/docker/gobinary/Dockerfile, , evobgp-all) (push) Successful in 2m7s
CI / docker-go (evobgp-api, 1, deploy/docker/gobinary/Dockerfile, , evobgp-api) (push) Successful in 1m21s
CI / docker-go (evobgp-deploy, 0, deploy/docker/gobinary/Dockerfile, , evobgp-deploy) (push) Successful in 1m18s
CI / docker-go (evobgp-ingest, 0, deploy/docker/gobinary/Dockerfile, , evobgp-ingest) (push) Successful in 1m22s
CI / docker-go (evobgp-node, 0, deploy/docker/gobinary/Dockerfile, , evobgp-node) (push) Successful in 1m5s
CI / docker-go (evobgp-render, 0, deploy/docker/gobinary/Dockerfile, , evobgp-render) (push) Successful in 1m28s
CI / docker-go (evobgp-scheduler, 0, deploy/docker/gobinary/Dockerfile, , evobgp-scheduler) (push) Successful in 1m19s

Queue module refresh jobs after AS, domain, and CDN source mutations (including CSV imports) to keep revisions current across all source types, and introduce a comprehensive project manual covering runtime modules, flows, and API operations.

Made-with: Cursor
This commit is contained in:
Denozordec
2026-04-07 21:09:38 +07:00
parent f877ec89a7
commit 1e77e1a2b8
3 changed files with 176 additions and 9 deletions
+33 -9
View File
@@ -254,11 +254,13 @@ func (s *Server) handlePostCDNSource(w http.ResponseWriter, r *http.Request) {
writeProblem(w, http.StatusBadRequest, "Bad Request", "invalid json")
return
}
x, err := s.store.CreateCDNSource(a.TenantID, r.PathValue("module_id"), &body)
mid := r.PathValue("module_id")
x, err := s.store.CreateCDNSource(a.TenantID, mid, &body)
if err != nil {
writeStoreErr(w, err)
return
}
s.enqueueModuleRefreshIfEnabled(a.TenantID, mid, "cdn_source_create")
writeJSON(w, http.StatusCreated, cdnSourceJSON(x))
}
@@ -272,11 +274,13 @@ func (s *Server) handlePatchCDNSource(w http.ResponseWriter, r *http.Request) {
writeProblem(w, http.StatusBadRequest, "Bad Request", "invalid json")
return
}
x, err := s.store.UpdateCDNSource(a.TenantID, r.PathValue("module_id"), r.PathValue("source_id"), &body)
mid := r.PathValue("module_id")
x, err := s.store.UpdateCDNSource(a.TenantID, mid, r.PathValue("source_id"), &body)
if err != nil {
writeStoreErr(w, err)
return
}
s.enqueueModuleRefreshIfEnabled(a.TenantID, mid, "cdn_source_patch")
writeJSON(w, http.StatusOK, cdnSourceJSON(x))
}
@@ -285,10 +289,12 @@ func (s *Server) handleDeleteCDNSource(w http.ResponseWriter, r *http.Request) {
if !ok || !s.requireAtLeast(w, a, "editor") {
return
}
if err := s.store.DeleteCDNSource(a.TenantID, r.PathValue("module_id"), r.PathValue("source_id")); err != nil {
mid := r.PathValue("module_id")
if err := s.store.DeleteCDNSource(a.TenantID, mid, r.PathValue("source_id")); err != nil {
writeStoreErr(w, err)
return
}
s.enqueueModuleRefreshIfEnabled(a.TenantID, mid, "cdn_source_delete")
w.WriteHeader(http.StatusNoContent)
}
@@ -344,11 +350,13 @@ func (s *Server) handlePostAS(w http.ResponseWriter, r *http.Request) {
writeProblem(w, http.StatusBadRequest, "Bad Request", "invalid json")
return
}
x, err := s.store.CreateASEntry(a.TenantID, r.PathValue("module_id"), &body)
mid := r.PathValue("module_id")
x, err := s.store.CreateASEntry(a.TenantID, mid, &body)
if err != nil {
writeStoreErr(w, err)
return
}
s.enqueueModuleRefreshIfEnabled(a.TenantID, mid, "as_entry_create")
writeJSON(w, http.StatusCreated, asEntryJSON(x))
}
@@ -362,11 +370,13 @@ func (s *Server) handlePatchAS(w http.ResponseWriter, r *http.Request) {
writeProblem(w, http.StatusBadRequest, "Bad Request", "invalid json")
return
}
x, err := s.store.UpdateASEntry(a.TenantID, r.PathValue("module_id"), r.PathValue("entry_id"), &body)
mid := r.PathValue("module_id")
x, err := s.store.UpdateASEntry(a.TenantID, mid, r.PathValue("entry_id"), &body)
if err != nil {
writeStoreErr(w, err)
return
}
s.enqueueModuleRefreshIfEnabled(a.TenantID, mid, "as_entry_patch")
writeJSON(w, http.StatusOK, asEntryJSON(x))
}
@@ -375,10 +385,12 @@ func (s *Server) handleDeleteAS(w http.ResponseWriter, r *http.Request) {
if !ok || !s.requireAtLeast(w, a, "editor") {
return
}
if err := s.store.DeleteASEntry(a.TenantID, r.PathValue("module_id"), r.PathValue("entry_id")); err != nil {
mid := r.PathValue("module_id")
if err := s.store.DeleteASEntry(a.TenantID, mid, r.PathValue("entry_id")); err != nil {
writeStoreErr(w, err)
return
}
s.enqueueModuleRefreshIfEnabled(a.TenantID, mid, "as_entry_delete")
w.WriteHeader(http.StatusNoContent)
}
@@ -419,11 +431,13 @@ func (s *Server) handlePostDomain(w http.ResponseWriter, r *http.Request) {
writeProblem(w, http.StatusBadRequest, "Bad Request", "invalid json")
return
}
x, err := s.store.CreateDomainEntry(a.TenantID, r.PathValue("module_id"), &body)
mid := r.PathValue("module_id")
x, err := s.store.CreateDomainEntry(a.TenantID, mid, &body)
if err != nil {
writeStoreErr(w, err)
return
}
s.enqueueModuleRefreshIfEnabled(a.TenantID, mid, "domain_entry_create")
writeJSON(w, http.StatusCreated, domainEntryJSON(x))
}
@@ -437,11 +451,13 @@ func (s *Server) handlePatchDomain(w http.ResponseWriter, r *http.Request) {
writeProblem(w, http.StatusBadRequest, "Bad Request", "invalid json")
return
}
x, err := s.store.UpdateDomainEntry(a.TenantID, r.PathValue("module_id"), r.PathValue("entry_id"), &body)
mid := r.PathValue("module_id")
x, err := s.store.UpdateDomainEntry(a.TenantID, mid, r.PathValue("entry_id"), &body)
if err != nil {
writeStoreErr(w, err)
return
}
s.enqueueModuleRefreshIfEnabled(a.TenantID, mid, "domain_entry_patch")
writeJSON(w, http.StatusOK, domainEntryJSON(x))
}
@@ -450,10 +466,12 @@ func (s *Server) handleDeleteDomain(w http.ResponseWriter, r *http.Request) {
if !ok || !s.requireAtLeast(w, a, "editor") {
return
}
if err := s.store.DeleteDomainEntry(a.TenantID, r.PathValue("module_id"), r.PathValue("entry_id")); err != nil {
mid := r.PathValue("module_id")
if err := s.store.DeleteDomainEntry(a.TenantID, mid, r.PathValue("entry_id")); err != nil {
writeStoreErr(w, err)
return
}
s.enqueueModuleRefreshIfEnabled(a.TenantID, mid, "domain_entry_delete")
w.WriteHeader(http.StatusNoContent)
}
@@ -713,6 +731,9 @@ func (s *Server) handleImportModuleEntriesCSV(w http.ResponseWriter, r *http.Req
}
imported++
}
if imported > 0 {
s.enqueueModuleRefreshIfEnabled(a.TenantID, moduleID, "as_entry_import_csv")
}
case "DOMAINS":
for i := start; i < len(rows); i++ {
rec := rows[i]
@@ -740,6 +761,9 @@ func (s *Server) handleImportModuleEntriesCSV(w http.ResponseWriter, r *http.Req
}
imported++
}
if imported > 0 {
s.enqueueModuleRefreshIfEnabled(a.TenantID, moduleID, "domain_entry_import_csv")
}
case "IP_RANGES":
for i := start; i < len(rows); i++ {
rec := rows[i]