feat: production release with React SPA, file attachments, and deploy tooling
Backend: - Add file_handlers.go: presigned upload/download for item attachments - Add item_files.go: item file and thumbnail DB operations - Add migration 011: item_files table and thumbnail_key column - Update items/projects/relationships DB with extended field support - Update routes: React SPA serving from web/dist, file upload endpoints - Update auth handlers and middleware for cookie + bearer token auth - Remove Go HTML templates (replaced by React SPA) - Update storage client for presigned URL generation Frontend: - Add TagInput component for tag/keyword entry - Add SVG assets for Silo branding and UI icons - Update API client and types for file uploads, auth, extended fields - Update AuthContext for session-based auth flow - Update LoginPage, ProjectsPage, SchemasPage, SettingsPage - Fix tsconfig.node.json Deployment: - Update config.prod.yaml: single-binary SPA layout at /opt/silo - Update silod.service: ReadOnlyPaths for /opt/silo - Add scripts/deploy.sh: build, package, ship, migrate, start - Update docker-compose.yaml and Dockerfile - Add frontend-spec.md design document
This commit is contained in:
@@ -5,6 +5,8 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
@@ -110,6 +112,36 @@ func (s *Storage) Delete(ctx context.Context, key string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Bucket returns the bucket name.
|
||||
func (s *Storage) Bucket() string {
|
||||
return s.bucket
|
||||
}
|
||||
|
||||
// PresignPut generates a presigned PUT URL for direct browser upload.
|
||||
func (s *Storage) PresignPut(ctx context.Context, key string, expiry time.Duration) (*url.URL, error) {
|
||||
u, err := s.client.PresignedPutObject(ctx, s.bucket, key, expiry)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("generating presigned put URL: %w", err)
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// Copy copies an object within the same bucket from srcKey to dstKey.
|
||||
func (s *Storage) Copy(ctx context.Context, srcKey, dstKey string) error {
|
||||
src := minio.CopySrcOptions{
|
||||
Bucket: s.bucket,
|
||||
Object: srcKey,
|
||||
}
|
||||
dst := minio.CopyDestOptions{
|
||||
Bucket: s.bucket,
|
||||
Object: dstKey,
|
||||
}
|
||||
if _, err := s.client.CopyObject(ctx, dst, src); err != nil {
|
||||
return fmt.Errorf("copying object: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FileKey generates a storage key for an item file.
|
||||
func FileKey(partNumber string, revision int) string {
|
||||
return fmt.Sprintf("items/%s/rev%d.FCStd", partNumber, revision)
|
||||
|
||||
Reference in New Issue
Block a user