mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-11 01:36:37 +08:00
Merge branch 'wavelet/main' into main
This commit is contained in:
@@ -108,7 +108,7 @@ cross-build:
|
|||||||
(version=$(or $(VERSION),dev))..."
|
(version=$(or $(VERSION),dev))..."
|
||||||
@mkdir -p bin
|
@mkdir -p bin
|
||||||
docker build \
|
docker build \
|
||||||
--file docker/Dockerfile.cross \
|
--file manifest/docker/Dockerfile.cross \
|
||||||
--target export \
|
--target export \
|
||||||
--build-arg VERSION=$(or $(VERSION),dev) \
|
--build-arg VERSION=$(or $(VERSION),dev) \
|
||||||
--build-arg BUILD_DATE="$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')" \
|
--build-arg BUILD_DATE="$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')" \
|
||||||
|
|||||||
+1
-1
@@ -213,7 +213,7 @@ wavelet/
|
|||||||
├── main.go # 程序入口(委托给 internal/cmd)
|
├── main.go # 程序入口(委托给 internal/cmd)
|
||||||
├── config.example.yaml # 配置模板
|
├── config.example.yaml # 配置模板
|
||||||
├── Makefile # 常用命令(swagger、tidy、license、cross-build)
|
├── Makefile # 常用命令(swagger、tidy、license、cross-build)
|
||||||
├── docker/ # Docker 镜像构建文件(集成/前端/后端)
|
├── manifest/ # 项目清单与编排:docker 镜像构建/compose、deploy (k8s)、config 配置
|
||||||
├── docs/ # Swagger 自动生成文档
|
├── docs/ # Swagger 自动生成文档
|
||||||
├── frontend/ # Next.js 前端应用
|
├── frontend/ # Next.js 前端应用
|
||||||
│ ├── app/ # App Router 页面
|
│ ├── app/ # App Router 页面
|
||||||
|
|||||||
@@ -10,13 +10,18 @@ import (
|
|||||||
|
|
||||||
// SystemConfigDTO represents a system configuration key-value entry.
|
// SystemConfigDTO represents a system configuration key-value entry.
|
||||||
type SystemConfigDTO struct {
|
type SystemConfigDTO struct {
|
||||||
Key string `json:"key"`
|
Key string `json:"key" gorm:"primaryKey;column:key;size:255"`
|
||||||
Value string `json:"value"`
|
Value string `json:"value" gorm:"column:value;type:text"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type" gorm:"column:type;size:50"`
|
||||||
Visibility int `json:"visibility"`
|
Visibility int `json:"visibility" gorm:"column:visibility"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description" gorm:"column:description;size:500"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName returns the default table name for SystemConfigDTO.
|
||||||
|
func (SystemConfigDTO) TableName() string {
|
||||||
|
return "w_system_configs"
|
||||||
}
|
}
|
||||||
|
|
||||||
// SystemConfigService defines the unified contract for querying and mutating system configurations.
|
// SystemConfigService defines the unified contract for querying and mutating system configurations.
|
||||||
|
|||||||
@@ -71,6 +71,11 @@ type TaskExecutionDTO struct {
|
|||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TableName returns the default table name for TaskExecutionDTO.
|
||||||
|
func (TaskExecutionDTO) TableName() string {
|
||||||
|
return "w_task_executions"
|
||||||
|
}
|
||||||
|
|
||||||
// Canonical triggered_by values persisted on task executions and shown in admin UI.
|
// Canonical triggered_by values persisted on task executions and shown in admin UI.
|
||||||
const (
|
const (
|
||||||
TaskTriggerSystem = "system"
|
TaskTriggerSystem = "system"
|
||||||
|
|||||||
@@ -23,20 +23,25 @@ type UploadMetadataDTO struct {
|
|||||||
|
|
||||||
// UploadDTO represents an uploaded file record.
|
// UploadDTO represents an uploaded file record.
|
||||||
type UploadDTO struct {
|
type UploadDTO struct {
|
||||||
ID uint64 `json:"id"`
|
ID uint64 `json:"id" gorm:"primaryKey"`
|
||||||
UserID uint64 `json:"user_id"`
|
UserID uint64 `json:"user_id" gorm:"index"`
|
||||||
FileName string `json:"file_name"`
|
FileName string `json:"file_name" gorm:"size:255"`
|
||||||
FilePath string `json:"file_path"`
|
FilePath string `json:"file_path" gorm:"size:500"`
|
||||||
MimeType string `json:"mime_type"`
|
MimeType string `json:"mime_type" gorm:"size:100"`
|
||||||
Size int64 `json:"size"`
|
Size int64 `json:"size"`
|
||||||
Hash string `json:"hash"`
|
Hash string `json:"hash" gorm:"size:64"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status" gorm:"type:varchar(20)"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type" gorm:"size:50;index"`
|
||||||
Metadata UploadMetadataDTO `json:"metadata"`
|
Metadata UploadMetadataDTO `json:"metadata" gorm:"serializer:json;type:jsonb"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TableName returns the default table name for UploadDTO.
|
||||||
|
func (UploadDTO) TableName() string {
|
||||||
|
return "w_uploads"
|
||||||
|
}
|
||||||
|
|
||||||
// OpenedUploadDTO encapsulates the retrieved object stream and its metadata.
|
// OpenedUploadDTO encapsulates the retrieved object stream and its metadata.
|
||||||
type OpenedUploadDTO struct {
|
type OpenedUploadDTO struct {
|
||||||
Upload UploadDTO
|
Upload UploadDTO
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ func (PublicConfigAdapter) PublicConfig(ctx context.Context) (map[string]string,
|
|||||||
// SystemConfigServiceImpl implements contracts.SystemConfigService.
|
// SystemConfigServiceImpl implements contracts.SystemConfigService.
|
||||||
type SystemConfigServiceImpl struct{}
|
type SystemConfigServiceImpl struct{}
|
||||||
|
|
||||||
|
// GetByKey retrieves a system config by its unique key.
|
||||||
func (SystemConfigServiceImpl) GetByKey(ctx context.Context, key string) (contracts.SystemConfigDTO, error) {
|
func (SystemConfigServiceImpl) GetByKey(ctx context.Context, key string) (contracts.SystemConfigDTO, error) {
|
||||||
cfg, err := repository.GetSystemConfigByKey(ctx, key)
|
cfg, err := repository.GetSystemConfigByKey(ctx, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -37,6 +38,7 @@ func (SystemConfigServiceImpl) GetByKey(ctx context.Context, key string) (contra
|
|||||||
return toSystemConfigDTO(cfg), nil
|
return toSystemConfigDTO(cfg), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListByKeys retrieves multiple system configs by their keys.
|
||||||
func (SystemConfigServiceImpl) ListByKeys(ctx context.Context, keys []string) (map[string]contracts.SystemConfigDTO, error) {
|
func (SystemConfigServiceImpl) ListByKeys(ctx context.Context, keys []string) (map[string]contracts.SystemConfigDTO, error) {
|
||||||
cfgs, err := repository.ListSystemConfigsByKeys(ctx, keys)
|
cfgs, err := repository.ListSystemConfigsByKeys(ctx, keys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -49,6 +51,7 @@ func (SystemConfigServiceImpl) ListByKeys(ctx context.Context, keys []string) (m
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListVisible returns all user-visible system configs.
|
||||||
func (SystemConfigServiceImpl) ListVisible(ctx context.Context) ([]contracts.SystemConfigDTO, error) {
|
func (SystemConfigServiceImpl) ListVisible(ctx context.Context) ([]contracts.SystemConfigDTO, error) {
|
||||||
cfgs, err := repository.ListVisibleSystemConfigs(ctx)
|
cfgs, err := repository.ListVisibleSystemConfigs(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -61,6 +64,7 @@ func (SystemConfigServiceImpl) ListVisible(ctx context.Context) ([]contracts.Sys
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListByType returns all system configs belonging to a specific configuration type.
|
||||||
func (SystemConfigServiceImpl) ListByType(ctx context.Context, configType string) ([]contracts.SystemConfigDTO, error) {
|
func (SystemConfigServiceImpl) ListByType(ctx context.Context, configType string) ([]contracts.SystemConfigDTO, error) {
|
||||||
cfgs, err := repository.ListAdminSystemConfigs(ctx, configType)
|
cfgs, err := repository.ListAdminSystemConfigs(ctx, configType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -73,22 +77,27 @@ func (SystemConfigServiceImpl) ListByType(ctx context.Context, configType string
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetIntByKey retrieves an integer system config value.
|
||||||
func (SystemConfigServiceImpl) GetIntByKey(ctx context.Context, key string) (int, error) {
|
func (SystemConfigServiceImpl) GetIntByKey(ctx context.Context, key string) (int, error) {
|
||||||
return repository.GetIntByKey(ctx, key)
|
return repository.GetIntByKey(ctx, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBoolByKey retrieves a boolean system config value.
|
||||||
func (SystemConfigServiceImpl) GetBoolByKey(ctx context.Context, key string) (bool, error) {
|
func (SystemConfigServiceImpl) GetBoolByKey(ctx context.Context, key string) (bool, error) {
|
||||||
return repository.GetBoolByKey(ctx, key)
|
return repository.GetBoolByKey(ctx, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SaveOrUpdate persists or updates a system config key-value pair.
|
||||||
func (SystemConfigServiceImpl) SaveOrUpdate(ctx context.Context, key, value string) error {
|
func (SystemConfigServiceImpl) SaveOrUpdate(ctx context.Context, key, value string) error {
|
||||||
return repository.SaveOrUpdateSystemConfig(ctx, key, value)
|
return repository.SaveOrUpdateSystemConfig(ctx, key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InvalidateCache evicts the cache entry for the specified key.
|
||||||
func (SystemConfigServiceImpl) InvalidateCache(ctx context.Context, key string) error {
|
func (SystemConfigServiceImpl) InvalidateCache(ctx context.Context, key string) error {
|
||||||
return repository.InvalidateSystemConfigCache(ctx, key)
|
return repository.InvalidateSystemConfigCache(ctx, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InvalidateAllCaches purges all system configuration cache entries.
|
||||||
func (SystemConfigServiceImpl) InvalidateAllCaches(ctx context.Context) error {
|
func (SystemConfigServiceImpl) InvalidateAllCaches(ctx context.Context) error {
|
||||||
return repository.InvalidateAllSystemConfigCaches(ctx)
|
return repository.InvalidateAllSystemConfigCaches(ctx)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,15 +69,15 @@ func (s *uploadServiceImpl) RebuildStats(ctx context.Context) error {
|
|||||||
|
|
||||||
func toUploadDTO(u *models.Upload) contracts.UploadDTO {
|
func toUploadDTO(u *models.Upload) contracts.UploadDTO {
|
||||||
return contracts.UploadDTO{
|
return contracts.UploadDTO{
|
||||||
ID: u.ID,
|
ID: u.ID,
|
||||||
UserID: u.UserID,
|
UserID: u.UserID,
|
||||||
FileName: u.FileName,
|
FileName: u.FileName,
|
||||||
FilePath: u.FilePath,
|
FilePath: u.FilePath,
|
||||||
MimeType: u.MimeType,
|
MimeType: u.MimeType,
|
||||||
Size: u.FileSize,
|
Size: u.FileSize,
|
||||||
Hash: u.Hash,
|
Hash: u.Hash,
|
||||||
Status: string(u.Status),
|
Status: string(u.Status),
|
||||||
Type: u.Type,
|
Type: u.Type,
|
||||||
Metadata: contracts.UploadMetadataDTO{
|
Metadata: contracts.UploadMetadataDTO{
|
||||||
Width: u.Metadata.Width,
|
Width: u.Metadata.Width,
|
||||||
Height: u.Metadata.Height,
|
Height: u.Metadata.Height,
|
||||||
|
|||||||
@@ -112,30 +112,25 @@ func (s *inprocTaskService) ListExecutions(ctx context.Context, taskType, status
|
|||||||
return res, total, nil
|
return res, total, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *inprocTaskService) GetExecution(ctx context.Context, id uint64) (*contracts.TaskExecutionDTO, error) {
|
func findExecution(ctx context.Context, query any, args ...any) (*contracts.TaskExecutionDTO, error) {
|
||||||
db := getDB(ctx)
|
db := getDB(ctx)
|
||||||
if db == nil {
|
if db == nil {
|
||||||
return nil, errors.New("driver_inproc_worker: db not initialized")
|
return nil, errors.New("driver_inproc_worker: db not initialized")
|
||||||
}
|
}
|
||||||
var exec taskExecution
|
var exec taskExecution
|
||||||
if err := db.Where("id = ?", id).First(&exec).Error; err != nil {
|
if err := db.Where(query, args...).First(&exec).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
dto := toExecutionDTO(&exec)
|
dto := toExecutionDTO(&exec)
|
||||||
return &dto, nil
|
return &dto, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *inprocTaskService) GetExecution(ctx context.Context, id uint64) (*contracts.TaskExecutionDTO, error) {
|
||||||
|
return findExecution(ctx, "id = ?", id)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *inprocTaskService) GetExecutionByTaskID(ctx context.Context, taskID string) (*contracts.TaskExecutionDTO, error) {
|
func (s *inprocTaskService) GetExecutionByTaskID(ctx context.Context, taskID string) (*contracts.TaskExecutionDTO, error) {
|
||||||
db := getDB(ctx)
|
return findExecution(ctx, "task_id = ?", taskID)
|
||||||
if db == nil {
|
|
||||||
return nil, errors.New("driver_inproc_worker: db not initialized")
|
|
||||||
}
|
|
||||||
var exec taskExecution
|
|
||||||
if err := db.Where("task_id = ?", taskID).First(&exec).Error; err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
dto := toExecutionDTO(&exec)
|
|
||||||
return &dto, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func toExecutionDTO(exec *taskExecution) contracts.TaskExecutionDTO {
|
func toExecutionDTO(exec *taskExecution) contracts.TaskExecutionDTO {
|
||||||
|
|||||||
@@ -19,6 +19,13 @@ import (
|
|||||||
// DefaultFileName is the configuration file looked up when CONFIG_PATH is unset.
|
// DefaultFileName is the configuration file looked up when CONFIG_PATH is unset.
|
||||||
const DefaultFileName = "config.yaml"
|
const DefaultFileName = "config.yaml"
|
||||||
|
|
||||||
|
// DefaultCandidates defines the prioritized list of configuration file paths looked up
|
||||||
|
// when CONFIG_PATH is unset.
|
||||||
|
var DefaultCandidates = []string{
|
||||||
|
"manifest/config/config.yaml",
|
||||||
|
DefaultFileName,
|
||||||
|
}
|
||||||
|
|
||||||
// EnvOnlyOrigin is reported by Describe when no configuration file was loaded.
|
// EnvOnlyOrigin is reported by Describe when no configuration file was loaded.
|
||||||
const EnvOnlyOrigin = "<env only>"
|
const EnvOnlyOrigin = "<env only>"
|
||||||
|
|
||||||
@@ -56,7 +63,7 @@ func NewSource(opts ...Option) (*Source, error) {
|
|||||||
s.path = os.Getenv("CONFIG_PATH")
|
s.path = os.Getenv("CONFIG_PATH")
|
||||||
}
|
}
|
||||||
if s.path == "" {
|
if s.path == "" {
|
||||||
s.path = findConfigPath(DefaultFileName)
|
s.path = findConfigPath(DefaultCandidates...)
|
||||||
}
|
}
|
||||||
|
|
||||||
v := viper.New()
|
v := viper.New()
|
||||||
@@ -107,20 +114,28 @@ func (s *Source) Describe() string {
|
|||||||
return s.path
|
return s.path
|
||||||
}
|
}
|
||||||
|
|
||||||
// findConfigPath searches upward from the working directory so tests and binaries run
|
// findConfigPath searches upward from the working directory through candidate paths so
|
||||||
// from backend/ still find the repository-root configuration file.
|
// tests and binaries run from subdirectories still find configuration files in manifest/config/
|
||||||
func findConfigPath(configPath string) string {
|
// or the repository root.
|
||||||
if _, err := os.Stat(configPath); err == nil {
|
func findConfigPath(candidates ...string) string {
|
||||||
return configPath
|
if len(candidates) == 0 {
|
||||||
|
return DefaultFileName
|
||||||
|
}
|
||||||
|
for _, candidate := range candidates {
|
||||||
|
if _, err := os.Stat(candidate); err == nil {
|
||||||
|
return candidate
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dir := "."
|
dir := "."
|
||||||
for range maxSearchDepth {
|
for range maxSearchDepth {
|
||||||
dir += "/.."
|
dir += "/.."
|
||||||
path := dir + "/" + configPath
|
for _, candidate := range candidates {
|
||||||
if _, err := os.Stat(path); err == nil {
|
path := dir + "/" + candidate
|
||||||
return path
|
if _, err := os.Stat(path); err == nil {
|
||||||
|
return path
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return configPath
|
return candidates[0]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,3 +91,32 @@ func TestSourcePrefersConfigPathEnvironmentVariable(t *testing.T) {
|
|||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
assert.Equal(t, ":9100", value)
|
assert.Equal(t, ":9100", value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSourceFindsManifestConfigInParentDirectory(t *testing.T) {
|
||||||
|
tempRoot := t.TempDir()
|
||||||
|
manifestConfigDir := filepath.Join(tempRoot, "manifest", "config")
|
||||||
|
require.NoError(t, os.MkdirAll(manifestConfigDir, 0o755))
|
||||||
|
require.NoError(t, os.WriteFile(
|
||||||
|
filepath.Join(manifestConfigDir, "config.yaml"),
|
||||||
|
[]byte("app:\n addr: \":9200\"\n"),
|
||||||
|
0o600,
|
||||||
|
))
|
||||||
|
|
||||||
|
subDir := filepath.Join(tempRoot, "backend", "cmd")
|
||||||
|
require.NoError(t, os.MkdirAll(subDir, 0o755))
|
||||||
|
|
||||||
|
origWd, err := os.Getwd()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, os.Chdir(subDir))
|
||||||
|
t.Cleanup(func() {
|
||||||
|
_ = os.Chdir(origWd)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Setenv("CONFIG_PATH", "")
|
||||||
|
|
||||||
|
src, err := config.NewSource()
|
||||||
|
require.NoError(t, err)
|
||||||
|
value, ok := src.Lookup("app.addr")
|
||||||
|
require.True(t, ok)
|
||||||
|
assert.Equal(t, ":9200", value)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,21 +1,24 @@
|
|||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: template-single
|
name: wavelet
|
||||||
labels:
|
labels:
|
||||||
app: template-single
|
app: wavelet
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app: template-single
|
app: wavelet
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: template-single
|
app: wavelet
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name : main
|
- name: wavelet
|
||||||
image: template-single
|
image: wavelet
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
|
ports:
|
||||||
|
- containerPort: 8000
|
||||||
|
name: http
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: template-single
|
name: wavelet
|
||||||
spec:
|
spec:
|
||||||
ports:
|
ports:
|
||||||
- port: 80
|
- port: 80
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
targetPort: 8000
|
targetPort: 8000
|
||||||
selector:
|
selector:
|
||||||
app: template-single
|
app: wavelet
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
name: template-single-configmap
|
name: wavelet-configmap
|
||||||
data:
|
data:
|
||||||
config.yaml: |
|
config.yaml: |
|
||||||
server:
|
app:
|
||||||
address: ":8000"
|
env: "development"
|
||||||
openapiPath: "/api.json"
|
addr: ":8000"
|
||||||
swaggerPath: "/swagger"
|
node_id: 1
|
||||||
|
database:
|
||||||
logger:
|
enabled: false
|
||||||
level : "all"
|
sqlite_path: "data/wavelet.db"
|
||||||
stdout: true
|
redis:
|
||||||
|
enabled: false
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: template-single
|
name: wavelet
|
||||||
spec:
|
spec:
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name : main
|
- name: wavelet
|
||||||
image: template-single:develop
|
image: ghcr.io/rain-kl/wavelet:canary
|
||||||
@@ -5,8 +5,8 @@ resources:
|
|||||||
- ../../base
|
- ../../base
|
||||||
- configmap.yaml
|
- configmap.yaml
|
||||||
|
|
||||||
patchesStrategicMerge:
|
patches:
|
||||||
- deployment.yaml
|
- path: deployment.yaml
|
||||||
|
|
||||||
namespace: default
|
namespace: default
|
||||||
|
|
||||||
|
|||||||
+74
-13
@@ -1,16 +1,77 @@
|
|||||||
FROM loads/alpine:3.8
|
# syntax=docker/dockerfile:1.7
|
||||||
|
|
||||||
###############################################################################
|
ARG GO_VERSION=1.25
|
||||||
# INSTALLATION
|
ARG NODE_VERSION=22
|
||||||
###############################################################################
|
ARG ALPINE_VERSION=3.23
|
||||||
|
|
||||||
ENV WORKDIR /app
|
FROM node:${NODE_VERSION}-alpine AS frontend-builder
|
||||||
ADD resource $WORKDIR/
|
|
||||||
ADD ./temp/linux_amd64/main $WORKDIR/main
|
|
||||||
RUN chmod +x $WORKDIR/main
|
|
||||||
|
|
||||||
###############################################################################
|
ARG VERSION=dev
|
||||||
# START
|
ARG BUILD_DATE=""
|
||||||
###############################################################################
|
|
||||||
WORKDIR $WORKDIR
|
ENV PNPM_HOME="/pnpm"
|
||||||
CMD ./main
|
ENV PATH="$PNPM_HOME:$PATH"
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ENV NEXT_STANDALONE_EXPORT=true
|
||||||
|
ENV NEXT_PUBLIC_APP_VERSION=${VERSION}
|
||||||
|
ENV NEXT_PUBLIC_APP_BUILD_DATE=${BUILD_DATE}
|
||||||
|
|
||||||
|
RUN corepack enable && corepack prepare pnpm@10.10.0 --activate
|
||||||
|
|
||||||
|
WORKDIR /workspace/frontend
|
||||||
|
|
||||||
|
COPY frontend/package.json frontend/pnpm-lock.yaml frontend/pnpm-workspace.yaml ./
|
||||||
|
RUN pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
COPY frontend/ ./
|
||||||
|
|
||||||
|
# Assert index.html so a silent export failure cannot ship an API-only binary.
|
||||||
|
RUN pnpm build:embed && test -f out/index.html
|
||||||
|
|
||||||
|
FROM golang:${GO_VERSION}-alpine AS backend-builder
|
||||||
|
|
||||||
|
ARG VERSION=dev
|
||||||
|
ARG BUILD_DATE=""
|
||||||
|
|
||||||
|
RUN apk add --no-cache ca-certificates git
|
||||||
|
|
||||||
|
WORKDIR /workspace
|
||||||
|
|
||||||
|
# Cache dependency layer
|
||||||
|
COPY backend/go.mod backend/go.sum ./backend/
|
||||||
|
RUN cd backend && go mod download
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Drop any stale bundle carried in the context, then overlay the fresh export so
|
||||||
|
# `//go:embed all:dist` in driver_http picks up exactly this frontend build.
|
||||||
|
RUN rm -rf backend/plugins/drivers/driver_http/dist
|
||||||
|
COPY --from=frontend-builder /workspace/frontend/out backend/plugins/drivers/driver_http/dist
|
||||||
|
|
||||||
|
WORKDIR /workspace/backend
|
||||||
|
|
||||||
|
RUN test -f plugins/drivers/driver_http/dist/index.html && \
|
||||||
|
CGO_ENABLED=0 GOOS=linux go build \
|
||||||
|
-tags embed_frontend \
|
||||||
|
-trimpath \
|
||||||
|
-ldflags="-s -w -X Wavelet/pkg/buildinfo.Version=${VERSION} -X Wavelet/pkg/buildinfo.BuildTime=${BUILD_DATE}" \
|
||||||
|
-o /out/wavelet \
|
||||||
|
./main.go
|
||||||
|
|
||||||
|
FROM alpine:${ALPINE_VERSION}
|
||||||
|
|
||||||
|
ARG TZ=Asia/Shanghai
|
||||||
|
|
||||||
|
RUN apk add --no-cache ca-certificates tzdata postgresql-client && \
|
||||||
|
cp /usr/share/zoneinfo/${TZ} /etc/localtime && \
|
||||||
|
echo "${TZ}" > /etc/timezone
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=backend-builder /out/wavelet ./wavelet
|
||||||
|
COPY docs ./docs
|
||||||
|
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
ENTRYPOINT ["./wavelet"]
|
||||||
|
CMD ["all"]
|
||||||
|
|||||||
@@ -8,18 +8,16 @@
|
|||||||
# TARGET_ARCH — restrict to one arch: amd64 | arm64 (default: all)
|
# TARGET_ARCH — restrict to one arch: amd64 | arm64 (default: all)
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# docker build -f docker/Dockerfile.cross --output ./bin .
|
# docker build -f manifest/docker/Dockerfile.cross --output ./bin .
|
||||||
# docker build -f docker/Dockerfile.cross \
|
# docker build -f manifest/docker/Dockerfile.cross \
|
||||||
# --build-arg VERSION=v1.2.3 --output ./bin .
|
# --build-arg TARGET_OS=linux --build-arg TARGET_ARCH=amd64 --output ./bin .
|
||||||
# docker build -f docker/Dockerfile.cross \
|
# docker build -f manifest/docker/Dockerfile.cross \
|
||||||
# --build-arg TARGET_OS=linux --build-arg TARGET_ARCH=arm64 \
|
# --build-arg TARGET_OS=windows --build-arg TARGET_ARCH=amd64 --output ./bin .
|
||||||
# --output ./bin .
|
|
||||||
#
|
#
|
||||||
# Or via Makefile:
|
# Or via Makefile:
|
||||||
# make cross-build
|
# make cross-build
|
||||||
# make cross-build VERSION=v1.2.3
|
# make cross-build VERSION=v1.2.3
|
||||||
# make cross-build GOOS=linux GOARCH=arm64
|
# make cross-build GOOS=linux GOARCH=arm64
|
||||||
# make cross-build GOOS=darwin
|
|
||||||
|
|
||||||
ARG GO_VERSION=1.25
|
ARG GO_VERSION=1.25
|
||||||
ARG NODE_VERSION=22
|
ARG NODE_VERSION=22
|
||||||
Reference in New Issue
Block a user