list all (wip)

This commit is contained in:
2025-10-21 19:39:49 +02:00
parent ec631e3c3a
commit 381a4e4988
4 changed files with 225 additions and 21 deletions

View File

@@ -43,6 +43,7 @@ func NewServer(data *storage.Repository, port int) *HTTPServer {
// Get information about the server // Get information about the server
r.Get("/version", s.Information) r.Get("/version", s.Information)
r.Route("/projects", func(r chi.Router) { r.Route("/projects", func(r chi.Router) {
r.Get("/all", s.ProjectsHandler)
r.Get("/{name}", func(w http.ResponseWriter, r *http.Request) {}) r.Get("/{name}", func(w http.ResponseWriter, r *http.Request) {})
r.Post("/{name}", s.ProjectPostHandler) r.Post("/{name}", s.ProjectPostHandler)
r.Delete("/{name}", func(w http.ResponseWriter, r *http.Request) {}) r.Delete("/{name}", func(w http.ResponseWriter, r *http.Request) {})
@@ -91,3 +92,14 @@ func (s *HTTPServer) ProjectPostHandler(w http.ResponseWriter, r *http.Request)
w.WriteHeader(201) w.WriteHeader(201)
} }
func (s *HTTPServer) ProjectsHandler(w http.ResponseWriter, r *http.Request) {
prs, err := s.data.List()
if err != nil {
slog.Error("failed to fetch all the projects from the database", "err", err)
internalServerError(err, w, r)
return
}
ok(prs, w, r)
}

View File

@@ -1,18 +1,20 @@
-- +goose Up -- +goose Up
CREATE TABLE Projects ( CREATE TABLE Projects (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, uuid TEXT NOT NULL,
name TEXT NOT NULL name TEXT NOT NULL
); );
CREATE INDEX Projects_uuid_IDX ON Projects (uuid);
CREATE TABLE Repositories ( CREATE TABLE Repositories (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, uuid TEXT NOT NULL,
name TEXT NOT NULL, name TEXT NOT NULL,
schedule TEXT NOT NULL, schedule TEXT NOT NULL,
"source" TEXT NOT NULL, "source" TEXT NOT NULL,
destination TEXT NOT NULL, destination TEXT NOT NULL,
project INTEGER NOT NULL project INTEGER NOT NULL
); );
CREATE INDEX Repositories_uuid_IDX ON Repositories (uuid);
-- +goose Down -- +goose Down
DROP TABLE Projects; DROP TABLE Projects;
DROP TABLE Repositories;

View File

@@ -2,10 +2,12 @@ package storage
import ( import (
"database/sql" "database/sql"
"errors"
"fmt" "fmt"
"mirror-sync/pkg/project" "mirror-sync/pkg/project"
_ "github.com/glebarez/go-sqlite" _ "github.com/glebarez/go-sqlite"
"github.com/google/uuid"
) )
type ( type (
@@ -27,6 +29,70 @@ func OpenDB(path string) (*Repository, error) {
} }
func (r *Repository) Save(pr project.Project) (err error) { func (r *Repository) Save(pr project.Project) (err error) {
exists, err := r.ProjectExistsByName(pr.Name)
if err != nil {
return err
}
if exists {
return r.Update(pr)
}
return r.Create(pr)
}
func (r *Repository) ProjectExistsByUUID(uuid string) (bool, error) {
row := r.db.QueryRow("SELECT uuid FROM Projects WHERE uuid = ?", uuid)
if row.Err() != nil {
return false, fmt.Errorf("failed to get row from database: %w", row.Err())
}
var id string
if err := row.Scan(&id); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return false, nil
}
return false, fmt.Errorf("failed to scan row: %w", err)
}
return true, nil
}
func (r *Repository) ProjectExistsByName(name string) (bool, error) {
row := r.db.QueryRow("SELECT uuid FROM Projects WHERE name = ?", name)
if row.Err() != nil {
return false, fmt.Errorf("failed to get row from database: %w", row.Err())
}
var uuid string
if err := row.Scan(&uuid); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return false, nil
}
return false, fmt.Errorf("failed to scan row: %w", err)
}
return true, nil
}
func (r *Repository) RepositoryExistsByName(name string) (bool, error) {
row := r.db.QueryRow("SELECT uuid FROM Repositories WHERE name = ?", name)
if row.Err() != nil {
return false, fmt.Errorf("failed to get row from database: %w", row.Err())
}
var uuid string
if err := row.Scan(&uuid); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return false, nil
}
return false, fmt.Errorf("failed to scan row: %w", err)
}
return true, nil
}
func (r *Repository) Create(pr project.Project) error {
tx, err := r.db.Begin() tx, err := r.db.Begin()
if err != nil { if err != nil {
return fmt.Errorf("failed to create transaction: %s", err) return fmt.Errorf("failed to create transaction: %s", err)
@@ -39,35 +105,159 @@ func (r *Repository) Save(pr project.Project) (err error) {
tx.Commit() tx.Commit()
}() }()
stmt, err := tx.Prepare("INSERT INTO Projects (name) VALUES (?)") // Create Project entry
projectUUID := uuid.NewString()
stmt, err := tx.Prepare("INSERT INTO Projects (uuid, name) VALUES (?, ?)")
if err != nil { if err != nil {
return fmt.Errorf("failed to create statement: %s", err) return fmt.Errorf("failed to create statement: %s", err)
} }
if _, err := stmt.Exec(pr.Name); err != nil { if _, err := stmt.Exec(projectUUID, pr.Name); err != nil {
return fmt.Errorf("failed to execute sql query: %s", err) return fmt.Errorf("failed to execute sql query: %s", err)
} }
rows, err := tx.Query("SELECT id FROM Projects WHERE name = ?", pr.Name) // Create repositories entries
stmt, err = tx.Prepare("INSERT INTO Repositories (uuid, name, source, destination, schedule, project) VALUES (?, ?, ?, ?, ?, ?)")
if err != nil { if err != nil {
return fmt.Errorf("failed to query project id: %s", err) return fmt.Errorf("failed to create statement: %s", err)
}
defer rows.Close()
var id int
rows.Next()
if err := rows.Scan(&id); err != nil {
return fmt.Errorf("failed to query project id: %s", err)
} }
for _, repo := range pr.Repositories { for _, repo := range pr.Repositories {
stmt, err := tx.Prepare("INSERT INTO Repositories (name, source, destination, schedule, project) VALUES (?, ?, ?, ?, ?)") repoUUID := uuid.NewString()
if err != nil {
return fmt.Errorf("failed to create statement: %s", err) if _, err := stmt.Exec(repoUUID, repo.Name, repo.Source, repo.Destination, repo.Schedule, projectUUID); err != nil {
}
if _, err := stmt.Exec(repo.Name, repo.Source, repo.Destination, repo.Schedule, id); err != nil {
return fmt.Errorf("failed to execute sql query: %s", err) return fmt.Errorf("failed to execute sql query: %s", err)
} }
} }
return nil return nil
} }
func (r *Repository) ProjectUUID(name string) (string, error) {
row := r.db.QueryRow("SELECT uuid FROM Projects WHERE name = ?", name)
if row.Err() != nil {
return "", fmt.Errorf("failed to get row from database: %w", row.Err())
}
var uuid string
if err := row.Scan(&uuid); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return "", nil
}
return "", fmt.Errorf("failed to scan row: %w", err)
}
return uuid, nil
}
func (r *Repository) RepositoryUUID(name string) (string, error) {
row := r.db.QueryRow("SELECT uuid FROM Repositories WHERE name = ?", name)
if row.Err() != nil {
return "", fmt.Errorf("failed to get row from database: %w", row.Err())
}
var uuid string
if err := row.Scan(&uuid); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return "", nil
}
return "", fmt.Errorf("failed to scan row: %w", err)
}
return uuid, nil
}
func (r *Repository) Update(pr project.Project) error {
tx, err := r.db.Begin()
if err != nil {
return fmt.Errorf("failed to create transaction: %s", err)
}
defer func() {
if err != nil {
tx.Rollback()
return
}
tx.Commit()
}()
projectUUID, err := r.ProjectUUID(pr.Name)
if err != nil {
return fmt.Errorf("failed to get project uuid: %w", err)
}
stmt, err := tx.Prepare("UPDATE Repositories SET schedule = ?, source = ?, destination = ? WHERE uuid = ?")
if err != nil {
return fmt.Errorf("failed to create statement: %w", err)
}
// this loop does NOT remove orphan
for _, repo := range pr.Repositories {
// checks if the repo exists
exists, err := r.RepositoryExistsByName(repo.Name)
if err != nil {
return fmt.Errorf("failed to fetch uuid from the database: %w", err)
}
if exists {
// if it exists, just update it
uuid, err := r.RepositoryUUID(repo.Name)
if err != nil {
return fmt.Errorf("failed to get uuid from database: %w", err)
}
if _, err := stmt.Exec(repo.Schedule, repo.Source, repo.Destination, uuid); err != nil {
return fmt.Errorf("failed to update repository entry for %s::'%s'", uuid, repo.Name)
}
} else {
// if not, create a new uuid and create the entry
repoUUID := uuid.NewString()
if _, err := stmt.Exec(repoUUID, repo.Name, repo.Source, repo.Destination, repo.Schedule, projectUUID); err != nil {
return fmt.Errorf("failed to execute sql query: %s", err)
}
}
if _, err := stmt.Exec(repo.Schedule, repo.Source, repo.Destination, repo.Name); err != nil {
return fmt.Errorf("failed to update repository entry for '%s'", repo.Name)
}
}
return nil
}
func (r *Repository) List() ([]project.Project, error) {
var prs []project.Project
rows, err := r.db.Query("SELECT uuid, name WHERE Projects")
if err != nil {
return nil, fmt.Errorf("failed to get the list of projects: %w", err)
}
defer rows.Close()
stmt, err := r.db.Prepare("SELECT name, schedule, source, destination WHERE uuid = ?")
if err != nil {
return nil, fmt.Errorf("invalid syntax: %w", err)
}
for rows.Next() {
var pr project.Project
var prUUID string
if err := rows.Scan(&prUUID, &pr.Name); err != nil {
return nil, fmt.Errorf("failed to scan project name: %w", err)
}
for rows.Next() {
var repo project.Repository
rows, err := stmt.Query(prUUID)
if err != nil {
return nil, fmt.Errorf("failed to get the list of projects: %w", err)
}
if err := rows.Scan(&repo.Name, &repo.Schedule, &repo.Source, &repo.Destination); err != nil {
return nil, fmt.Errorf("failed to scan repository entry: %w", err)
}
pr.Repositories = append(pr.Repositories, repo)
}
prs = append(prs, pr)
}
return prs, nil
}

2
go.mod
View File

@@ -8,6 +8,7 @@ require (
github.com/go-git/go-git/v6 v6.0.0-20250929195514-145daf2492dd github.com/go-git/go-git/v6 v6.0.0-20250929195514-145daf2492dd
github.com/goccy/go-yaml v1.18.0 github.com/goccy/go-yaml v1.18.0
github.com/google/subcommands v1.2.0 github.com/google/subcommands v1.2.0
github.com/google/uuid v1.6.0
github.com/pressly/goose/v3 v3.26.0 github.com/pressly/goose/v3 v3.26.0
) )
@@ -21,7 +22,6 @@ require (
github.com/go-git/gcfg/v2 v2.0.2 // indirect github.com/go-git/gcfg/v2 v2.0.2 // indirect
github.com/go-git/go-billy/v6 v6.0.0-20250627091229-31e2a16eef30 // indirect github.com/go-git/go-billy/v6 v6.0.0-20250627091229-31e2a16eef30 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/kevinburke/ssh_config v1.4.0 // indirect github.com/kevinburke/ssh_config v1.4.0 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect