|
|
|
@@ -0,0 +1,411 @@
|
|
|
|
|
package database
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"database/sql"
|
|
|
|
|
"fmt"
|
|
|
|
|
"quay/app/models"
|
|
|
|
|
"quay/app/repository"
|
|
|
|
|
|
|
|
|
|
"github.com/google/uuid"
|
|
|
|
|
_ "github.com/mattn/go-sqlite3"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type SQLiteSiteRepository struct {
|
|
|
|
|
db *sql.DB
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewSQLiteSiteRepository(db *sql.DB) *SQLiteSiteRepository {
|
|
|
|
|
return &SQLiteSiteRepository{db: db}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var _ repository.SiteRepository = (*SQLiteSiteRepository)(nil)
|
|
|
|
|
|
|
|
|
|
// Sites
|
|
|
|
|
|
|
|
|
|
func (r *SQLiteSiteRepository) GetSite(id string) (*models.Site, error) {
|
|
|
|
|
row := r.db.QueryRow(`
|
|
|
|
|
SELECT id, git_server, owner, repository, branch, domain, deploy_token, enabled, not_found_file
|
|
|
|
|
FROM sites WHERE id = ?`, id)
|
|
|
|
|
|
|
|
|
|
s, err := scanSite(row)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("get site: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := r.populateSiteRelations(s); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return s, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *SQLiteSiteRepository) ListSites() ([]models.Site, error) {
|
|
|
|
|
rows, err := r.db.Query(`
|
|
|
|
|
SELECT id, git_server, owner, repository, branch, domain, deploy_token, enabled, not_found_file
|
|
|
|
|
FROM sites`)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("list sites: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var sites []models.Site
|
|
|
|
|
for rows.Next() {
|
|
|
|
|
s, err := scanSite(rows)
|
|
|
|
|
if err != nil {
|
|
|
|
|
rows.Close()
|
|
|
|
|
return nil, fmt.Errorf("list sites scan: %w", err)
|
|
|
|
|
}
|
|
|
|
|
sites = append(sites, *s)
|
|
|
|
|
}
|
|
|
|
|
rows.Close()
|
|
|
|
|
if err := rows.Err(); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i := range sites {
|
|
|
|
|
if err := r.populateSiteRelations(&sites[i]); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return sites, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *SQLiteSiteRepository) CreateSite(s *models.Site) error {
|
|
|
|
|
tx, err := r.db.Begin()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("create site begin tx: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer tx.Rollback()
|
|
|
|
|
|
|
|
|
|
s.ID = uuid.NewString()
|
|
|
|
|
|
|
|
|
|
_, err = tx.Exec(`
|
|
|
|
|
INSERT INTO sites (id, git_server, owner, repository, branch, domain, deploy_token, enabled, not_found_file)
|
|
|
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
|
|
|
s.ID, s.GitServer, s.Owner, s.Repository, s.Branch,
|
|
|
|
|
s.Domain, s.DeployToken, s.Enabled, s.NotFoundFile,
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("create site insert: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i := range s.ForwardRules {
|
|
|
|
|
if err := insertForwardRule(tx, s.ID, &s.ForwardRules[i]); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i := range s.CustomHeaders {
|
|
|
|
|
if err := insertCustomHeaders(tx, s.ID, &s.CustomHeaders[i]); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tx.Commit()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *SQLiteSiteRepository) UpdateSite(s *models.Site) error {
|
|
|
|
|
tx, err := r.db.Begin()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("update site begin tx: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer tx.Rollback()
|
|
|
|
|
|
|
|
|
|
_, err = tx.Exec(`
|
|
|
|
|
UPDATE sites SET git_server=?, owner=?, repository=?, branch=?, domain=?,
|
|
|
|
|
deploy_token=?, enabled=?, not_found_file=? WHERE id=?`,
|
|
|
|
|
s.GitServer, s.Owner, s.Repository, s.Branch, s.Domain,
|
|
|
|
|
s.DeployToken, s.Enabled, s.NotFoundFile, s.ID,
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("update site: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if _, err := tx.Exec(`DELETE FROM forward_rules WHERE site_id = ?`, s.ID); err != nil {
|
|
|
|
|
return fmt.Errorf("update site delete forward rules: %w", err)
|
|
|
|
|
}
|
|
|
|
|
for i := range s.ForwardRules {
|
|
|
|
|
if err := insertForwardRule(tx, s.ID, &s.ForwardRules[i]); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if _, err := tx.Exec(`
|
|
|
|
|
DELETE FROM headers WHERE custom_header_id IN (
|
|
|
|
|
SELECT id FROM custom_headers WHERE site_id = ?
|
|
|
|
|
)`, s.ID); err != nil {
|
|
|
|
|
return fmt.Errorf("update site delete headers: %w", err)
|
|
|
|
|
}
|
|
|
|
|
if _, err := tx.Exec(`DELETE FROM custom_headers WHERE site_id = ?`, s.ID); err != nil {
|
|
|
|
|
return fmt.Errorf("update site delete custom headers: %w", err)
|
|
|
|
|
}
|
|
|
|
|
for _, ch := range s.CustomHeaders {
|
|
|
|
|
if err := insertCustomHeaders(tx, s.ID, &ch); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tx.Commit()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *SQLiteSiteRepository) DeleteSite(id string) error {
|
|
|
|
|
_, err := r.db.Exec(`DELETE FROM sites WHERE id = ?`, id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("delete site: %w", err)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Forward Rules
|
|
|
|
|
|
|
|
|
|
func (r *SQLiteSiteRepository) GetForwardRule(id string) (*models.ForwardRule, error) {
|
|
|
|
|
row := r.db.QueryRow(`
|
|
|
|
|
SELECT id, source, destination, status_code, regex
|
|
|
|
|
FROM forward_rules WHERE id = ?`, id)
|
|
|
|
|
|
|
|
|
|
var fr models.ForwardRule
|
|
|
|
|
var regex int
|
|
|
|
|
err := row.Scan(&fr.ID, &fr.Source, &fr.Destination, &fr.StatusCode, ®ex)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("get forward rule: %w", err)
|
|
|
|
|
}
|
|
|
|
|
fr.Regex = regex != 0
|
|
|
|
|
return &fr, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *SQLiteSiteRepository) CreateForwardRule(siteID string, fr *models.ForwardRule) error {
|
|
|
|
|
tx, err := r.db.Begin()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("create forward rule begin tx: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer tx.Rollback()
|
|
|
|
|
if err := insertForwardRule(tx, siteID, fr); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return tx.Commit()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *SQLiteSiteRepository) UpdateForwardRule(fr *models.ForwardRule) error {
|
|
|
|
|
_, err := r.db.Exec(`
|
|
|
|
|
UPDATE forward_rules SET source=?, destination=?, status_code=?, regex=? WHERE id=?`,
|
|
|
|
|
fr.Source, fr.Destination, fr.StatusCode, fr.Regex, fr.ID,
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("update forward rule: %w", err)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *SQLiteSiteRepository) DeleteForwardRule(id string) error {
|
|
|
|
|
_, err := r.db.Exec(`DELETE FROM forward_rules WHERE id = ?`, id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("delete forward rule: %w", err)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Custom Headers
|
|
|
|
|
|
|
|
|
|
func (r *SQLiteSiteRepository) GetCustomHeaders(id string) (*models.CustomHeaders, error) {
|
|
|
|
|
row := r.db.QueryRow(`SELECT id, source FROM custom_headers WHERE id = ?`, id)
|
|
|
|
|
|
|
|
|
|
var ch models.CustomHeaders
|
|
|
|
|
if err := row.Scan(&ch.ID, &ch.Source); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("get custom headers: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
headers, err := r.listHeaders(ch.ID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
ch.Headers = headers
|
|
|
|
|
return &ch, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *SQLiteSiteRepository) CreateCustomHeaders(siteID string, ch *models.CustomHeaders) error {
|
|
|
|
|
tx, err := r.db.Begin()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("create custom headers begin tx: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer tx.Rollback()
|
|
|
|
|
if err := insertCustomHeaders(tx, siteID, ch); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return tx.Commit()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *SQLiteSiteRepository) UpdateCustomHeaders(ch *models.CustomHeaders) error {
|
|
|
|
|
tx, err := r.db.Begin()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("update custom headers begin tx: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer tx.Rollback()
|
|
|
|
|
|
|
|
|
|
if _, err := tx.Exec(`UPDATE custom_headers SET source=? WHERE id=?`, ch.Source, ch.ID); err != nil {
|
|
|
|
|
return fmt.Errorf("update custom headers: %w", err)
|
|
|
|
|
}
|
|
|
|
|
if _, err := tx.Exec(`DELETE FROM headers WHERE custom_header_id = ?`, ch.ID); err != nil {
|
|
|
|
|
return fmt.Errorf("update custom headers delete headers: %w", err)
|
|
|
|
|
}
|
|
|
|
|
for _, h := range ch.Headers {
|
|
|
|
|
if err := insertHeader(tx, ch.ID, &h); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return tx.Commit()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *SQLiteSiteRepository) DeleteCustomHeaders(id string) error {
|
|
|
|
|
_, err := r.db.Exec(`DELETE FROM custom_headers WHERE id = ?`, id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("delete custom headers: %w", err)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Helpers
|
|
|
|
|
|
|
|
|
|
type scanner interface {
|
|
|
|
|
Scan(dest ...any) error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func scanSite(s scanner) (*models.Site, error) {
|
|
|
|
|
var site models.Site
|
|
|
|
|
var enabled int
|
|
|
|
|
err := s.Scan(
|
|
|
|
|
&site.ID, &site.GitServer, &site.Owner, &site.Repository,
|
|
|
|
|
&site.Branch, &site.Domain, &site.DeployToken, &enabled, &site.NotFoundFile,
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
site.Enabled = enabled != 0
|
|
|
|
|
return &site, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *SQLiteSiteRepository) populateSiteRelations(s *models.Site) error {
|
|
|
|
|
rules, err := r.listForwardRules(s.ID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
s.ForwardRules = rules
|
|
|
|
|
|
|
|
|
|
customHeaders, err := r.listCustomHeaders(s.ID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
s.CustomHeaders = customHeaders
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *SQLiteSiteRepository) listForwardRules(siteID string) ([]models.ForwardRule, error) {
|
|
|
|
|
rows, err := r.db.Query(`
|
|
|
|
|
SELECT id, source, destination, status_code, regex
|
|
|
|
|
FROM forward_rules WHERE site_id = ?`, siteID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("list forward rules: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
|
|
var rules []models.ForwardRule
|
|
|
|
|
for rows.Next() {
|
|
|
|
|
var fr models.ForwardRule
|
|
|
|
|
var regex int
|
|
|
|
|
if err := rows.Scan(&fr.ID, &fr.Source, &fr.Destination, &fr.StatusCode, ®ex); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("list forward rules scan: %w", err)
|
|
|
|
|
}
|
|
|
|
|
fr.Regex = regex != 0
|
|
|
|
|
rules = append(rules, fr)
|
|
|
|
|
}
|
|
|
|
|
return rules, rows.Err()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *SQLiteSiteRepository) listCustomHeaders(siteID string) ([]models.CustomHeaders, error) {
|
|
|
|
|
rows, err := r.db.Query(`SELECT id, source FROM custom_headers WHERE site_id = ?`, siteID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("list custom headers: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var result []models.CustomHeaders
|
|
|
|
|
for rows.Next() {
|
|
|
|
|
var ch models.CustomHeaders
|
|
|
|
|
if err := rows.Scan(&ch.ID, &ch.Source); err != nil {
|
|
|
|
|
rows.Close()
|
|
|
|
|
return nil, fmt.Errorf("list custom headers scan: %w", err)
|
|
|
|
|
}
|
|
|
|
|
result = append(result, ch)
|
|
|
|
|
}
|
|
|
|
|
rows.Close()
|
|
|
|
|
if err := rows.Err(); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i := range result {
|
|
|
|
|
headers, err := r.listHeaders(result[i].ID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
result[i].Headers = headers
|
|
|
|
|
}
|
|
|
|
|
return result, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *SQLiteSiteRepository) listHeaders(customHeaderID string) ([]models.Header, error) {
|
|
|
|
|
rows, err := r.db.Query(`
|
|
|
|
|
SELECT id, key, value FROM headers WHERE custom_header_id = ?`, customHeaderID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("list headers: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
|
|
var headers []models.Header
|
|
|
|
|
for rows.Next() {
|
|
|
|
|
var h models.Header
|
|
|
|
|
if err := rows.Scan(&h.ID, &h.Key, &h.Value); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("list headers scan: %w", err)
|
|
|
|
|
}
|
|
|
|
|
headers = append(headers, h)
|
|
|
|
|
}
|
|
|
|
|
return headers, rows.Err()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func insertForwardRule(tx *sql.Tx, siteID string, fr *models.ForwardRule) error {
|
|
|
|
|
fr.ID = uuid.NewString()
|
|
|
|
|
_, err := tx.Exec(`
|
|
|
|
|
INSERT INTO forward_rules (id, site_id, source, destination, status_code, regex)
|
|
|
|
|
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
|
|
|
fr.ID, siteID, fr.Source, fr.Destination, fr.StatusCode, fr.Regex,
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("insert forward rule: %w", err)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func insertCustomHeaders(tx *sql.Tx, siteID string, ch *models.CustomHeaders) error {
|
|
|
|
|
ch.ID = uuid.NewString()
|
|
|
|
|
_, err := tx.Exec(`
|
|
|
|
|
INSERT INTO custom_headers (id, site_id, source) VALUES (?, ?, ?)`,
|
|
|
|
|
ch.ID, siteID, ch.Source,
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("insert custom headers: %w", err)
|
|
|
|
|
}
|
|
|
|
|
for _, h := range ch.Headers {
|
|
|
|
|
if err := insertHeader(tx, ch.ID, &h); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func insertHeader(tx *sql.Tx, customHeaderID string, h *models.Header) error {
|
|
|
|
|
h.ID = uuid.NewString()
|
|
|
|
|
_, err := tx.Exec(`
|
|
|
|
|
INSERT INTO headers (id, custom_header_id, key, value) VALUES (?, ?, ?, ?)`,
|
|
|
|
|
h.ID, customHeaderID, h.Key, h.Value,
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("insert header: %w", err)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|