mirror of
https://github.com/DeBrosOfficial/network.git
synced 2026-01-30 09:53:03 +00:00
bug fixing
This commit is contained in:
parent
3d3b0d2ee6
commit
6101455f4a
45
migrations/009_dns_records_multi.sql
Normal file
45
migrations/009_dns_records_multi.sql
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
-- Migration 009: Update DNS Records to Support Multiple Records per FQDN
|
||||||
|
-- This allows round-robin A records and multiple NS records for the same domain
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
-- SQLite doesn't support DROP CONSTRAINT, so we recreate the table
|
||||||
|
-- First, create the new table structure
|
||||||
|
CREATE TABLE IF NOT EXISTS dns_records_new (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
fqdn TEXT NOT NULL, -- Fully qualified domain name (e.g., myapp.node-7prvNa.orama.network)
|
||||||
|
record_type TEXT NOT NULL DEFAULT 'A',-- DNS record type: A, AAAA, CNAME, TXT, NS, SOA
|
||||||
|
value TEXT NOT NULL, -- IP address or target value
|
||||||
|
ttl INTEGER NOT NULL DEFAULT 300, -- Time to live in seconds
|
||||||
|
priority INTEGER DEFAULT 0, -- Priority for MX/SRV records, or weight for round-robin
|
||||||
|
namespace TEXT NOT NULL DEFAULT 'system', -- Namespace that owns this record
|
||||||
|
deployment_id TEXT, -- Optional: deployment that created this record
|
||||||
|
node_id TEXT, -- Optional: specific node ID for node-specific routing
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,-- Enable/disable without deleting
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
created_by TEXT NOT NULL DEFAULT 'system', -- Wallet address or 'system' for auto-created records
|
||||||
|
UNIQUE(fqdn, record_type, value) -- Allow multiple records of same type for same FQDN, but not duplicates
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Copy existing data if the old table exists
|
||||||
|
INSERT OR IGNORE INTO dns_records_new (id, fqdn, record_type, value, ttl, namespace, deployment_id, node_id, is_active, created_at, updated_at, created_by)
|
||||||
|
SELECT id, fqdn, record_type, value, ttl, namespace, deployment_id, node_id, is_active, created_at, updated_at, created_by
|
||||||
|
FROM dns_records WHERE 1=1;
|
||||||
|
|
||||||
|
-- Drop old table and rename new one
|
||||||
|
DROP TABLE IF EXISTS dns_records;
|
||||||
|
ALTER TABLE dns_records_new RENAME TO dns_records;
|
||||||
|
|
||||||
|
-- Recreate indexes
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_dns_records_fqdn ON dns_records(fqdn);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_dns_records_fqdn_type ON dns_records(fqdn, record_type);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_dns_records_namespace ON dns_records(namespace);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_dns_records_deployment ON dns_records(deployment_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_dns_records_node_id ON dns_records(node_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_dns_records_active ON dns_records(is_active);
|
||||||
|
|
||||||
|
-- Mark migration as applied
|
||||||
|
INSERT OR IGNORE INTO schema_migrations(version) VALUES (9);
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
@ -341,6 +341,18 @@ func (o *Orchestrator) restartServices() error {
|
|||||||
|
|
||||||
// Restart services to apply changes - use getProductionServices to only restart existing services
|
// Restart services to apply changes - use getProductionServices to only restart existing services
|
||||||
services := utils.GetProductionServices()
|
services := utils.GetProductionServices()
|
||||||
|
|
||||||
|
// If this is a nameserver, also restart CoreDNS and Caddy
|
||||||
|
if o.setup.IsNameserver() {
|
||||||
|
nameserverServices := []string{"coredns", "caddy"}
|
||||||
|
for _, svc := range nameserverServices {
|
||||||
|
unitPath := filepath.Join("/etc/systemd/system", svc+".service")
|
||||||
|
if _, err := os.Stat(unitPath); err == nil {
|
||||||
|
services = append(services, svc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(services) == 0 {
|
if len(services) == 0 {
|
||||||
fmt.Printf(" ⚠️ No services found to restart\n")
|
fmt.Printf(" ⚠️ No services found to restart\n")
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -142,11 +142,66 @@ func (b *Backend) parseValue(recordType, value string) (interface{}, error) {
|
|||||||
case "TXT":
|
case "TXT":
|
||||||
return []string{value}, nil
|
return []string{value}, nil
|
||||||
|
|
||||||
|
case "NS":
|
||||||
|
return dns.Fqdn(value), nil
|
||||||
|
|
||||||
|
case "SOA":
|
||||||
|
// SOA format: "mname rname serial refresh retry expire minimum"
|
||||||
|
// Example: "ns1.dbrs.space. admin.dbrs.space. 2026012401 3600 1800 604800 300"
|
||||||
|
return b.parseSOA(value)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported record type: %s", recordType)
|
return nil, fmt.Errorf("unsupported record type: %s", recordType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseSOA parses a SOA record value string
|
||||||
|
// Format: "mname rname serial refresh retry expire minimum"
|
||||||
|
func (b *Backend) parseSOA(value string) (*dns.SOA, error) {
|
||||||
|
parts := strings.Fields(value)
|
||||||
|
if len(parts) < 7 {
|
||||||
|
return nil, fmt.Errorf("invalid SOA format, expected 7 fields: %s", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
serial, err := parseUint32(parts[2])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid SOA serial: %w", err)
|
||||||
|
}
|
||||||
|
refresh, err := parseUint32(parts[3])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid SOA refresh: %w", err)
|
||||||
|
}
|
||||||
|
retry, err := parseUint32(parts[4])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid SOA retry: %w", err)
|
||||||
|
}
|
||||||
|
expire, err := parseUint32(parts[5])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid SOA expire: %w", err)
|
||||||
|
}
|
||||||
|
minttl, err := parseUint32(parts[6])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid SOA minimum: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &dns.SOA{
|
||||||
|
Ns: dns.Fqdn(parts[0]),
|
||||||
|
Mbox: dns.Fqdn(parts[1]),
|
||||||
|
Serial: serial,
|
||||||
|
Refresh: refresh,
|
||||||
|
Retry: retry,
|
||||||
|
Expire: expire,
|
||||||
|
Minttl: minttl,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseUint32 parses a string to uint32
|
||||||
|
func parseUint32(s string) (uint32, error) {
|
||||||
|
var val uint32
|
||||||
|
_, err := fmt.Sscanf(s, "%d", &val)
|
||||||
|
return val, err
|
||||||
|
}
|
||||||
|
|
||||||
// ping tests the RQLite connection
|
// ping tests the RQLite connection
|
||||||
func (b *Backend) ping() error {
|
func (b *Backend) ping() error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
@ -205,6 +260,10 @@ func qTypeToString(qtype uint16) string {
|
|||||||
return "CNAME"
|
return "CNAME"
|
||||||
case dns.TypeTXT:
|
case dns.TypeTXT:
|
||||||
return "TXT"
|
return "TXT"
|
||||||
|
case dns.TypeNS:
|
||||||
|
return "NS"
|
||||||
|
case dns.TypeSOA:
|
||||||
|
return "SOA"
|
||||||
default:
|
default:
|
||||||
return dns.TypeToString[qtype]
|
return dns.TypeToString[qtype]
|
||||||
}
|
}
|
||||||
@ -221,6 +280,10 @@ func stringToQType(s string) uint16 {
|
|||||||
return dns.TypeCNAME
|
return dns.TypeCNAME
|
||||||
case "TXT":
|
case "TXT":
|
||||||
return dns.TypeTXT
|
return dns.TypeTXT
|
||||||
|
case "NS":
|
||||||
|
return dns.TypeNS
|
||||||
|
case "SOA":
|
||||||
|
return dns.TypeSOA
|
||||||
default:
|
default:
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
@ -150,6 +150,15 @@ func (p *RQLitePlugin) buildRR(qname string, record *DNSRecord) dns.RR {
|
|||||||
Hdr: header,
|
Hdr: header,
|
||||||
Txt: record.ParsedValue.([]string),
|
Txt: record.ParsedValue.([]string),
|
||||||
}
|
}
|
||||||
|
case dns.TypeNS:
|
||||||
|
return &dns.NS{
|
||||||
|
Hdr: header,
|
||||||
|
Ns: record.ParsedValue.(string),
|
||||||
|
}
|
||||||
|
case dns.TypeSOA:
|
||||||
|
soa := record.ParsedValue.(*dns.SOA)
|
||||||
|
soa.Hdr = header
|
||||||
|
return soa
|
||||||
default:
|
default:
|
||||||
p.logger.Warn("Unsupported record type",
|
p.logger.Warn("Unsupported record type",
|
||||||
zap.Uint16("type", record.Type),
|
zap.Uint16("type", record.Type),
|
||||||
|
|||||||
@ -1,11 +1,15 @@
|
|||||||
package installers
|
package installers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -191,23 +195,26 @@ func (ci *CoreDNSInstaller) Install() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure creates CoreDNS configuration files
|
// Configure creates CoreDNS configuration files and seeds static DNS records into RQLite
|
||||||
func (ci *CoreDNSInstaller) Configure(domain string, rqliteDSN string, ns1IP, ns2IP, ns3IP string) error {
|
func (ci *CoreDNSInstaller) Configure(domain string, rqliteDSN string, ns1IP, ns2IP, ns3IP string) error {
|
||||||
configDir := "/etc/coredns"
|
configDir := "/etc/coredns"
|
||||||
if err := os.MkdirAll(configDir, 0755); err != nil {
|
if err := os.MkdirAll(configDir, 0755); err != nil {
|
||||||
return fmt.Errorf("failed to create config directory: %w", err)
|
return fmt.Errorf("failed to create config directory: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create Corefile
|
// Create Corefile (uses only RQLite plugin)
|
||||||
corefile := ci.generateCorefile(domain, rqliteDSN, configDir)
|
corefile := ci.generateCorefile(domain, rqliteDSN)
|
||||||
if err := os.WriteFile(filepath.Join(configDir, "Corefile"), []byte(corefile), 0644); err != nil {
|
if err := os.WriteFile(filepath.Join(configDir, "Corefile"), []byte(corefile), 0644); err != nil {
|
||||||
return fmt.Errorf("failed to write Corefile: %w", err)
|
return fmt.Errorf("failed to write Corefile: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create zone file
|
// Seed static DNS records into RQLite
|
||||||
zonefile := ci.generateZoneFile(domain, ns1IP, ns2IP, ns3IP)
|
fmt.Fprintf(ci.logWriter, " Seeding static DNS records into RQLite...\n")
|
||||||
if err := os.WriteFile(filepath.Join(configDir, "db."+domain), []byte(zonefile), 0644); err != nil {
|
if err := ci.seedStaticRecords(domain, rqliteDSN, ns1IP, ns2IP, ns3IP); err != nil {
|
||||||
return fmt.Errorf("failed to write zone file: %w", err)
|
// Don't fail on seed errors - RQLite might not be up yet
|
||||||
|
fmt.Fprintf(ci.logWriter, " ⚠️ Could not seed DNS records (RQLite may not be ready): %v\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(ci.logWriter, " ✓ Static DNS records seeded\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -263,14 +270,14 @@ rqlite:rqlite
|
|||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateCorefile creates the CoreDNS configuration
|
// generateCorefile creates the CoreDNS configuration (RQLite only)
|
||||||
func (ci *CoreDNSInstaller) generateCorefile(domain, rqliteDSN, configDir string) string {
|
func (ci *CoreDNSInstaller) generateCorefile(domain, rqliteDSN string) string {
|
||||||
return fmt.Sprintf(`# CoreDNS configuration for %s
|
return fmt.Sprintf(`# CoreDNS configuration for %s
|
||||||
# Uses RQLite for dynamic DNS records (deployments, ACME challenges)
|
# Uses RQLite for ALL DNS records (static + dynamic)
|
||||||
# Falls back to static zone file for base records (SOA, NS)
|
# Static records (SOA, NS, A) are seeded into RQLite during installation
|
||||||
|
|
||||||
%s {
|
%s {
|
||||||
# First try RQLite for dynamic records (TXT for ACME, A for deployments)
|
# RQLite handles all records: SOA, NS, A, TXT (ACME), etc.
|
||||||
rqlite {
|
rqlite {
|
||||||
dsn %s
|
dsn %s
|
||||||
refresh 5s
|
refresh 5s
|
||||||
@ -278,9 +285,6 @@ func (ci *CoreDNSInstaller) generateCorefile(domain, rqliteDSN, configDir string
|
|||||||
cache_size 10000
|
cache_size 10000
|
||||||
}
|
}
|
||||||
|
|
||||||
# Fall back to static zone file for SOA/NS records
|
|
||||||
file %s/db.%s
|
|
||||||
|
|
||||||
# Enable logging and error reporting
|
# Enable logging and error reporting
|
||||||
log
|
log
|
||||||
errors
|
errors
|
||||||
@ -293,44 +297,95 @@ func (ci *CoreDNSInstaller) generateCorefile(domain, rqliteDSN, configDir string
|
|||||||
cache 300
|
cache 300
|
||||||
errors
|
errors
|
||||||
}
|
}
|
||||||
`, domain, domain, rqliteDSN, configDir, domain)
|
`, domain, domain, rqliteDSN)
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateZoneFile creates the static DNS zone file
|
// seedStaticRecords inserts static zone records into RQLite
|
||||||
func (ci *CoreDNSInstaller) generateZoneFile(domain, ns1IP, ns2IP, ns3IP string) string {
|
func (ci *CoreDNSInstaller) seedStaticRecords(domain, rqliteDSN, ns1IP, ns2IP, ns3IP string) error {
|
||||||
return fmt.Sprintf(`$ORIGIN %s.
|
// Generate serial based on current date
|
||||||
$TTL 300
|
serial := fmt.Sprintf("%d", time.Now().Unix())
|
||||||
|
|
||||||
@ IN SOA ns1.%s. admin.%s. (
|
// SOA record format: "mname rname serial refresh retry expire minimum"
|
||||||
2024012401 ; Serial
|
soaValue := fmt.Sprintf("ns1.%s. admin.%s. %s 3600 1800 604800 300", domain, domain, serial)
|
||||||
3600 ; Refresh
|
|
||||||
1800 ; Retry
|
|
||||||
604800 ; Expire
|
|
||||||
300 ) ; Negative TTL
|
|
||||||
|
|
||||||
; Nameservers
|
// Define all static records
|
||||||
@ IN NS ns1.%s.
|
records := []struct {
|
||||||
@ IN NS ns2.%s.
|
fqdn string
|
||||||
@ IN NS ns3.%s.
|
recordType string
|
||||||
|
value string
|
||||||
|
ttl int
|
||||||
|
}{
|
||||||
|
// SOA record
|
||||||
|
{domain + ".", "SOA", soaValue, 300},
|
||||||
|
|
||||||
; Nameserver A records
|
// NS records
|
||||||
ns1 IN A %s
|
{domain + ".", "NS", "ns1." + domain + ".", 300},
|
||||||
ns2 IN A %s
|
{domain + ".", "NS", "ns2." + domain + ".", 300},
|
||||||
ns3 IN A %s
|
{domain + ".", "NS", "ns3." + domain + ".", 300},
|
||||||
|
|
||||||
; Root domain points to all nodes (round-robin)
|
// Nameserver A records (glue)
|
||||||
@ IN A %s
|
{"ns1." + domain + ".", "A", ns1IP, 300},
|
||||||
@ IN A %s
|
{"ns2." + domain + ".", "A", ns2IP, 300},
|
||||||
@ IN A %s
|
{"ns3." + domain + ".", "A", ns3IP, 300},
|
||||||
|
|
||||||
; Wildcard fallback (RQLite records take precedence for specific subdomains)
|
// Root domain A records (round-robin)
|
||||||
* IN A %s
|
{domain + ".", "A", ns1IP, 300},
|
||||||
* IN A %s
|
{domain + ".", "A", ns2IP, 300},
|
||||||
* IN A %s
|
{domain + ".", "A", ns3IP, 300},
|
||||||
`, domain, domain, domain, domain, domain, domain,
|
|
||||||
ns1IP, ns2IP, ns3IP,
|
// Wildcard A records (round-robin)
|
||||||
ns1IP, ns2IP, ns3IP,
|
{"*." + domain + ".", "A", ns1IP, 300},
|
||||||
ns1IP, ns2IP, ns3IP)
|
{"*." + domain + ".", "A", ns2IP, 300},
|
||||||
|
{"*." + domain + ".", "A", ns3IP, 300},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build SQL statements
|
||||||
|
var statements []string
|
||||||
|
for _, r := range records {
|
||||||
|
// Use INSERT OR REPLACE to handle updates
|
||||||
|
stmt := fmt.Sprintf(
|
||||||
|
`INSERT OR REPLACE INTO dns_records (fqdn, record_type, value, ttl, namespace, created_by) VALUES ('%s', '%s', '%s', %d, 'system', 'system')`,
|
||||||
|
r.fqdn, r.recordType, r.value, r.ttl,
|
||||||
|
)
|
||||||
|
statements = append(statements, stmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute via RQLite HTTP API
|
||||||
|
return ci.executeRQLiteStatements(rqliteDSN, statements)
|
||||||
|
}
|
||||||
|
|
||||||
|
// executeRQLiteStatements executes SQL statements via RQLite HTTP API
|
||||||
|
func (ci *CoreDNSInstaller) executeRQLiteStatements(rqliteDSN string, statements []string) error {
|
||||||
|
// RQLite execute endpoint
|
||||||
|
executeURL := rqliteDSN + "/db/execute?pretty&timings"
|
||||||
|
|
||||||
|
// Build request body
|
||||||
|
body, err := json.Marshal(statements)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to marshal statements: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create request
|
||||||
|
req, err := http.NewRequest("POST", executeURL, bytes.NewReader(body))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create request: %w", err)
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
// Execute with timeout
|
||||||
|
client := &http.Client{Timeout: 10 * time.Second}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute request: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
respBody, _ := io.ReadAll(resp.Body)
|
||||||
|
return fmt.Errorf("RQLite returned status %d: %s", resp.StatusCode, string(respBody))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// containsLine checks if a string contains a specific line
|
// containsLine checks if a string contains a specific line
|
||||||
|
|||||||
@ -457,15 +457,18 @@ func (h *DomainHandler) createDNSRecord(ctx context.Context, domain, deploymentI
|
|||||||
|
|
||||||
// Create DNS A record
|
// Create DNS A record
|
||||||
dnsQuery := `
|
dnsQuery := `
|
||||||
INSERT INTO dns_records (fqdn, record_type, value, ttl, namespace, deployment_id, node_id, created_by, created_at)
|
INSERT INTO dns_records (fqdn, record_type, value, ttl, namespace, deployment_id, node_id, created_by, created_at, updated_at)
|
||||||
VALUES (?, 'A', ?, 300, ?, ?, ?, 'system', ?)
|
VALUES (?, 'A', ?, 300, ?, ?, ?, 'system', ?, ?)
|
||||||
ON CONFLICT(fqdn) DO UPDATE SET value = ?, updated_at = ?
|
ON CONFLICT(fqdn, record_type, value) DO UPDATE SET
|
||||||
|
deployment_id = excluded.deployment_id,
|
||||||
|
node_id = excluded.node_id,
|
||||||
|
updated_at = excluded.updated_at
|
||||||
`
|
`
|
||||||
|
|
||||||
fqdn := domain + "."
|
fqdn := domain + "."
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
_, err = h.service.db.Exec(ctx, dnsQuery, fqdn, nodeIP, "", deploymentID, homeNodeID, now, nodeIP, now)
|
_, err = h.service.db.Exec(ctx, dnsQuery, fqdn, nodeIP, "", deploymentID, homeNodeID, now, now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.logger.Error("Failed to create DNS record", zap.Error(err))
|
h.logger.Error("Failed to create DNS record", zap.Error(err))
|
||||||
return
|
return
|
||||||
|
|||||||
@ -324,7 +324,10 @@ func (s *DeploymentService) createDNSRecord(ctx context.Context, fqdn, recordTyp
|
|||||||
query := `
|
query := `
|
||||||
INSERT INTO dns_records (fqdn, record_type, value, ttl, namespace, deployment_id, is_active, created_at, updated_at, created_by)
|
INSERT INTO dns_records (fqdn, record_type, value, ttl, namespace, deployment_id, is_active, created_at, updated_at, created_by)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
ON CONFLICT(fqdn) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at
|
ON CONFLICT(fqdn, record_type, value) DO UPDATE SET
|
||||||
|
deployment_id = excluded.deployment_id,
|
||||||
|
updated_at = excluded.updated_at,
|
||||||
|
is_active = TRUE
|
||||||
`
|
`
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|||||||
35
scripts/generate-source-archive.sh
Executable file
35
scripts/generate-source-archive.sh
Executable file
@ -0,0 +1,35 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Generates a tarball of the current codebase for deployment
|
||||||
|
# Output: /tmp/network-source.tar.gz
|
||||||
|
#
|
||||||
|
# Usage: ./scripts/generate-source-archive.sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||||
|
OUTPUT="/tmp/network-source.tar.gz"
|
||||||
|
|
||||||
|
echo "Generating source archive..."
|
||||||
|
|
||||||
|
cd "$PROJECT_ROOT"
|
||||||
|
|
||||||
|
# Remove root-level binaries before archiving (they'll be rebuilt on VPS)
|
||||||
|
rm -f gateway cli node orama-cli-linux 2>/dev/null
|
||||||
|
|
||||||
|
tar czf "$OUTPUT" \
|
||||||
|
--exclude='.git' \
|
||||||
|
--exclude='node_modules' \
|
||||||
|
--exclude='*.log' \
|
||||||
|
--exclude='.DS_Store' \
|
||||||
|
--exclude='bin/' \
|
||||||
|
--exclude='dist/' \
|
||||||
|
--exclude='coverage/' \
|
||||||
|
--exclude='.claude/' \
|
||||||
|
--exclude='testdata/' \
|
||||||
|
--exclude='examples/' \
|
||||||
|
--exclude='*.tar.gz' \
|
||||||
|
.
|
||||||
|
|
||||||
|
echo "Archive created: $OUTPUT"
|
||||||
|
echo "Size: $(du -h $OUTPUT | cut -f1)"
|
||||||
Loading…
x
Reference in New Issue
Block a user