feat: V2 microservices stack — backend services, gateway, JWT auth
Add full V2 architecture: identity, content, studio (.NET 10) and file, render, notification, gateway (Go) services with vendored deps, plus DB migrations, event/API contracts, and an init-db script. Wire the Next.js frontend to the gateway: server-side JWT auth routes (login/register/refresh/logout/me), gateway fetch helper, and session/ cookie/jwt helpers under src/lib. Containerize the stack via docker-compose.v2.yml and per-service Dockerfiles. Base images resolve through a Nexus mirror (Docker Hub) and MCR directly; npm/NuGet pull from Nexus groups. Self-host fonts via next/font/local to avoid Google Fonts (geo-blocked). Add CI workflow and ignore .env.v2, *.stackdump, and .NET bin/obj. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
)
|
||||
|
||||
type MinioClient struct {
|
||||
client *minio.Client
|
||||
endpoint string
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Endpoint string
|
||||
AccessKeyID string
|
||||
SecretAccessKey string
|
||||
UseSSL bool
|
||||
}
|
||||
|
||||
func NewMinioClient(cfg Config) (*MinioClient, error) {
|
||||
client, err := minio.New(cfg.Endpoint, &minio.Options{
|
||||
Creds: credentials.NewStaticV4(cfg.AccessKeyID, cfg.SecretAccessKey, ""),
|
||||
Secure: cfg.UseSSL,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("minio client: %w", err)
|
||||
}
|
||||
return &MinioClient{client: client, endpoint: cfg.Endpoint}, nil
|
||||
}
|
||||
|
||||
func (m *MinioClient) PresignedPutURL(ctx context.Context, bucket, key string, expiry time.Duration) (string, error) {
|
||||
u, err := m.client.PresignedPutObject(ctx, bucket, key, expiry)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return u.String(), nil
|
||||
}
|
||||
|
||||
func (m *MinioClient) PresignedGetURL(ctx context.Context, bucket, key string, expiry time.Duration) (string, error) {
|
||||
reqParams := url.Values{}
|
||||
u, err := m.client.PresignedGetObject(ctx, bucket, key, expiry, reqParams)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return u.String(), nil
|
||||
}
|
||||
|
||||
func (m *MinioClient) GenerateKey(folder string) string {
|
||||
return fmt.Sprintf("%s/%s", folder, uuid.New().String())
|
||||
}
|
||||
|
||||
func (m *MinioClient) DeleteObject(ctx context.Context, bucket, key string) error {
|
||||
return m.client.RemoveObject(ctx, bucket, key, minio.RemoveObjectOptions{})
|
||||
}
|
||||
|
||||
func (m *MinioClient) GetObject(ctx context.Context, bucket, key string) (io.ReadCloser, error) {
|
||||
obj, err := m.client.GetObject(ctx, bucket, key, minio.GetObjectOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
func (m *MinioClient) InitiateMultipartUpload(ctx context.Context, bucket, key string) (string, error) {
|
||||
// MinIO SDK doesn't directly expose multipart — use presigned PUT per chunk instead.
|
||||
// Return a generated upload ID for session tracking.
|
||||
return uuid.New().String(), nil
|
||||
}
|
||||
Reference in New Issue
Block a user