mirror of
https://github.com/Dvorinka/SEEN.git
synced 2026-06-05 04:53:01 +00:00
small fix, don't worry about it
This commit is contained in:
@@ -0,0 +1,506 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"github.com/tdvorak/seen/backend/internal/services/download"
|
||||
)
|
||||
|
||||
const createDownloadJobSQL = `
|
||||
INSERT INTO download_jobs (
|
||||
user_id,
|
||||
source_type,
|
||||
source,
|
||||
title,
|
||||
status,
|
||||
queue_position,
|
||||
progress_percent,
|
||||
bytes_total,
|
||||
bytes_downloaded,
|
||||
download_speed_mbps,
|
||||
eta_seconds,
|
||||
error_message,
|
||||
retry_count,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES (
|
||||
$1::uuid,
|
||||
$2::text,
|
||||
$3::text,
|
||||
COALESCE(NULLIF($4::text, ''), $3::text),
|
||||
'queued',
|
||||
NULL,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
NULL,
|
||||
'',
|
||||
0,
|
||||
NOW(),
|
||||
NOW()
|
||||
)
|
||||
RETURNING
|
||||
id::text,
|
||||
user_id::text,
|
||||
source_type,
|
||||
source,
|
||||
title,
|
||||
status,
|
||||
COALESCE(queue_position, 0),
|
||||
progress_percent,
|
||||
bytes_total,
|
||||
bytes_downloaded,
|
||||
download_speed_mbps,
|
||||
COALESCE(eta_seconds, 0),
|
||||
error_message,
|
||||
retry_count,
|
||||
created_at,
|
||||
updated_at;
|
||||
`
|
||||
|
||||
const listDownloadJobsSQL = `
|
||||
SELECT
|
||||
id::text,
|
||||
user_id::text,
|
||||
source_type,
|
||||
source,
|
||||
title,
|
||||
status,
|
||||
COALESCE(queue_position, 0),
|
||||
progress_percent,
|
||||
bytes_total,
|
||||
bytes_downloaded,
|
||||
download_speed_mbps,
|
||||
COALESCE(eta_seconds, 0),
|
||||
error_message,
|
||||
retry_count,
|
||||
created_at,
|
||||
updated_at,
|
||||
COALESCE(started_at, NOW()),
|
||||
COALESCE(completed_at, NOW()),
|
||||
COALESCE(cancelled_at, NOW())
|
||||
FROM download_jobs
|
||||
WHERE user_id = $1::uuid
|
||||
AND ($2::text = '' OR status = $2::text)
|
||||
ORDER BY updated_at DESC, created_at DESC
|
||||
LIMIT $3::int
|
||||
OFFSET $4::int;
|
||||
`
|
||||
|
||||
const getDownloadJobByIDSQL = `
|
||||
SELECT
|
||||
id::text,
|
||||
user_id::text,
|
||||
source_type,
|
||||
source,
|
||||
title,
|
||||
status,
|
||||
COALESCE(queue_position, 0),
|
||||
progress_percent,
|
||||
bytes_total,
|
||||
bytes_downloaded,
|
||||
download_speed_mbps,
|
||||
COALESCE(eta_seconds, 0),
|
||||
error_message,
|
||||
retry_count,
|
||||
created_at,
|
||||
updated_at,
|
||||
COALESCE(started_at, NOW()),
|
||||
COALESCE(completed_at, NOW()),
|
||||
COALESCE(cancelled_at, NOW())
|
||||
FROM download_jobs
|
||||
WHERE user_id = $1::uuid
|
||||
AND id = $2::uuid
|
||||
LIMIT 1;
|
||||
`
|
||||
|
||||
const cancelDownloadJobSQL = `
|
||||
UPDATE download_jobs
|
||||
SET
|
||||
status = 'cancelled',
|
||||
cancelled_at = NOW(),
|
||||
updated_at = NOW()
|
||||
WHERE user_id = $1::uuid
|
||||
AND id = $2::uuid
|
||||
RETURNING
|
||||
id::text,
|
||||
user_id::text,
|
||||
source_type,
|
||||
source,
|
||||
title,
|
||||
status,
|
||||
COALESCE(queue_position, 0),
|
||||
progress_percent,
|
||||
bytes_total,
|
||||
bytes_downloaded,
|
||||
download_speed_mbps,
|
||||
COALESCE(eta_seconds, 0),
|
||||
error_message,
|
||||
retry_count,
|
||||
created_at,
|
||||
updated_at,
|
||||
COALESCE(started_at, NOW()),
|
||||
COALESCE(completed_at, NOW()),
|
||||
COALESCE(cancelled_at, NOW());
|
||||
`
|
||||
|
||||
const appendDownloadEventSQL = `
|
||||
INSERT INTO download_events (
|
||||
job_id,
|
||||
status,
|
||||
message,
|
||||
progress_percent,
|
||||
payload,
|
||||
created_at
|
||||
) VALUES (
|
||||
$1::uuid,
|
||||
$2::text,
|
||||
$3::text,
|
||||
$4::int,
|
||||
$5::jsonb,
|
||||
NOW()
|
||||
);
|
||||
`
|
||||
|
||||
const updateDownloadJobSQL = `
|
||||
UPDATE download_jobs
|
||||
SET
|
||||
status = $3::text,
|
||||
progress_percent = $4::int,
|
||||
bytes_total = $5::bigint,
|
||||
bytes_downloaded = $6::bigint,
|
||||
download_speed_mbps = $7::double precision,
|
||||
eta_seconds = CASE WHEN $8::int <= 0 THEN NULL ELSE $8::int END,
|
||||
error_message = $9::text,
|
||||
retry_count = $10::smallint,
|
||||
started_at = CASE
|
||||
WHEN $3::text IN ('preparing', 'downloading') AND started_at IS NULL THEN NOW()
|
||||
ELSE started_at
|
||||
END,
|
||||
completed_at = CASE
|
||||
WHEN $3::text = 'completed' THEN NOW()
|
||||
ELSE completed_at
|
||||
END,
|
||||
cancelled_at = CASE
|
||||
WHEN $3::text = 'cancelled' THEN NOW()
|
||||
ELSE cancelled_at
|
||||
END,
|
||||
updated_at = NOW()
|
||||
WHERE user_id = $1::uuid
|
||||
AND id = $2::uuid
|
||||
RETURNING
|
||||
id::text,
|
||||
user_id::text,
|
||||
source_type,
|
||||
source,
|
||||
title,
|
||||
status,
|
||||
COALESCE(queue_position, 0),
|
||||
progress_percent,
|
||||
bytes_total,
|
||||
bytes_downloaded,
|
||||
download_speed_mbps,
|
||||
COALESCE(eta_seconds, 0),
|
||||
error_message,
|
||||
retry_count,
|
||||
created_at,
|
||||
updated_at,
|
||||
COALESCE(started_at, NOW()),
|
||||
COALESCE(completed_at, NOW()),
|
||||
COALESCE(cancelled_at, NOW());
|
||||
`
|
||||
|
||||
const listDownloadEventsSQL = `
|
||||
SELECT
|
||||
id,
|
||||
job_id::text,
|
||||
status,
|
||||
message,
|
||||
progress_percent,
|
||||
payload::text,
|
||||
created_at
|
||||
FROM download_events
|
||||
WHERE job_id = $1::uuid
|
||||
AND ($2::timestamptz IS NULL OR created_at > $2::timestamptz)
|
||||
ORDER BY created_at DESC
|
||||
LIMIT $3::int;
|
||||
`
|
||||
|
||||
type DownloadRepository struct {
|
||||
pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewDownloadRepository(pool *pgxpool.Pool) *DownloadRepository {
|
||||
return &DownloadRepository{pool: pool}
|
||||
}
|
||||
|
||||
func (r *DownloadRepository) CreateJob(
|
||||
ctx context.Context,
|
||||
userID uuid.UUID,
|
||||
input download.CreateInput,
|
||||
) (download.Job, error) {
|
||||
var job download.Job
|
||||
err := r.pool.QueryRow(
|
||||
ctx,
|
||||
createDownloadJobSQL,
|
||||
userID,
|
||||
input.SourceType,
|
||||
input.Source,
|
||||
input.Title,
|
||||
).Scan(
|
||||
&job.ID,
|
||||
&job.UserID,
|
||||
&job.SourceType,
|
||||
&job.Source,
|
||||
&job.Title,
|
||||
&job.Status,
|
||||
&job.QueuePosition,
|
||||
&job.ProgressPercent,
|
||||
&job.BytesTotal,
|
||||
&job.BytesDownloaded,
|
||||
&job.DownloadSpeedMbps,
|
||||
&job.EtaSeconds,
|
||||
&job.ErrorMessage,
|
||||
&job.RetryCount,
|
||||
&job.CreatedAt,
|
||||
&job.UpdatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
return download.Job{}, err
|
||||
}
|
||||
|
||||
return job, nil
|
||||
}
|
||||
|
||||
func (r *DownloadRepository) ListJobs(
|
||||
ctx context.Context,
|
||||
userID uuid.UUID,
|
||||
params download.ListParams,
|
||||
) ([]download.Job, error) {
|
||||
rows, err := r.pool.Query(
|
||||
ctx,
|
||||
listDownloadJobsSQL,
|
||||
userID,
|
||||
params.Status,
|
||||
params.Limit,
|
||||
params.Offset,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
jobs := make([]download.Job, 0, params.Limit)
|
||||
for rows.Next() {
|
||||
var job download.Job
|
||||
if err := rows.Scan(
|
||||
&job.ID,
|
||||
&job.UserID,
|
||||
&job.SourceType,
|
||||
&job.Source,
|
||||
&job.Title,
|
||||
&job.Status,
|
||||
&job.QueuePosition,
|
||||
&job.ProgressPercent,
|
||||
&job.BytesTotal,
|
||||
&job.BytesDownloaded,
|
||||
&job.DownloadSpeedMbps,
|
||||
&job.EtaSeconds,
|
||||
&job.ErrorMessage,
|
||||
&job.RetryCount,
|
||||
&job.CreatedAt,
|
||||
&job.UpdatedAt,
|
||||
&job.StartedAt,
|
||||
&job.CompletedAt,
|
||||
&job.CancelledAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
jobs = append(jobs, job)
|
||||
}
|
||||
|
||||
return jobs, rows.Err()
|
||||
}
|
||||
|
||||
func (r *DownloadRepository) GetJobByID(
|
||||
ctx context.Context,
|
||||
userID uuid.UUID,
|
||||
jobID string,
|
||||
) (download.Job, error) {
|
||||
var job download.Job
|
||||
err := r.pool.QueryRow(ctx, getDownloadJobByIDSQL, userID, jobID).Scan(
|
||||
&job.ID,
|
||||
&job.UserID,
|
||||
&job.SourceType,
|
||||
&job.Source,
|
||||
&job.Title,
|
||||
&job.Status,
|
||||
&job.QueuePosition,
|
||||
&job.ProgressPercent,
|
||||
&job.BytesTotal,
|
||||
&job.BytesDownloaded,
|
||||
&job.DownloadSpeedMbps,
|
||||
&job.EtaSeconds,
|
||||
&job.ErrorMessage,
|
||||
&job.RetryCount,
|
||||
&job.CreatedAt,
|
||||
&job.UpdatedAt,
|
||||
&job.StartedAt,
|
||||
&job.CompletedAt,
|
||||
&job.CancelledAt,
|
||||
)
|
||||
if err != nil {
|
||||
return download.Job{}, download.ErrNotFound
|
||||
}
|
||||
|
||||
return job, nil
|
||||
}
|
||||
|
||||
func (r *DownloadRepository) CancelJob(
|
||||
ctx context.Context,
|
||||
userID uuid.UUID,
|
||||
jobID string,
|
||||
) (download.Job, error) {
|
||||
var job download.Job
|
||||
err := r.pool.QueryRow(ctx, cancelDownloadJobSQL, userID, jobID).Scan(
|
||||
&job.ID,
|
||||
&job.UserID,
|
||||
&job.SourceType,
|
||||
&job.Source,
|
||||
&job.Title,
|
||||
&job.Status,
|
||||
&job.QueuePosition,
|
||||
&job.ProgressPercent,
|
||||
&job.BytesTotal,
|
||||
&job.BytesDownloaded,
|
||||
&job.DownloadSpeedMbps,
|
||||
&job.EtaSeconds,
|
||||
&job.ErrorMessage,
|
||||
&job.RetryCount,
|
||||
&job.CreatedAt,
|
||||
&job.UpdatedAt,
|
||||
&job.StartedAt,
|
||||
&job.CompletedAt,
|
||||
&job.CancelledAt,
|
||||
)
|
||||
if err != nil {
|
||||
return download.Job{}, download.ErrNotFound
|
||||
}
|
||||
|
||||
return job, nil
|
||||
}
|
||||
|
||||
func (r *DownloadRepository) ListEvents(
|
||||
ctx context.Context,
|
||||
userID uuid.UUID,
|
||||
jobID string,
|
||||
params download.EventParams,
|
||||
) ([]download.Event, error) {
|
||||
var after pgtype.Timestamptz
|
||||
if params.After != "" {
|
||||
t, err := time.Parse(time.RFC3339, params.After)
|
||||
if err == nil {
|
||||
after = pgtype.Timestamptz{
|
||||
Time: t,
|
||||
Valid: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rows, err := r.pool.Query(ctx, listDownloadEventsSQL, jobID, after, params.Limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
events := make([]download.Event, 0, params.Limit)
|
||||
for rows.Next() {
|
||||
var event download.Event
|
||||
if err := rows.Scan(
|
||||
&event.ID,
|
||||
&event.JobID,
|
||||
&event.Status,
|
||||
&event.Message,
|
||||
&event.ProgressPercent,
|
||||
&event.Payload,
|
||||
&event.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
events = append(events, event)
|
||||
}
|
||||
|
||||
return events, rows.Err()
|
||||
}
|
||||
|
||||
func (r *DownloadRepository) AppendEvent(
|
||||
ctx context.Context,
|
||||
jobID string,
|
||||
event download.Event,
|
||||
) error {
|
||||
_, err := r.pool.Exec(
|
||||
ctx,
|
||||
appendDownloadEventSQL,
|
||||
jobID,
|
||||
event.Status,
|
||||
event.Message,
|
||||
event.ProgressPercent,
|
||||
event.Payload,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *DownloadRepository) UpdateJob(
|
||||
ctx context.Context,
|
||||
userID uuid.UUID,
|
||||
jobID string,
|
||||
input download.UpdateInput,
|
||||
) (download.Job, error) {
|
||||
var job download.Job
|
||||
err := r.pool.QueryRow(
|
||||
ctx,
|
||||
updateDownloadJobSQL,
|
||||
userID,
|
||||
jobID,
|
||||
input.Status,
|
||||
input.ProgressPercent,
|
||||
input.BytesTotal,
|
||||
input.BytesDownloaded,
|
||||
input.DownloadSpeedMbps,
|
||||
input.EtaSeconds,
|
||||
input.ErrorMessage,
|
||||
input.RetryCount,
|
||||
).Scan(
|
||||
&job.ID,
|
||||
&job.UserID,
|
||||
&job.SourceType,
|
||||
&job.Source,
|
||||
&job.Title,
|
||||
&job.Status,
|
||||
&job.QueuePosition,
|
||||
&job.ProgressPercent,
|
||||
&job.BytesTotal,
|
||||
&job.BytesDownloaded,
|
||||
&job.DownloadSpeedMbps,
|
||||
&job.EtaSeconds,
|
||||
&job.ErrorMessage,
|
||||
&job.RetryCount,
|
||||
&job.CreatedAt,
|
||||
&job.UpdatedAt,
|
||||
&job.StartedAt,
|
||||
&job.CompletedAt,
|
||||
&job.CancelledAt,
|
||||
)
|
||||
if err != nil {
|
||||
return download.Job{}, download.ErrNotFound
|
||||
}
|
||||
|
||||
return job, nil
|
||||
}
|
||||
Reference in New Issue
Block a user