diff --git a/backend/core/contracts/upload.go b/backend/core/contracts/upload.go index 072deeef..0bb5b4d6 100644 --- a/backend/core/contracts/upload.go +++ b/backend/core/contracts/upload.go @@ -9,19 +9,32 @@ import ( "time" ) +// UploadMetadataDTO represents upload metadata JSON. +type UploadMetadataDTO struct { + Width int `json:"width,omitempty"` + Height int `json:"height,omitempty"` + Duration float64 `json:"duration,omitempty"` + OriginalMime string `json:"original_mime,omitempty"` + UserAgent string `json:"user_agent,omitempty"` + ClientIP string `json:"client_ip,omitempty"` + Bucket string `json:"bucket,omitempty"` + Extra map[string]any `json:"extra,omitempty"` +} + // UploadDTO represents an uploaded file record. type UploadDTO struct { - ID uint64 `json:"id"` - UserID uint64 `json:"user_id"` - FileName string `json:"file_name"` - FilePath string `json:"file_path"` - MimeType string `json:"mime_type"` - Size int64 `json:"size"` - Hash string `json:"hash"` - Status string `json:"status"` - Type string `json:"type"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + ID uint64 `json:"id"` + UserID uint64 `json:"user_id"` + FileName string `json:"file_name"` + FilePath string `json:"file_path"` + MimeType string `json:"mime_type"` + Size int64 `json:"size"` + Hash string `json:"hash"` + Status string `json:"status"` + Type string `json:"type"` + Metadata UploadMetadataDTO `json:"metadata"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` } // OpenedUploadDTO encapsulates the retrieved object stream and its metadata. diff --git a/backend/plugins/domain/upload/service.go b/backend/plugins/domain/upload/service.go index 9c78bae8..7abdf002 100644 --- a/backend/plugins/domain/upload/service.go +++ b/backend/plugins/domain/upload/service.go @@ -78,6 +78,16 @@ func toUploadDTO(u *models.Upload) contracts.UploadDTO { Hash: u.Hash, Status: string(u.Status), Type: u.Type, + Metadata: contracts.UploadMetadataDTO{ + Width: u.Metadata.Width, + Height: u.Metadata.Height, + Duration: u.Metadata.Duration, + OriginalMime: u.Metadata.OriginalMime, + UserAgent: u.Metadata.UserAgent, + ClientIP: u.Metadata.ClientIP, + Bucket: u.Metadata.Bucket, + Extra: u.Metadata.Extra, + }, CreatedAt: u.CreatedAt, UpdatedAt: u.UpdatedAt, } diff --git a/manifest/config/config.example.yaml b/manifest/config/config.example.yaml new file mode 100644 index 00000000..91df9420 --- /dev/null +++ b/manifest/config/config.example.yaml @@ -0,0 +1,114 @@ +# wavelet — Full-Stack Boilerplate Config +# Copy this file to config.yaml and fill in your values. +# Fields marked with <...> are required; others have sensible defaults. + +# ─── Application ──────────────────────────────────────────────────────────────── +app: + app_name: "wavelet" + env: "development" # development | testing | production + addr: ":8000" + node_id: 1 # Snowflake node ID (0-1023). Must be unique per instance. + graceful_shutdown_timeout: 30 + session_cookie_name: "wavelet_session_id" # Change to something unique before deploy + session_secret: "" # Cannot be changed after first start + session_domain: "" # e.g. ".yourdomain.com" + session_age: 86400 # Session lifetime in seconds (default: 24h) + session_secure: false + session_http_only: false + api_prefix: "/api" + +# ─── PostgreSQL ───────────────────────────────────────────────────────────────── +# Supports Standalone and Primary-Replica (read/write split) modes. +database: + enabled: true + sqlite_path: "wavelet.db" # PostgreSQL 禁用时使用此 SQLite 文件路径 + host: "127.0.0.1" + port: 5432 + username: "postgres" + password: "postgres" + database: "wavelet" + max_idle_conn: 16 + max_open_conn: 128 + conn_max_lifetime: 1800 + conn_max_idle_time: 600 + log_level: "info" # error | warn | info | debug | silent;SQL 语句仅在 log.level=debug 时输出 + ssl_mode: "disable" + time_zone: "UTC" + application_name: "wavelet-server" + prefer_simple_protocol: false + search_path: "public" + statement_cache_capacity: 256 + default_query_exec_mode: "cache_statement" + slow_threshold: 200ms + # Optional read replicas: + # replicas: + # - host: "replica1.db.internal" + # port: 5432 + # - host: "replica2.db.internal" + # port: 5432 + +# ─── Redis ────────────────────────────────────────────────────────────────────── +# Supports Standalone, Sentinel (HA), and Cluster modes. +redis: + enabled: true + addrs: + - "127.0.0.1:6379" + username: "" + password: "" + db: 0 # Ignored in Cluster mode + cluster_mode: false # Set true to enable Cluster mode + master_name: "" # Set non-empty to enable Sentinel mode + key_prefix: "wavelet:" + pool_size: 100 + min_idle_conn: 10 + dial_timeout: 5 + read_timeout: 3 + write_timeout: 3 + max_retries: 3 + pool_timeout: 4 + conn_max_idle_time: 300 + maint_notifications: false # 启动时开关;启用 Redis maintenance notifications 自动协商,修改后需重启 + +# ─── Logging ──────────────────────────────────────────────────────────────────── +log: + level: "info" # debug | info | warn | error | fatal | panic + format: "console" # console | json + output: "stdout" # stdout | file + file_path: "./logs/app.log" + max_size: 100 # MB per log file + max_age: 30 # Days to retain old log files + max_backups: 10 + compress: true + + +# ─── Async Task Worker ────────────────────────────────────────────────────────── +worker: + concurrency: 20 + strict_priority: false + queues: + - name: webhook + priority: 10 + - name: whitelist_only + priority: 5 + - name: default + priority: 3 + +# ─── OpenTelemetry Tracing ────────────────────────────────────────────────────── +otel: + sampling_rate: 0.0 # Trace sampling rate (0.0 – 1.0) + tracer_name: "github.com/Rain-kl/Wavelet" # Global tracer instrumentation name + + +# ─── ClickHouse (optional) ────────────────────────────────────────────────────── +clickhouse: + enabled: false + hosts: + - "127.0.0.1:9000" + username: "default" + password: "" + database: "wavelet" + max_idle_conn: 10 + max_open_conn: 100 + conn_max_lifetime: 3600 + dial_timeout: 5 + block_buffer_size: 10 diff --git a/manifest/deploy/kustomize/base/deployment.yaml b/manifest/deploy/kustomize/base/deployment.yaml new file mode 100755 index 00000000..28f1d694 --- /dev/null +++ b/manifest/deploy/kustomize/base/deployment.yaml @@ -0,0 +1,21 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: template-single + labels: + app: template-single +spec: + replicas: 1 + selector: + matchLabels: + app: template-single + template: + metadata: + labels: + app: template-single + spec: + containers: + - name : main + image: template-single + imagePullPolicy: Always + diff --git a/manifest/deploy/kustomize/base/kustomization.yaml b/manifest/deploy/kustomize/base/kustomization.yaml new file mode 100755 index 00000000..302d92d6 --- /dev/null +++ b/manifest/deploy/kustomize/base/kustomization.yaml @@ -0,0 +1,8 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- deployment.yaml +- service.yaml + + + diff --git a/manifest/deploy/kustomize/base/service.yaml b/manifest/deploy/kustomize/base/service.yaml new file mode 100755 index 00000000..608771cc --- /dev/null +++ b/manifest/deploy/kustomize/base/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: template-single +spec: + ports: + - port: 80 + protocol: TCP + targetPort: 8000 + selector: + app: template-single + diff --git a/manifest/deploy/kustomize/overlays/develop/configmap.yaml b/manifest/deploy/kustomize/overlays/develop/configmap.yaml new file mode 100755 index 00000000..3b1d0afe --- /dev/null +++ b/manifest/deploy/kustomize/overlays/develop/configmap.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: template-single-configmap +data: + config.yaml: | + server: + address: ":8000" + openapiPath: "/api.json" + swaggerPath: "/swagger" + + logger: + level : "all" + stdout: true diff --git a/manifest/deploy/kustomize/overlays/develop/deployment.yaml b/manifest/deploy/kustomize/overlays/develop/deployment.yaml new file mode 100755 index 00000000..04e48519 --- /dev/null +++ b/manifest/deploy/kustomize/overlays/develop/deployment.yaml @@ -0,0 +1,10 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: template-single +spec: + template: + spec: + containers: + - name : main + image: template-single:develop \ No newline at end of file diff --git a/manifest/deploy/kustomize/overlays/develop/kustomization.yaml b/manifest/deploy/kustomize/overlays/develop/kustomization.yaml new file mode 100755 index 00000000..4731c47f --- /dev/null +++ b/manifest/deploy/kustomize/overlays/develop/kustomization.yaml @@ -0,0 +1,14 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- ../../base +- configmap.yaml + +patchesStrategicMerge: +- deployment.yaml + +namespace: default + + + diff --git a/manifest/docker/Dockerfile b/manifest/docker/Dockerfile new file mode 100755 index 00000000..d3abe8f5 --- /dev/null +++ b/manifest/docker/Dockerfile @@ -0,0 +1,16 @@ +FROM loads/alpine:3.8 + +############################################################################### +# INSTALLATION +############################################################################### + +ENV WORKDIR /app +ADD resource $WORKDIR/ +ADD ./temp/linux_amd64/main $WORKDIR/main +RUN chmod +x $WORKDIR/main + +############################################################################### +# START +############################################################################### +WORKDIR $WORKDIR +CMD ./main diff --git a/manifest/docker/docker.sh b/manifest/docker/docker.sh new file mode 100755 index 00000000..ff393f9c --- /dev/null +++ b/manifest/docker/docker.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# This shell is executed before docker build. + + + + + diff --git a/manifest/i18n/.gitkeep b/manifest/i18n/.gitkeep new file mode 100755 index 00000000..e69de29b diff --git a/manifest/protobuf/.keep-if-necessary b/manifest/protobuf/.keep-if-necessary new file mode 100755 index 00000000..e69de29b