diff --git a/db/models.go b/db/models.go index 5ff15b4..62531b8 100644 --- a/db/models.go +++ b/db/models.go @@ -4,9 +4,19 @@ package db +import ( + "database/sql" +) + type Config struct { ApiToken string `json:"api_token"` ZoneID string `json:"zone_id"` Domain string `json:"domain"` UpdatePeriod string `json:"update_period"` } + +type StaticRecord struct { + RecordID string `json:"record_id"` + RecordName string `json:"record_name"` + CreatedAt sql.NullTime `json:"created_at"` +} diff --git a/db/queries.sql b/db/queries.sql index ae40bc0..1f7b1a7 100644 --- a/db/queries.sql +++ b/db/queries.sql @@ -1,3 +1,4 @@ +-- db/queries.sql -- name: GetConfig :one SELECT * FROM config LIMIT 1; @@ -25,7 +26,26 @@ CREATE TABLE IF NOT EXISTS config ( update_period TEXT NOT NULL DEFAULT '0 */6 * * *' ); +CREATE TABLE IF NOT EXISTS static_records ( + record_id TEXT PRIMARY KEY, + record_name TEXT NOT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP +); + -- Insert default config if none exists INSERT OR IGNORE INTO config (api_token, zone_id, domain, update_period) SELECT '', '', 'mz.uy', '0 */6 * * *' WHERE NOT EXISTS (SELECT 1 FROM config); + +-- name: AddStaticRecord :exec +INSERT OR REPLACE INTO static_records (record_id, record_name) +VALUES (?, ?); + +-- name: RemoveStaticRecord :exec +DELETE FROM static_records WHERE record_id = ?; + +-- name: GetStaticRecords :many +SELECT record_id, record_name FROM static_records; + +-- name: IsStaticRecord :one +SELECT CAST(EXISTS(SELECT 1 FROM static_records WHERE record_id = ?) AS BOOLEAN) AS is_static; diff --git a/db/queries.sql.go b/db/queries.sql.go index 8ba426c..383f579 100644 --- a/db/queries.sql.go +++ b/db/queries.sql.go @@ -9,6 +9,21 @@ import ( "context" ) +const addStaticRecord = `-- name: AddStaticRecord :exec +INSERT OR REPLACE INTO static_records (record_id, record_name) +VALUES (?, ?) +` + +type AddStaticRecordParams struct { + RecordID string `json:"record_id"` + RecordName string `json:"record_name"` +} + +func (q *Queries) AddStaticRecord(ctx context.Context, arg AddStaticRecordParams) error { + _, err := q.db.ExecContext(ctx, addStaticRecord, arg.RecordID, arg.RecordName) + return err +} + const deleteAllConfig = `-- name: DeleteAllConfig :exec DELETE FROM config ` @@ -22,6 +37,7 @@ const getConfig = `-- name: GetConfig :one SELECT api_token, zone_id, domain, update_period FROM config LIMIT 1 ` +// db/queries.sql func (q *Queries) GetConfig(ctx context.Context) (Config, error) { row := q.db.QueryRowContext(ctx, getConfig) var i Config @@ -34,6 +50,38 @@ func (q *Queries) GetConfig(ctx context.Context) (Config, error) { return i, err } +const getStaticRecords = `-- name: GetStaticRecords :many +SELECT record_id, record_name FROM static_records +` + +type GetStaticRecordsRow struct { + RecordID string `json:"record_id"` + RecordName string `json:"record_name"` +} + +func (q *Queries) GetStaticRecords(ctx context.Context) ([]GetStaticRecordsRow, error) { + rows, err := q.db.QueryContext(ctx, getStaticRecords) + if err != nil { + return nil, err + } + defer rows.Close() + var items []GetStaticRecordsRow + for rows.Next() { + var i GetStaticRecordsRow + if err := rows.Scan(&i.RecordID, &i.RecordName); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const initSchema = `-- name: InitSchema :exec CREATE TABLE IF NOT EXISTS config ( api_token TEXT NOT NULL DEFAULT '', @@ -70,6 +118,26 @@ func (q *Queries) InsertConfig(ctx context.Context, arg InsertConfigParams) erro return err } +const isStaticRecord = `-- name: IsStaticRecord :one +SELECT CAST(EXISTS(SELECT 1 FROM static_records WHERE record_id = ?) AS BOOLEAN) AS is_static +` + +func (q *Queries) IsStaticRecord(ctx context.Context, recordID string) (bool, error) { + row := q.db.QueryRowContext(ctx, isStaticRecord, recordID) + var is_static bool + err := row.Scan(&is_static) + return is_static, err +} + +const removeStaticRecord = `-- name: RemoveStaticRecord :exec +DELETE FROM static_records WHERE record_id = ? +` + +func (q *Queries) RemoveStaticRecord(ctx context.Context, recordID string) error { + _, err := q.db.ExecContext(ctx, removeStaticRecord, recordID) + return err +} + const upsertConfig = `-- name: UpsertConfig :exec INSERT INTO config (api_token, zone_id, domain, update_period) VALUES (?, ?, ?, ?) diff --git a/db/schema.sql b/db/schema.sql index 1193686..5a74912 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -4,3 +4,10 @@ CREATE TABLE IF NOT EXISTS config ( domain TEXT NOT NULL DEFAULT 'mz.uy', update_period TEXT NOT NULL DEFAULT '0 */6 * * *' ); + +-- Tabla para marcar records como estáticos +CREATE TABLE IF NOT EXISTS static_records ( + record_id TEXT PRIMARY KEY, + record_name TEXT NOT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP +); diff --git a/go.mod b/go.mod index 53c6bc6..f7b4aee 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,8 @@ module ddns-manager go 1.24.1 require ( - github.com/a-h/templ v0.3.898 + github.com/a-h/templ v0.3.924 github.com/cloudflare/cloudflare-go v0.115.0 - github.com/davecgh/go-spew v1.1.1 github.com/labstack/echo/v4 v4.13.3 github.com/mattn/go-sqlite3 v1.14.28 github.com/robfig/cron/v3 v3.0.1 @@ -16,6 +15,7 @@ require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/cubicdaiya/gonp v1.0.4 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/fatih/structtag v1.2.0 // indirect github.com/go-sql-driver/mysql v1.9.2 // indirect diff --git a/go.sum b/go.sum index ef1d1fb..27e534e 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/a-h/templ v0.3.898 h1:g9oxL/dmM6tvwRe2egJS8hBDQTncokbMoOFk1oJMX7s= -github.com/a-h/templ v0.3.898/go.mod h1:oLBbZVQ6//Q6zpvSMPTuBK0F3qOtBdFBcGRspcT+VNQ= +github.com/a-h/templ v0.3.924 h1:t5gZqTneXqvehpNZsgtnlOscnBboNh9aASBH2MgV/0k= +github.com/a-h/templ v0.3.924/go.mod h1:FFAu4dI//ESmEN7PQkJ7E7QfnSEMdcnu7QrAY8Dn334= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -20,20 +20,27 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU= github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/cel-go v0.24.1 h1:jsBCtxG8mM5wiUJDSGUqU0K7Mtr3w7Eyv00rw4DiZxI= github.com/google/cel-go v0.24.1/go.mod h1:Hdf9TqOaTNSFQA1ybQaRqATVoK7m/zcf7IMhGXP5zI8= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -49,8 +56,12 @@ github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFr github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY= github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= @@ -84,6 +95,8 @@ github.com/riza-io/grpc-go v0.2.0 h1:2HxQKFVE7VuYstcJ8zqpN84VnAoJ4dCL6YFhJewNcHQ github.com/riza-io/grpc-go v0.2.0/go.mod h1:2bDvR9KkKC3KhtlSHfR3dAXjUMT86kg4UfWFyVGWqi8= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= @@ -110,12 +123,26 @@ github.com/wasilibs/go-pgquery v0.0.0-20250409022910-10ac41983c07 h1:mJdDDPblDfP github.com/wasilibs/go-pgquery v0.0.0-20250409022910-10ac41983c07/go.mod h1:Ak17IJ037caFp4jpCw/iQQ7/W74Sqpb1YuKJU6HTKfM= github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 h1:OvLBa8SqJnZ6P+mjlzc2K7PM22rRUPE1x32G9DTPrC4= github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52/go.mod h1:jMeV4Vpbi8osrE/pKUxRZkVaA0EX7NZN0A9/oRzgpgY= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -129,6 +156,8 @@ golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbV golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= @@ -149,6 +178,8 @@ golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= +golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24= @@ -163,6 +194,8 @@ google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9x google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= @@ -172,11 +205,27 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +modernc.org/cc/v4 v4.25.2 h1:T2oH7sZdGvTaie0BRNFbIYsabzCxUQg8nLqCdQ2i0ic= +modernc.org/cc/v4 v4.25.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= +modernc.org/ccgo/v4 v4.25.1 h1:TFSzPrAGmDsdnhT9X2UrcPMI3N/mJ9/X9ykKXwLhDsU= +modernc.org/ccgo/v4 v4.25.1/go.mod h1:njjuAYiPflywOOrm3B7kCB444ONP5pAVr8PIEoE0uDw= +modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= +modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= +modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= modernc.org/libc v1.62.1 h1:s0+fv5E3FymN8eJVmnk0llBe6rOxCu/DEU+XygRbS8s= modernc.org/libc v1.62.1/go.mod h1:iXhATfJQLjG3NWy56a6WVU73lWOcdYVxsvwCgoPljuo= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= modernc.org/memory v1.9.1 h1:V/Z1solwAVmMW1yttq3nDdZPJqV1rM05Ccq6KMSZ34g= modernc.org/memory v1.9.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= +modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= +modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= +modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= +modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= modernc.org/sqlite v1.37.0 h1:s1TMe7T3Q3ovQiK2Ouz4Jwh7dw4ZDqbebSDTlSJdfjI= modernc.org/sqlite v1.37.0/go.mod h1:5YiWv+YviqGMuGw4V+PNplcyaJ5v+vQd7TQOgkACoJM= +modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= +modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= diff --git a/main.go b/main.go index afed9bf..f4d71d1 100644 --- a/main.go +++ b/main.go @@ -231,6 +231,19 @@ func getDNSRecords(zoneID string) ([]templates.DNSRecord, error) { return nil, err } + // Get static records from database + staticRecords, err := queries.GetStaticRecords(ctx) + if err != nil { + log.Printf("Error getting static records: %v", err) + staticRecords = []db.GetStaticRecordsRow{} // Continue with empty list + } + + // Create a map for faster lookup + staticMap := make(map[string]bool) + for _, sr := range staticRecords { + staticMap[sr.RecordID] = true + } + var records []templates.DNSRecord for _, rec := range recs { records = append(records, templates.DNSRecord{ @@ -240,6 +253,7 @@ func getDNSRecords(zoneID string) ([]templates.DNSRecord, error) { Content: rec.Content, TTL: rec.TTL, Proxied: *rec.Proxied, + IsStatic: staticMap[rec.ID], CreatedOn: rec.CreatedOn.Format(time.RFC3339), }) } @@ -262,13 +276,32 @@ func updateAllRecordsWithCurrentIP(zoneID string) error { ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() + // Get static records to exclude from updates + staticRecords, err := queries.GetStaticRecords(ctx) + if err != nil { + log.Printf("Error getting static records: %v", err) + staticRecords = []db.GetStaticRecordsRow{} // Continue with empty list + } + + staticMap := make(map[string]bool) + for _, sr := range staticRecords { + staticMap[sr.RecordID] = true + } + rc := cloudflare.ZoneIdentifier(zoneID) records, _, err := api.ListDNSRecords(ctx, rc, cloudflare.ListDNSRecordsParams{Type: "A"}) if err != nil { return err } + updatedCount := 0 for _, rec := range records { + // Skip static records + if staticMap[rec.ID] { + log.Printf("Skipping static record %s", rec.Name) + continue + } + if rec.Content != currentIP { _, err := api.UpdateDNSRecord(ctx, rc, cloudflare.UpdateDNSRecordParams{ ID: rec.ID, @@ -280,10 +313,13 @@ func updateAllRecordsWithCurrentIP(zoneID string) error { }) if err != nil { log.Printf("Failed to update record %s: %v", rec.Name, err) + } else { + updatedCount++ } } } + log.Printf("Updated %d A records to current IP %s", updatedCount, currentIP) return nil } @@ -518,6 +554,9 @@ func main() { e.DELETE("/records/:id", func(c echo.Context) error { id := c.Param("id") + // Remove from static records if it exists + queries.RemoveStaticRecord(context.Background(), id) + if err := deleteDNSRecord(config.ZoneID, id); err != nil { return errorResponse(c, "Failed to delete record") } @@ -551,6 +590,68 @@ func main() { return templates.Render(c.Response(), templates.RecordForm("Edit DNS Record", id, config.Domain, record)) }) + // New route to toggle static status + e.POST("/records/:id/toggle-static", func(c echo.Context) error { + id := c.Param("id") + + // Check if record is currently static + isStatic, err := queries.IsStaticRecord(context.Background(), id) + if err != nil { + return errorResponse(c, "Failed to check record status") + } + + // Get record details for the name + records, err := getDNSRecords(config.ZoneID) + if err != nil { + return errorResponse(c, "Failed to load records") + } + + var recordName string + for _, r := range records { + if r.ID == id { + recordName = r.Name + break + } + } + + if recordName == "" { + return errorResponse(c, "Record not found") + } + + // Toggle static status + if isStatic { + // Remove from static records + err = queries.RemoveStaticRecord(context.Background(), id) + if err != nil { + return errorResponse(c, "Failed to remove static status") + } + } else { + // Add to static records + err = queries.AddStaticRecord(context.Background(), db.AddStaticRecordParams{ + RecordID: id, + RecordName: recordName, + }) + if err != nil { + return errorResponse(c, "Failed to add static status") + } + } + + // Return updated table + records, _ = getDNSRecords(config.ZoneID) + currentIP, _ := getCurrentIP() + + var message string + if isStatic { + message = "Record is now dynamic (will auto-update)" + } else { + message = "Record is now static (won't auto-update)" + } + + notification := templates.SuccessNotification(message) + table := templates.DNSRecordsTable(records, currentIP) + return templates.RenderMultiple(c.Response().Writer, notification, table) + }) + e.POST("/update-all-records", func(c echo.Context) error { if err := updateAllRecordsWithCurrentIP(config.ZoneID); err != nil { return errorResponse(c, "Failed to update records") @@ -558,7 +659,7 @@ func main() { records, _ := getDNSRecords(config.ZoneID) currentIP, _ := getCurrentIP() - notification := templates.SuccessNotification("All A records updated") + notification := templates.SuccessNotification("All non-static A records updated") table := templates.DNSRecordsTable(records, currentIP) return templates.RenderMultiple(c.Response().Writer, notification, table) }) diff --git a/templates/alert_templ.go b/templates/alert_templ.go index 83efe2d..76033db 100644 --- a/templates/alert_templ.go +++ b/templates/alert_templ.go @@ -1,6 +1,6 @@ // Code generated by templ - DO NOT EDIT. -// templ: version: v0.3.898 +// templ: version: v0.3.924 package templates diff --git a/templates/index.templ b/templates/index.templ index 86b942b..5381d4a 100644 --- a/templates/index.templ +++ b/templates/index.templ @@ -26,6 +26,7 @@ type DNSRecord struct { Content string TTL int Proxied bool + IsStatic bool // Nueva propiedad para indicar si es estático CreatedOn string } @@ -137,7 +138,7 @@ templ DNSRecordsSection(records []DNSRecord, currentIP string) { method="post" action="/update-all-records" x-target="dns-records-table" - @ajax:before="confirm('Are you sure you want to update all A records to your current IP?') || $event.preventDefault(); updating = true" + @ajax:before="confirm('Are you sure you want to update all non-static A records to your current IP?') || $event.preventDefault(); updating = true" @ajax:after="updating = false" @ajax:error="updating = false" style="display: inline;" @@ -201,13 +202,14 @@ templ DNSRecordsTable(records []DNSRecord, currentIP string) { Content TTL Proxied + Static Actions if len(records) == 0 { - No DNS records found + No DNS records found } else { for _, record := range records { @@ -220,12 +222,12 @@ templ DNSRecordsTable(records []DNSRecord, currentIP string) { } templ DNSRecordRow(record DNSRecord, currentIP string) { - + { record.Type } { record.Name } { record.Content } - if record.Type == "A" { + if record.Type == "A" && !record.IsStatic { if record.Content == currentIP { Current IP } else { @@ -245,6 +247,39 @@ templ DNSRecordRow(record DNSRecord, currentIP string) { } + + if record.Type == "A" { +
+ +
+ } else { + - + } +