Просмотр исходного кода

dev: automated commit - 2026-03-08 13:33:38

Mariano Z. 2 недель назад
Родитель
Сommit
bf43214c4b

+ 1 - 0
frontend/src/api/types.ts

@@ -16,6 +16,7 @@ export interface DnsRecord {
   content: string
   proxied: number
   is_static: number
+  last_updated_at?: string | null
   created_at: string
   updated_at: string
   cf_zone_id?: string

+ 28 - 3
frontend/src/views/RecordsView.vue

@@ -121,6 +121,13 @@ async function confirmDelete() {
   }
 }
 
+function formatDate(iso: string): string {
+  return new Intl.DateTimeFormat(undefined, {
+    dateStyle: 'medium',
+    timeStyle: 'short',
+  }).format(new Date(iso))
+}
+
 onMounted(loadData)
 </script>
 
@@ -218,13 +225,14 @@ onMounted(loadData)
                   <TableHead>Content</TableHead>
                   <TableHead>Zone</TableHead>
                   <TableHead>Proxied</TableHead>
+                  <TableHead>Last updated</TableHead>
                   <TableHead class="w-12" />
                 </TableRow>
               </TableHeader>
               <TableBody>
                 <template v-if="dynamicRecords.length === 0">
                   <TableRow>
-                    <TableCell colspan="6" class="text-center text-muted-foreground py-6">
+                    <TableCell colspan="7" class="text-center text-muted-foreground py-6">
                       No dynamic records. Use the actions menu to set a record as dynamic.
                     </TableCell>
                   </TableRow>
@@ -237,7 +245,17 @@ onMounted(loadData)
                   <TableCell class="font-mono text-sm">{{ record.content }}</TableCell>
                   <TableCell class="text-muted-foreground">{{ record.zone_name }}</TableCell>
                   <TableCell>
-                    <Badge v-if="record.proxied === 1" variant="secondary">proxied</Badge>
+                    <Badge
+                      :class="record.proxied === 1
+                        ? 'bg-green-500/15 text-green-700 dark:text-green-400 border-green-500/30'
+                        : 'bg-orange-500/15 text-orange-700 dark:text-orange-400 border-orange-500/30'"
+                      variant="outline"
+                    >
+                      {{ record.proxied === 1 ? 'Enabled' : 'Disabled' }}
+                    </Badge>
+                  </TableCell>
+                  <TableCell class="text-muted-foreground text-sm">
+                    {{ record.last_updated_at ? formatDate(record.last_updated_at) : '—' }}
                   </TableCell>
                   <TableCell>
                     <DropdownMenu>
@@ -310,7 +328,14 @@ onMounted(loadData)
                   <TableCell class="font-mono text-sm">{{ record.content }}</TableCell>
                   <TableCell class="text-muted-foreground">{{ record.zone_name }}</TableCell>
                   <TableCell>
-                    <Badge v-if="record.proxied === 1" variant="secondary">proxied</Badge>
+                    <Badge
+                      :class="record.proxied === 1
+                        ? 'bg-green-500/15 text-green-700 dark:text-green-400 border-green-500/30'
+                        : 'bg-orange-500/15 text-orange-700 dark:text-orange-400 border-orange-500/30'"
+                      variant="outline"
+                    >
+                      {{ record.proxied === 1 ? 'Enabled' : 'Disabled' }}
+                    </Badge>
                   </TableCell>
                   <TableCell>
                     <DropdownMenu>

+ 1 - 0
internal/database/migrations/000002_record_last_updated_at.down.sql

@@ -0,0 +1 @@
+-- SQLite does not support DROP COLUMN in older versions; this migration cannot be reversed.

+ 1 - 0
internal/database/migrations/000002_record_last_updated_at.up.sql

@@ -0,0 +1 @@
+ALTER TABLE records ADD COLUMN last_updated_at DATETIME;

+ 11 - 10
internal/database/queries/models.go

@@ -19,16 +19,17 @@ type IpProvider struct {
 }
 
 type Record struct {
-	ID         int64     `json:"id"`
-	ZoneID     int64     `json:"zone_id"`
-	CfRecordID string    `json:"cf_record_id"`
-	Name       string    `json:"name"`
-	Type       string    `json:"type"`
-	Content    string    `json:"content"`
-	Proxied    int64     `json:"proxied"`
-	IsStatic   int64     `json:"is_static"`
-	CreatedAt  time.Time `json:"created_at"`
-	UpdatedAt  time.Time `json:"updated_at"`
+	ID            int64      `json:"id"`
+	ZoneID        int64      `json:"zone_id"`
+	CfRecordID    string     `json:"cf_record_id"`
+	Name          string     `json:"name"`
+	Type          string     `json:"type"`
+	Content       string     `json:"content"`
+	Proxied       int64      `json:"proxied"`
+	IsStatic      int64      `json:"is_static"`
+	LastUpdatedAt *time.Time `json:"last_updated_at"`
+	CreatedAt     time.Time  `json:"created_at"`
+	UpdatedAt     time.Time  `json:"updated_at"`
 }
 
 type Setting struct {

+ 78 - 63
internal/database/queries/records.sql.go

@@ -13,7 +13,7 @@ import (
 const createRecord = `-- name: CreateRecord :one
 INSERT INTO records (zone_id, cf_record_id, name, type, content, proxied, is_static)
 VALUES (?, ?, ?, ?, ?, ?, ?)
-RETURNING id, zone_id, cf_record_id, name, type, content, proxied, is_static, created_at, updated_at
+RETURNING id, zone_id, cf_record_id, name, type, content, proxied, is_static, last_updated_at, created_at, updated_at
 `
 
 type CreateRecordParams struct {
@@ -46,6 +46,7 @@ func (q *Queries) CreateRecord(ctx context.Context, arg CreateRecordParams) (Rec
 		&i.Content,
 		&i.Proxied,
 		&i.IsStatic,
+		&i.LastUpdatedAt,
 		&i.CreatedAt,
 		&i.UpdatedAt,
 	)
@@ -76,25 +77,26 @@ func (q *Queries) DeleteRecordByCfID(ctx context.Context, arg DeleteRecordByCfID
 }
 
 const getRecord = `-- name: GetRecord :one
-SELECT r.id, r.zone_id, r.cf_record_id, r.name, r.type, r.content, r.proxied, r.is_static, r.created_at, r.updated_at, z.zone_id as cf_zone_id, z.name as zone_name
+SELECT r.id, r.zone_id, r.cf_record_id, r.name, r.type, r.content, r.proxied, r.is_static, r.last_updated_at, r.created_at, r.updated_at, z.zone_id as cf_zone_id, z.name as zone_name
 FROM records r
 JOIN zones z ON z.id = r.zone_id
 WHERE r.id = ?
 `
 
 type GetRecordRow struct {
-	ID         int64     `json:"id"`
-	ZoneID     int64     `json:"zone_id"`
-	CfRecordID string    `json:"cf_record_id"`
-	Name       string    `json:"name"`
-	Type       string    `json:"type"`
-	Content    string    `json:"content"`
-	Proxied    int64     `json:"proxied"`
-	IsStatic   int64     `json:"is_static"`
-	CreatedAt  time.Time `json:"created_at"`
-	UpdatedAt  time.Time `json:"updated_at"`
-	CfZoneID   string    `json:"cf_zone_id"`
-	ZoneName   string    `json:"zone_name"`
+	ID            int64      `json:"id"`
+	ZoneID        int64      `json:"zone_id"`
+	CfRecordID    string     `json:"cf_record_id"`
+	Name          string     `json:"name"`
+	Type          string     `json:"type"`
+	Content       string     `json:"content"`
+	Proxied       int64      `json:"proxied"`
+	IsStatic      int64      `json:"is_static"`
+	LastUpdatedAt *time.Time `json:"last_updated_at"`
+	CreatedAt     time.Time  `json:"created_at"`
+	UpdatedAt     time.Time  `json:"updated_at"`
+	CfZoneID      string     `json:"cf_zone_id"`
+	ZoneName      string     `json:"zone_name"`
 }
 
 func (q *Queries) GetRecord(ctx context.Context, id int64) (GetRecordRow, error) {
@@ -109,6 +111,7 @@ func (q *Queries) GetRecord(ctx context.Context, id int64) (GetRecordRow, error)
 		&i.Content,
 		&i.Proxied,
 		&i.IsStatic,
+		&i.LastUpdatedAt,
 		&i.CreatedAt,
 		&i.UpdatedAt,
 		&i.CfZoneID,
@@ -118,25 +121,26 @@ func (q *Queries) GetRecord(ctx context.Context, id int64) (GetRecordRow, error)
 }
 
 const listNonStaticRecords = `-- name: ListNonStaticRecords :many
-SELECT r.id, r.zone_id, r.cf_record_id, r.name, r.type, r.content, r.proxied, r.is_static, r.created_at, r.updated_at, z.zone_id as cf_zone_id, z.api_key as zone_api_key
+SELECT r.id, r.zone_id, r.cf_record_id, r.name, r.type, r.content, r.proxied, r.is_static, r.last_updated_at, r.created_at, r.updated_at, z.zone_id as cf_zone_id, z.api_key as zone_api_key
 FROM records r
 JOIN zones z ON z.id = r.zone_id
 WHERE r.is_static = 0
 `
 
 type ListNonStaticRecordsRow struct {
-	ID         int64     `json:"id"`
-	ZoneID     int64     `json:"zone_id"`
-	CfRecordID string    `json:"cf_record_id"`
-	Name       string    `json:"name"`
-	Type       string    `json:"type"`
-	Content    string    `json:"content"`
-	Proxied    int64     `json:"proxied"`
-	IsStatic   int64     `json:"is_static"`
-	CreatedAt  time.Time `json:"created_at"`
-	UpdatedAt  time.Time `json:"updated_at"`
-	CfZoneID   string    `json:"cf_zone_id"`
-	ZoneApiKey string    `json:"zone_api_key"`
+	ID            int64      `json:"id"`
+	ZoneID        int64      `json:"zone_id"`
+	CfRecordID    string     `json:"cf_record_id"`
+	Name          string     `json:"name"`
+	Type          string     `json:"type"`
+	Content       string     `json:"content"`
+	Proxied       int64      `json:"proxied"`
+	IsStatic      int64      `json:"is_static"`
+	LastUpdatedAt *time.Time `json:"last_updated_at"`
+	CreatedAt     time.Time  `json:"created_at"`
+	UpdatedAt     time.Time  `json:"updated_at"`
+	CfZoneID      string     `json:"cf_zone_id"`
+	ZoneApiKey    string     `json:"zone_api_key"`
 }
 
 func (q *Queries) ListNonStaticRecords(ctx context.Context) ([]ListNonStaticRecordsRow, error) {
@@ -157,6 +161,7 @@ func (q *Queries) ListNonStaticRecords(ctx context.Context) ([]ListNonStaticReco
 			&i.Content,
 			&i.Proxied,
 			&i.IsStatic,
+			&i.LastUpdatedAt,
 			&i.CreatedAt,
 			&i.UpdatedAt,
 			&i.CfZoneID,
@@ -203,25 +208,26 @@ func (q *Queries) ListRecordCfIDsByZone(ctx context.Context, zoneID int64) ([]st
 }
 
 const listRecords = `-- name: ListRecords :many
-SELECT r.id, r.zone_id, r.cf_record_id, r.name, r.type, r.content, r.proxied, r.is_static, r.created_at, r.updated_at, z.zone_id as cf_zone_id, z.name as zone_name
+SELECT r.id, r.zone_id, r.cf_record_id, r.name, r.type, r.content, r.proxied, r.is_static, r.last_updated_at, r.created_at, r.updated_at, z.zone_id as cf_zone_id, z.name as zone_name
 FROM records r
 JOIN zones z ON z.id = r.zone_id
 ORDER BY r.name
 `
 
 type ListRecordsRow struct {
-	ID         int64     `json:"id"`
-	ZoneID     int64     `json:"zone_id"`
-	CfRecordID string    `json:"cf_record_id"`
-	Name       string    `json:"name"`
-	Type       string    `json:"type"`
-	Content    string    `json:"content"`
-	Proxied    int64     `json:"proxied"`
-	IsStatic   int64     `json:"is_static"`
-	CreatedAt  time.Time `json:"created_at"`
-	UpdatedAt  time.Time `json:"updated_at"`
-	CfZoneID   string    `json:"cf_zone_id"`
-	ZoneName   string    `json:"zone_name"`
+	ID            int64      `json:"id"`
+	ZoneID        int64      `json:"zone_id"`
+	CfRecordID    string     `json:"cf_record_id"`
+	Name          string     `json:"name"`
+	Type          string     `json:"type"`
+	Content       string     `json:"content"`
+	Proxied       int64      `json:"proxied"`
+	IsStatic      int64      `json:"is_static"`
+	LastUpdatedAt *time.Time `json:"last_updated_at"`
+	CreatedAt     time.Time  `json:"created_at"`
+	UpdatedAt     time.Time  `json:"updated_at"`
+	CfZoneID      string     `json:"cf_zone_id"`
+	ZoneName      string     `json:"zone_name"`
 }
 
 func (q *Queries) ListRecords(ctx context.Context) ([]ListRecordsRow, error) {
@@ -242,6 +248,7 @@ func (q *Queries) ListRecords(ctx context.Context) ([]ListRecordsRow, error) {
 			&i.Content,
 			&i.Proxied,
 			&i.IsStatic,
+			&i.LastUpdatedAt,
 			&i.CreatedAt,
 			&i.UpdatedAt,
 			&i.CfZoneID,
@@ -261,7 +268,7 @@ func (q *Queries) ListRecords(ctx context.Context) ([]ListRecordsRow, error) {
 }
 
 const listRecordsByZone = `-- name: ListRecordsByZone :many
-SELECT r.id, r.zone_id, r.cf_record_id, r.name, r.type, r.content, r.proxied, r.is_static, r.created_at, r.updated_at, z.zone_id as cf_zone_id, z.name as zone_name
+SELECT r.id, r.zone_id, r.cf_record_id, r.name, r.type, r.content, r.proxied, r.is_static, r.last_updated_at, r.created_at, r.updated_at, z.zone_id as cf_zone_id, z.name as zone_name
 FROM records r
 JOIN zones z ON z.id = r.zone_id
 WHERE r.zone_id = ?
@@ -269,18 +276,19 @@ ORDER BY r.name
 `
 
 type ListRecordsByZoneRow struct {
-	ID         int64     `json:"id"`
-	ZoneID     int64     `json:"zone_id"`
-	CfRecordID string    `json:"cf_record_id"`
-	Name       string    `json:"name"`
-	Type       string    `json:"type"`
-	Content    string    `json:"content"`
-	Proxied    int64     `json:"proxied"`
-	IsStatic   int64     `json:"is_static"`
-	CreatedAt  time.Time `json:"created_at"`
-	UpdatedAt  time.Time `json:"updated_at"`
-	CfZoneID   string    `json:"cf_zone_id"`
-	ZoneName   string    `json:"zone_name"`
+	ID            int64      `json:"id"`
+	ZoneID        int64      `json:"zone_id"`
+	CfRecordID    string     `json:"cf_record_id"`
+	Name          string     `json:"name"`
+	Type          string     `json:"type"`
+	Content       string     `json:"content"`
+	Proxied       int64      `json:"proxied"`
+	IsStatic      int64      `json:"is_static"`
+	LastUpdatedAt *time.Time `json:"last_updated_at"`
+	CreatedAt     time.Time  `json:"created_at"`
+	UpdatedAt     time.Time  `json:"updated_at"`
+	CfZoneID      string     `json:"cf_zone_id"`
+	ZoneName      string     `json:"zone_name"`
 }
 
 func (q *Queries) ListRecordsByZone(ctx context.Context, zoneID int64) ([]ListRecordsByZoneRow, error) {
@@ -301,6 +309,7 @@ func (q *Queries) ListRecordsByZone(ctx context.Context, zoneID int64) ([]ListRe
 			&i.Content,
 			&i.Proxied,
 			&i.IsStatic,
+			&i.LastUpdatedAt,
 			&i.CreatedAt,
 			&i.UpdatedAt,
 			&i.CfZoneID,
@@ -321,18 +330,21 @@ func (q *Queries) ListRecordsByZone(ctx context.Context, zoneID int64) ([]ListRe
 
 const updateRecord = `-- name: UpdateRecord :one
 UPDATE records
-SET name = ?, type = ?, content = ?, proxied = ?, is_static = ?, updated_at = CURRENT_TIMESTAMP
+SET name = ?, type = ?, content = ?, proxied = ?, is_static = ?,
+    last_updated_at = CASE WHEN ? = 1 THEN NULL ELSE last_updated_at END,
+    updated_at = CURRENT_TIMESTAMP
 WHERE id = ?
-RETURNING id, zone_id, cf_record_id, name, type, content, proxied, is_static, created_at, updated_at
+RETURNING id, zone_id, cf_record_id, name, type, content, proxied, is_static, last_updated_at, created_at, updated_at
 `
 
 type UpdateRecordParams struct {
-	Name     string `json:"name"`
-	Type     string `json:"type"`
-	Content  string `json:"content"`
-	Proxied  int64  `json:"proxied"`
-	IsStatic int64  `json:"is_static"`
-	ID       int64  `json:"id"`
+	Name     string      `json:"name"`
+	Type     string      `json:"type"`
+	Content  string      `json:"content"`
+	Proxied  int64       `json:"proxied"`
+	IsStatic int64       `json:"is_static"`
+	Column6  interface{} `json:"column_6"`
+	ID       int64       `json:"id"`
 }
 
 func (q *Queries) UpdateRecord(ctx context.Context, arg UpdateRecordParams) (Record, error) {
@@ -342,6 +354,7 @@ func (q *Queries) UpdateRecord(ctx context.Context, arg UpdateRecordParams) (Rec
 		arg.Content,
 		arg.Proxied,
 		arg.IsStatic,
+		arg.Column6,
 		arg.ID,
 	)
 	var i Record
@@ -354,6 +367,7 @@ func (q *Queries) UpdateRecord(ctx context.Context, arg UpdateRecordParams) (Rec
 		&i.Content,
 		&i.Proxied,
 		&i.IsStatic,
+		&i.LastUpdatedAt,
 		&i.CreatedAt,
 		&i.UpdatedAt,
 	)
@@ -362,7 +376,7 @@ func (q *Queries) UpdateRecord(ctx context.Context, arg UpdateRecordParams) (Rec
 
 const updateRecordContent = `-- name: UpdateRecordContent :exec
 UPDATE records
-SET content = ?, updated_at = CURRENT_TIMESTAMP
+SET content = ?, last_updated_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP
 WHERE id = ?
 `
 
@@ -385,7 +399,7 @@ ON CONFLICT(zone_id, cf_record_id) DO UPDATE SET
   content = excluded.content,
   proxied = excluded.proxied,
   updated_at = CURRENT_TIMESTAMP
-RETURNING id, zone_id, cf_record_id, name, type, content, proxied, is_static, created_at, updated_at
+RETURNING id, zone_id, cf_record_id, name, type, content, proxied, is_static, last_updated_at, created_at, updated_at
 `
 
 type UpsertRecordParams struct {
@@ -418,6 +432,7 @@ func (q *Queries) UpsertRecord(ctx context.Context, arg UpsertRecordParams) (Rec
 		&i.Content,
 		&i.Proxied,
 		&i.IsStatic,
+		&i.LastUpdatedAt,
 		&i.CreatedAt,
 		&i.UpdatedAt,
 	)

+ 4 - 1
internal/handler/record.go

@@ -165,13 +165,15 @@ func (h *RecordHandler) Update(c echo.Context) error {
 	}
 
 	ctx := c.Request().Context()
+	isStatic := boolToInt64(req.IsStatic)
 	record, err := h.q.UpdateRecord(ctx, queries.UpdateRecordParams{
 		ID:       id,
 		Name:     req.Name,
 		Type:     req.Type,
 		Content:  req.Content,
 		Proxied:  boolToInt64(req.Proxied),
-		IsStatic: boolToInt64(req.IsStatic),
+		IsStatic: isStatic,
+		Column6:  isStatic,
 	})
 	if errors.Is(err, sql.ErrNoRows) {
 		return c.JSON(http.StatusNotFound, map[string]string{"error": "record not found"})
@@ -252,6 +254,7 @@ func (h *RecordHandler) PartialUpdate(c echo.Context) error {
 	if req.IsStatic != nil {
 		params.IsStatic = boolToInt64(*req.IsStatic)
 	}
+	params.Column6 = params.IsStatic
 
 	record, err := h.q.UpdateRecord(ctx, params)
 	if err != nil {

+ 4 - 2
sql/queries/records.sql

@@ -30,13 +30,15 @@ RETURNING *;
 
 -- name: UpdateRecord :one
 UPDATE records
-SET name = ?, type = ?, content = ?, proxied = ?, is_static = ?, updated_at = CURRENT_TIMESTAMP
+SET name = ?, type = ?, content = ?, proxied = ?, is_static = ?,
+    last_updated_at = CASE WHEN ? = 1 THEN NULL ELSE last_updated_at END,
+    updated_at = CURRENT_TIMESTAMP
 WHERE id = ?
 RETURNING *;
 
 -- name: UpdateRecordContent :exec
 UPDATE records
-SET content = ?, updated_at = CURRENT_TIMESTAMP
+SET content = ?, last_updated_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP
 WHERE id = ?;
 
 -- name: DeleteRecord :exec

+ 1 - 0
sql/schema/schema.sql

@@ -26,6 +26,7 @@ CREATE TABLE IF NOT EXISTS records (
     content TEXT NOT NULL,
     proxied INTEGER NOT NULL DEFAULT 0,
     is_static INTEGER NOT NULL DEFAULT 0,
+    last_updated_at DATETIME,
     created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
     updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
 );