2021-09-24 08:11:41 +00:00
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
2021-12-04 04:06:45 +00:00
|
|
|
|
"flag"
|
|
|
|
|
|
"fmt"
|
2022-03-20 15:55:05 +00:00
|
|
|
|
"io"
|
|
|
|
|
|
"net/http"
|
|
|
|
|
|
"os"
|
2025-10-01 12:43:33 +00:00
|
|
|
|
|
|
|
|
|
|
"github.com/beancount-gs/script"
|
|
|
|
|
|
"github.com/beancount-gs/service"
|
|
|
|
|
|
"github.com/beancount-gs/utils/venv"
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
2021-09-24 08:11:41 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
2025-10-01 12:43:33 +00:00
|
|
|
|
// 全局变量,方便其他模块使用
|
|
|
|
|
|
var venvExecutor *venv.VenvExecutor
|
|
|
|
|
|
var venvPath string // 新增:虚拟环境路径变量
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* 初始化服务器文件
|
|
|
|
|
|
* 检查账本目录是否存在,如果不存在则创建
|
|
|
|
|
|
* 返回error,表示操作是否成功
|
|
|
|
|
|
*/
|
2021-11-18 08:27:28 +00:00
|
|
|
|
func InitServerFiles() error {
|
|
|
|
|
|
dataPath := script.GetServerConfig().DataPath
|
2021-11-17 09:59:06 +00:00
|
|
|
|
// 账本目录不存在,则创建
|
2021-12-01 09:32:15 +00:00
|
|
|
|
if dataPath != "" && !script.FileIfExist(dataPath) {
|
2021-11-18 08:27:28 +00:00
|
|
|
|
return script.MkDir(dataPath)
|
2021-11-17 09:59:06 +00:00
|
|
|
|
}
|
2021-11-18 08:27:28 +00:00
|
|
|
|
return nil
|
2021-11-17 06:16:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-01 12:43:33 +00:00
|
|
|
|
/*
|
|
|
|
|
|
* 加载服务器缓存
|
|
|
|
|
|
* 加载账本配置映射和账户映射
|
|
|
|
|
|
* 返回error,表示加载过程中是否出错
|
|
|
|
|
|
*/
|
2021-11-18 08:27:28 +00:00
|
|
|
|
func LoadServerCache() error {
|
2021-11-22 14:50:10 +00:00
|
|
|
|
err := script.LoadLedgerConfigMap()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
return script.LoadLedgerAccountsMap()
|
2021-11-17 06:16:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-01 12:43:33 +00:00
|
|
|
|
/*
|
|
|
|
|
|
* 授权中间件
|
|
|
|
|
|
* 检查请求头中的ledgerId是否有效
|
|
|
|
|
|
* 如果有效则继续处理请求,否则返回未授权错误
|
|
|
|
|
|
*/
|
2021-11-18 10:10:19 +00:00
|
|
|
|
func AuthorizedHandler() gin.HandlerFunc {
|
|
|
|
|
|
return func(c *gin.Context) {
|
|
|
|
|
|
ledgerId := c.GetHeader("ledgerId")
|
|
|
|
|
|
ledgerConfig := script.GetLedgerConfig(ledgerId)
|
|
|
|
|
|
if ledgerConfig != nil {
|
2021-11-19 09:54:02 +00:00
|
|
|
|
c.Set("LedgerConfig", ledgerConfig)
|
2021-11-18 10:10:19 +00:00
|
|
|
|
c.Next()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
service.Unauthorized(c)
|
|
|
|
|
|
c.Abort()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-01 12:43:33 +00:00
|
|
|
|
/*
|
|
|
|
|
|
* 注册路由
|
|
|
|
|
|
* 配置静态文件服务、API路由和需要授权的路由组
|
|
|
|
|
|
*/
|
2021-11-18 08:27:28 +00:00
|
|
|
|
func RegisterRouter(router *gin.Engine) {
|
2021-11-19 09:54:02 +00:00
|
|
|
|
// fix wildcard and static file router conflict, https://github.com/gin-gonic/gin/issues/360
|
|
|
|
|
|
router.GET("/", func(c *gin.Context) {
|
|
|
|
|
|
c.Redirect(http.StatusMovedPermanently, "/web")
|
|
|
|
|
|
})
|
|
|
|
|
|
router.StaticFS("/web", http.Dir("./public"))
|
2025-10-01 12:43:33 +00:00
|
|
|
|
// 公开API路由,无需授权
|
2021-12-03 08:54:52 +00:00
|
|
|
|
router.GET("/api/version", service.QueryVersion)
|
2021-11-29 16:13:41 +00:00
|
|
|
|
router.POST("/api/check", service.CheckBeancount)
|
|
|
|
|
|
router.GET("/api/config", service.QueryServerConfig)
|
|
|
|
|
|
router.POST("/api/config", service.UpdateServerConfig)
|
2021-12-04 05:12:12 +00:00
|
|
|
|
router.GET("/api/ledger", service.QueryLedgerList)
|
2021-11-18 08:27:28 +00:00
|
|
|
|
router.POST("/api/ledger", service.OpenOrCreateLedger)
|
2025-10-01 12:43:33 +00:00
|
|
|
|
// 需要授权的API路由组
|
2021-11-18 10:10:19 +00:00
|
|
|
|
authorized := router.Group("/api/auth/")
|
|
|
|
|
|
authorized.Use(AuthorizedHandler())
|
|
|
|
|
|
{
|
|
|
|
|
|
// need authorized
|
2021-11-23 06:58:37 +00:00
|
|
|
|
authorized.GET("/account/valid", service.QueryValidAccount)
|
2021-11-23 14:42:03 +00:00
|
|
|
|
authorized.GET("/account/all", service.QueryAllAccount)
|
2021-11-23 06:58:37 +00:00
|
|
|
|
authorized.GET("/account/type", service.QueryAccountType)
|
2021-11-28 12:19:40 +00:00
|
|
|
|
authorized.POST("/account", service.AddAccount)
|
|
|
|
|
|
authorized.POST("/account/type", service.AddAccountType)
|
|
|
|
|
|
authorized.POST("/account/close", service.CloseAccount)
|
|
|
|
|
|
authorized.POST("/account/icon", service.ChangeAccountIcon)
|
|
|
|
|
|
authorized.POST("/account/balance", service.BalanceAccount)
|
2021-12-14 14:09:17 +00:00
|
|
|
|
authorized.POST("/account/refresh", service.RefreshAccountCache)
|
2021-11-28 12:19:40 +00:00
|
|
|
|
authorized.POST("/commodity/price", service.SyncCommodityPrice)
|
2023-12-06 17:24:24 +00:00
|
|
|
|
authorized.GET("/commodity/currencies", service.QueryAllCurrencies)
|
2021-11-19 09:54:02 +00:00
|
|
|
|
authorized.GET("/stats/months", service.MonthsList)
|
2021-11-22 14:50:10 +00:00
|
|
|
|
authorized.GET("/stats/total", service.StatsTotal)
|
2021-11-26 09:12:07 +00:00
|
|
|
|
authorized.GET("/stats/payee", service.StatsPayee)
|
2021-11-24 15:45:45 +00:00
|
|
|
|
authorized.GET("/stats/account/percent", service.StatsAccountPercent)
|
|
|
|
|
|
authorized.GET("/stats/account/trend", service.StatsAccountTrend)
|
2021-12-02 14:48:45 +00:00
|
|
|
|
authorized.GET("/stats/account/balance", service.StatsAccountBalance)
|
2024-10-01 06:43:07 +00:00
|
|
|
|
authorized.GET("/stats/account/flow", service.StatsAccountSankey)
|
2021-11-25 07:41:28 +00:00
|
|
|
|
authorized.GET("/stats/month/total", service.StatsMonthTotal)
|
2022-06-05 03:07:03 +00:00
|
|
|
|
authorized.GET("/stats/month/calendar", service.StatsMonthCalendar)
|
2022-04-13 16:23:08 +00:00
|
|
|
|
authorized.GET("/stats/commodity/price", service.StatsCommodityPrice)
|
2024-10-24 15:26:07 +00:00
|
|
|
|
authorized.GET("/transaction/detail", service.QueryTransactionDetailById)
|
|
|
|
|
|
authorized.GET("/transaction/raw", service.QueryTransactionRawTextById)
|
2021-11-24 09:32:24 +00:00
|
|
|
|
authorized.GET("/transaction", service.QueryTransactions)
|
2021-11-28 12:19:40 +00:00
|
|
|
|
authorized.POST("/transaction", service.AddTransactions)
|
2024-10-29 06:00:37 +00:00
|
|
|
|
authorized.POST("/transaction/raw", service.UpdateTransactionRawTextById)
|
2024-10-24 15:26:07 +00:00
|
|
|
|
authorized.DELETE("/transaction", service.DeleteTransactionById)
|
2021-12-12 14:42:07 +00:00
|
|
|
|
authorized.POST("/transaction/batch", service.AddBatchTransactions)
|
2021-11-24 09:32:24 +00:00
|
|
|
|
authorized.GET("/transaction/payee", service.QueryTransactionPayees)
|
|
|
|
|
|
authorized.GET("/transaction/template", service.QueryTransactionTemplates)
|
2021-11-29 14:21:25 +00:00
|
|
|
|
authorized.POST("/transaction/template", service.AddTransactionTemplate)
|
|
|
|
|
|
authorized.DELETE("/transaction/template", service.DeleteTransactionTemplate)
|
2023-12-05 16:29:38 +00:00
|
|
|
|
authorized.GET("/event/all", service.GetAllEvents)
|
2023-12-05 09:57:48 +00:00
|
|
|
|
authorized.POST("/event", service.AddEvent)
|
2023-12-05 16:29:38 +00:00
|
|
|
|
authorized.DELETE("/event", service.DeleteEvent)
|
2021-11-22 10:05:12 +00:00
|
|
|
|
authorized.GET("/tags", service.QueryTags)
|
2021-11-23 15:33:14 +00:00
|
|
|
|
authorized.GET("/file/dir", service.QueryLedgerSourceFileDir)
|
|
|
|
|
|
authorized.GET("/file/content", service.QueryLedgerSourceFileContent)
|
|
|
|
|
|
authorized.POST("/file", service.UpdateLedgerSourceFileContent)
|
2021-12-12 14:42:07 +00:00
|
|
|
|
authorized.POST("/import/alipay", service.ImportAliPayCSV)
|
2021-12-14 13:24:39 +00:00
|
|
|
|
authorized.POST("/import/wx", service.ImportWxPayCSV)
|
2022-12-27 15:55:53 +00:00
|
|
|
|
authorized.POST("/import/icbc", service.ImportICBCCSV)
|
|
|
|
|
|
authorized.POST("/import/abc", service.ImportABCCSV)
|
2022-08-14 16:38:50 +00:00
|
|
|
|
authorized.GET("/ledger/check", service.CheckLedger)
|
2022-03-11 15:18:38 +00:00
|
|
|
|
authorized.DELETE("/ledger", service.DeleteLedger)
|
2021-11-18 10:10:19 +00:00
|
|
|
|
}
|
2021-11-17 06:16:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-01 12:43:33 +00:00
|
|
|
|
// initVenvExecutor 初始化虚拟环境执行器
|
|
|
|
|
|
func initVenvExecutor(venvDir string) {
|
|
|
|
|
|
venvPath = venvDir
|
|
|
|
|
|
script.SetVenvPath(venvDir) // 设置路径到 script 包
|
|
|
|
|
|
|
|
|
|
|
|
// 检查虚拟环境是否存在
|
|
|
|
|
|
if !venv.CheckVenvExists(venvPath) {
|
|
|
|
|
|
script.LogSystemError("虚拟环境不存在,请先运行 setup script: " + venvPath)
|
|
|
|
|
|
fmt.Println("警告: 虚拟环境不存在,某些功能可能无法正常工作")
|
|
|
|
|
|
fmt.Println("请运行: ./start_dev.sh 或手动创建虚拟环境")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
venvExecutor = venv.NewVenvExecutor(venvPath)
|
|
|
|
|
|
script.SetVenvExecutor(venvExecutor) // 设置执行器到 script 包
|
|
|
|
|
|
|
|
|
|
|
|
// 测试 bean-query 是否可用
|
|
|
|
|
|
_, err := venvExecutor.GetCommandPath("bean-query")
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
script.LogSystemError("bean-query 不可用: " + err.Error())
|
|
|
|
|
|
fmt.Println("警告: bean-query 命令不可用,价格查询功能将受限")
|
|
|
|
|
|
} else {
|
|
|
|
|
|
script.LogSystemInfo("虚拟环境初始化成功: bean-query 可用, 路径: " + venvPath)
|
|
|
|
|
|
fmt.Println("虚拟环境初始化成功: " + venvPath)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-09-24 08:11:41 +00:00
|
|
|
|
func main() {
|
2021-12-04 04:06:45 +00:00
|
|
|
|
var secret string
|
|
|
|
|
|
var port int
|
2025-10-01 12:43:33 +00:00
|
|
|
|
var debugFlag bool
|
|
|
|
|
|
var venvDir string // 新增:虚拟环境目录参数
|
|
|
|
|
|
|
2021-12-04 04:06:45 +00:00
|
|
|
|
flag.StringVar(&secret, "secret", "", "服务器密钥")
|
2023-04-09 14:53:26 +00:00
|
|
|
|
flag.IntVar(&port, "p", 10000, "端口号")
|
2025-10-01 12:43:33 +00:00
|
|
|
|
flag.BoolVar(&debugFlag, "debug", false, "调试模式")
|
|
|
|
|
|
flag.StringVar(&venvDir, "venv", ".env_beancount-v3", "虚拟环境目录名称,默认值为 .env_beancount-v3") // 新增参数
|
|
|
|
|
|
|
2021-12-04 04:06:45 +00:00
|
|
|
|
flag.Parse()
|
|
|
|
|
|
|
2025-10-01 12:43:33 +00:00
|
|
|
|
// 初始化虚拟环境执行器
|
|
|
|
|
|
initVenvExecutor(venvDir)
|
|
|
|
|
|
|
2021-11-17 09:59:06 +00:00
|
|
|
|
// 读取配置文件
|
2021-11-18 08:27:28 +00:00
|
|
|
|
err := script.LoadServerConfig()
|
|
|
|
|
|
if err != nil {
|
2021-11-21 14:37:13 +00:00
|
|
|
|
script.LogSystemError("Failed to load server config, " + err.Error())
|
2021-11-18 08:27:28 +00:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-10-01 12:43:33 +00:00
|
|
|
|
|
|
|
|
|
|
// 如果命令行指定了debug参数,覆盖配置文件中的设置
|
|
|
|
|
|
if debugFlag {
|
|
|
|
|
|
err = script.SetDebugMode(true)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
fmt.Println("Warning: Failed to set debug mode:", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 现在可以在任何地方使用 script.IsDebugMode() 来检查调试模式
|
|
|
|
|
|
if script.IsDebugMode() {
|
|
|
|
|
|
fmt.Println("调试模式已启用")
|
|
|
|
|
|
} else {
|
|
|
|
|
|
fmt.Println("调试模式未启用")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-01 09:32:15 +00:00
|
|
|
|
serverConfig := script.GetServerConfig()
|
|
|
|
|
|
// 若 DataPath == "" 则配置未初始化
|
|
|
|
|
|
if serverConfig.DataPath != "" {
|
|
|
|
|
|
// 初始化账本文件结构
|
|
|
|
|
|
err = InitServerFiles()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
script.LogSystemError("Failed to init server files, " + err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
// 加载缓存
|
|
|
|
|
|
err = LoadServerCache()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
script.LogSystemError("Failed to load server cache, " + err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2021-11-18 08:27:28 +00:00
|
|
|
|
}
|
2025-10-01 12:43:33 +00:00
|
|
|
|
|
2021-12-02 14:48:45 +00:00
|
|
|
|
// gin 日志设置
|
|
|
|
|
|
gin.DisableConsoleColor()
|
2023-04-09 14:53:26 +00:00
|
|
|
|
fs, _ := os.Create("logs/gin.log")
|
2023-04-09 17:27:41 +00:00
|
|
|
|
gin.DefaultWriter = io.MultiWriter(fs, os.Stdout)
|
2021-11-18 08:27:28 +00:00
|
|
|
|
router := gin.Default()
|
2025-10-01 12:43:33 +00:00
|
|
|
|
|
2021-11-17 06:16:22 +00:00
|
|
|
|
// 注册路由
|
2021-11-18 08:27:28 +00:00
|
|
|
|
RegisterRouter(router)
|
2021-12-04 04:06:45 +00:00
|
|
|
|
|
|
|
|
|
|
portStr := fmt.Sprintf(":%d", port)
|
|
|
|
|
|
url := "http://localhost" + portStr
|
2021-12-03 08:46:18 +00:00
|
|
|
|
ip := script.GetIpAddress()
|
|
|
|
|
|
startLog := "beancount-gs start at " + url
|
|
|
|
|
|
if ip != "" {
|
2021-12-04 04:06:45 +00:00
|
|
|
|
startLog += " or http://" + ip + portStr
|
2021-12-03 08:46:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
script.LogSystemInfo(startLog)
|
2025-10-01 12:43:33 +00:00
|
|
|
|
|
2021-12-04 04:06:45 +00:00
|
|
|
|
// 打开浏览器
|
|
|
|
|
|
script.OpenBrowser(url)
|
2025-10-01 12:43:33 +00:00
|
|
|
|
|
2021-12-04 04:06:45 +00:00
|
|
|
|
// 打印密钥
|
|
|
|
|
|
script.LogSystemInfo("Secret token is " + script.GenerateServerSecret(secret))
|
2025-10-01 12:43:33 +00:00
|
|
|
|
|
2021-12-04 04:06:45 +00:00
|
|
|
|
// 启动服务
|
|
|
|
|
|
err = router.Run(portStr)
|
2021-11-18 08:27:28 +00:00
|
|
|
|
if err != nil {
|
2021-11-21 14:37:13 +00:00
|
|
|
|
script.LogSystemError("Failed to start server, " + err.Error())
|
2021-11-18 08:27:28 +00:00
|
|
|
|
}
|
2021-09-24 08:11:41 +00:00
|
|
|
|
}
|