feat: Go backend, enhanced search, new widgets, Docker deploy
Major changes: - Add Go backend (backend/) with microservices architecture - Enhanced master-agents-svc: reranker, content-classifier, stealth-crawler, proxy-manager, media-search, fastClassifier, language detection - New web-svc widgets: KnowledgeCard, ProductCard, ProfileCard, VideoCard, UnifiedCard, CardGallery, InlineImageGallery, SourcesPanel, RelatedQuestions - Improved discover-svc with discover-db integration - Docker deployment improvements (Caddyfile, vendor.sh, BUILD.md) - Library-svc: project_id schema migration - Remove deprecated finance-svc and travel-svc - Localization improvements across services Made-with: Cursor
This commit is contained in:
322
backend/internal/db/computer_artifact_repo.go
Normal file
322
backend/internal/db/computer_artifact_repo.go
Normal file
@@ -0,0 +1,322 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/gooseek/backend/internal/computer"
|
||||
)
|
||||
|
||||
type ComputerArtifactRepo struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func NewComputerArtifactRepo(db *sql.DB) *ComputerArtifactRepo {
|
||||
return &ComputerArtifactRepo{db: db}
|
||||
}
|
||||
|
||||
func (r *ComputerArtifactRepo) Migrate() error {
|
||||
query := `
|
||||
CREATE TABLE IF NOT EXISTS computer_artifacts (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
task_id UUID NOT NULL,
|
||||
type VARCHAR(50) NOT NULL,
|
||||
name VARCHAR(255),
|
||||
content BYTEA,
|
||||
url TEXT,
|
||||
size BIGINT DEFAULT 0,
|
||||
mime_type VARCHAR(100),
|
||||
metadata JSONB,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_computer_artifacts_task_id ON computer_artifacts(task_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_computer_artifacts_type ON computer_artifacts(type);
|
||||
CREATE INDEX IF NOT EXISTS idx_computer_artifacts_created ON computer_artifacts(created_at DESC);
|
||||
`
|
||||
|
||||
_, err := r.db.Exec(query)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *ComputerArtifactRepo) Create(ctx context.Context, artifact *computer.Artifact) error {
|
||||
metadataJSON, _ := json.Marshal(artifact.Metadata)
|
||||
|
||||
query := `
|
||||
INSERT INTO computer_artifacts (id, task_id, type, name, content, url, size, mime_type, metadata, created_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||
`
|
||||
|
||||
_, err := r.db.ExecContext(ctx, query,
|
||||
artifact.ID,
|
||||
artifact.TaskID,
|
||||
artifact.Type,
|
||||
artifact.Name,
|
||||
artifact.Content,
|
||||
artifact.URL,
|
||||
artifact.Size,
|
||||
artifact.MimeType,
|
||||
metadataJSON,
|
||||
artifact.CreatedAt,
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *ComputerArtifactRepo) GetByID(ctx context.Context, id string) (*computer.Artifact, error) {
|
||||
query := `
|
||||
SELECT id, task_id, type, name, content, url, size, mime_type, metadata, created_at
|
||||
FROM computer_artifacts
|
||||
WHERE id = $1
|
||||
`
|
||||
|
||||
var artifact computer.Artifact
|
||||
var content []byte
|
||||
var url, mimeType sql.NullString
|
||||
var metadataJSON []byte
|
||||
|
||||
err := r.db.QueryRowContext(ctx, query, id).Scan(
|
||||
&artifact.ID,
|
||||
&artifact.TaskID,
|
||||
&artifact.Type,
|
||||
&artifact.Name,
|
||||
&content,
|
||||
&url,
|
||||
&artifact.Size,
|
||||
&mimeType,
|
||||
&metadataJSON,
|
||||
&artifact.CreatedAt,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
artifact.Content = content
|
||||
if url.Valid {
|
||||
artifact.URL = url.String
|
||||
}
|
||||
if mimeType.Valid {
|
||||
artifact.MimeType = mimeType.String
|
||||
}
|
||||
if len(metadataJSON) > 0 {
|
||||
json.Unmarshal(metadataJSON, &artifact.Metadata)
|
||||
}
|
||||
|
||||
return &artifact, nil
|
||||
}
|
||||
|
||||
func (r *ComputerArtifactRepo) GetByTaskID(ctx context.Context, taskID string) ([]computer.Artifact, error) {
|
||||
query := `
|
||||
SELECT id, task_id, type, name, url, size, mime_type, metadata, created_at
|
||||
FROM computer_artifacts
|
||||
WHERE task_id = $1
|
||||
ORDER BY created_at ASC
|
||||
`
|
||||
|
||||
rows, err := r.db.QueryContext(ctx, query, taskID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var artifacts []computer.Artifact
|
||||
|
||||
for rows.Next() {
|
||||
var artifact computer.Artifact
|
||||
var url, mimeType sql.NullString
|
||||
var metadataJSON []byte
|
||||
|
||||
err := rows.Scan(
|
||||
&artifact.ID,
|
||||
&artifact.TaskID,
|
||||
&artifact.Type,
|
||||
&artifact.Name,
|
||||
&url,
|
||||
&artifact.Size,
|
||||
&mimeType,
|
||||
&metadataJSON,
|
||||
&artifact.CreatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if url.Valid {
|
||||
artifact.URL = url.String
|
||||
}
|
||||
if mimeType.Valid {
|
||||
artifact.MimeType = mimeType.String
|
||||
}
|
||||
if len(metadataJSON) > 0 {
|
||||
json.Unmarshal(metadataJSON, &artifact.Metadata)
|
||||
}
|
||||
|
||||
artifacts = append(artifacts, artifact)
|
||||
}
|
||||
|
||||
return artifacts, nil
|
||||
}
|
||||
|
||||
func (r *ComputerArtifactRepo) GetByType(ctx context.Context, taskID, artifactType string) ([]computer.Artifact, error) {
|
||||
query := `
|
||||
SELECT id, task_id, type, name, url, size, mime_type, metadata, created_at
|
||||
FROM computer_artifacts
|
||||
WHERE task_id = $1 AND type = $2
|
||||
ORDER BY created_at ASC
|
||||
`
|
||||
|
||||
rows, err := r.db.QueryContext(ctx, query, taskID, artifactType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var artifacts []computer.Artifact
|
||||
|
||||
for rows.Next() {
|
||||
var artifact computer.Artifact
|
||||
var url, mimeType sql.NullString
|
||||
var metadataJSON []byte
|
||||
|
||||
err := rows.Scan(
|
||||
&artifact.ID,
|
||||
&artifact.TaskID,
|
||||
&artifact.Type,
|
||||
&artifact.Name,
|
||||
&url,
|
||||
&artifact.Size,
|
||||
&mimeType,
|
||||
&metadataJSON,
|
||||
&artifact.CreatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if url.Valid {
|
||||
artifact.URL = url.String
|
||||
}
|
||||
if mimeType.Valid {
|
||||
artifact.MimeType = mimeType.String
|
||||
}
|
||||
if len(metadataJSON) > 0 {
|
||||
json.Unmarshal(metadataJSON, &artifact.Metadata)
|
||||
}
|
||||
|
||||
artifacts = append(artifacts, artifact)
|
||||
}
|
||||
|
||||
return artifacts, nil
|
||||
}
|
||||
|
||||
func (r *ComputerArtifactRepo) GetContent(ctx context.Context, id string) ([]byte, error) {
|
||||
query := `SELECT content FROM computer_artifacts WHERE id = $1`
|
||||
var content []byte
|
||||
err := r.db.QueryRowContext(ctx, query, id).Scan(&content)
|
||||
return content, err
|
||||
}
|
||||
|
||||
func (r *ComputerArtifactRepo) UpdateURL(ctx context.Context, id, url string) error {
|
||||
query := `UPDATE computer_artifacts SET url = $1 WHERE id = $2`
|
||||
_, err := r.db.ExecContext(ctx, query, url, id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *ComputerArtifactRepo) Delete(ctx context.Context, id string) error {
|
||||
query := `DELETE FROM computer_artifacts WHERE id = $1`
|
||||
_, err := r.db.ExecContext(ctx, query, id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *ComputerArtifactRepo) DeleteByTaskID(ctx context.Context, taskID string) error {
|
||||
query := `DELETE FROM computer_artifacts WHERE task_id = $1`
|
||||
_, err := r.db.ExecContext(ctx, query, taskID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *ComputerArtifactRepo) DeleteOlderThan(ctx context.Context, days int) (int64, error) {
|
||||
query := `
|
||||
DELETE FROM computer_artifacts
|
||||
WHERE created_at < NOW() - INTERVAL '1 day' * $1
|
||||
`
|
||||
result, err := r.db.ExecContext(ctx, query, days)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return result.RowsAffected()
|
||||
}
|
||||
|
||||
func (r *ComputerArtifactRepo) GetTotalSize(ctx context.Context, taskID string) (int64, error) {
|
||||
query := `SELECT COALESCE(SUM(size), 0) FROM computer_artifacts WHERE task_id = $1`
|
||||
var size int64
|
||||
err := r.db.QueryRowContext(ctx, query, taskID).Scan(&size)
|
||||
return size, err
|
||||
}
|
||||
|
||||
func (r *ComputerArtifactRepo) Count(ctx context.Context, taskID string) (int64, error) {
|
||||
query := `SELECT COUNT(*) FROM computer_artifacts WHERE task_id = $1`
|
||||
var count int64
|
||||
err := r.db.QueryRowContext(ctx, query, taskID).Scan(&count)
|
||||
return count, err
|
||||
}
|
||||
|
||||
type ArtifactSummary struct {
|
||||
ID string `json:"id"`
|
||||
TaskID string `json:"taskId"`
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
Size int64 `json:"size"`
|
||||
MimeType string `json:"mimeType"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
}
|
||||
|
||||
func (r *ComputerArtifactRepo) GetSummaries(ctx context.Context, taskID string) ([]ArtifactSummary, error) {
|
||||
query := `
|
||||
SELECT id, task_id, type, name, url, size, mime_type, created_at
|
||||
FROM computer_artifacts
|
||||
WHERE task_id = $1
|
||||
ORDER BY created_at ASC
|
||||
`
|
||||
|
||||
rows, err := r.db.QueryContext(ctx, query, taskID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var summaries []ArtifactSummary
|
||||
|
||||
for rows.Next() {
|
||||
var s ArtifactSummary
|
||||
var url, mimeType sql.NullString
|
||||
|
||||
err := rows.Scan(
|
||||
&s.ID,
|
||||
&s.TaskID,
|
||||
&s.Type,
|
||||
&s.Name,
|
||||
&url,
|
||||
&s.Size,
|
||||
&mimeType,
|
||||
&s.CreatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if url.Valid {
|
||||
s.URL = url.String
|
||||
}
|
||||
if mimeType.Valid {
|
||||
s.MimeType = mimeType.String
|
||||
}
|
||||
|
||||
summaries = append(summaries, s)
|
||||
}
|
||||
|
||||
return summaries, nil
|
||||
}
|
||||
Reference in New Issue
Block a user