mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-01 23:15:27 +08:00
feat: add support for stripping inline comments in .env files and make Docker host port configurable via DS2API_HOST_PORT
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
# DS2API runtime
|
||||
# Runtime listen port inside the app/container
|
||||
PORT=5001
|
||||
# Docker Compose host port (compose only; container still listens on PORT)
|
||||
DS2API_HOST_PORT=6011
|
||||
LOG_LEVEL=INFO
|
||||
|
||||
# Admin authentication
|
||||
|
||||
@@ -191,7 +191,7 @@ go run ./cmd/ds2api
|
||||
cp .env.example .env
|
||||
cp config.example.json config.json
|
||||
|
||||
# 2. 编辑 .env(至少设置 DS2API_ADMIN_KEY)
|
||||
# 2. 编辑 .env(至少设置 DS2API_ADMIN_KEY;如需修改宿主机端口,可额外设置 DS2API_HOST_PORT)
|
||||
# DS2API_ADMIN_KEY=请替换为强密码
|
||||
|
||||
# 3. 启动
|
||||
@@ -201,7 +201,7 @@ docker-compose up -d
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
默认 `docker-compose.yml` 会把宿主机 `6011` 映射到容器内的 `5001`。如果你希望直接对外暴露 `5001`,请调整 `ports` 配置。
|
||||
默认 `docker-compose.yml` 会把宿主机 `6011` 映射到容器内的 `5001`。如果你希望直接对外暴露 `5001`,请设置 `DS2API_HOST_PORT=5001`(或者手动调整 `ports` 配置)。
|
||||
|
||||
更新镜像:`docker-compose up -d --build`
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ Default URL: `http://localhost:5001`
|
||||
cp .env.example .env
|
||||
cp config.example.json config.json
|
||||
|
||||
# 2. Edit .env (at least set DS2API_ADMIN_KEY)
|
||||
# 2. Edit .env (at least set DS2API_ADMIN_KEY; optionally set DS2API_HOST_PORT to change the host port)
|
||||
# DS2API_ADMIN_KEY=replace-with-a-strong-secret
|
||||
|
||||
# 3. Start
|
||||
@@ -201,7 +201,7 @@ docker-compose up -d
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
The default `docker-compose.yml` maps host port `6011` to container port `5001`. If you want `5001` exposed directly, adjust the `ports` mapping.
|
||||
The default `docker-compose.yml` maps host port `6011` to container port `5001`. If you want `5001` exposed directly, set `DS2API_HOST_PORT=5001` (or adjust the `ports` mapping).
|
||||
|
||||
Rebuild after updates: `docker-compose up -d --build`
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@ services:
|
||||
container_name: ds2api-dev
|
||||
command: ["go", "run", "./cmd/ds2api"]
|
||||
ports:
|
||||
- "${PORT:-5001}:${PORT:-5001}"
|
||||
# Host port is configurable via DS2API_HOST_PORT; container port stays fixed at 5001.
|
||||
- "${DS2API_HOST_PORT:-6011}:5001"
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
|
||||
@@ -6,10 +6,10 @@ services:
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
- "${DS2API_HOST_PORT:-6011}:${PORT:-5001}"
|
||||
# Host port is configurable via DS2API_HOST_PORT; container port stays fixed at 5001.
|
||||
- "${DS2API_HOST_PORT:-6011}:5001"
|
||||
volumes:
|
||||
- ./config.json:/app/config.json # 配置文件
|
||||
- ./.env:/app/.env # 环境变量
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
- LOG_LEVEL=INFO
|
||||
|
||||
@@ -117,6 +117,8 @@ cp config.example.json config.json
|
||||
|
||||
# Edit .env and set at least:
|
||||
# DS2API_ADMIN_KEY=your-admin-key
|
||||
# Optionally set the host port:
|
||||
# DS2API_HOST_PORT=6011
|
||||
|
||||
# Start
|
||||
docker-compose up -d
|
||||
@@ -125,7 +127,7 @@ docker-compose up -d
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
The default `docker-compose.yml` maps host port `6011` to container port `5001`. If you want `5001` exposed directly, adjust the `ports` mapping.
|
||||
The default `docker-compose.yml` maps host port `6011` to container port `5001`. If you want `5001` exposed directly, set `DS2API_HOST_PORT=5001` (or adjust the `ports` mapping).
|
||||
|
||||
### 2.2 Update
|
||||
|
||||
|
||||
@@ -117,6 +117,8 @@ cp config.example.json config.json
|
||||
|
||||
# 编辑 .env(请改成你的强密码),至少设置:
|
||||
# DS2API_ADMIN_KEY=your-admin-key
|
||||
# 如需修改宿主机端口,可额外设置:
|
||||
# DS2API_HOST_PORT=6011
|
||||
|
||||
# 启动
|
||||
docker-compose up -d
|
||||
@@ -125,7 +127,7 @@ docker-compose up -d
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
默认 `docker-compose.yml` 会把宿主机 `6011` 映射到容器内的 `5001`。如果你希望直接对外暴露 `5001`,请调整 `ports` 配置。
|
||||
默认 `docker-compose.yml` 会把宿主机 `6011` 映射到容器内的 `5001`。如果你希望直接对外暴露 `5001`,请设置 `DS2API_HOST_PORT=5001`(或者手动调整 `ports` 配置)。
|
||||
|
||||
### 2.2 更新
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ func loadDotEnvFromPath(path string) error {
|
||||
if _, exists := os.LookupEnv(key); exists {
|
||||
continue
|
||||
}
|
||||
if err := os.Setenv(key, normalizeDotEnvValue(strings.TrimSpace(value))); err != nil {
|
||||
if err := os.Setenv(key, normalizeDotEnvValue(trimDotEnvValue(strings.TrimSpace(value)))); err != nil {
|
||||
return fmt.Errorf("%s:%d set env %q: %w", path, i+1, key, err)
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,62 @@ func loadDotEnvFromPath(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Preserve quoted values, but drop Compose-style inline comments from unquoted values.
|
||||
func trimDotEnvValue(raw string) string {
|
||||
if raw == "" {
|
||||
return raw
|
||||
}
|
||||
|
||||
switch raw[0] {
|
||||
case '"':
|
||||
if trimmed, ok := trimQuotedDotEnvValue(raw, '"'); ok {
|
||||
return trimmed
|
||||
}
|
||||
case '\'':
|
||||
if trimmed, ok := trimQuotedDotEnvValue(raw, '\''); ok {
|
||||
return trimmed
|
||||
}
|
||||
default:
|
||||
if idx := inlineDotEnvCommentStart(raw); idx >= 0 {
|
||||
return strings.TrimSpace(raw[:idx])
|
||||
}
|
||||
}
|
||||
|
||||
return raw
|
||||
}
|
||||
|
||||
func trimQuotedDotEnvValue(raw string, quote byte) (string, bool) {
|
||||
escaped := false
|
||||
for i := 1; i < len(raw); i++ {
|
||||
ch := raw[i]
|
||||
if quote == '"' && escaped {
|
||||
escaped = false
|
||||
continue
|
||||
}
|
||||
if quote == '"' && ch == '\\' {
|
||||
escaped = true
|
||||
continue
|
||||
}
|
||||
if ch == quote {
|
||||
return strings.TrimSpace(raw[:i+1]), true
|
||||
}
|
||||
}
|
||||
return raw, false
|
||||
}
|
||||
|
||||
func inlineDotEnvCommentStart(raw string) int {
|
||||
for i := 1; i < len(raw); i++ {
|
||||
if raw[i] == '#' && isDotEnvCommentSpacer(raw[i-1]) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func isDotEnvCommentSpacer(b byte) bool {
|
||||
return b == ' ' || b == '\t'
|
||||
}
|
||||
|
||||
func normalizeDotEnvValue(raw string) string {
|
||||
if len(raw) < 2 {
|
||||
return raw
|
||||
|
||||
@@ -3,6 +3,7 @@ package config
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -67,6 +68,57 @@ func TestLoadDotEnvIgnoresMissingFile(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadDotEnvStripsInlineCommentsFromUnquotedValues(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
oldWD, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("getwd: %v", err)
|
||||
}
|
||||
if err := os.Chdir(dir); err != nil {
|
||||
t.Fatalf("chdir temp dir: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
_ = os.Chdir(oldWD)
|
||||
})
|
||||
|
||||
const plainKey = "DS2API_TEST_DOTENV_PLAIN"
|
||||
const hashKey = "DS2API_TEST_DOTENV_HASH"
|
||||
const quotedKey = "DS2API_TEST_DOTENV_QUOTED_COMMENT"
|
||||
const exportKey = "DS2API_TEST_DOTENV_EXPORT"
|
||||
|
||||
unsetEnv(t, plainKey)
|
||||
unsetEnv(t, hashKey)
|
||||
unsetEnv(t, quotedKey)
|
||||
unsetEnv(t, exportKey)
|
||||
|
||||
content := strings.Join([]string{
|
||||
plainKey + "=5001 # local",
|
||||
hashKey + "=5001#local",
|
||||
quotedKey + `="5001 # local" # keep the inner hash`,
|
||||
"export " + exportKey + "=enabled # exported",
|
||||
}, "\n") + "\n"
|
||||
if err := os.WriteFile(filepath.Join(dir, ".env"), []byte(content), 0o644); err != nil {
|
||||
t.Fatalf("write .env: %v", err)
|
||||
}
|
||||
|
||||
if err := LoadDotEnv(); err != nil {
|
||||
t.Fatalf("LoadDotEnv() error: %v", err)
|
||||
}
|
||||
|
||||
if got := os.Getenv(plainKey); got != "5001" {
|
||||
t.Fatalf("expected inline comment to be stripped, got %q", got)
|
||||
}
|
||||
if got := os.Getenv(hashKey); got != "5001#local" {
|
||||
t.Fatalf("expected hash without preceding whitespace to remain, got %q", got)
|
||||
}
|
||||
if got := os.Getenv(quotedKey); got != "5001 # local" {
|
||||
t.Fatalf("expected quoted value to preserve hash text, got %q", got)
|
||||
}
|
||||
if got := os.Getenv(exportKey); got != "enabled" {
|
||||
t.Fatalf("expected export syntax to load, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func unsetEnv(t *testing.T, key string) {
|
||||
t.Helper()
|
||||
old, had := os.LookupEnv(key)
|
||||
|
||||
Reference in New Issue
Block a user