mirror of
https://git.fightbot.fun/hxuanyu/BingPaper.git
synced 2026-02-15 05:59:32 +08:00
项目名调整,功能优化
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -50,6 +50,6 @@ desktop.ini
|
||||
/data/
|
||||
/picture/
|
||||
/config.yaml
|
||||
/bing_daily_image.db
|
||||
/bing_paper.db
|
||||
/req.txt
|
||||
/BingDailyImage
|
||||
/BingPaper
|
||||
|
||||
@@ -5,15 +5,15 @@ COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
RUN go build -o BingDailyImage .
|
||||
RUN go build -o BingPaper .
|
||||
|
||||
FROM alpine:latest
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=builder /app/BingDailyImage .
|
||||
COPY --from=builder /app/BingPaper .
|
||||
RUN mkdir -p data
|
||||
COPY --from=builder /app/config.example.yaml ./data/config.yaml
|
||||
COPY --from=builder /app/web ./web
|
||||
|
||||
EXPOSE 8080
|
||||
ENTRYPOINT ["./BingDailyImage"]
|
||||
ENTRYPOINT ["./BingPaper"]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# BingDailyImage
|
||||
# BingPaper
|
||||
|
||||
必应每日一图抓取、存储、多分辨率管理与公共 API 服务。
|
||||
|
||||
@@ -71,10 +71,10 @@ go run .
|
||||
|
||||
```bash
|
||||
# 构建二进制
|
||||
go build -o BingDailyImage .
|
||||
go build -o BingPaper .
|
||||
|
||||
# 构建 Docker 镜像
|
||||
docker build -t bing-daily-image .
|
||||
docker build -t bing-paper .
|
||||
```
|
||||
|
||||
## 许可证
|
||||
|
||||
47
config.example.yaml
Normal file
47
config.example.yaml
Normal file
@@ -0,0 +1,47 @@
|
||||
server:
|
||||
port: 8080
|
||||
base_url: ""
|
||||
|
||||
log:
|
||||
level: info
|
||||
|
||||
api:
|
||||
mode: local # local | redirect
|
||||
|
||||
cron:
|
||||
enabled: true
|
||||
daily_spec: "0 10 * * *"
|
||||
|
||||
retention:
|
||||
days: 30
|
||||
|
||||
db:
|
||||
type: sqlite # sqlite | mysql | postgres
|
||||
dsn: data/bing_paper.db
|
||||
|
||||
storage:
|
||||
type: local # local | s3 | webdav
|
||||
local:
|
||||
root: data/picture
|
||||
s3:
|
||||
endpoint: ""
|
||||
region: ""
|
||||
bucket: ""
|
||||
access_key: ""
|
||||
secret_key: ""
|
||||
public_url_prefix: ""
|
||||
force_path_style: false
|
||||
webdav:
|
||||
url: ""
|
||||
username: ""
|
||||
password: ""
|
||||
public_url_prefix: ""
|
||||
|
||||
admin:
|
||||
password_bcrypt: "$2a$10$fYHPeWHmwObephJvtlyH1O8DIgaLk5TINbi9BOezo2M8cSjmJchka" # 默认密码: admin123
|
||||
|
||||
token:
|
||||
default_ttl: 168h
|
||||
|
||||
feature:
|
||||
write_daily_files: true
|
||||
77
docs/docs.go
77
docs/docs.go
@@ -189,6 +189,66 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/admin/password": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "验证旧密码并设置新密码,自动更新配置文件",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"admin"
|
||||
],
|
||||
"summary": "修改管理员密码",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "修改密码请求",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ChangePasswordRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/admin/tokens": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -747,6 +807,21 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.ChangePasswordRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"new_password",
|
||||
"old_password"
|
||||
],
|
||||
"properties": {
|
||||
"new_password": {
|
||||
"type": "string"
|
||||
},
|
||||
"old_password": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.CreateTokenRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -835,7 +910,7 @@ var SwaggerInfo = &swag.Spec{
|
||||
Host: "localhost:8080",
|
||||
BasePath: "/api/v1",
|
||||
Schemes: []string{},
|
||||
Title: "BingDailyImage API",
|
||||
Title: "BingPaper API",
|
||||
Description: "必应每日一图抓取、存储、管理与公共 API 服务。",
|
||||
InfoInstanceName: "swagger",
|
||||
SwaggerTemplate: docTemplate,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "必应每日一图抓取、存储、管理与公共 API 服务。",
|
||||
"title": "BingDailyImage API",
|
||||
"title": "BingPaper API",
|
||||
"contact": {},
|
||||
"version": "1.0"
|
||||
},
|
||||
@@ -183,6 +183,66 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/admin/password": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "验证旧密码并设置新密码,自动更新配置文件",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"admin"
|
||||
],
|
||||
"summary": "修改管理员密码",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "修改密码请求",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ChangePasswordRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/admin/tokens": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -741,6 +801,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.ChangePasswordRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"new_password",
|
||||
"old_password"
|
||||
],
|
||||
"properties": {
|
||||
"new_password": {
|
||||
"type": "string"
|
||||
},
|
||||
"old_password": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.CreateTokenRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
||||
@@ -121,6 +121,16 @@ definitions:
|
||||
username:
|
||||
type: string
|
||||
type: object
|
||||
handlers.ChangePasswordRequest:
|
||||
properties:
|
||||
new_password:
|
||||
type: string
|
||||
old_password:
|
||||
type: string
|
||||
required:
|
||||
- new_password
|
||||
- old_password
|
||||
type: object
|
||||
handlers.CreateTokenRequest:
|
||||
properties:
|
||||
expires_at:
|
||||
@@ -172,7 +182,7 @@ host: localhost:8080
|
||||
info:
|
||||
contact: {}
|
||||
description: 必应每日一图抓取、存储、管理与公共 API 服务。
|
||||
title: BingDailyImage API
|
||||
title: BingPaper API
|
||||
version: "1.0"
|
||||
paths:
|
||||
/admin/cleanup:
|
||||
@@ -283,6 +293,44 @@ paths:
|
||||
summary: 管理员登录
|
||||
tags:
|
||||
- admin
|
||||
/admin/password:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 验证旧密码并设置新密码,自动更新配置文件
|
||||
parameters:
|
||||
- description: 修改密码请求
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ChangePasswordRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: 修改管理员密码
|
||||
tags:
|
||||
- admin
|
||||
/admin/tokens:
|
||||
get:
|
||||
description: 获取所有已创建的 API Token 列表
|
||||
|
||||
6
go.mod
6
go.mod
@@ -1,4 +1,4 @@
|
||||
module BingDailyImage
|
||||
module BingPaper
|
||||
|
||||
go 1.25.5
|
||||
|
||||
@@ -13,6 +13,7 @@ require (
|
||||
github.com/chai2010/webp v1.4.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/disintegration/imaging v1.6.2 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
@@ -46,6 +47,7 @@ require (
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/quic-go v0.54.0 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
@@ -57,6 +59,7 @@ require (
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/spf13/viper v1.21.0 // indirect
|
||||
github.com/stretchr/testify v1.11.1 // indirect
|
||||
github.com/studio-b12/gowebdav v0.12.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/swaggo/files v1.0.1 // indirect
|
||||
@@ -80,6 +83,7 @@ require (
|
||||
golang.org/x/tools v0.40.0 // indirect
|
||||
google.golang.org/protobuf v1.36.9 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gorm.io/driver/mysql v1.6.0 // indirect
|
||||
gorm.io/driver/postgres v1.6.0 // indirect
|
||||
gorm.io/driver/sqlite v1.6.0 // indirect
|
||||
|
||||
5
go.sum
5
go.sum
@@ -21,6 +21,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSY
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||
@@ -101,6 +102,7 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||
@@ -133,6 +135,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/studio-b12/gowebdav v0.12.0 h1:kFRtQECt8jmVAvA6RHBz3geXUGJHUZA6/IKpOVUs5kM=
|
||||
github.com/studio-b12/gowebdav v0.12.0/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
@@ -220,6 +224,7 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
|
||||
gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
|
||||
|
||||
@@ -6,16 +6,16 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"BingDailyImage/internal/config"
|
||||
"BingDailyImage/internal/cron"
|
||||
"BingDailyImage/internal/http"
|
||||
"BingDailyImage/internal/repo"
|
||||
"BingDailyImage/internal/service/fetcher"
|
||||
"BingDailyImage/internal/storage"
|
||||
"BingDailyImage/internal/storage/local"
|
||||
"BingDailyImage/internal/storage/s3"
|
||||
"BingDailyImage/internal/storage/webdav"
|
||||
"BingDailyImage/internal/util"
|
||||
"BingPaper/internal/config"
|
||||
"BingPaper/internal/cron"
|
||||
"BingPaper/internal/http"
|
||||
"BingPaper/internal/repo"
|
||||
"BingPaper/internal/service/fetcher"
|
||||
"BingPaper/internal/storage"
|
||||
"BingPaper/internal/storage/local"
|
||||
"BingPaper/internal/storage/s3"
|
||||
"BingPaper/internal/storage/webdav"
|
||||
"BingPaper/internal/util"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
@@ -93,7 +93,7 @@ func LogWelcomeInfo() {
|
||||
}
|
||||
|
||||
fmt.Println("\n---------------------------------------------------------")
|
||||
fmt.Println(" BingDailyImage 服务已启动!")
|
||||
fmt.Println(" BingPaper 服务已启动!")
|
||||
fmt.Printf(" - 首页地址: %s/\n", baseURL)
|
||||
fmt.Printf(" - 管理后台: %s/admin\n", baseURL)
|
||||
fmt.Printf(" - API 文档: %s/swagger/index.html\n", baseURL)
|
||||
|
||||
@@ -120,7 +120,7 @@ func Init(configPath string) error {
|
||||
v.SetDefault("cron.daily_spec", "0 10 * * *")
|
||||
v.SetDefault("retention.days", 30)
|
||||
v.SetDefault("db.type", "sqlite")
|
||||
v.SetDefault("db.dsn", "data/bing_daily_image.db")
|
||||
v.SetDefault("db.dsn", "data/bing_paper.db")
|
||||
v.SetDefault("storage.type", "local")
|
||||
v.SetDefault("storage.local.root", "data/picture")
|
||||
v.SetDefault("token.default_ttl", "168h")
|
||||
|
||||
@@ -3,10 +3,10 @@ package cron
|
||||
import (
|
||||
"context"
|
||||
|
||||
"BingDailyImage/internal/config"
|
||||
"BingDailyImage/internal/service/fetcher"
|
||||
"BingDailyImage/internal/service/image"
|
||||
"BingDailyImage/internal/util"
|
||||
"BingPaper/internal/config"
|
||||
"BingPaper/internal/service/fetcher"
|
||||
"BingPaper/internal/service/image"
|
||||
"BingPaper/internal/util"
|
||||
|
||||
"github.com/robfig/cron/v3"
|
||||
"go.uber.org/zap"
|
||||
|
||||
@@ -6,12 +6,13 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"BingDailyImage/internal/config"
|
||||
"BingDailyImage/internal/service/fetcher"
|
||||
"BingDailyImage/internal/service/image"
|
||||
"BingDailyImage/internal/service/token"
|
||||
"BingPaper/internal/config"
|
||||
"BingPaper/internal/service/fetcher"
|
||||
"BingPaper/internal/service/image"
|
||||
"BingPaper/internal/service/token"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type LoginRequest struct {
|
||||
@@ -109,6 +110,55 @@ type UpdateTokenRequest struct {
|
||||
Disabled bool `json:"disabled"`
|
||||
}
|
||||
|
||||
type ChangePasswordRequest struct {
|
||||
OldPassword string `json:"old_password" binding:"required"`
|
||||
NewPassword string `json:"new_password" binding:"required"`
|
||||
}
|
||||
|
||||
// ChangePassword 修改管理员密码
|
||||
// @Summary 修改管理员密码
|
||||
// @Description 验证旧密码并设置新密码,自动更新配置文件
|
||||
// @Tags admin
|
||||
// @Security BearerAuth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body ChangePasswordRequest true "修改密码请求"
|
||||
// @Success 200 {object} map[string]string
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Failure 401 {object} map[string]string
|
||||
// @Router /admin/password [post]
|
||||
func ChangePassword(c *gin.Context) {
|
||||
var req ChangePasswordRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
|
||||
return
|
||||
}
|
||||
|
||||
cfg := config.GetConfig()
|
||||
// 验证旧密码
|
||||
err := bcrypt.CompareHashAndPassword([]byte(cfg.Admin.PasswordBcrypt), []byte(req.OldPassword))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid old password"})
|
||||
return
|
||||
}
|
||||
|
||||
// 生成新密码 Hash
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(req.NewPassword), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to hash password"})
|
||||
return
|
||||
}
|
||||
|
||||
// 更新配置
|
||||
cfg.Admin.PasswordBcrypt = string(hash)
|
||||
if err := config.SaveConfig(cfg); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save config"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"status": "ok", "message": "password updated successfully"})
|
||||
}
|
||||
|
||||
// UpdateToken 更新 Token 状态
|
||||
// @Summary 更新 Token 状态
|
||||
// @Description 启用或禁用指定的 API Token
|
||||
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"BingDailyImage/internal/config"
|
||||
"BingDailyImage/internal/model"
|
||||
"BingDailyImage/internal/service/image"
|
||||
"BingDailyImage/internal/storage"
|
||||
"BingPaper/internal/config"
|
||||
"BingPaper/internal/model"
|
||||
"BingPaper/internal/service/image"
|
||||
"BingPaper/internal/storage"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"BingDailyImage/internal/service/token"
|
||||
"BingPaper/internal/service/token"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
_ "BingDailyImage/docs"
|
||||
"BingDailyImage/internal/http/handlers"
|
||||
"BingDailyImage/internal/http/middleware"
|
||||
_ "BingPaper/docs"
|
||||
"BingPaper/internal/http/handlers"
|
||||
"BingPaper/internal/http/middleware"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
swaggerFiles "github.com/swaggo/files"
|
||||
@@ -50,6 +50,8 @@ func SetupRouter() *gin.Engine {
|
||||
authorized.PATCH("/tokens/:id", handlers.UpdateToken)
|
||||
authorized.DELETE("/tokens/:id", handlers.DeleteToken)
|
||||
|
||||
authorized.POST("/password", handlers.ChangePassword)
|
||||
|
||||
authorized.GET("/config", handlers.GetConfig)
|
||||
authorized.PUT("/config", handlers.UpdateConfig)
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"BingDailyImage/internal/config"
|
||||
"BingDailyImage/internal/model"
|
||||
"BingDailyImage/internal/util"
|
||||
"BingPaper/internal/config"
|
||||
"BingPaper/internal/model"
|
||||
"BingPaper/internal/util"
|
||||
"fmt"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
@@ -13,11 +13,11 @@ import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"BingDailyImage/internal/config"
|
||||
"BingDailyImage/internal/model"
|
||||
"BingDailyImage/internal/repo"
|
||||
"BingDailyImage/internal/storage"
|
||||
"BingDailyImage/internal/util"
|
||||
"BingPaper/internal/config"
|
||||
"BingPaper/internal/model"
|
||||
"BingPaper/internal/repo"
|
||||
"BingPaper/internal/storage"
|
||||
"BingPaper/internal/util"
|
||||
|
||||
"github.com/chai2010/webp"
|
||||
"github.com/disintegration/imaging"
|
||||
|
||||
@@ -5,11 +5,11 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"BingDailyImage/internal/config"
|
||||
"BingDailyImage/internal/model"
|
||||
"BingDailyImage/internal/repo"
|
||||
"BingDailyImage/internal/storage"
|
||||
"BingDailyImage/internal/util"
|
||||
"BingPaper/internal/config"
|
||||
"BingPaper/internal/model"
|
||||
"BingPaper/internal/repo"
|
||||
"BingPaper/internal/storage"
|
||||
"BingPaper/internal/util"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"BingDailyImage/internal/config"
|
||||
"BingDailyImage/internal/model"
|
||||
"BingDailyImage/internal/repo"
|
||||
"BingPaper/internal/config"
|
||||
"BingPaper/internal/model"
|
||||
"BingPaper/internal/repo"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
@@ -51,7 +51,21 @@ func Login(password string) (*model.Token, error) {
|
||||
}
|
||||
|
||||
ttl := config.GetTokenTTL()
|
||||
return CreateToken("login-token", time.Now().Add(ttl))
|
||||
expiresAt := time.Now().Add(ttl)
|
||||
name := "login-token"
|
||||
|
||||
// 如果已存在同名 token,则刷新时间并返回
|
||||
var t model.Token
|
||||
if err := repo.DB.Where("name = ?", name).First(&t).Error; err == nil {
|
||||
t.ExpiresAt = expiresAt
|
||||
t.Disabled = false
|
||||
if err := repo.DB.Save(&t).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
return CreateToken(name, expiresAt)
|
||||
}
|
||||
|
||||
func ListTokens() ([]model.Token, error) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"BingDailyImage/internal/storage"
|
||||
"BingPaper/internal/storage"
|
||||
)
|
||||
|
||||
type LocalStorage struct {
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"BingDailyImage/internal/storage"
|
||||
"BingPaper/internal/storage"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"BingDailyImage/internal/storage"
|
||||
"BingPaper/internal/storage"
|
||||
|
||||
"github.com/studio-b12/gowebdav"
|
||||
)
|
||||
|
||||
8
main.go
8
main.go
@@ -3,14 +3,14 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"BingDailyImage/internal/bootstrap"
|
||||
"BingDailyImage/internal/config"
|
||||
"BingDailyImage/internal/util"
|
||||
"BingPaper/internal/bootstrap"
|
||||
"BingPaper/internal/config"
|
||||
"BingPaper/internal/util"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// @title BingDailyImage API
|
||||
// @title BingPaper API
|
||||
// @version 1.0
|
||||
// @description 必应每日一图抓取、存储、管理与公共 API 服务。
|
||||
// @host localhost:8080
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>BingDailyImage - 必应每日一图</title>
|
||||
<title>BingPaper - 必应每日一图</title>
|
||||
<style>
|
||||
:root {
|
||||
--primary-color: #007bff;
|
||||
@@ -67,7 +67,7 @@
|
||||
<body>
|
||||
|
||||
<nav>
|
||||
<a href="/" class="logo">BingDailyImage</a>
|
||||
<a href="/" class="logo">BingPaper</a>
|
||||
<div>
|
||||
<a href="/">首页</a>
|
||||
<a href="/admin">管理</a>
|
||||
@@ -138,6 +138,13 @@
|
||||
<button onclick="triggerFetch()" style="margin-bottom: 10px;">手动抓取 (最近8天)</button>
|
||||
<button class="secondary" onclick="triggerCleanup()" style="margin-bottom: 10px;">手动清理旧图</button>
|
||||
</div>
|
||||
|
||||
<div class="admin-card">
|
||||
<h3>修改密码</h3>
|
||||
<input type="password" id="old-password" placeholder="旧密码" style="margin-bottom: 10px;">
|
||||
<input type="password" id="new-password" placeholder="新密码" style="margin-bottom: 10px;">
|
||||
<button onclick="changePassword()">提交修改</button>
|
||||
</div>
|
||||
|
||||
<div class="admin-card">
|
||||
<h3>今日图预览</h3>
|
||||
@@ -354,6 +361,26 @@
|
||||
alert('清理任务已启动');
|
||||
}
|
||||
|
||||
async function changePassword() {
|
||||
const oldPassword = document.getElementById('old-password').value;
|
||||
const newPassword = document.getElementById('new-password').value;
|
||||
if (!oldPassword || !newPassword) return alert('请输入完整信息');
|
||||
|
||||
const resp = await fetch('/api/v1/admin/password', {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ old_password: oldPassword, new_password: newPassword })
|
||||
});
|
||||
|
||||
const data = await resp.json();
|
||||
if (resp.ok) {
|
||||
alert('密码修改成功,请重新登录');
|
||||
logout();
|
||||
} else {
|
||||
alert('修改失败: ' + (data.error || '未知错误'));
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
router();
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user