182 lines
5.3 KiB
Go
182 lines
5.3 KiB
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"time"
|
|
)
|
|
|
|
// ---------- Страницы ----------
|
|
|
|
type AdminPage struct {
|
|
ID int64 `json:"id"`
|
|
Slug string `json:"slug"`
|
|
Title string `json:"title"`
|
|
Blocks json.RawMessage `json:"blocks"`
|
|
Published bool `json:"published"`
|
|
SEOTitle string `json:"seo_title"`
|
|
SEODesc string `json:"seo_description"`
|
|
SEOH1 string `json:"seo_h1"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
func (s *Storage) AdminPages(ctx context.Context) ([]AdminPage, error) {
|
|
rows, err := s.pool.Query(ctx, `
|
|
SELECT id, slug, title, blocks, published, seo_title, seo_description, seo_h1, updated_at
|
|
FROM pages ORDER BY slug`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var out []AdminPage
|
|
for rows.Next() {
|
|
var p AdminPage
|
|
if err := rows.Scan(&p.ID, &p.Slug, &p.Title, &p.Blocks, &p.Published,
|
|
&p.SEOTitle, &p.SEODesc, &p.SEOH1, &p.UpdatedAt); err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, p)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
func (s *Storage) AdminPage(ctx context.Context, id int64) (*AdminPage, error) {
|
|
var p AdminPage
|
|
err := s.pool.QueryRow(ctx, `
|
|
SELECT id, slug, title, blocks, published, seo_title, seo_description, seo_h1, updated_at
|
|
FROM pages WHERE id=$1`, id).Scan(&p.ID, &p.Slug, &p.Title, &p.Blocks, &p.Published,
|
|
&p.SEOTitle, &p.SEODesc, &p.SEOH1, &p.UpdatedAt)
|
|
if err != nil {
|
|
return nil, wrapNotFound(err)
|
|
}
|
|
return &p, nil
|
|
}
|
|
|
|
type PageInput struct {
|
|
Title string
|
|
Blocks json.RawMessage
|
|
Published bool
|
|
SEOTitle string
|
|
SEODesc string
|
|
SEOH1 string
|
|
}
|
|
|
|
// AdminUpdatePage меняет содержимое страницы. Адрес (slug) не меняется: страницы
|
|
// привязаны к маршрутам сайта. Новых страниц админка не создаёт по той же причине.
|
|
func (s *Storage) AdminUpdatePage(ctx context.Context, id int64, in PageInput) error {
|
|
tag, err := s.pool.Exec(ctx, `
|
|
UPDATE pages SET title=$2, blocks=$3, published=$4, seo_title=$5, seo_description=$6,
|
|
seo_h1=$7, updated_at=now()
|
|
WHERE id=$1`,
|
|
id, in.Title, in.Blocks, in.Published, in.SEOTitle, in.SEODesc, in.SEOH1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if tag.RowsAffected() == 0 {
|
|
return ErrNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ---------- Настройки ----------
|
|
|
|
// AdminSetSettings записывает несколько ключей за раз в одной транзакции.
|
|
func (s *Storage) AdminSetSettings(ctx context.Context, values map[string]any) error {
|
|
tx, err := s.pool.Begin(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer tx.Rollback(ctx) //nolint:errcheck // после Commit откат ничего не делает
|
|
for key, value := range values {
|
|
raw, err := json.Marshal(value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err := tx.Exec(ctx, `
|
|
INSERT INTO settings (key, value, updated_at) VALUES ($1, $2, now())
|
|
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = now()`,
|
|
key, raw); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return tx.Commit(ctx)
|
|
}
|
|
|
|
// ---------- Филиалы ----------
|
|
|
|
type AdminBranch struct {
|
|
ID int64 `json:"id"`
|
|
Slug string `json:"slug"`
|
|
Name string `json:"name"`
|
|
Address string `json:"address"`
|
|
Phones []string `json:"phones"`
|
|
WorkHours []string `json:"work_hours"`
|
|
Lat *float64 `json:"lat"`
|
|
Lon *float64 `json:"lon"`
|
|
Sort int `json:"sort"`
|
|
Published bool `json:"published"`
|
|
}
|
|
|
|
func (s *Storage) AdminBranches(ctx context.Context) ([]AdminBranch, error) {
|
|
rows, err := s.pool.Query(ctx, `
|
|
SELECT id, slug, name, address, phones, work_hours, lat, lon, sort, published
|
|
FROM branches ORDER BY sort, name`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var out []AdminBranch
|
|
for rows.Next() {
|
|
var b AdminBranch
|
|
if err := rows.Scan(&b.ID, &b.Slug, &b.Name, &b.Address, &b.Phones, &b.WorkHours,
|
|
&b.Lat, &b.Lon, &b.Sort, &b.Published); err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, b)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
type BranchInput struct {
|
|
Slug string
|
|
Name string
|
|
Address string
|
|
Phones []string
|
|
WorkHours []string
|
|
Lat *float64
|
|
Lon *float64
|
|
Sort int
|
|
Published bool
|
|
}
|
|
|
|
func (s *Storage) AdminCreateBranch(ctx context.Context, in BranchInput) (int64, error) {
|
|
phones, _ := json.Marshal(in.Phones)
|
|
hours, _ := json.Marshal(in.WorkHours)
|
|
var id int64
|
|
err := s.pool.QueryRow(ctx, `
|
|
INSERT INTO branches (slug, name, address, phones, work_hours, lat, lon, sort, published)
|
|
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)
|
|
RETURNING id`,
|
|
in.Slug, in.Name, in.Address, phones, hours, in.Lat, in.Lon, in.Sort, in.Published).Scan(&id)
|
|
return id, wrapConflict(err)
|
|
}
|
|
|
|
func (s *Storage) AdminUpdateBranch(ctx context.Context, id int64, in BranchInput) error {
|
|
phones, _ := json.Marshal(in.Phones)
|
|
hours, _ := json.Marshal(in.WorkHours)
|
|
tag, err := s.pool.Exec(ctx, `
|
|
UPDATE branches SET slug=$2, name=$3, address=$4, phones=$5, work_hours=$6, lat=$7, lon=$8,
|
|
sort=$9, published=$10, updated_at=now()
|
|
WHERE id=$1`,
|
|
id, in.Slug, in.Name, in.Address, phones, hours, in.Lat, in.Lon, in.Sort, in.Published)
|
|
if err != nil {
|
|
return wrapConflict(err)
|
|
}
|
|
if tag.RowsAffected() == 0 {
|
|
return ErrNotFound
|
|
}
|
|
return nil
|
|
}
|