feat: initpage com sync Go + auth + frontend em web/

This commit is contained in:
2026-10-21 18:06:25 -03:00
commit 17b81a808a
24 changed files with 4807 additions and 0 deletions

91
internal/sync/sync.go Normal file
View File

@@ -0,0 +1,91 @@
package sync
import (
"database/sql"
"encoding/json"
"net/http"
"time"
mw "github.com/weltonmoura/initpage/internal/middleware"
)
type Sync struct {
DB *sql.DB
}
type syncPayload struct {
Data json.RawMessage `json:"data"`
Version int `json:"version"`
}
func (s *Sync) Get(w http.ResponseWriter, r *http.Request) {
uid := mw.GetUserID(r.Context())
if uid == "" {
w.WriteHeader(http.StatusUnauthorized)
return
}
var data string
var version int
var updatedAt string
_ = s.DB.QueryRow(`SELECT data, version, updated_at FROM user_sync WHERE user_id=?`, uid).Scan(&data, &version, &updatedAt)
if data == "" {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"data":null,"version":0}`))
return
}
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"data":` + data + `,"version":` + itoa(version) + `}`))
}
func (s *Sync) Put(w http.ResponseWriter, r *http.Request) {
uid := mw.GetUserID(r.Context())
if uid == "" {
w.WriteHeader(http.StatusUnauthorized)
return
}
var p syncPayload
if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(`{"error":"json inválido"}`))
return
}
if len(p.Data) > 256*1024 {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(`{"error":"dados muito grandes"}`))
return
}
// valida JSON básico
if !json.Valid(p.Data) {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(`{"error":"json inválido"}`))
return
}
_, err := s.DB.Exec(`INSERT INTO user_sync(user_id,data,version,updated_at) VALUES(?,?,?,?)
ON CONFLICT(user_id) DO UPDATE SET data=excluded.data, version=excluded.version, updated_at=excluded.updated_at`,
uid, string(p.Data), p.Version, time.Now())
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"ok":true}`))
}
func itoa(i int) string {
if i == 0 {
return "0"
}
sign := ""
if i < 0 {
sign = "-"
i = -i
}
b := [12]byte{}
n := len(b)
for i > 0 {
n--
b[n] = byte('0' + i%10)
i /= 10
}
return sign + string(b[n:])
}