Changeset 0.22.4 (#146)
This commit is contained in:
@@ -14,18 +14,19 @@ type AttachmentStore struct{}
|
||||
func NewAttachmentStore() *AttachmentStore { return &AttachmentStore{} }
|
||||
|
||||
// ── columns shared across queries ──────────
|
||||
const attachmentCols = `id, channel_id, user_id, message_id, filename, content_type,
|
||||
const attachmentCols = `id, channel_id, user_id, message_id, project_id, filename, content_type,
|
||||
size_bytes, storage_key, extracted_text, metadata, created_at`
|
||||
|
||||
// scanAttachment scans a row into an Attachment struct.
|
||||
func scanAttachment(row interface{ Scan(dest ...interface{}) error }) (*models.Attachment, error) {
|
||||
var a models.Attachment
|
||||
var messageID sql.NullString
|
||||
var projectID sql.NullString
|
||||
var extractedText sql.NullString
|
||||
var metadataJSON []byte
|
||||
|
||||
err := row.Scan(
|
||||
&a.ID, &a.ChannelID, &a.UserID, &messageID,
|
||||
&a.ID, &a.ChannelID, &a.UserID, &messageID, &projectID,
|
||||
&a.Filename, &a.ContentType, &a.SizeBytes,
|
||||
&a.StorageKey, &extractedText, &metadataJSON, &a.CreatedAt,
|
||||
)
|
||||
@@ -34,6 +35,7 @@ func scanAttachment(row interface{ Scan(dest ...interface{}) error }) (*models.A
|
||||
}
|
||||
|
||||
a.MessageID = NullableStringPtr(messageID)
|
||||
a.ProjectID = NullableStringPtr(projectID)
|
||||
if extractedText.Valid {
|
||||
a.ExtractedText = &extractedText.String
|
||||
}
|
||||
@@ -45,11 +47,12 @@ func scanAttachment(row interface{ Scan(dest ...interface{}) error }) (*models.A
|
||||
|
||||
func (s *AttachmentStore) Create(ctx context.Context, a *models.Attachment) error {
|
||||
return DB.QueryRowContext(ctx, `
|
||||
INSERT INTO attachments (channel_id, user_id, message_id, filename, content_type,
|
||||
INSERT INTO attachments (channel_id, user_id, message_id, project_id, filename, content_type,
|
||||
size_bytes, storage_key, extracted_text, metadata)
|
||||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)
|
||||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)
|
||||
RETURNING id, created_at`,
|
||||
a.ChannelID, a.UserID, models.NullString(a.MessageID),
|
||||
models.NullString(a.ProjectID),
|
||||
a.Filename, a.ContentType, a.SizeBytes,
|
||||
a.StorageKey, models.NullString(a.ExtractedText),
|
||||
ToJSON(a.Metadata),
|
||||
@@ -99,6 +102,26 @@ func (s *AttachmentStore) GetByMessage(ctx context.Context, messageID string) ([
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
// GetByProject returns all attachments for a project (v0.22.4).
|
||||
func (s *AttachmentStore) GetByProject(ctx context.Context, projectID string) ([]models.Attachment, error) {
|
||||
rows, err := DB.QueryContext(ctx,
|
||||
`SELECT `+attachmentCols+` FROM attachments WHERE project_id = $1 ORDER BY created_at`, projectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var out []models.Attachment
|
||||
for rows.Next() {
|
||||
a, err := scanAttachment(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, *a)
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
func (s *AttachmentStore) SetMessageID(ctx context.Context, attachmentID, messageID string) error {
|
||||
_, err := DB.ExecContext(ctx,
|
||||
`UPDATE attachments SET message_id = $1 WHERE id = $2`,
|
||||
|
||||
@@ -20,20 +20,21 @@ func NewHealthStore(db *sql.DB) *HealthStore {
|
||||
func (s *HealthStore) UpsertWindow(ctx context.Context, w *models.ProviderHealthWindow) error {
|
||||
_, err := s.db.ExecContext(ctx, `
|
||||
INSERT INTO provider_health (provider_config_id, window_start,
|
||||
request_count, error_count, timeout_count,
|
||||
request_count, error_count, timeout_count, rate_limit_count,
|
||||
total_latency_ms, max_latency_ms, last_error, last_error_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, now())
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, now())
|
||||
ON CONFLICT (provider_config_id, window_start) DO UPDATE SET
|
||||
request_count = provider_health.request_count + EXCLUDED.request_count,
|
||||
error_count = provider_health.error_count + EXCLUDED.error_count,
|
||||
timeout_count = provider_health.timeout_count + EXCLUDED.timeout_count,
|
||||
request_count = provider_health.request_count + EXCLUDED.request_count,
|
||||
error_count = provider_health.error_count + EXCLUDED.error_count,
|
||||
timeout_count = provider_health.timeout_count + EXCLUDED.timeout_count,
|
||||
rate_limit_count = provider_health.rate_limit_count + EXCLUDED.rate_limit_count,
|
||||
total_latency_ms = provider_health.total_latency_ms + EXCLUDED.total_latency_ms,
|
||||
max_latency_ms = GREATEST(provider_health.max_latency_ms, EXCLUDED.max_latency_ms),
|
||||
last_error = COALESCE(EXCLUDED.last_error, provider_health.last_error),
|
||||
last_error_at = COALESCE(EXCLUDED.last_error_at, provider_health.last_error_at),
|
||||
updated_at = now()
|
||||
max_latency_ms = GREATEST(provider_health.max_latency_ms, EXCLUDED.max_latency_ms),
|
||||
last_error = COALESCE(EXCLUDED.last_error, provider_health.last_error),
|
||||
last_error_at = COALESCE(EXCLUDED.last_error_at, provider_health.last_error_at),
|
||||
updated_at = now()
|
||||
`, w.ProviderConfigID, w.WindowStart,
|
||||
w.RequestCount, w.ErrorCount, w.TimeoutCount,
|
||||
w.RequestCount, w.ErrorCount, w.TimeoutCount, w.RateLimitCount,
|
||||
w.TotalLatencyMs, w.MaxLatencyMs, w.LastError, w.LastErrorAt,
|
||||
)
|
||||
return err
|
||||
@@ -44,13 +45,13 @@ func (s *HealthStore) GetCurrentWindow(ctx context.Context, providerConfigID str
|
||||
var w models.ProviderHealthWindow
|
||||
err := s.db.QueryRowContext(ctx, `
|
||||
SELECT id, provider_config_id, window_start,
|
||||
request_count, error_count, timeout_count,
|
||||
request_count, error_count, timeout_count, rate_limit_count,
|
||||
total_latency_ms, max_latency_ms, last_error, last_error_at
|
||||
FROM provider_health
|
||||
WHERE provider_config_id = $1 AND window_start = $2
|
||||
`, providerConfigID, windowStart).Scan(
|
||||
&w.ID, &w.ProviderConfigID, &w.WindowStart,
|
||||
&w.RequestCount, &w.ErrorCount, &w.TimeoutCount,
|
||||
&w.RequestCount, &w.ErrorCount, &w.TimeoutCount, &w.RateLimitCount,
|
||||
&w.TotalLatencyMs, &w.MaxLatencyMs, &w.LastError, &w.LastErrorAt,
|
||||
)
|
||||
if err == sql.ErrNoRows {
|
||||
@@ -62,7 +63,7 @@ func (s *HealthStore) GetCurrentWindow(ctx context.Context, providerConfigID str
|
||||
func (s *HealthStore) ListWindows(ctx context.Context, providerConfigID string, hours int) ([]models.ProviderHealthWindow, error) {
|
||||
rows, err := s.db.QueryContext(ctx, `
|
||||
SELECT id, provider_config_id, window_start,
|
||||
request_count, error_count, timeout_count,
|
||||
request_count, error_count, timeout_count, rate_limit_count,
|
||||
total_latency_ms, max_latency_ms, last_error, last_error_at
|
||||
FROM provider_health
|
||||
WHERE provider_config_id = $1
|
||||
@@ -79,7 +80,7 @@ func (s *HealthStore) ListWindows(ctx context.Context, providerConfigID string,
|
||||
var w models.ProviderHealthWindow
|
||||
if err := rows.Scan(
|
||||
&w.ID, &w.ProviderConfigID, &w.WindowStart,
|
||||
&w.RequestCount, &w.ErrorCount, &w.TimeoutCount,
|
||||
&w.RequestCount, &w.ErrorCount, &w.TimeoutCount, &w.RateLimitCount,
|
||||
&w.TotalLatencyMs, &w.MaxLatencyMs, &w.LastError, &w.LastErrorAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
@@ -93,7 +94,7 @@ func (s *HealthStore) ListAllCurrent(ctx context.Context) ([]models.ProviderHeal
|
||||
windowStart := time.Now().UTC().Truncate(time.Hour)
|
||||
rows, err := s.db.QueryContext(ctx, `
|
||||
SELECT id, provider_config_id, window_start,
|
||||
request_count, error_count, timeout_count,
|
||||
request_count, error_count, timeout_count, rate_limit_count,
|
||||
total_latency_ms, max_latency_ms, last_error, last_error_at
|
||||
FROM provider_health
|
||||
WHERE window_start = $1
|
||||
@@ -109,7 +110,7 @@ func (s *HealthStore) ListAllCurrent(ctx context.Context) ([]models.ProviderHeal
|
||||
var w models.ProviderHealthWindow
|
||||
if err := rows.Scan(
|
||||
&w.ID, &w.ProviderConfigID, &w.WindowStart,
|
||||
&w.RequestCount, &w.ErrorCount, &w.TimeoutCount,
|
||||
&w.RequestCount, &w.ErrorCount, &w.TimeoutCount, &w.RateLimitCount,
|
||||
&w.TotalLatencyMs, &w.MaxLatencyMs, &w.LastError, &w.LastErrorAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
@@ -126,5 +127,66 @@ func (s *HealthStore) Prune(ctx context.Context, before time.Time) (int64, error
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Also prune tool health
|
||||
s.db.ExecContext(ctx, `DELETE FROM tool_health WHERE window_start < $1`, before)
|
||||
return result.RowsAffected()
|
||||
}
|
||||
|
||||
// ── Tool Health (v0.22.4) ────────────────────
|
||||
|
||||
func (s *HealthStore) UpsertToolWindow(ctx context.Context, w *models.ToolHealthWindow) error {
|
||||
_, err := s.db.ExecContext(ctx, `
|
||||
INSERT INTO tool_health (tool_name, window_start,
|
||||
request_count, error_count, total_latency_ms, max_latency_ms,
|
||||
last_error, last_error_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, now())
|
||||
ON CONFLICT (tool_name, window_start) DO UPDATE SET
|
||||
request_count = tool_health.request_count + EXCLUDED.request_count,
|
||||
error_count = tool_health.error_count + EXCLUDED.error_count,
|
||||
total_latency_ms = tool_health.total_latency_ms + EXCLUDED.total_latency_ms,
|
||||
max_latency_ms = GREATEST(tool_health.max_latency_ms, EXCLUDED.max_latency_ms),
|
||||
last_error = COALESCE(EXCLUDED.last_error, tool_health.last_error),
|
||||
last_error_at = COALESCE(EXCLUDED.last_error_at, tool_health.last_error_at),
|
||||
updated_at = now()
|
||||
`, w.ToolName, w.WindowStart,
|
||||
w.RequestCount, w.ErrorCount, w.TotalLatencyMs, w.MaxLatencyMs,
|
||||
w.LastError, w.LastErrorAt,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *HealthStore) ListAllToolCurrent(ctx context.Context) ([]models.ToolHealthWindow, error) {
|
||||
windowStart := time.Now().UTC().Truncate(time.Hour)
|
||||
rows, err := s.db.QueryContext(ctx, `
|
||||
SELECT id, tool_name, window_start,
|
||||
request_count, error_count, total_latency_ms, max_latency_ms,
|
||||
last_error, last_error_at
|
||||
FROM tool_health
|
||||
WHERE window_start = $1
|
||||
ORDER BY tool_name
|
||||
`, windowStart)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var result []models.ToolHealthWindow
|
||||
for rows.Next() {
|
||||
var w models.ToolHealthWindow
|
||||
if err := rows.Scan(
|
||||
&w.ID, &w.ToolName, &w.WindowStart,
|
||||
&w.RequestCount, &w.ErrorCount, &w.TotalLatencyMs, &w.MaxLatencyMs,
|
||||
&w.LastError, &w.LastErrorAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, w)
|
||||
}
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
// DeactivateProvider marks a provider config as inactive (for auto-disable).
|
||||
func (s *HealthStore) DeactivateProvider(ctx context.Context, configID string) error {
|
||||
_, err := s.db.ExecContext(ctx, `UPDATE provider_configs SET is_active = false WHERE id = $1`, configID)
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user