Files
geo/server/internal/ops/repository/desktop_client_release.go
T
root 6a4eaf2827
Frontend CI / Frontend (push) Successful in 2m12s
Backend CI / Backend (push) Successful in 14m12s
fix: allow desktop release version history
2026-06-10 13:13:06 +08:00

341 lines
11 KiB
Go

package repository
import (
"context"
"errors"
"fmt"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/geo-platform/tenant-api/internal/ops/domain"
)
var ErrDesktopClientReleaseNotFound = errors.New("desktop client release not found")
type DesktopClientReleaseRepository struct {
pool *pgxpool.Pool
}
func NewDesktopClientReleaseRepository(pool *pgxpool.Pool) *DesktopClientReleaseRepository {
return &DesktopClientReleaseRepository{pool: pool}
}
const desktopClientReleaseColumns = `
id, platform, arch, channel, version, min_supported_version, download_source,
oss_object_key, custom_download_url, file_name, file_size_bytes, sha256,
updater_download_source, updater_oss_object_key, updater_custom_download_url,
updater_file_name, updater_file_size_bytes, updater_sha256,
release_notes, force_update, enabled, published_at, created_by, updated_by,
created_at, updated_at`
type DesktopClientReleaseFilter struct {
Keyword string
Platform string
Channel string
Enabled *bool
Limit int
Offset int
}
type DesktopClientReleaseTarget struct {
Platform string
Arch string
Channel string
}
type UpsertDesktopClientReleaseInput struct {
Platform string
Arch string
Channel string
Version string
MinSupportedVersion *string
DownloadSource string
OSSObjectKey *string
CustomDownloadURL *string
FileName *string
FileSizeBytes *int64
SHA256 *string
UpdaterDownloadSource *string
UpdaterOSSObjectKey *string
UpdaterCustomDownloadURL *string
UpdaterFileName *string
UpdaterFileSizeBytes *int64
UpdaterSHA256 *string
ReleaseNotes *string
ForceUpdate bool
Enabled bool
PublishedAtNow bool
ActorID *int64
}
func scanDesktopClientRelease(row pgx.Row) (*domain.DesktopClientRelease, error) {
var item domain.DesktopClientRelease
err := row.Scan(
&item.ID,
&item.Platform,
&item.Arch,
&item.Channel,
&item.Version,
&item.MinSupportedVersion,
&item.DownloadSource,
&item.OSSObjectKey,
&item.CustomDownloadURL,
&item.FileName,
&item.FileSizeBytes,
&item.SHA256,
&item.UpdaterDownloadSource,
&item.UpdaterOSSObjectKey,
&item.UpdaterCustomDownloadURL,
&item.UpdaterFileName,
&item.UpdaterFileSizeBytes,
&item.UpdaterSHA256,
&item.ReleaseNotes,
&item.ForceUpdate,
&item.Enabled,
&item.PublishedAt,
&item.CreatedBy,
&item.UpdatedBy,
&item.CreatedAt,
&item.UpdatedAt,
)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrDesktopClientReleaseNotFound
}
return nil, err
}
return &item, nil
}
func (r *DesktopClientReleaseRepository) List(ctx context.Context, f DesktopClientReleaseFilter) ([]domain.DesktopClientRelease, int64, error) {
args := []any{}
where := "1=1"
if f.Keyword != "" {
args = append(args, "%"+f.Keyword+"%")
where += fmt.Sprintf(
" AND (version ILIKE $%d OR platform ILIKE $%d OR arch ILIKE $%d OR channel ILIKE $%d OR COALESCE(file_name, '') ILIKE $%d OR COALESCE(oss_object_key, '') ILIKE $%d OR COALESCE(custom_download_url, '') ILIKE $%d OR COALESCE(updater_file_name, '') ILIKE $%d OR COALESCE(updater_oss_object_key, '') ILIKE $%d OR COALESCE(updater_custom_download_url, '') ILIKE $%d)",
len(args), len(args), len(args), len(args), len(args), len(args), len(args), len(args), len(args), len(args),
)
}
if f.Platform != "" {
args = append(args, f.Platform)
where += fmt.Sprintf(" AND platform = $%d", len(args))
}
if f.Channel != "" {
args = append(args, f.Channel)
where += fmt.Sprintf(" AND channel = $%d", len(args))
}
if f.Enabled != nil {
args = append(args, *f.Enabled)
where += fmt.Sprintf(" AND enabled = $%d", len(args))
}
var total int64
if err := r.pool.QueryRow(ctx, "SELECT COUNT(*) FROM ops.desktop_client_releases WHERE "+where, args...).Scan(&total); err != nil {
return nil, 0, err
}
limit := f.Limit
if limit <= 0 || limit > 200 {
limit = 20
}
offset := f.Offset
if offset < 0 {
offset = 0
}
args = append(args, limit, offset)
limitArg := len(args) - 1
offsetArg := len(args)
rows, err := r.pool.Query(ctx, fmt.Sprintf(`
SELECT %s
FROM ops.desktop_client_releases
WHERE %s
ORDER BY enabled DESC, channel ASC, platform ASC, arch ASC, updated_at DESC, id DESC
LIMIT $%d OFFSET $%d`, desktopClientReleaseColumns, where, limitArg, offsetArg), args...)
if err != nil {
return nil, 0, err
}
defer rows.Close()
items := make([]domain.DesktopClientRelease, 0, limit)
for rows.Next() {
item, scanErr := scanDesktopClientRelease(rows)
if scanErr != nil {
return nil, 0, scanErr
}
items = append(items, *item)
}
return items, total, rows.Err()
}
func (r *DesktopClientReleaseRepository) GetByID(ctx context.Context, id int64) (*domain.DesktopClientRelease, error) {
return scanDesktopClientRelease(r.pool.QueryRow(ctx, `
SELECT `+desktopClientReleaseColumns+`
FROM ops.desktop_client_releases
WHERE id = $1`, id))
}
func (r *DesktopClientReleaseRepository) GetByTarget(ctx context.Context, target DesktopClientReleaseTarget) (*domain.DesktopClientRelease, error) {
return scanDesktopClientRelease(r.pool.QueryRow(ctx, `
SELECT `+desktopClientReleaseColumns+`
FROM ops.desktop_client_releases
WHERE platform = $1 AND arch = $2 AND channel = $3`,
target.Platform, target.Arch, target.Channel))
}
func (r *DesktopClientReleaseRepository) FindEnabled(ctx context.Context, target DesktopClientReleaseTarget) (*domain.DesktopClientRelease, error) {
return scanDesktopClientRelease(r.pool.QueryRow(ctx, `
SELECT `+desktopClientReleaseColumns+`
FROM ops.desktop_client_releases
WHERE platform = $1
AND channel = $2
AND enabled = TRUE
AND (arch = $3 OR arch = 'universal')
ORDER BY CASE WHEN arch = $3 THEN 0 ELSE 1 END, updated_at DESC, id DESC
LIMIT 1`,
target.Platform, target.Channel, target.Arch))
}
func (r *DesktopClientReleaseRepository) ListEnabledByTarget(ctx context.Context, target DesktopClientReleaseTarget) ([]domain.DesktopClientRelease, error) {
rows, err := r.pool.Query(ctx, `
SELECT `+desktopClientReleaseColumns+`
FROM ops.desktop_client_releases
WHERE platform = $1
AND channel = $2
AND enabled = TRUE
AND (arch = $3 OR arch = 'universal')
ORDER BY CASE WHEN arch = $3 THEN 0 ELSE 1 END, updated_at DESC, id DESC`,
target.Platform, target.Channel, target.Arch)
if err != nil {
return nil, err
}
defer rows.Close()
items := []domain.DesktopClientRelease{}
for rows.Next() {
item, scanErr := scanDesktopClientRelease(rows)
if scanErr != nil {
return nil, scanErr
}
items = append(items, *item)
}
return items, rows.Err()
}
func (r *DesktopClientReleaseRepository) ListEnabledByChannel(ctx context.Context, channel string) ([]domain.DesktopClientRelease, error) {
rows, err := r.pool.Query(ctx, `
SELECT `+desktopClientReleaseColumns+`
FROM ops.desktop_client_releases
WHERE channel = $1
AND enabled = TRUE
ORDER BY channel ASC, platform ASC, arch ASC, updated_at DESC, id DESC`,
channel)
if err != nil {
return nil, err
}
defer rows.Close()
items := []domain.DesktopClientRelease{}
for rows.Next() {
item, scanErr := scanDesktopClientRelease(rows)
if scanErr != nil {
return nil, scanErr
}
items = append(items, *item)
}
return items, rows.Err()
}
func (r *DesktopClientReleaseRepository) Create(ctx context.Context, in UpsertDesktopClientReleaseInput) (*domain.DesktopClientRelease, error) {
return scanDesktopClientRelease(r.pool.QueryRow(ctx, `
INSERT INTO ops.desktop_client_releases (
platform, arch, channel, version, min_supported_version, download_source,
oss_object_key, custom_download_url, file_name, file_size_bytes, sha256,
updater_download_source, updater_oss_object_key, updater_custom_download_url,
updater_file_name, updater_file_size_bytes, updater_sha256,
release_notes, force_update, enabled, published_at, created_by, updated_by
)
VALUES (
$1, $2, $3, $4, $5, $6,
$7, $8, $9, $10, $11,
$12, $13, $14, $15, $16, $17,
$18, $19, $20,
CASE WHEN $21 THEN NOW() ELSE NULL END,
$22, $22
)
RETURNING `+desktopClientReleaseColumns,
in.Platform, in.Arch, in.Channel, in.Version, in.MinSupportedVersion, in.DownloadSource,
in.OSSObjectKey, in.CustomDownloadURL, in.FileName, in.FileSizeBytes, in.SHA256,
in.UpdaterDownloadSource, in.UpdaterOSSObjectKey, in.UpdaterCustomDownloadURL,
in.UpdaterFileName, in.UpdaterFileSizeBytes, in.UpdaterSHA256,
in.ReleaseNotes, in.ForceUpdate, in.Enabled, in.PublishedAtNow, in.ActorID))
}
func (r *DesktopClientReleaseRepository) Update(ctx context.Context, id int64, in UpsertDesktopClientReleaseInput) (*domain.DesktopClientRelease, error) {
return scanDesktopClientRelease(r.pool.QueryRow(ctx, `
UPDATE ops.desktop_client_releases
SET platform = $1,
arch = $2,
channel = $3,
version = $4,
min_supported_version = $5,
download_source = $6,
oss_object_key = $7,
custom_download_url = $8,
file_name = $9,
file_size_bytes = $10,
sha256 = $11,
updater_download_source = $12,
updater_oss_object_key = $13,
updater_custom_download_url = $14,
updater_file_name = $15,
updater_file_size_bytes = $16,
updater_sha256 = $17,
release_notes = $18,
force_update = $19,
enabled = $20,
published_at = CASE
WHEN $20 = TRUE AND published_at IS NULL THEN NOW()
WHEN $20 = FALSE THEN NULL
ELSE published_at
END,
updated_by = $21,
updated_at = NOW()
WHERE id = $22
RETURNING `+desktopClientReleaseColumns,
in.Platform, in.Arch, in.Channel, in.Version, in.MinSupportedVersion, in.DownloadSource,
in.OSSObjectKey, in.CustomDownloadURL, in.FileName, in.FileSizeBytes, in.SHA256,
in.UpdaterDownloadSource, in.UpdaterOSSObjectKey, in.UpdaterCustomDownloadURL,
in.UpdaterFileName, in.UpdaterFileSizeBytes, in.UpdaterSHA256,
in.ReleaseNotes, in.ForceUpdate, in.Enabled, in.ActorID, id))
}
func (r *DesktopClientReleaseRepository) SetEnabled(ctx context.Context, id int64, enabled bool, actorID *int64) (*domain.DesktopClientRelease, error) {
return scanDesktopClientRelease(r.pool.QueryRow(ctx, `
UPDATE ops.desktop_client_releases
SET enabled = $1,
published_at = CASE
WHEN $1 = TRUE AND published_at IS NULL THEN NOW()
WHEN $1 = FALSE THEN NULL
ELSE published_at
END,
updated_by = $2,
updated_at = NOW()
WHERE id = $3
RETURNING `+desktopClientReleaseColumns, enabled, actorID, id))
}
func (r *DesktopClientReleaseRepository) Delete(ctx context.Context, id int64) error {
cmd, err := r.pool.Exec(ctx, `DELETE FROM ops.desktop_client_releases WHERE id = $1`, id)
if err != nil {
return err
}
if cmd.RowsAffected() == 0 {
return ErrDesktopClientReleaseNotFound
}
return nil
}