Initial open-source release (MIT): image2api AI gateway
Full Go backend + Vue 3 frontend, OpenAI-compatible API, multi-provider account pools, billing/admin, Docker one-command deploy with auto HTTPS. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"backend/internal/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type ShowcaseRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewShowcaseRepository(db *gorm.DB) *ShowcaseRepository {
|
||||
return &ShowcaseRepository{db: db}
|
||||
}
|
||||
|
||||
func (r *ShowcaseRepository) IsPublicFile(ctx context.Context, rel string) (bool, error) {
|
||||
normalized := strings.TrimLeft(strings.TrimSpace(rel), "/")
|
||||
if normalized == "" {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
var count int64
|
||||
if err := r.db.WithContext(ctx).
|
||||
Model(&model.ShowcaseItem{}).
|
||||
Where("image = ? OR image = ?", normalized, "/"+normalized).
|
||||
Count(&count).Error; err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
// PublicFileSet returns the set of image keys referenced by any showcase item
|
||||
// (normalized, no leading slash). The media-prune sweep uses it to never delete
|
||||
// a file the homepage still shows, regardless of how old the file is.
|
||||
func (r *ShowcaseRepository) PublicFileSet(ctx context.Context) (map[string]struct{}, error) {
|
||||
var images []string
|
||||
if err := r.db.WithContext(ctx).
|
||||
Model(&model.ShowcaseItem{}).
|
||||
Where("image <> ''").
|
||||
Pluck("image", &images).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
set := make(map[string]struct{}, len(images))
|
||||
for _, img := range images {
|
||||
n := strings.TrimLeft(strings.TrimSpace(img), "/")
|
||||
if n != "" {
|
||||
set[n] = struct{}{}
|
||||
}
|
||||
}
|
||||
return set, nil
|
||||
}
|
||||
|
||||
func (r *ShowcaseRepository) Grouped(ctx context.Context) (map[string][]model.ShowcaseItem, error) {
|
||||
var items []model.ShowcaseItem
|
||||
if err := r.db.WithContext(ctx).Find(&items).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
grouped := map[string][]model.ShowcaseItem{
|
||||
"hero": {},
|
||||
"bento": {},
|
||||
"work": {},
|
||||
}
|
||||
for _, item := range items {
|
||||
grouped[item.Kind] = append(grouped[item.Kind], item)
|
||||
}
|
||||
for kind := range grouped {
|
||||
sort.Slice(grouped[kind], func(i, j int) bool {
|
||||
return grouped[kind][i].Weight > grouped[kind][j].Weight
|
||||
})
|
||||
}
|
||||
return grouped, nil
|
||||
}
|
||||
|
||||
func (r *ShowcaseRepository) Create(ctx context.Context, item *model.ShowcaseItem) error {
|
||||
return r.db.WithContext(ctx).Create(item).Error
|
||||
}
|
||||
|
||||
func (r *ShowcaseRepository) Update(ctx context.Context, entryID string, patch map[string]any) (*model.ShowcaseItem, error) {
|
||||
patch["updated_at"] = time.Now()
|
||||
if err := r.db.WithContext(ctx).Model(&model.ShowcaseItem{}).Where("id = ?", entryID).Updates(patch).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var item model.ShowcaseItem
|
||||
if err := r.db.WithContext(ctx).First(&item, "id = ?", entryID).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &item, nil
|
||||
}
|
||||
|
||||
func (r *ShowcaseRepository) Delete(ctx context.Context, entryID string) (int64, error) {
|
||||
res := r.db.WithContext(ctx).Delete(&model.ShowcaseItem{}, "id = ?", entryID)
|
||||
return res.RowsAffected, res.Error
|
||||
}
|
||||
Reference in New Issue
Block a user