fix: mount config directory instead of file for Docker Compose
Build and Push to GHCR / build-and-push (push) Has been cancelled
Build and Push to GHCR / build-and-push (push) Has been cancelled
- Add CONFIG_PATH env support to avoid Docker creating config.json as a directory - Config Save() now auto-creates parent directories - Update docker-compose.yml to mount ./config:/app/config - Update README deployment docs accordingly
This commit is contained in:
+1
-1
@@ -16,7 +16,7 @@ RUN apk --no-cache add ca-certificates
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# 创建必要目录
|
# 创建必要目录
|
||||||
RUN mkdir -p web/static cache
|
RUN mkdir -p web/static cache config
|
||||||
|
|
||||||
# 复制编译后的二进制文件和静态资源
|
# 复制编译后的二进制文件和静态资源
|
||||||
COPY --from=builder /app/mirror-proxy .
|
COPY --from=builder /app/mirror-proxy .
|
||||||
|
|||||||
@@ -49,10 +49,11 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
volumes:
|
volumes:
|
||||||
- ./config.json:/app/config.json
|
- ./config:/app/config
|
||||||
- ./cache:/app/cache
|
- ./cache:/app/cache
|
||||||
environment:
|
environment:
|
||||||
- TZ=Asia/Shanghai
|
- TZ=Asia/Shanghai
|
||||||
|
- CONFIG_PATH=/app/config/config.json
|
||||||
EOF
|
EOF
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -62,7 +63,7 @@ EOF
|
|||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
首次启动会自动创建 `config.json`(默认账号 `admin` / `admin123`)。
|
首次启动会自动在 `./config/` 目录下创建 `config.json`(默认账号 `admin` / `admin123`),无需手动提前创建配置文件。
|
||||||
|
|
||||||
**3. 常用命令**
|
**3. 常用命令**
|
||||||
|
|
||||||
@@ -96,12 +97,16 @@ docker-compose pull && docker-compose up -d
|
|||||||
### 使用 Docker 直接运行
|
### 使用 Docker 直接运行
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# 创建配置目录
|
||||||
|
mkdir -p config cache
|
||||||
|
|
||||||
# 拉取并运行(首次运行自动生成 config.json)
|
# 拉取并运行(首次运行自动生成 config.json)
|
||||||
docker run -d \
|
docker run -d \
|
||||||
--name mirror-proxy \
|
--name mirror-proxy \
|
||||||
-p 8080:8080 \
|
-p 8080:8080 \
|
||||||
-v $(pwd)/config.json:/app/config.json \
|
-v $(pwd)/config:/app/config \
|
||||||
-v $(pwd)/cache:/app/cache \
|
-v $(pwd)/cache:/app/cache \
|
||||||
|
-e CONFIG_PATH=/app/config/config.json \
|
||||||
--restart unless-stopped \
|
--restart unless-stopped \
|
||||||
ghcr.io/arkylin/mirror-proxy:latest
|
ghcr.io/arkylin/mirror-proxy:latest
|
||||||
```
|
```
|
||||||
|
|||||||
+5
-1
@@ -15,7 +15,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cfg, err := config.Load("config.json")
|
configPath := os.Getenv("CONFIG_PATH")
|
||||||
|
if configPath == "" {
|
||||||
|
configPath = "config.json"
|
||||||
|
}
|
||||||
|
cfg, err := config.Load(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to load config: %v", err)
|
log.Fatalf("Failed to load config: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-4
@@ -1,14 +1,13 @@
|
|||||||
services:
|
services:
|
||||||
mirror-proxy:
|
mirror-proxy:
|
||||||
# 使用 GHCR 预构建镜像(推荐)
|
image: ghcr.io/arkylin/mirror-proxy:latest
|
||||||
# image: ghcr.io/arkylin/mirror-proxy:latest
|
|
||||||
build: .
|
|
||||||
container_name: mirror-proxy
|
container_name: mirror-proxy
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
volumes:
|
volumes:
|
||||||
- ./config.json:/app/config.json
|
- ./config:/app/config
|
||||||
- ./cache:/app/cache
|
- ./cache:/app/cache
|
||||||
environment:
|
environment:
|
||||||
- TZ=Asia/Shanghai
|
- TZ=Asia/Shanghai
|
||||||
|
- CONFIG_PATH=/app/config/config.json
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ func (h *Handler) CreateLink(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
h.cfg.SetLink(link)
|
h.cfg.SetLink(link)
|
||||||
if err := h.cfg.Save("config.json"); err != nil {
|
if err := h.cfg.Save(config.GetConfigFilePath()); err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -112,7 +112,7 @@ func (h *Handler) UpdateLink(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
h.cfg.SetLink(link)
|
h.cfg.SetLink(link)
|
||||||
if err := h.cfg.Save("config.json"); err != nil {
|
if err := h.cfg.Save(config.GetConfigFilePath()); err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -132,7 +132,7 @@ func (h *Handler) DeleteLink(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
h.cfg.DeleteLink(id)
|
h.cfg.DeleteLink(id)
|
||||||
if err := h.cfg.Save("config.json"); err != nil {
|
if err := h.cfg.Save(config.GetConfigFilePath()); err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -152,7 +152,7 @@ func (h *Handler) RegenerateToken(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
link.Token = auth.GenerateToken()
|
link.Token = auth.GenerateToken()
|
||||||
h.cfg.SetLink(link)
|
h.cfg.SetLink(link)
|
||||||
if err := h.cfg.Save("config.json"); err != nil {
|
if err := h.cfg.Save(config.GetConfigFilePath()); err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -221,7 +221,7 @@ func (h *Handler) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
|||||||
h.cfg.AdminPass = req.AdminPass
|
h.cfg.AdminPass = req.AdminPass
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := h.cfg.Save("config.json"); err != nil {
|
if err := h.cfg.Save(config.GetConfigFilePath()); err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,20 @@ package config
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var configFilePath string = "config.json"
|
||||||
|
|
||||||
|
func SetConfigFilePath(path string) {
|
||||||
|
configFilePath = path
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetConfigFilePath() string {
|
||||||
|
return configFilePath
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
ListenAddr string `json:"listen_addr"`
|
ListenAddr string `json:"listen_addr"`
|
||||||
AdminPath string `json:"admin_path"`
|
AdminPath string `json:"admin_path"`
|
||||||
@@ -38,6 +49,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Load(path string) (*Config, error) {
|
func Load(path string) (*Config, error) {
|
||||||
|
configFilePath = path
|
||||||
data, err := os.ReadFile(path)
|
data, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
@@ -73,6 +85,14 @@ func defaultConfig() *Config {
|
|||||||
func (c *Config) Save(path string) error {
|
func (c *Config) Save(path string) error {
|
||||||
c.mu.RLock()
|
c.mu.RLock()
|
||||||
defer c.mu.RUnlock()
|
defer c.mu.RUnlock()
|
||||||
|
|
||||||
|
dir := filepath.Dir(path)
|
||||||
|
if dir != "" && dir != "." {
|
||||||
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data, err := json.MarshalIndent(c, "", " ")
|
data, err := json.MarshalIndent(c, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
Reference in New Issue
Block a user