diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c3d0420 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +/api_file/logs/ +/api_iris/logs/ +/api_iris/upload-note/ +/api_iris/upload-video/ +/api_iris/upload/ +/api_iris/static/ +/web_vue/node_modules/ diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..704e8a6 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..060d2c5 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/web_ylsa.iml b/.idea/web_ylsa.iml new file mode 100644 index 0000000..9039cd3 --- /dev/null +++ b/.idea/web_ylsa.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..ed13fef --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,417 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + "associatedIndex": 1 +} + + + + + + + + + { + "keyToString": { + "ASKED_MARK_IGNORED_FILES_AS_EXCLUDED": "true", + "Django 服务器.web_ylsa.executor": "Run", + "Python.build (1).executor": "Run", + "Python.build.executor": "Run", + "Python.test-2.executor": "Run", + "Python.test-api (1).executor": "Run", + "Python.test-database.executor": "Run", + "Python.test.executor": "Run", + "Python.test1.executor": "Run", + "RunOnceActivity.OpenDjangoStructureViewOnStart": "true", + "RunOnceActivity.ShowReadmeOnStart": "true", + "RunOnceActivity.go.formatter.settings.were.checked": "true", + "RunOnceActivity.go.migrated.go.modules.settings": "true", + "RunOnceActivity.go.modules.go.list.on.any.changes.was.set": "true", + "RunOnceActivity.pycharm.django.structure.promotion.once.per.project": "true", + "go.import.settings.migrated": "true", + "go.sdk.automatically.set": "true", + "ignore.virus.scanning.warn.message": "true", + "last_opened_file_path": "C:/Users/m1582/Documents/project/git/web_ylsa", + "node.js.detected.package.eslint": "true", + "node.js.detected.package.tslint": "true", + "node.js.selected.package.eslint": "(autodetect)", + "node.js.selected.package.tslint": "(autodetect)", + "nodejs_package_manager_path": "npm", + "settings.editor.selected.configurable": "com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable", + "vue.rearranger.settings.migration": "true" + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1728439714627 + + + 1728439714627 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1730080444691 + + + + 1730080444691 + + + + 1730170240151 + + + + 1730170240151 + + + + 1730859757009 + + + + 1730859757009 + + + + 1739259478761 + + + + 1739259478761 + + + + 1739259517144 + + + + 1739259517144 + + + + 1739260593698 + + + + 1739260593698 + + + + 1739848928421 + + + + 1739848928421 + + + + 1740130391214 + + + + 1740130391214 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + true + + + + + + + + + + + \ No newline at end of file diff --git a/api_file/Dockerfile b/api_file/Dockerfile new file mode 100644 index 0000000..e0481ad --- /dev/null +++ b/api_file/Dockerfile @@ -0,0 +1,15 @@ +FROM golang:1.22.4 + +RUN mkdir -p /web \ + mkdir -p /web/logs + +ENV GOPATH=/web +ENV GOPROXY=https://goproxy.cn,direct + +WORKDIR $GOPATH/api_file + +COPY . $GOPATH/api_file + +RUN go build . + +ENTRYPOINT ["./main"] \ No newline at end of file diff --git a/api_file/config/config.dev.yaml b/api_file/config/config.dev.yaml new file mode 100644 index 0000000..c75d592 --- /dev/null +++ b/api_file/config/config.dev.yaml @@ -0,0 +1,11 @@ +database: + dsn: web:Song1875.@tcp(43.154.168.226:3306)/web_vue?charset=utf8mb4&parseTime=True&loc=Local + prd: false + +logs: + log: logs + nginx: logs/nginx + +sys: + jwt: False + User: admin diff --git a/api_file/config/config.go b/api_file/config/config.go new file mode 100644 index 0000000..c9daa56 --- /dev/null +++ b/api_file/config/config.go @@ -0,0 +1,31 @@ +package config + +import ( + "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" + "os" +) + +// Config 公共参数 +var Config settings + +// InitConfig 配置初始化 +func InitConfig(version string) error { + var file []byte + var err error + if version == "dev" { + file, err = os.ReadFile("./config/config.dev.yaml") + } else { + file, err = os.ReadFile("./config/config.prd.yaml") + } + if err != nil { + logrus.Errorln("Error reading config file:", err) + return err + } + err = yaml.Unmarshal(file, &Config) + if err != nil { + logrus.Errorln("Error parsing config file:", err) + return err + } + return nil +} diff --git a/api_file/config/config.prd.yaml b/api_file/config/config.prd.yaml new file mode 100644 index 0000000..6b89917 --- /dev/null +++ b/api_file/config/config.prd.yaml @@ -0,0 +1,11 @@ +database: + dsn: web_prd:SongPrd1875.@tcp(43.154.168.226:3306)/web_prd?charset=utf8mb4&parseTime=True&loc=Local + prd: true + +logs: + log: logs + nginx: logs/nginx + +sys: + jwt: True + User: diff --git a/api_file/config/type.d.go b/api_file/config/type.d.go new file mode 100644 index 0000000..784e1c7 --- /dev/null +++ b/api_file/config/type.d.go @@ -0,0 +1,21 @@ +package config + +type settings struct { + Logs *logs `yaml:"logs"` + Database *database `yaml:"database"` +} + +type logs struct { + Nginx string `yaml:"nginx"` + Log string `yaml:"log"` +} + +type database struct { + Dsn string `yaml:"dsn"` + Prd bool `yaml:"prd"` +} + +type sys struct { + Jwt string `yaml:"jwt"` + User string `yaml:"user"` +} diff --git a/api_file/database/database.go b/api_file/database/database.go new file mode 100644 index 0000000..e82c106 --- /dev/null +++ b/api_file/database/database.go @@ -0,0 +1,48 @@ +package database + +import ( + "github.com/sirupsen/logrus" + "gorm.io/driver/mysql" + "gorm.io/gorm" + "gorm.io/gorm/logger" + "main/config" + "sync" +) + +var instance *MysqlConnectionPool +var once sync.Once +var db *gorm.DB +var err error + +// GetInstance 获取数据库连接池 +func GetInstance() *MysqlConnectionPool { + once.Do(func() { + instance = &MysqlConnectionPool{} + }) + return instance +} + +// InitDataPool 数据库初始化连接 +func (m *MysqlConnectionPool) InitDataPool() (isSuccess bool) { + dbConfig := config.Config.Database + if dbConfig.Prd { + db, err = gorm.Open(mysql.Open(dbConfig.Dsn), &gorm.Config{}) + } else { + db, err = gorm.Open(mysql.Open(dbConfig.Dsn), &gorm.Config{ + Logger: logger.Default.LogMode(logger.Info), + }) + } + if err != nil { + logrus.Errorln("数据库配置失败:", err) + return false + } + return true +} + +// GetMysqlDb 获取数据库连接 +func (m *MysqlConnectionPool) GetMysqlDb() (dbCon *gorm.DB) { + return db +} + +type MysqlConnectionPool struct { +} diff --git a/api_file/go.mod b/api_file/go.mod new file mode 100644 index 0000000..02f4892 --- /dev/null +++ b/api_file/go.mod @@ -0,0 +1,65 @@ +module main + +go 1.22.4 + +require ( + github.com/antonfisher/nested-logrus-formatter v1.3.1 + github.com/golang-jwt/jwt/v4 v4.5.1 + github.com/iris-contrib/middleware/jwt v0.0.0-20240502084239-34f27409ce72 + github.com/kataras/iris/v12 v12.2.11 + github.com/shopspring/decimal v1.4.0 + github.com/sirupsen/logrus v1.9.3 + gopkg.in/yaml.v3 v3.0.1 + gorm.io/driver/mysql v1.5.7 + gorm.io/gorm v1.25.10 +) + +require ( + github.com/BurntSushi/toml v1.3.2 // indirect + github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect + github.com/CloudyKit/jet/v6 v6.2.0 // indirect + github.com/Joker/jade v1.1.3 // indirect + github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 // indirect + github.com/andybalholm/brotli v1.1.0 // indirect + github.com/aymerick/douceur v0.2.0 // indirect + github.com/fatih/structs v1.1.0 // indirect + github.com/flosch/pongo2/v4 v4.0.2 // indirect + github.com/go-sql-driver/mysql v1.7.0 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/css v1.0.0 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/iris-contrib/schema v0.0.6 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kataras/blocks v0.0.8 // indirect + github.com/kataras/golog v0.1.11 // indirect + github.com/kataras/pio v0.0.13 // indirect + github.com/kataras/sitemap v0.0.6 // indirect + github.com/kataras/tunnel v0.0.4 // indirect + github.com/klauspost/compress v1.17.7 // indirect + github.com/mailgun/raymond/v2 v2.0.48 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/microcosm-cc/bluemonday v1.0.26 // indirect + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/schollz/closestmatch v2.1.0+incompatible // indirect + github.com/tdewolff/minify/v2 v2.20.19 // indirect + github.com/tdewolff/parse/v2 v2.7.12 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + github.com/yosssi/ace v0.0.5 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + golang.org/x/time v0.5.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect +) diff --git a/api_file/go.sum b/api_file/go.sum new file mode 100644 index 0000000..4035fa1 --- /dev/null +++ b/api_file/go.sum @@ -0,0 +1,207 @@ +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c= +github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= +github.com/CloudyKit/jet/v6 v6.2.0 h1:EpcZ6SR9n28BUGtNJSvlBqf90IpjeFr36Tizxhn/oME= +github.com/CloudyKit/jet/v6 v6.2.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4= +github.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc= +github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= +github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk= +github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM= +github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 h1:KkH3I3sJuOLP3TjA/dfr4NAY8bghDwnXiU7cTKxQqo0= +github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjKLwalezA0k99cWs5L11HWOAPNjdUZ6RxH1BXbbM= +github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= +github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/antonfisher/nested-logrus-formatter v1.3.1 h1:NFJIr+pzwv5QLHTPyKz9UMEoHck02Q9L0FP13b/xSbQ= +github.com/antonfisher/nested-logrus-formatter v1.3.1/go.mod h1:6WTfyWFkBc9+zyBaKIqRrg/KwMqBbodBjgbHjDz7zjA= +github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= +github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= +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/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw= +github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8= +github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= +github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0 h1:4gjrh/PN2MuWCCElk8/I4OCKRKWCCo2zEct3VKCbibU= +github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= +github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk= +github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= +github.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go= +github.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE= +github.com/iris-contrib/middleware/jwt v0.0.0-20240502084239-34f27409ce72 h1:wbkA/QXv1RZmuY2iLvsgmHeiGq4DNvv2Rhj5PQ2oHTI= +github.com/iris-contrib/middleware/jwt v0.0.0-20240502084239-34f27409ce72/go.mod h1:hIyBTK1zUxUaC4Hu8Ba9Z70r2S5ET4+ZKNescqjXld0= +github.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw= +github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM= +github.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg= +github.com/kataras/golog v0.1.11 h1:dGkcCVsIpqiAMWTlebn/ZULHxFvfG4K43LF1cNWSh20= +github.com/kataras/golog v0.1.11/go.mod h1:mAkt1vbPowFUuUGvexyQ5NFW6djEgGyxQBIARJ0AH4A= +github.com/kataras/iris/v12 v12.2.11 h1:sGgo43rMPfzDft8rjVhPs6L3qDJy3TbBrMD/zGL1pzk= +github.com/kataras/iris/v12 v12.2.11/go.mod h1:uMAeX8OqG9vqdhyrIPv8Lajo/wXTtAF43wchP9WHt2w= +github.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM= +github.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM= +github.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY= +github.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4= +github.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA= +github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw= +github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= +github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw= +github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58= +github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo= +github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U= +github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk= +github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= +github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tdewolff/minify/v2 v2.20.19 h1:tX0SR0LUrIqGoLjXnkIzRSIbKJ7PaNnSENLD4CyH6Xo= +github.com/tdewolff/minify/v2 v2.20.19/go.mod h1:ulkFoeAVWMLEyjuDz1ZIWOA31g5aWOawCFRp9R/MudM= +github.com/tdewolff/parse/v2 v2.7.12 h1:tgavkHc2ZDEQVKy1oWxwIyh5bP4F5fEh/JmBwPP/3LQ= +github.com/tdewolff/parse/v2 v2.7.12/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA= +github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= +github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03u/dMQK9g+Iw9ewps4mCl1nB8Sscbo= +github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY= +github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= +github.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA= +github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0= +github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA= +github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 h1:985EYyeCOxTpcgOTJpflJUwOeEz0CQOdPt73OzpE9F8= +golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= +gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= +gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= +gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +moul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs= +moul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE= diff --git a/api_file/jwtSet/jwt.go b/api_file/jwtSet/jwt.go new file mode 100644 index 0000000..cbaf659 --- /dev/null +++ b/api_file/jwtSet/jwt.go @@ -0,0 +1,78 @@ +package jwtSet + +// +//import ( +// "github.com/iris-contrib/middleware/jwt" +// "github.com/sirupsen/logrus" +// "main/database" +// "main/model" +// "main/utils" +// "time" +//) +// +//var Jwt *jwt.Middleware +//var keys model.JwtKeys +// +//func GetJwtKeys() model.JwtKeys { +// db := database.GetInstance().GetMysqlDb() +// if utils.DataIsNil(keys) || keys.Date != time.Now().Format("2006-01-02") { +// keys = model.JwtKeys{} +// if err := db.Where("date = ? and token = ''", time.Now().Format("2006-01-02")).First(&keys).Error; err != nil { +// logrus.Errorln("sql执行失败:", err) +// return model.JwtKeys{} +// } +// } +// if keys.Key == "" { +// keys.Date = time.Now().Format("2006-01-02") +// keys.Key = utils.NewKey(32) +// if err := db.Create(&keys).Error; err != nil { +// logrus.Errorln("sql执行失败:", err) +// return model.JwtKeys{} +// } +// } +// if !keys.Updated && !utils.DataIsNil(Jwt) { +// UpdateJwt() +// } +// return keys +//} +////func Init() { +//// if utils.DataIsNil(keys) { +//// GetJwtKeys() +//// } +//// Jwt = jwt.New(jwt.Config{ +//// ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) { +//// return []byte(keys.Key), nil +//// }, +//// ErrorHandler: func(context iris.Context, err error) { +//// if err == nil { +//// return +//// } +//// context.StopExecution() +//// context.StatusCode(iris.StatusUnauthorized) +//// context.SetErr(err) +//// }, +//// Extractor: jwt.FromAuthHeader, +//// SigningMethod: jwt.SigningMethodHS256, +//// }) +//// db := database.GetInstance().GetMysqlDb() +//// keys.Updated = true +//// if err := db.Updates(&keys).Error; err != nil { +//// logrus.Errorln("sql执行失败:", err) +//// return +//// } +////} +// +//func UpdateJwt() { +// if utils.DataIsNil(keys) { +// GetJwtKeys() +// } +// Jwt.Config.ValidationKeyGetter = func(token *jwt.Token) (interface{}, error) { +// return []byte(keys.Key), nil +// } +// db := database.GetInstance().GetMysqlDb() +// keys.Updated = true +// if err := db.Updates(&keys).Error; err != nil { +// logrus.Errorln("sql执行失败:", err) +// return +// } +//} diff --git a/api_file/main.go b/api_file/main.go new file mode 100644 index 0000000..68509ea --- /dev/null +++ b/api_file/main.go @@ -0,0 +1,81 @@ +package main + +import ( + "fmt" + nested "github.com/antonfisher/nested-logrus-formatter" + "github.com/kataras/iris/v12" + "github.com/kataras/iris/v12/middleware/accesslog" + "github.com/sirupsen/logrus" + "main/config" + "main/database" + "main/service" + "main/utils" + "os" + "path" + "time" +) + +func main() { + logrus.SetFormatter(&nested.Formatter{HideKeys: true, TimestampFormat: time.RFC3339}) + //配置文件初始化 + err := config.InitConfig(utils.GetEnvDefault("version", "dev")) + if err != nil { + logrus.Errorln("配置文件初始化失败:", err) + return + } + logrus.Infoln("配置文件初始化完成") + //日志初始化 + ac := makeAccessLog() + //应用初始化 + app := iris.New() + //应用ac-log + app.UseRouter(ac.Handler) + //错误处理 + app.OnAnyErrorCode(handler) + //数据库连接初始化 + if !database.GetInstance().InitDataPool() { + return + } + logrus.Infoln("数据库已连接") + //初始化用户信息,系统配置信息,菜单列表 + utils.UpdateUserInfo() + utils.UpdateSysSettings() + logrus.Infoln("数据库结构初始化完成") + //jwtSet.Init() + //logrus.Infoln("jwt认证初始化完成") + //接口组 + app.PartyFunc("/api", service.Apis) + logrus.Infoln("接口组配置完成") + //应用启动 + logrus.Infoln("应用启动") + err = app.Run(iris.Addr(":8081")) + if err != nil { + return + } +} + +// 生成acLog实例 +func makeAccessLog() *accesslog.AccessLog { + cPath, _ := os.Getwd() + ac := accesslog.File(path.Join(cPath, config.Config.Logs.Log, fmt.Sprintf("access-%s.log", time.Now().Format("2006-01-02")))) + ac.AddOutput(os.Stdout) + ac.IP = true + ac.Delim = ' ' + ac.ResponseBody = false + return ac +} + +// 接口错误处理 +func handler(ctx iris.Context) { + if ctx.GetErr() != nil { + err := ctx.JSON(utils.FormatRes(ctx.GetStatusCode(), ctx.GetErr().Error(), nil)) + if err != nil { + return + } + } else { + err := ctx.JSON(utils.FormatRes(ctx.GetStatusCode(), "", nil)) + if err != nil { + return + } + } +} diff --git a/api_file/model/backgammon.go b/api_file/model/backgammon.go new file mode 100644 index 0000000..0c2419d --- /dev/null +++ b/api_file/model/backgammon.go @@ -0,0 +1,12 @@ +package model + +import "gorm.io/gorm" + +type BackgammonRoom struct { + RoomId int `json:"room_id"` + Player string `json:"player"` + Winner string `json:"winner"` + Current string `json:"current"` + PawnStatus string `json:"pawn_status"` + gorm.Model +} diff --git a/api_file/model/balance.go b/api_file/model/balance.go new file mode 100644 index 0000000..dce461a --- /dev/null +++ b/api_file/model/balance.go @@ -0,0 +1,19 @@ +package model + +import "gorm.io/gorm" + +type Balances struct { + Username string `json:"username"` + Card string `json:"card"` + Type bool `gorm:"default:false" json:"type"` //1:支出型;0:收入型 + Balance float64 `json:"balance"` + gorm.Model +} + +type BalanceLogs struct { + Username string + Card string + Date string + Balance float64 + gorm.Model +} diff --git a/api_file/model/chess.go b/api_file/model/chess.go new file mode 100644 index 0000000..074a722 --- /dev/null +++ b/api_file/model/chess.go @@ -0,0 +1,28 @@ +package model + +import ( + "gorm.io/gorm" + "time" +) + +type ChessStatus struct { + gorm.Model + Players string `json:"players"` + Current string `json:"current"` + IsEnd bool `json:"is_end"` + Status string `json:"status"` + Winner string `json:"winner"` + ChessId string `json:"chess_id"` +} + +type ChessStatusLog struct { + gorm.Model + RoomId uint `json:"roomId"` + Current string `json:"current"` + Players string `json:"players"` + IsEnd bool `json:"is_end"` + Status string `json:"status"` + Winner string `json:"winner"` + ChessId string `json:"chess_id"` + Time time.Time `json:"time"` +} diff --git a/api_file/model/crontab.go b/api_file/model/crontab.go new file mode 100644 index 0000000..2be7620 --- /dev/null +++ b/api_file/model/crontab.go @@ -0,0 +1,9 @@ +package model + +import "gorm.io/gorm" + +type RunningCrontab struct { + Name string + CronId int + gorm.Model +} diff --git a/api_file/model/keys.go b/api_file/model/keys.go new file mode 100644 index 0000000..b23a4d3 --- /dev/null +++ b/api_file/model/keys.go @@ -0,0 +1,20 @@ +package model + +import "gorm.io/gorm" + +// DayKeys 每天对应key表 +type DayKeys struct { + Date string + Key string + AesKey string + User string + gorm.Model +} +type JwtKeys struct { + Username string + Date string + Key string + Token string + Updated bool `gorm:"default:false"` + gorm.Model +} diff --git a/api_file/model/logs.go b/api_file/model/logs.go new file mode 100644 index 0000000..bd0e743 --- /dev/null +++ b/api_file/model/logs.go @@ -0,0 +1,31 @@ +package model + +import "gorm.io/gorm" + +// Logs 日志表 +type Logs struct { + Method string `json:"method,omitempty"` + Path string `json:"path,omitempty"` + Status string `json:"status,omitempty"` + UserAgent string `json:"user_agent,omitempty"` + Time string `json:"time,omitempty"` + Location string `json:"location,omitempty"` + Ip string `json:"ip,omitempty"` + gorm.Model +} + +type LogFileDealLog struct { + Date string + Success bool + gorm.Model +} + +// SysLogs 系统容器日志表 +type SysLogs struct { + Time string `json:"time,omitempty"` + ContainerName string `json:"container_name,omitempty"` + ContainerImage string `json:"container_image,omitempty"` + Message string `json:"message,omitempty"` + Offset int `json:"offset,omitempty"` + gorm.Model +} diff --git a/api_file/model/menus.go b/api_file/model/menus.go new file mode 100644 index 0000000..11a5170 --- /dev/null +++ b/api_file/model/menus.go @@ -0,0 +1,14 @@ +package model + +import "gorm.io/gorm" + +type Menus struct { + MenuId string `json:"menu_id"` + Name string `json:"name"` + Icon string `json:"icon"` + Path string `json:"path"` + RouteOnly bool `json:"route_only"` + UserType string `json:"user_type"` + WhiteList string `json:"white_list"` + gorm.Model +} diff --git a/api_file/model/note.go b/api_file/model/note.go new file mode 100644 index 0000000..151bc92 --- /dev/null +++ b/api_file/model/note.go @@ -0,0 +1,9 @@ +package model + +import "gorm.io/gorm" + +type UserNotes struct { + Username string `json:"username"` + Content string `json:"content"` + gorm.Model +} diff --git a/api_file/model/sudoku.go b/api_file/model/sudoku.go new file mode 100644 index 0000000..ca3f3e0 --- /dev/null +++ b/api_file/model/sudoku.go @@ -0,0 +1,18 @@ +package model + +import "gorm.io/gorm" + +type Sudoku struct { + Sudoku string `json:"sudoku"` + Username string `json:"username"` + Result string `json:"result"` + gorm.Model +} + +type SudokuStatus struct { + SudokuId uint `json:"sudoku_id"` + Username string `json:"username"` + Status string `json:"status"` + Complete bool `json:"complete"` + gorm.Model +} diff --git a/api_file/model/sysError.go b/api_file/model/sysError.go new file mode 100644 index 0000000..0cc0806 --- /dev/null +++ b/api_file/model/sysError.go @@ -0,0 +1,14 @@ +package model + +import ( + "gorm.io/gorm" + "time" +) + +type SysError struct { + Username string `json:"username"` + Time time.Time `json:"time"` + Function string `json:"function"` + ErrorInfo string `json:"error_info"` + gorm.Model +} diff --git a/api_file/model/sysSettings.go b/api_file/model/sysSettings.go new file mode 100644 index 0000000..368b367 --- /dev/null +++ b/api_file/model/sysSettings.go @@ -0,0 +1,17 @@ +package model + +import "gorm.io/gorm" + +// SysIcons 系统图标库 +type SysIcons struct { + Icon string `json:"icon"` + gorm.Model +} + +type SysSettings struct { + Name string `json:"name"` + CnName string `json:"cn_name"` + Value string `json:"value"` + DType string `json:"d_type"` + gorm.Model +} diff --git a/api_file/model/sysinfo.go b/api_file/model/sysinfo.go new file mode 100644 index 0000000..bbded6a --- /dev/null +++ b/api_file/model/sysinfo.go @@ -0,0 +1,55 @@ +package model + +import "gorm.io/gorm" + +type SysInfo struct { + Date string `json:"date" gorm:"index:select_info"` + Datetime string `json:"datetime" gorm:"index:datetime_index"` + Username string `json:"username" gorm:"index:select_info"` + Hostname string `json:"hostname"` + Ip string `json:"ip" gorm:"index:select_info"` + SysVersion string `json:"sysVersion"` + PhysicalCount int `json:"physicalCount"` + LogicalCount int `json:"logicalCount"` + CpuPer float64 `json:"cpuPer"` + MemTotal string `json:"memTotal"` + MemUsed string `json:"memUsed"` + MemPer float64 `json:"memPer"` + DiskPoint string `json:"diskPoint"` + DiskTotal string `json:"diskTotal"` + DiskUsed string `json:"diskUsed"` + DiskPer string `json:"diskPer"` + NetSent int64 `json:"netSent"` + NetRec int64 `json:"netRec"` + SentSpeed string `json:"sentSpeed"` + RecSpeed string `json:"recSpeed"` + gorm.Model +} + +type SysInfoOld struct { + Datetime string `json:"datetime" gorm:"index:datetime_index"` + Username string `json:"username"` + Hostname string `json:"hostname"` + Ip string `json:"ip"` + SysVersion string `json:"sysVersion"` + PhysicalCount int `json:"physicalCount"` + LogicalCount int `json:"logicalCount"` + CpuPer float64 `json:"cpuPer"` + MemTotal string `json:"memTotal"` + MemUsed string `json:"memUsed"` + MemPer float64 `json:"memPer"` + DiskPoint string `json:"diskPoint"` + DiskTotal string `json:"diskTotal"` + DiskUsed string `json:"diskUsed"` + DiskPer string `json:"diskPer"` + NetSent int64 `json:"netSent"` + NetRec int64 `json:"netRec"` + SentSpeed string `json:"sentSpeed"` + RecSpeed string `json:"recSpeed"` +} + +type SysInfoUpdateLog struct { + Datetime string `json:"datetime"` + Update bool `json:"update"` + gorm.Model +} diff --git a/api_file/model/user.go b/api_file/model/user.go new file mode 100644 index 0000000..c47771c --- /dev/null +++ b/api_file/model/user.go @@ -0,0 +1,38 @@ +package model + +import "gorm.io/gorm" + +// User 用户登录信息表 +type User struct { + Username string + Password string + Date string + ConfirmCode string + gorm.Model +} + +// Userinfo 用户信息表 +type Userinfo struct { + Username string `json:"username"` + Avatar string `json:"avatar"` + Nickname string `json:"nickname"` + Mobile string `json:"mobile"` + Email string `json:"email"` + Location string `json:"location"` + Type string `json:"type"` + gorm.Model +} + +// UserAction 用户行为表 +type UserAction struct { + Username string + Action string + gorm.Model +} + +type UserAutoLogin struct { + Username string + DeviceId string + Location string + gorm.Model +} diff --git a/api_file/model/utils.go b/api_file/model/utils.go new file mode 100644 index 0000000..a52a5e3 --- /dev/null +++ b/api_file/model/utils.go @@ -0,0 +1,10 @@ +package model + +import "gorm.io/gorm" + +// IpsLocation ip转地址表 +type IpsLocation struct { + Ip string + Location string + gorm.Model +} diff --git a/api_file/model/weather.go b/api_file/model/weather.go new file mode 100644 index 0000000..52cd3ce --- /dev/null +++ b/api_file/model/weather.go @@ -0,0 +1,11 @@ +package model + +import "gorm.io/gorm" + +// Weather 每日天气表 +type Weather struct { + Date string + Location string + Weather string + gorm.Model +} diff --git a/api_file/service/api/file.go b/api_file/service/api/file.go new file mode 100644 index 0000000..1b7f2a2 --- /dev/null +++ b/api_file/service/api/file.go @@ -0,0 +1,319 @@ +package api + +import ( + "errors" + "fmt" + "github.com/kataras/iris/v12" + "github.com/sirupsen/logrus" + "main/database" + "main/model" + "main/utils" + "mime/multipart" + "os" + "path" + "path/filepath" + "strings" + "time" +) + +func getRootFile(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + userPath := fmt.Sprintf("./upload/%s", username.Username) + if !utils.FileIsExist(userPath) { + err := os.MkdirAll(userPath, 0755) + if utils.ErrHandle(ctx, err) { + return + } + } + var files filesRes + fileList, err := os.ReadDir(userPath) + if utils.ErrHandle(ctx, err) { + return + } + for _, file := range fileList { + if file.IsDir() { + files.Dirs = append(files.Dirs, file.Name()) + } else { + var item fileItem + item.Name = file.Name() + f, err := os.Stat(path.Join(userPath, file.Name())) + if utils.ErrHandle(ctx, err) { + return + } + item.Size = f.Size() + item.Type = utils.GetFileType(f.Name()) + files.Files = append(files.Files, item) + } + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", files)) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 获取指定目录下文件 +func getFile(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + filePath := ctx.Params().Get("path") + userPath := fmt.Sprintf("./upload/%s/%s", username.Username, filePath) + if !utils.FileIsExist(userPath) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New(userPath + ":目录不存在")) + return + } + var files filesRes + fileList, err := os.ReadDir(userPath) + if utils.ErrHandle(ctx, err) { + return + } + for _, file := range fileList { + if file.IsDir() { + files.Dirs = append(files.Dirs, file.Name()) + } else { + var item fileItem + item.Name = file.Name() + f, err := os.Stat(path.Join(userPath, file.Name())) + if utils.ErrHandle(ctx, err) { + return + } + item.Size = f.Size() + item.Type = utils.GetFileType(f.Name()) + files.Files = append(files.Files, item) + } + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", files)) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 删除文件或目录 +func deleteFile(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + filePath := ctx.Params().Get("path") + userPath := fmt.Sprintf("./upload/%s/%s", username.Username, filePath) + if !utils.FileIsExist(userPath) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("目录不存在")) + return + } + if info, _ := os.Stat(userPath); info.IsDir() { + err := os.RemoveAll(userPath) + if utils.ErrHandle(ctx, err) { + return + } + } else { + err := os.Remove(userPath) + if utils.ErrHandle(ctx, err) { + return + } + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 上传头像 +func uploadAvatar(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + file, info, err := ctx.FormFile("file") + if utils.ErrHandle(ctx, err) { + return + } + defer func(file multipart.File) { + err = file.Close() + if utils.ErrHandle(ctx, err) { + return + } + }(file) + userPath := fmt.Sprintf("./static/%s", username.Username) + if !utils.FileIsExist(userPath) { + err = os.MkdirAll(userPath, 0755) + if utils.ErrHandle(ctx, err) { + return + } + } + fileType := strings.Split(info.Filename, ".")[len(strings.Split(info.Filename, "."))-1] + avatarName := fmt.Sprintf("./static/%s/avatar-%s.%s", username.Username, time.Now().Format("2006-01-02"), fileType) + _, err = ctx.SaveFormFile(info, avatarName) + if utils.ErrHandle(ctx, err) { + return + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", avatarName[1:])) + if utils.ErrHandle(ctx, err) { + return + } +} + +func uploadDir(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + filePath := ctx.Params().Get("path") + userPath := fmt.Sprintf("./upload/%s/%s", username.Username, filePath) + if utils.FileIsExist(userPath) { + ctx.StatusCode(iris.StatusInternalServerError) + ctx.SetErr(errors.New("目录已存在")) + return + } + err := os.MkdirAll(userPath, 0755) + if utils.ErrHandle(ctx, err) { + return + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 上传文件 +func uploadFile(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + filePath := ctx.Params().Get("path") + file, info, err := ctx.FormFile("file") + if utils.ErrHandle(ctx, err) { + return + } + defer func(file multipart.File) { + err = file.Close() + if utils.ErrHandle(ctx, err) { + return + } + }(file) + userPath := fmt.Sprintf("./upload/%s/%s", username.Username, filePath) + if !utils.FileIsExist(userPath) { + err = os.MkdirAll(userPath, 0755) + if utils.ErrHandle(ctx, err) { + return + } + } + _, err = ctx.SaveFormFile(info, fmt.Sprintf("./upload/%s/%s/%s", username.Username, filePath, info.Filename)) + if utils.ErrHandle(ctx, err) { + return + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 文件下载,转到nginx-upload目录 +func downloadFile(ctx iris.Context) { + authToken := ctx.GetCookie("token") + activeTime := time.Now().Add(-2 * time.Hour) + var userToken model.JwtKeys + db := database.GetInstance().GetMysqlDb() + if err := db.Where("token = ? and created_at >= ?", authToken, activeTime).First(&userToken).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if utils.DataIsNil(userToken.Username) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("未登录")) + return + } + filePath := ctx.Params().Get("path") + userPath := fmt.Sprintf("./upload/%s/%s", userToken.Username, filePath) + if !utils.FileIsExist(userPath) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("文件不存在")) + return + } + if info, _ := os.Stat(userPath); info.IsDir() { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("只可下载文件")) + return + } + ctx.Recorder().Header().Add("X-Accel-Redirect", fmt.Sprintf("/upload/%s/%s", userToken.Username, filePath)) + ctx.Recorder().Header().Add("X-Accel-Charset", "utf-8") + ctx.Recorder().Header().Add("Content-Disposition", "attachment") + ctx.Recorder().Header().Add("Content-Type", "application/octet-stream; charset=utf-8") + return +} + +func getDownloadFileType(ctx iris.Context) { + authToken := ctx.GetCookie("token") + activeTime := time.Now().Add(-2 * time.Hour) + var userToken model.JwtKeys + var res videoM3u8 + db := database.GetInstance().GetMysqlDb() + if err := db.Where("token = ? and created_at >= ?", authToken, activeTime).First(&userToken).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if utils.DataIsNil(userToken.Username) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("未登录")) + return + } + username := userToken.Username + filePath := ctx.Params().Get("path") + userPath := fmt.Sprintf("./upload/%s/%s", username, filePath) + currentPath, _ := filepath.Split(filePath) + filename := strings.TrimSuffix(path.Base(userPath), path.Ext(userPath)) + res.Video = utils.GetFileType(userPath) == "video" + res.M3u8 = utils.FileIsExist(fmt.Sprintf("./upload-video/%s/%s%s/%s.m3u8", username, currentPath, filename, filename)) + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", res)) + if err != nil { + return + } + return +} + +func downloadVideo(ctx iris.Context) { + authToken := ctx.GetCookie("token") + activeTime := time.Now().Add(-2 * time.Hour) + var userToken model.JwtKeys + db := database.GetInstance().GetMysqlDb() + if err := db.Where("token = ? and created_at >= ?", authToken, activeTime).First(&userToken).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if utils.DataIsNil(userToken.Username) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("未登录")) + return + } + username := userToken.Username + filePath := ctx.Params().Get("path") + userPath := fmt.Sprintf("./upload/%s/%s", username, filePath) + currentPath, _ := filepath.Split(filePath) + filename := strings.TrimSuffix(path.Base(userPath), path.Ext(userPath)) + //fmt.Println(filePath, currentPath, filename) + if path.Ext(userPath) != ".ts" && !utils.FileIsExist(userPath) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("文件不存在")) + return + } + if info, err := os.Stat(userPath); err == nil && info.IsDir() { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("只可下载文件")) + return + } + if utils.GetFileType(userPath) == "video" && utils.FileIsExist(fmt.Sprintf("./upload-video/%s/%s%s/%s.m3u8", username, currentPath, filename, filename)) { + ctx.Recorder().Header().Add("X-Accel-Redirect", fmt.Sprintf("/upload-video/%s/%s%s/%s.m3u8", username, currentPath, filename, filename)) + } else if utils.GetFileType(userPath) == "video" { + ctx.Recorder().Header().Add("X-Accel-Redirect", fmt.Sprintf("/upload/%s/%s", username, filePath)) + } else { + tsPath := fmt.Sprintf("./upload-video/%s/%s%s/%s.ts", username, currentPath, filename[:len(filename)-6], filename) + ctx.Recorder().Header().Add("X-Accel-Redirect", tsPath[1:]) + } + ctx.Recorder().Header().Add("X-Accel-Charset", "utf-8") + ctx.Recorder().Header().Add("Content-Disposition", "attachment") + ctx.Recorder().Header().Add("Content-Type", "application/octet-stream; charset=utf-8") + return +} diff --git a/api_file/service/api/fileExport.go b/api_file/service/api/fileExport.go new file mode 100644 index 0000000..b131acb --- /dev/null +++ b/api_file/service/api/fileExport.go @@ -0,0 +1,17 @@ +package api + +import ( + "github.com/kataras/iris/v12" +) + +func File(party iris.Party) { + party.Get("/upload", getRootFile) + party.Get("/upload/{path:path}", getFile) + party.Post("/upload/{path:path}", uploadDir) + party.Post("/upload-file/{path:path}", uploadFile) + party.Delete("/upload/{path:path}", deleteFile) + party.Post("/static/avatar", uploadAvatar) + party.Get("/download/{path:path}", downloadFile) + party.Get("/download-video-check/{path:path}", getDownloadFileType) + party.Get("/download-video/{path:path}", downloadVideo) +} diff --git a/api_file/service/api/init.go b/api_file/service/api/init.go new file mode 100644 index 0000000..617d4b0 --- /dev/null +++ b/api_file/service/api/init.go @@ -0,0 +1,13 @@ +package api + +import ( + "github.com/kataras/iris/v12" + "main/utils" +) + +func Apis(ctx iris.Context) { + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", "allFileApis")) + if err != nil { + return + } +} diff --git a/api_file/service/api/type.d.go b/api_file/service/api/type.d.go new file mode 100644 index 0000000..6716592 --- /dev/null +++ b/api_file/service/api/type.d.go @@ -0,0 +1,172 @@ +package api + +import "time" + +// user----------------------------------------------------------------------------------------------- +type result struct { + Date string + Password string + ConfirmCode string + UpdatedAt time.Time + Key string + AesKey string +} + +// weather--------------------------------------------------------------------------------------------- +type resWeather struct { + Status int `json:"status"` + Message string `json:"message"` + Data resData `json:"data"` +} +type resData struct { + Forecast24h resDate `json:"forecast_24h"` +} +type resDate struct { + D0 res24h `json:"1"` + D1 res24h `json:"2"` +} +type res24h struct { + Time string `json:"time"` + MaxDegree string `json:"max_degree"` + MinDegree string `json:"min_degree"` + DayWeather string `json:"day_weather"` + DayWindDirection string `json:"day_wind_direction"` + DayWindPower string `json:"day_wind_power"` + NightWeather string `json:"night_weather"` + NightWindPower string `json:"night_wind_power"` + NightWindDirection string `json:"night_wind_direction"` +} +type resLocation struct { + Data map[string]string `json:"data"` + Message string `json:"message"` + Status int `json:"status"` +} + +// backgammon----------------------------------------------------------------------------------------------- +type typeRoomStatus struct { + RoomId int `json:"room_id"` + Player string `json:"player"` +} + +// file----------------------------------------------------------------------------------------------------- +type filesRes struct { + Dirs []string `json:"dirs"` + Files []fileItem `json:"files"` +} +type fileItem struct { + Name string `json:"name"` + Type string `json:"type"` + Size int64 `json:"size"` +} +type videoM3u8 struct { + Video bool `json:"video"` + M3u8 bool `json:"m3u8"` +} + +// notes------------------------------------------------------------------------------------------------------ +type noteParam struct { + ID uint `json:"id"` + Content string `json:"content"` +} + +// sysInfo------------------------------------------------------------------------------------------------------ +type resSysInfo struct { + Datetime string `json:"datetime"` + CpuPer float64 `json:"cpu_per"` + Mem resMem `json:"mem"` + Disk []resDisk `json:"disk"` + Net resNet `json:"net"` +} +type resMem struct { + MemTotal string `json:"mem_total"` + MemUsed string `json:"mem_used"` + MemPer float64 `json:"mem_per"` +} +type resDisk struct { + Point string `json:"point"` + Total string `json:"total"` + Used string `json:"used"` + Per float64 `json:"per"` +} +type resNet struct { + Sent string `json:"sent"` + Rec string `json:"rec"` +} +type resSystem struct { + Hostname string `json:"hostname"` + Ip string `json:"ip"` +} +type sysInfoIDs struct { + IDs []int `json:"ids"` +} + +// yeb---------------------------------------------------------------------------------------------------------- +type paramsBalance struct { + Card string `json:"card"` + Type bool `json:"type"` + Balance float64 `json:"balance"` +} + +type paramSZBalance struct { + Card string `json:"card"` + Type bool `json:"type"` // 1-支出;0-收入 + Amount float64 `json:"amount"` +} + +// 月余额 +type dateLog struct { + Date string `json:"date"` + Duration string `json:"duration"` + Changes float64 `json:"changes"` + Balance float64 `json:"balance"` + Detail []cardLog `json:"detail"` +} + +// 详情 +type cardLog struct { + Card string `json:"card"` + Balance float64 `json:"balance"` + Changes float64 `json:"changes"` +} + +// menus----------------------------------------------------------------------------- +type resMenu struct { + MenuId string `json:"menu_id"` + Name string `json:"name"` + Icon string `json:"icon"` + Path string `json:"path"` + RouteOnly bool `json:"route_only"` + Detail []subMenu `json:"detail"` +} + +type subMenu struct { + MenuId string `json:"menu_id"` + Name string `json:"name"` + Icon string `json:"icon"` + Path string `json:"path"` + RouteOnly bool `json:"route_only"` +} + +type sysIconsParam struct { + Icons string `json:"icons"` +} + +// sudoku----------------------------------------------------------------------------- +type resSudokuListType struct { + New []sudokuListType `json:"new"` + Starting []sudokuListType `json:"starting"` + Complete []sudokuListType `json:"complete"` +} + +type sudokuListType struct { + Sudoku string `json:"sudoku"` + SudokuId uint `json:"sudokuId"` + Username string `json:"username"` + Status string `json:"status"` + Complete bool `json:"complete"` + Id uint `json:"id"` +} +type addSudokuParam struct { + Sudoku string `json:"sudoku"` + Result string `json:"result"` +} diff --git a/api_file/service/init.go b/api_file/service/init.go new file mode 100644 index 0000000..94e5660 --- /dev/null +++ b/api_file/service/init.go @@ -0,0 +1,11 @@ +package service + +import ( + "github.com/kataras/iris/v12" + "main/service/api" +) + +func Apis(p iris.Party) { + p.Get("/", api.Apis) + p.PartyFunc("/file", api.File) +} diff --git a/api_file/utils/sysSettings.go b/api_file/utils/sysSettings.go new file mode 100644 index 0000000..bd34138 --- /dev/null +++ b/api_file/utils/sysSettings.go @@ -0,0 +1,17 @@ +package utils + +import ( + "github.com/sirupsen/logrus" + "main/database" + "main/model" +) + +var SysSettings []model.SysSettings + +func UpdateSysSettings() { + db := database.GetInstance().GetMysqlDb() + if err := db.Order("name").Find(&SysSettings).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + logrus.Infoln("更新系统配置列表") +} diff --git a/api_file/utils/type.d.go b/api_file/utils/type.d.go new file mode 100644 index 0000000..89d51cf --- /dev/null +++ b/api_file/utils/type.d.go @@ -0,0 +1,24 @@ +package utils + +// ResponseBean result结构 +type ResponseBean struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data interface{} `json:"data"` +} + +type ResApiUtils struct { + Flag bool `json:"flag"` + Data interface{} `json:"data"` + Msg string `json:"msg"` +} + +// ResSudoku sudoku -------------------------------------------------------------------- +type ResSudoku struct { + Flag bool `json:"flag"` + Data resSudokuData +} +type resSudokuData struct { + Check bool `json:"check"` + Result string `json:"result"` +} diff --git a/api_file/utils/user.go b/api_file/utils/user.go new file mode 100644 index 0000000..6cc203f --- /dev/null +++ b/api_file/utils/user.go @@ -0,0 +1,73 @@ +package utils + +import ( + "errors" + "fmt" + "github.com/kataras/iris/v12" + "github.com/sirupsen/logrus" + "main/database" + "main/model" + "strings" +) + +var UserList []model.Userinfo + +// GetLoginUser 根据token获取用户,返回用户信息 +func GetLoginUser(ctx iris.Context) model.Userinfo { + auth := ctx.GetHeader("Authorization") + fmt.Println(auth) + if auth == "" || !strings.Contains(auth, "Bearer") { + ctx.StatusCode(iris.StatusUnauthorized) + ctx.SetErr(errors.New("未登录")) + logrus.Warningln("请求未携带token信息") + return model.Userinfo{} + } + var tokens []model.JwtKeys + db := database.GetInstance().GetMysqlDb() + fmt.Println(strings.Split(auth, " ")[1]) + if err := db.Where("token = ?", strings.Split(auth, " ")[1]).Find(&tokens).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if len(tokens) == 0 { + ctx.StatusCode(iris.StatusUnauthorized) + ctx.SetErr(errors.New("未登录")) + logrus.Warningln(auth, "token信息无效") + return model.Userinfo{} + } else { + return GetUserInfo(tokens[0].Username) + } + //foobar := auth.Claims.(jwt.MapClaims) + //for key, value := range foobar { + // if key == "username" { + // return GetUserInfo(value.(string)) + // } + //} + //ctx.StatusCode(iris.StatusInternalServerError) + //ctx.SetErr(errors.New("系统错误,请联系管理员")) + //logrus.Errorln("token存在但获取用户信息失败") + //return model.Userinfo{} +} + +// GetUserInfo 根据用户名获取用户信息,先更新本地数据 +func GetUserInfo(username string) model.Userinfo { + if len(UserList) == 0 { + logrus.Warnln("暂存用户列表为空,刷新数据") + UpdateUserInfo() + } + for _, u := range UserList { + if u.Username == username { + return u + } + } + logrus.Warnln("未找到对应的用户信息") + return model.Userinfo{} +} + +// UpdateUserInfo 更新当前用户信息 +func UpdateUserInfo() { + db := database.GetInstance().GetMysqlDb() + if err := db.Find(&UserList).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + logrus.Infoln("刷新暂存用户列表") +} diff --git a/api_file/utils/utils.go b/api_file/utils/utils.go new file mode 100644 index 0000000..0915ba0 --- /dev/null +++ b/api_file/utils/utils.go @@ -0,0 +1,235 @@ +package utils + +import ( + "bufio" + "encoding/json" + "fmt" + "github.com/kataras/iris/v12" + "github.com/shopspring/decimal" + "github.com/sirupsen/logrus" + "io" + "main/database" + "main/model" + "math/rand" + "net/http" + "net/url" + "os" + "path" + "reflect" + "regexp" + "runtime" + "strings" + "time" + "unsafe" +) + +// FormatRes 格式化result +func FormatRes(code int, msg string, data interface{}) ResponseBean { + return ResponseBean{ + Code: code, + Msg: msg, + Data: data, + } +} + +// GetEnvDefault 获取带默认值环境变量 +func GetEnvDefault(name string, defaultVal string) string { + val, ok := os.LookupEnv(name) + if ok { + return val + } else { + return defaultVal + } +} + +// FileRead 读取文件,返回行列表 +func FileRead(file string, condition bool) ([]string, error) { + f, err := os.Open(file) + var res []string + if err != nil { + return []string{}, err + } + defer func(f *os.File) { + err = f.Close() + if err != nil { + return + } + }(f) + reader := bufio.NewReader(f) + for { + line, _, err := reader.ReadLine() + if err != nil { + break + } + if condition && len(line) > 1 { + res = append(res, string(line)) + } + } + return res, nil +} + +func SendEmail(receiver []string, subject string, content string, test bool) error { + var emailUrl string + if !test { + emailUrl = fmt.Sprintf("https://git-ylsa0.cn/api_django/send_email/?subject=%s&receivers=%s&content=%s", url.QueryEscape(subject), strings.Join(receiver, ";"), url.QueryEscape(content)) + } else { + emailUrl = fmt.Sprintf("http://localhost:8000/api/send_email/?subject=%s&receivers=%s&content=%s", url.QueryEscape(subject), strings.Join(receiver, ";"), url.QueryEscape(content)) + } + resp, err := http.Get(emailUrl) + fmt.Println(emailUrl) + if err != nil { + logrus.Errorln("连接send_email接口失败:", err) + return err + } + defer func(Body io.ReadCloser) { + err = Body.Close() + if err != nil { + } + }(resp.Body) + body, _ := io.ReadAll(resp.Body) + var r ResApiUtils + //解析json结构 + err = json.Unmarshal(body, &r) + if err != nil { + logrus.Errorln("send_email接口结果json解析失败:", err) + return err + } + return nil +} + +// DataIsNil 判断数据是否为空 +func DataIsNil(arg interface{}) bool { + if reflect.ValueOf(arg).Kind().String() == "ptr" || reflect.ValueOf(arg).Kind().String() == "slice" { + if reflect.ValueOf(arg).IsValid() { + return true + } + } else { + if reflect.ValueOf(arg).IsZero() { + return true + } + } + return false +} + +// ErrHandle 错误处理 +func ErrHandle(ctx iris.Context, err error) bool { + if err != nil { + pc, _, _, _ := runtime.Caller(1) + f := runtime.FuncForPC(pc).Name() + db := database.GetInstance().GetMysqlDb() + if err1 := db.Create(&model.SysError{ + Username: GetLoginUser(ctx).Username, + Time: time.Now(), + Function: f, + ErrorInfo: err.Error(), + }); err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + ctx.StatusCode(iris.StatusInternalServerError) + ctx.SetErr(err) + return true + } + return false +} + +// NewKey 取n位随机数 +func NewKey(n int) string { + const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890()-+*/~" + var src = rand.NewSource(time.Now().UnixNano()) + const ( + // 6 bits to represent a letter index + letterIdBits = 6 + // All 1-bits as many as letterIdBits + letterIdMask = 1<= 0; { + if remain == 0 { + cache, remain = src.Int63(), letterIdMax + } + if idx := int(cache & letterIdMask); idx < len(letters) { + b[i] = letters[idx] + i-- + } + cache >>= letterIdBits + remain-- + } + return *(*string)(unsafe.Pointer(&b)) +} + +// CheckListItem 判断item是否在list内 +func CheckListItem[T comparable](data []T, item T) bool { + for _, value := range data { + if value == item { + return true + } + } + return false +} + +// FileIsExist 判断路径是否存在 +func FileIsExist(path string) bool { + if _, err := os.Stat(path); os.IsNotExist(err) { + return false + } + return true +} + +// Round float取小数 +func Round(num float64, n int32) float64 { + res, _ := decimal.NewFromFloat(num).Round(n).Float64() + return res +} + +// GetFileType 获取文件类型 +func GetFileType(f string) string { + fileSuffix := path.Ext(f) + fileSuffix = strings.ToLower(fileSuffix) + patternFileSuffix := fmt.Sprintf("^%s,|,%s,|,%s$|%s", fileSuffix, fileSuffix, fileSuffix, fileSuffix) + regFileSuffix := regexp.MustCompile(patternFileSuffix) + for _, v := range SysSettings { + if strings.HasPrefix(v.Name, "fileType:") && regFileSuffix.MatchString(v.Value) { + return strings.Split(v.Name, "fileType:")[1] + } + } + //switch fileSuffix { + //case ".png", ".jpg", ".jpeg", ".bmp", ".gif": + // return "image" + //case ".mp4", ".m2v", ".mkv", ".rmvb", ".avi", ".flv", ".mov", ".m4v", ".wmv", ".f4v": + // return "video" + //case ".mp3", ".wav", ".flac": + // return "audio" + //case ".zip", ".rar", ".7z": + // return "zip" + //} + return "doc" +} + +// IntAbs 数字取绝对值 +func IntAbs(i int) int { + if i < 0 { + return -i + } else { + return i + } +} + +// Reverse 字符串反转 +func Reverse(s string) string { + a := []rune(s) + for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 { + a[i], a[j] = a[j], a[i] + } + return string(a) +} + +func GetRequestIp(ctx iris.Context) string { + realIp := ctx.Request().Header.Get("X-Real-IP") + if realIp != "" { + return realIp + } + ip := ctx.RemoteAddr() + return ip +} diff --git a/api_iris/Dockerfile b/api_iris/Dockerfile new file mode 100644 index 0000000..fd30816 --- /dev/null +++ b/api_iris/Dockerfile @@ -0,0 +1,15 @@ +FROM golang:1.22.4 + +RUN mkdir -p /web \ + && mkdir /web/logs + +ENV GOPATH=/web +ENV GOPROXY=https://goproxy.cn,direct + +WORKDIR $GOPATH/api_iris + +COPY . $GOPATH/api_iris + +RUN go build . + +ENTRYPOINT ["./main"] \ No newline at end of file diff --git a/api_iris/config/config.dev.yaml b/api_iris/config/config.dev.yaml new file mode 100644 index 0000000..48d0a38 --- /dev/null +++ b/api_iris/config/config.dev.yaml @@ -0,0 +1,15 @@ +database: + dsn: web:Song1875.@tcp(43.154.168.226:3306)/web_vue?charset=utf8mb4&parseTime=True&loc=Local + prd: false + +logs: + log: logs + nginx: logs/nginx + +sys: + jwt: False + User: admin + +email: + username: 15822909392@163.com + password: RXWDMRGRNTZBNAML diff --git a/api_iris/config/config.go b/api_iris/config/config.go new file mode 100644 index 0000000..c9daa56 --- /dev/null +++ b/api_iris/config/config.go @@ -0,0 +1,31 @@ +package config + +import ( + "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" + "os" +) + +// Config 公共参数 +var Config settings + +// InitConfig 配置初始化 +func InitConfig(version string) error { + var file []byte + var err error + if version == "dev" { + file, err = os.ReadFile("./config/config.dev.yaml") + } else { + file, err = os.ReadFile("./config/config.prd.yaml") + } + if err != nil { + logrus.Errorln("Error reading config file:", err) + return err + } + err = yaml.Unmarshal(file, &Config) + if err != nil { + logrus.Errorln("Error parsing config file:", err) + return err + } + return nil +} diff --git a/api_iris/config/config.prd.yaml b/api_iris/config/config.prd.yaml new file mode 100644 index 0000000..3f993b3 --- /dev/null +++ b/api_iris/config/config.prd.yaml @@ -0,0 +1,15 @@ +database: + dsn: web_prd:SongPrd1875.@tcp(43.154.168.226:3306)/web_prd?charset=utf8mb4&parseTime=True&loc=Local + prd: true + +logs: + log: logs + nginx: logs/nginx + +sys: + jwt: True + User: + +email: + username: 15822909392@163.com + password: RXWDMRGRNTZBNAML diff --git a/api_iris/config/type.d.go b/api_iris/config/type.d.go new file mode 100644 index 0000000..1a437cc --- /dev/null +++ b/api_iris/config/type.d.go @@ -0,0 +1,22 @@ +package config + +type settings struct { + Logs *logs `yaml:"logs"` + Database *database `yaml:"database"` + Email *email `yaml:"email"` +} + +type logs struct { + Nginx string `yaml:"nginx"` + Log string `yaml:"log"` +} + +type database struct { + Dsn string `yaml:"dsn"` + Prd bool `yaml:"prd"` +} + +type email struct { + Username string `yaml:"username"` + Password string `yaml:"password"` +} diff --git a/api_iris/crontab/init.go b/api_iris/crontab/init.go new file mode 100644 index 0000000..a72b89e --- /dev/null +++ b/api_iris/crontab/init.go @@ -0,0 +1,40 @@ +package crontab + +import ( + "github.com/robfig/cron/v3" + "github.com/sirupsen/logrus" + "main/database" + "main/model" +) + +var MyCron *cron.Cron + +func InitAllCron() { + MyCron = cron.New(cron.WithSeconds()) + MyCron.Start() + db := database.GetInstance().GetMysqlDb() + var runningCron []model.RunningCrontab + if err := db.Where("deleted_at is null").Delete(&runningCron).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + return + } +} + +func UpdateCronDb(id int, name string, status bool) { + db := database.GetInstance().GetMysqlDb() + var runningCron model.RunningCrontab + if status { + if err := db.Create(&model.RunningCrontab{ + Name: name, + CronId: id, + }).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + return + } + } else { + if err := db.Delete(&runningCron, "cron_id = ?", id).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + return + } + } +} diff --git a/api_iris/crontab/nginxLog.go b/api_iris/crontab/nginxLog.go new file mode 100644 index 0000000..7d22f50 --- /dev/null +++ b/api_iris/crontab/nginxLog.go @@ -0,0 +1,70 @@ +package crontab + +import ( + "fmt" + "github.com/sirupsen/logrus" + "main/config" + "main/database" + "main/model" + "main/service/admin" + "main/utils" + "os" + "path" + "strings" + "time" +) + +// CornSaveNginxLog 定时任务处理nginx日志 +func CornSaveNginxLog() { + cPath, _ := os.Getwd() + logPath := path.Join(cPath, config.Config.Logs.Nginx) + fileList, _ := os.ReadDir(logPath) + db := database.GetInstance().GetMysqlDb() + var logDealList []model.LogFileDealLog + var resLogList []model.Logs + if err := db.Find(&logDealList).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + return + } + for _, file := range fileList { + if strings.Contains(file.Name(), "access-") { + date := strings.Split(strings.Split(file.Name(), "access-")[1], ".")[0] + if getDateDealBool(date, logDealList) || date == time.Now().Format("2006-01-02") { + continue + } + logrus.Infoln(date, ":nginx日志开始处理") + if err := db.Where("time like ?", fmt.Sprintf("%s%%", date)).Delete(&resLogList).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + return + } + logList, _ := utils.FileRead(path.Join(logPath, file.Name()), true) + for _, v := range logList { + c := make(chan model.Logs) + go admin.FormatLog(v, date, c) + resLogItem := <-c + if err := db.Create(&resLogItem).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + return + } + } + if err := db.Create(&model.LogFileDealLog{ + Date: date, + Success: true, + }).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + return + } + logrus.Infoln(date, ":nginx日志处理完成") + } + } +} + +// 获取当前日期是否已处理 +func getDateDealBool(date string, dealLog []model.LogFileDealLog) bool { + for _, v := range dealLog { + if v.Date == date && v.Success { + return true + } + } + return false +} diff --git a/api_iris/crontab/type.d.go b/api_iris/crontab/type.d.go new file mode 100644 index 0000000..c1d9b22 --- /dev/null +++ b/api_iris/crontab/type.d.go @@ -0,0 +1,5 @@ +package crontab + +type sysLogDateType struct { + Datetime string `json:"datetime"` +} diff --git a/api_iris/database/database.go b/api_iris/database/database.go new file mode 100644 index 0000000..5d22ef7 --- /dev/null +++ b/api_iris/database/database.go @@ -0,0 +1,85 @@ +package database + +import ( + "github.com/sirupsen/logrus" + "gorm.io/driver/mysql" + "gorm.io/gorm" + "gorm.io/gorm/logger" + "main/config" + "main/model" + "sync" +) + +var instance *MysqlConnectionPool +var once sync.Once +var db *gorm.DB +var err error + +// GetInstance 获取数据库连接池 +func GetInstance() *MysqlConnectionPool { + once.Do(func() { + instance = &MysqlConnectionPool{} + }) + return instance +} + +// InitDataPool 数据库初始化连接 +func (m *MysqlConnectionPool) InitDataPool() (isSuccess bool) { + dbConfig := config.Config.Database + if dbConfig.Prd { + db, err = gorm.Open(mysql.Open(dbConfig.Dsn), &gorm.Config{}) + } else { + db, err = gorm.Open(mysql.Open(dbConfig.Dsn), &gorm.Config{ + Logger: logger.Default.LogMode(logger.Info), + }) + } + if err != nil { + logrus.Errorln("数据库配置失败:", err) + return false + } + return true +} + +// GetMysqlDb 获取数据库连接 +func (m *MysqlConnectionPool) GetMysqlDb() (dbCon *gorm.DB) { + return db +} + +// UpdateDbStruct 数据库数据结构更新 +func UpdateDbStruct() error { + err = db.AutoMigrate( + &model.Logs{}, + &model.Weather{}, + &model.DayKeys{}, + &model.JwtKeys{}, + &model.IpsLocation{}, + &model.User{}, + &model.Userinfo{}, + &model.UserAction{}, + &model.UserAutoLogin{}, + &model.LogFileDealLog{}, + &model.RunningCrontab{}, + &model.Balances{}, + &model.BalanceLogs{}, + &model.UserNotes{}, + &model.BackgammonRoom{}, + &model.SysInfo{}, + &model.SysInfoUpdateLog{}, + &model.ChessStatus{}, + &model.ChessStatusLog{}, + &model.Menus{}, + &model.SysIcons{}, + &model.SysSettings{}, + &model.SysError{}, + &model.Sudoku{}, + &model.SudokuStatus{}, + &model.SysLogs{}, + ) + if err != nil { + logrus.Errorln("数据库自动迁移失败:", err) + } + return err +} + +type MysqlConnectionPool struct { +} diff --git a/api_iris/go.mod b/api_iris/go.mod new file mode 100644 index 0000000..40c1c44 --- /dev/null +++ b/api_iris/go.mod @@ -0,0 +1,77 @@ +module main + +go 1.22.4 + +require ( + github.com/golang-jwt/jwt/v4 v4.5.1 + github.com/gorilla/websocket v1.5.3 + github.com/iris-contrib/middleware/jwt v0.0.0-20240502084239-34f27409ce72 + github.com/jakehl/goid v1.1.0 + github.com/kataras/iris/v12 v12.2.11 + github.com/kataras/neffos v0.0.24-0.20240408172741-99c879ba0ede + github.com/robfig/cron/v3 v3.0.1 + github.com/shopspring/decimal v1.4.0 + gopkg.in/yaml.v3 v3.0.1 + gorm.io/driver/mysql v1.5.7 + gorm.io/gorm v1.25.10 +) + +require ( + github.com/BurntSushi/toml v1.3.2 // indirect + github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect + github.com/CloudyKit/jet/v6 v6.2.0 // indirect + github.com/Joker/jade v1.1.3 // indirect + github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 // indirect + github.com/andybalholm/brotli v1.1.0 // indirect + github.com/antonfisher/nested-logrus-formatter v1.3.1 // indirect + github.com/aymerick/douceur v0.2.0 // indirect + github.com/fatih/structs v1.1.0 // indirect + github.com/flosch/pongo2/v4 v4.0.2 // indirect + github.com/go-sql-driver/mysql v1.7.0 // indirect + github.com/gobwas/httphead v0.1.0 // indirect + github.com/gobwas/pool v0.2.1 // indirect + github.com/gobwas/ws v1.3.2 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/css v1.0.0 // indirect + github.com/iris-contrib/schema v0.0.6 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kataras/blocks v0.0.8 // indirect + github.com/kataras/golog v0.1.11 // indirect + github.com/kataras/pio v0.0.13 // indirect + github.com/kataras/sitemap v0.0.6 // indirect + github.com/kataras/tunnel v0.0.4 // indirect + github.com/klauspost/compress v1.17.7 // indirect + github.com/mailgun/raymond/v2 v2.0.48 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mediocregopher/radix/v3 v3.8.1 // indirect + github.com/microcosm-cc/bluemonday v1.0.26 // indirect + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/nats-io/nats.go v1.34.1 // indirect + github.com/nats-io/nkeys v0.4.7 // indirect + github.com/nats-io/nuid v1.0.1 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/schollz/closestmatch v2.1.0+incompatible // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/tdewolff/minify/v2 v2.20.19 // indirect + github.com/tdewolff/parse/v2 v2.7.12 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + github.com/yosssi/ace v0.0.5 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect +) diff --git a/api_iris/go.sum b/api_iris/go.sum new file mode 100644 index 0000000..ed3f525 --- /dev/null +++ b/api_iris/go.sum @@ -0,0 +1,234 @@ +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c= +github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= +github.com/CloudyKit/jet/v6 v6.2.0 h1:EpcZ6SR9n28BUGtNJSvlBqf90IpjeFr36Tizxhn/oME= +github.com/CloudyKit/jet/v6 v6.2.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4= +github.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc= +github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= +github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk= +github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM= +github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 h1:KkH3I3sJuOLP3TjA/dfr4NAY8bghDwnXiU7cTKxQqo0= +github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjKLwalezA0k99cWs5L11HWOAPNjdUZ6RxH1BXbbM= +github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= +github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/antonfisher/nested-logrus-formatter v1.3.1 h1:NFJIr+pzwv5QLHTPyKz9UMEoHck02Q9L0FP13b/xSbQ= +github.com/antonfisher/nested-logrus-formatter v1.3.1/go.mod h1:6WTfyWFkBc9+zyBaKIqRrg/KwMqBbodBjgbHjDz7zjA= +github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= +github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= +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/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw= +github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8= +github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.3.2 h1:zlnbNHxumkRvfPWgfXu8RBwyNR1x8wh9cf5PTOCqs9Q= +github.com/gobwas/ws v1.3.2/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= +github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= +github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0 h1:4gjrh/PN2MuWCCElk8/I4OCKRKWCCo2zEct3VKCbibU= +github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= +github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk= +github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= +github.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go= +github.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE= +github.com/iris-contrib/middleware/jwt v0.0.0-20240502084239-34f27409ce72 h1:wbkA/QXv1RZmuY2iLvsgmHeiGq4DNvv2Rhj5PQ2oHTI= +github.com/iris-contrib/middleware/jwt v0.0.0-20240502084239-34f27409ce72/go.mod h1:hIyBTK1zUxUaC4Hu8Ba9Z70r2S5ET4+ZKNescqjXld0= +github.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw= +github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA= +github.com/jakehl/goid v1.1.0 h1:c08GO8z16wWJtfQhyiD8BQRFkpf1oDxaPmA+uYAG+50= +github.com/jakehl/goid v1.1.0/go.mod h1:V6bQh+tr2Oay5WHL0jmTTJWrABYIO+cs4/P6e1prV1o= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA= +github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM= +github.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg= +github.com/kataras/golog v0.1.11 h1:dGkcCVsIpqiAMWTlebn/ZULHxFvfG4K43LF1cNWSh20= +github.com/kataras/golog v0.1.11/go.mod h1:mAkt1vbPowFUuUGvexyQ5NFW6djEgGyxQBIARJ0AH4A= +github.com/kataras/iris/v12 v12.2.11 h1:sGgo43rMPfzDft8rjVhPs6L3qDJy3TbBrMD/zGL1pzk= +github.com/kataras/iris/v12 v12.2.11/go.mod h1:uMAeX8OqG9vqdhyrIPv8Lajo/wXTtAF43wchP9WHt2w= +github.com/kataras/neffos v0.0.24-0.20240408172741-99c879ba0ede h1:ZnSJQ+ri9x46Yz15wHqSb93Q03yY12XMVLUFDJJ0+/g= +github.com/kataras/neffos v0.0.24-0.20240408172741-99c879ba0ede/go.mod h1:i0dtcTbpnw1lqIbojYtGtZlu6gDWPxJ4Xl2eJ6oQ1bE= +github.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM= +github.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM= +github.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY= +github.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4= +github.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA= +github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw= +github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= +github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw= +github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mediocregopher/radix/v3 v3.8.1 h1:rOkHflVuulFKlwsLY01/M2cM2tWCjDoETcMqKbAWu1M= +github.com/mediocregopher/radix/v3 v3.8.1/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= +github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58= +github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/nats-io/nats.go v1.34.1 h1:syWey5xaNHZgicYBemv0nohUPPmaLteiBEUT6Q5+F/4= +github.com/nats-io/nats.go v1.34.1/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= +github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= +github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo= +github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U= +github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk= +github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= +github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tdewolff/minify/v2 v2.20.19 h1:tX0SR0LUrIqGoLjXnkIzRSIbKJ7PaNnSENLD4CyH6Xo= +github.com/tdewolff/minify/v2 v2.20.19/go.mod h1:ulkFoeAVWMLEyjuDz1ZIWOA31g5aWOawCFRp9R/MudM= +github.com/tdewolff/parse/v2 v2.7.12 h1:tgavkHc2ZDEQVKy1oWxwIyh5bP4F5fEh/JmBwPP/3LQ= +github.com/tdewolff/parse/v2 v2.7.12/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA= +github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= +github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03u/dMQK9g+Iw9ewps4mCl1nB8Sscbo= +github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY= +github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= +github.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA= +github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0= +github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA= +github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 h1:985EYyeCOxTpcgOTJpflJUwOeEz0CQOdPt73OzpE9F8= +golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= +gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= +gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= +gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +moul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs= +moul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE= diff --git a/api_iris/jwtSet/jwt.go b/api_iris/jwtSet/jwt.go new file mode 100644 index 0000000..1785811 --- /dev/null +++ b/api_iris/jwtSet/jwt.go @@ -0,0 +1,78 @@ +package jwtSet + +import ( + "github.com/iris-contrib/middleware/jwt" + "github.com/kataras/iris/v12" + "github.com/sirupsen/logrus" + "main/database" + "main/model" + "main/utils" + "time" +) + +var Jwt *jwt.Middleware +var keys model.JwtKeys + +func GetJwtKeys() model.JwtKeys { + db := database.GetInstance().GetMysqlDb() + if utils.DataIsNil(keys) || keys.Date != time.Now().Format("2006-01-02") { + keys = model.JwtKeys{} + if err := db.Where("date = ? and token = ''", time.Now().Format("2006-01-02")).First(&keys).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + //return model.JwtKeys{} + } + } + if keys.Key == "" { + keys.Date = time.Now().Format("2006-01-02") + keys.Key = utils.NewKey(32) + if err := db.Create(&keys).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + return model.JwtKeys{} + } + } + if !keys.Updated && !utils.DataIsNil(Jwt) { + UpdateJwt() + } + return keys +} +func Init() { + if utils.DataIsNil(keys) { + GetJwtKeys() + } + Jwt = jwt.New(jwt.Config{ + ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) { + return []byte(keys.Key), nil + }, + ErrorHandler: func(context iris.Context, err error) { + if err == nil { + return + } + context.StopExecution() + context.StatusCode(iris.StatusUnauthorized) + context.SetErr(err) + }, + Extractor: jwt.FromAuthHeader, + SigningMethod: jwt.SigningMethodHS256, + }) + db := database.GetInstance().GetMysqlDb() + keys.Updated = true + if err := db.Updates(&keys).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + return + } +} + +func UpdateJwt() { + if utils.DataIsNil(keys) { + GetJwtKeys() + } + Jwt.Config.ValidationKeyGetter = func(token *jwt.Token) (interface{}, error) { + return []byte(keys.Key), nil + } + db := database.GetInstance().GetMysqlDb() + keys.Updated = true + if err := db.Updates(&keys).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + return + } +} diff --git a/api_iris/main.go b/api_iris/main.go new file mode 100644 index 0000000..3a99e65 --- /dev/null +++ b/api_iris/main.go @@ -0,0 +1,119 @@ +package main + +import ( + "fmt" + nested "github.com/antonfisher/nested-logrus-formatter" + "github.com/kataras/iris/v12" + "github.com/kataras/iris/v12/middleware/accesslog" + "github.com/kataras/iris/v12/websocket" + "github.com/sirupsen/logrus" + "main/config" + "main/crontab" + "main/database" + "main/jwtSet" + "main/service" + "main/service/admin" + "main/service/ws" + "main/utils" + "os" + "path" + "time" +) + +func main() { + logrus.SetFormatter(&nested.Formatter{HideKeys: true, TimestampFormat: time.RFC3339}) + //配置文件初始化 + err := config.InitConfig(utils.GetEnvDefault("version", "dev")) + if err != nil { + logrus.Errorln("配置文件初始化失败:", err) + return + } + logrus.Infoln("配置文件初始化完成") + //日志初始化 + ac := makeAccessLog() + //应用初始化 + app := iris.New() + //应用ac-log + app.UseRouter(ac.Handler) + //错误处理 + app.OnAnyErrorCode(handler) + //数据库连接初始化 + if !database.GetInstance().InitDataPool() { + return + } + logrus.Infoln("数据库连接初始化完成") + if e := database.UpdateDbStruct(); e != nil { + logrus.Errorln("数据库初始化失败:", e) + return + } + //初始化用户信息,系统配置信息,菜单列表,本地IP地址信息 + utils.UpdateUserInfo() + utils.UpdateSysSettings() + utils.UpdateMenuList() + admin.UpdateLocalIpList() + logrus.Infoln("数据库结构初始化完成") + jwtSet.Init() + logrus.Infoln("jwt认证初始化完成") + crontab.InitAllCron() + id, err := crontab.MyCron.AddFunc("0 0 0 * * *", func() { + utils.UpdateUserInfo() + jwtSet.GetJwtKeys() + }) + if err != nil { + logrus.Errorln("cron任务添加失败:", err) + return + } + crontab.UpdateCronDb(int(id), "每天初始化jwt密钥", true) + if utils.GetEnvDefault("version", "dev") == "prd" { + //id, err = crontab.MyCron.AddFunc("0 10 * * * *", func() { + // crontab.UpdateSysInfo("") + //}) + //crontab.UpdateCronDb(int(id), "每小时10分同步历史系统信息记录", true) + id, err = crontab.MyCron.AddFunc("0 30 0 * * *", func() { + crontab.CornSaveNginxLog() + }) + crontab.UpdateCronDb(int(id), "每天00:30同步nginx日志", true) + } + logrus.Infoln("定时任务初始化完成") + //接口组 + app.PartyFunc("/api", service.Apis) + app.PartyFunc("/admin", service.Admin) + app.PartyFunc("/test", service.Test) + logrus.Infoln("接口组配置完成") + //启动websocket + wss := ws.SetupWebsocket() + app.Get("/ws", websocket.Handler(wss)) + logrus.Infoln("websocket配置完成") + //应用启动 + logrus.Infoln("应用启动") + err = app.Run(iris.Addr(":8080")) + if err != nil { + return + } +} + +// 生成acLog实例 +func makeAccessLog() *accesslog.AccessLog { + cPath, _ := os.Getwd() + ac := accesslog.File(path.Join(cPath, config.Config.Logs.Log, fmt.Sprintf("access-%s.log", time.Now().Format("2006-01-02")))) + ac.AddOutput(os.Stdout) + ac.IP = true + ac.Delim = ' ' + ac.ResponseBody = false + return ac +} + +// 接口错误处理 +func handler(ctx iris.Context) { + if ctx.GetErr() != nil { + err := ctx.JSON(utils.FormatRes(ctx.GetStatusCode(), ctx.GetErr().Error(), nil)) + if err != nil { + return + } + } else { + err := ctx.JSON(utils.FormatRes(ctx.GetStatusCode(), "", nil)) + if err != nil { + return + } + } +} diff --git a/api_iris/model/backgammon.go b/api_iris/model/backgammon.go new file mode 100644 index 0000000..0c2419d --- /dev/null +++ b/api_iris/model/backgammon.go @@ -0,0 +1,12 @@ +package model + +import "gorm.io/gorm" + +type BackgammonRoom struct { + RoomId int `json:"room_id"` + Player string `json:"player"` + Winner string `json:"winner"` + Current string `json:"current"` + PawnStatus string `json:"pawn_status"` + gorm.Model +} diff --git a/api_iris/model/balance.go b/api_iris/model/balance.go new file mode 100644 index 0000000..dce461a --- /dev/null +++ b/api_iris/model/balance.go @@ -0,0 +1,19 @@ +package model + +import "gorm.io/gorm" + +type Balances struct { + Username string `json:"username"` + Card string `json:"card"` + Type bool `gorm:"default:false" json:"type"` //1:支出型;0:收入型 + Balance float64 `json:"balance"` + gorm.Model +} + +type BalanceLogs struct { + Username string + Card string + Date string + Balance float64 + gorm.Model +} diff --git a/api_iris/model/chess.go b/api_iris/model/chess.go new file mode 100644 index 0000000..074a722 --- /dev/null +++ b/api_iris/model/chess.go @@ -0,0 +1,28 @@ +package model + +import ( + "gorm.io/gorm" + "time" +) + +type ChessStatus struct { + gorm.Model + Players string `json:"players"` + Current string `json:"current"` + IsEnd bool `json:"is_end"` + Status string `json:"status"` + Winner string `json:"winner"` + ChessId string `json:"chess_id"` +} + +type ChessStatusLog struct { + gorm.Model + RoomId uint `json:"roomId"` + Current string `json:"current"` + Players string `json:"players"` + IsEnd bool `json:"is_end"` + Status string `json:"status"` + Winner string `json:"winner"` + ChessId string `json:"chess_id"` + Time time.Time `json:"time"` +} diff --git a/api_iris/model/crontab.go b/api_iris/model/crontab.go new file mode 100644 index 0000000..2be7620 --- /dev/null +++ b/api_iris/model/crontab.go @@ -0,0 +1,9 @@ +package model + +import "gorm.io/gorm" + +type RunningCrontab struct { + Name string + CronId int + gorm.Model +} diff --git a/api_iris/model/keys.go b/api_iris/model/keys.go new file mode 100644 index 0000000..b23a4d3 --- /dev/null +++ b/api_iris/model/keys.go @@ -0,0 +1,20 @@ +package model + +import "gorm.io/gorm" + +// DayKeys 每天对应key表 +type DayKeys struct { + Date string + Key string + AesKey string + User string + gorm.Model +} +type JwtKeys struct { + Username string + Date string + Key string + Token string + Updated bool `gorm:"default:false"` + gorm.Model +} diff --git a/api_iris/model/logs.go b/api_iris/model/logs.go new file mode 100644 index 0000000..bd0e743 --- /dev/null +++ b/api_iris/model/logs.go @@ -0,0 +1,31 @@ +package model + +import "gorm.io/gorm" + +// Logs 日志表 +type Logs struct { + Method string `json:"method,omitempty"` + Path string `json:"path,omitempty"` + Status string `json:"status,omitempty"` + UserAgent string `json:"user_agent,omitempty"` + Time string `json:"time,omitempty"` + Location string `json:"location,omitempty"` + Ip string `json:"ip,omitempty"` + gorm.Model +} + +type LogFileDealLog struct { + Date string + Success bool + gorm.Model +} + +// SysLogs 系统容器日志表 +type SysLogs struct { + Time string `json:"time,omitempty"` + ContainerName string `json:"container_name,omitempty"` + ContainerImage string `json:"container_image,omitempty"` + Message string `json:"message,omitempty"` + Offset int `json:"offset,omitempty"` + gorm.Model +} diff --git a/api_iris/model/menus.go b/api_iris/model/menus.go new file mode 100644 index 0000000..11a5170 --- /dev/null +++ b/api_iris/model/menus.go @@ -0,0 +1,14 @@ +package model + +import "gorm.io/gorm" + +type Menus struct { + MenuId string `json:"menu_id"` + Name string `json:"name"` + Icon string `json:"icon"` + Path string `json:"path"` + RouteOnly bool `json:"route_only"` + UserType string `json:"user_type"` + WhiteList string `json:"white_list"` + gorm.Model +} diff --git a/api_iris/model/note.go b/api_iris/model/note.go new file mode 100644 index 0000000..151bc92 --- /dev/null +++ b/api_iris/model/note.go @@ -0,0 +1,9 @@ +package model + +import "gorm.io/gorm" + +type UserNotes struct { + Username string `json:"username"` + Content string `json:"content"` + gorm.Model +} diff --git a/api_iris/model/sudoku.go b/api_iris/model/sudoku.go new file mode 100644 index 0000000..ca3f3e0 --- /dev/null +++ b/api_iris/model/sudoku.go @@ -0,0 +1,18 @@ +package model + +import "gorm.io/gorm" + +type Sudoku struct { + Sudoku string `json:"sudoku"` + Username string `json:"username"` + Result string `json:"result"` + gorm.Model +} + +type SudokuStatus struct { + SudokuId uint `json:"sudoku_id"` + Username string `json:"username"` + Status string `json:"status"` + Complete bool `json:"complete"` + gorm.Model +} diff --git a/api_iris/model/sysError.go b/api_iris/model/sysError.go new file mode 100644 index 0000000..0cc0806 --- /dev/null +++ b/api_iris/model/sysError.go @@ -0,0 +1,14 @@ +package model + +import ( + "gorm.io/gorm" + "time" +) + +type SysError struct { + Username string `json:"username"` + Time time.Time `json:"time"` + Function string `json:"function"` + ErrorInfo string `json:"error_info"` + gorm.Model +} diff --git a/api_iris/model/sysSettings.go b/api_iris/model/sysSettings.go new file mode 100644 index 0000000..368b367 --- /dev/null +++ b/api_iris/model/sysSettings.go @@ -0,0 +1,17 @@ +package model + +import "gorm.io/gorm" + +// SysIcons 系统图标库 +type SysIcons struct { + Icon string `json:"icon"` + gorm.Model +} + +type SysSettings struct { + Name string `json:"name"` + CnName string `json:"cn_name"` + Value string `json:"value"` + DType string `json:"d_type"` + gorm.Model +} diff --git a/api_iris/model/sysinfo.go b/api_iris/model/sysinfo.go new file mode 100644 index 0000000..bbded6a --- /dev/null +++ b/api_iris/model/sysinfo.go @@ -0,0 +1,55 @@ +package model + +import "gorm.io/gorm" + +type SysInfo struct { + Date string `json:"date" gorm:"index:select_info"` + Datetime string `json:"datetime" gorm:"index:datetime_index"` + Username string `json:"username" gorm:"index:select_info"` + Hostname string `json:"hostname"` + Ip string `json:"ip" gorm:"index:select_info"` + SysVersion string `json:"sysVersion"` + PhysicalCount int `json:"physicalCount"` + LogicalCount int `json:"logicalCount"` + CpuPer float64 `json:"cpuPer"` + MemTotal string `json:"memTotal"` + MemUsed string `json:"memUsed"` + MemPer float64 `json:"memPer"` + DiskPoint string `json:"diskPoint"` + DiskTotal string `json:"diskTotal"` + DiskUsed string `json:"diskUsed"` + DiskPer string `json:"diskPer"` + NetSent int64 `json:"netSent"` + NetRec int64 `json:"netRec"` + SentSpeed string `json:"sentSpeed"` + RecSpeed string `json:"recSpeed"` + gorm.Model +} + +type SysInfoOld struct { + Datetime string `json:"datetime" gorm:"index:datetime_index"` + Username string `json:"username"` + Hostname string `json:"hostname"` + Ip string `json:"ip"` + SysVersion string `json:"sysVersion"` + PhysicalCount int `json:"physicalCount"` + LogicalCount int `json:"logicalCount"` + CpuPer float64 `json:"cpuPer"` + MemTotal string `json:"memTotal"` + MemUsed string `json:"memUsed"` + MemPer float64 `json:"memPer"` + DiskPoint string `json:"diskPoint"` + DiskTotal string `json:"diskTotal"` + DiskUsed string `json:"diskUsed"` + DiskPer string `json:"diskPer"` + NetSent int64 `json:"netSent"` + NetRec int64 `json:"netRec"` + SentSpeed string `json:"sentSpeed"` + RecSpeed string `json:"recSpeed"` +} + +type SysInfoUpdateLog struct { + Datetime string `json:"datetime"` + Update bool `json:"update"` + gorm.Model +} diff --git a/api_iris/model/user.go b/api_iris/model/user.go new file mode 100644 index 0000000..c47771c --- /dev/null +++ b/api_iris/model/user.go @@ -0,0 +1,38 @@ +package model + +import "gorm.io/gorm" + +// User 用户登录信息表 +type User struct { + Username string + Password string + Date string + ConfirmCode string + gorm.Model +} + +// Userinfo 用户信息表 +type Userinfo struct { + Username string `json:"username"` + Avatar string `json:"avatar"` + Nickname string `json:"nickname"` + Mobile string `json:"mobile"` + Email string `json:"email"` + Location string `json:"location"` + Type string `json:"type"` + gorm.Model +} + +// UserAction 用户行为表 +type UserAction struct { + Username string + Action string + gorm.Model +} + +type UserAutoLogin struct { + Username string + DeviceId string + Location string + gorm.Model +} diff --git a/api_iris/model/utils.go b/api_iris/model/utils.go new file mode 100644 index 0000000..a52a5e3 --- /dev/null +++ b/api_iris/model/utils.go @@ -0,0 +1,10 @@ +package model + +import "gorm.io/gorm" + +// IpsLocation ip转地址表 +type IpsLocation struct { + Ip string + Location string + gorm.Model +} diff --git a/api_iris/model/weather.go b/api_iris/model/weather.go new file mode 100644 index 0000000..52cd3ce --- /dev/null +++ b/api_iris/model/weather.go @@ -0,0 +1,11 @@ +package model + +import "gorm.io/gorm" + +// Weather 每日天气表 +type Weather struct { + Date string + Location string + Weather string + gorm.Model +} diff --git a/api_iris/service/admin/init.go b/api_iris/service/admin/init.go new file mode 100644 index 0000000..b46cb57 --- /dev/null +++ b/api_iris/service/admin/init.go @@ -0,0 +1,25 @@ +package admin + +import ( + "errors" + "github.com/kataras/iris/v12" + "main/utils" +) + +func Admin(ctx iris.Context) { + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", "adminPage")) + if err != nil { + return + } +} + +// CheckAdmin 检查admin权限 +func checkAdmin(ctx iris.Context) { + user := utils.GetLoginUser(ctx) + if user.Type != "admin" { + ctx.StatusCode(iris.StatusForbidden) + ctx.SetErr(errors.New("权限不足")) + return + } + ctx.Next() +} diff --git a/api_iris/service/admin/logs.go b/api_iris/service/admin/logs.go new file mode 100644 index 0000000..e85b54b --- /dev/null +++ b/api_iris/service/admin/logs.go @@ -0,0 +1,199 @@ +package admin + +import ( + "errors" + "fmt" + "github.com/kataras/iris/v12" + "github.com/sirupsen/logrus" + "gorm.io/gorm" + "main/config" + "main/database" + "main/model" + "main/utils" + "os" + "path" + "strconv" + "strings" + "time" +) + +var ipLocationList []model.IpsLocation + +func getLogStats(ctx iris.Context) { + var logStats []resLogStats + db := database.GetInstance().GetMysqlDb() + if err := db.Model(&model.Logs{}).Select("left(time, 10) name", "count(1) value").Order("left(time,10)").Group("left(time,10)").Scan(&logStats).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", logStats)) + if utils.ErrHandle(ctx, err) { + return + } +} + +func getLogDetail(ctx iris.Context) { + var logs []model.Logs + date := ctx.URLParam("date") + db := database.GetInstance().GetMysqlDb() + if err := db.Where("left(time,10) = ?", date).Find(&logs).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", logs)) + if utils.ErrHandle(ctx, err) { + return + } +} + +func getSysLogs(ctx iris.Context) { + var logs []model.SysLogs + var resLogs []model.SysLogs + startTime := ctx.URLParam("start_time") + endTime := ctx.URLParam("end_time") + containerName := ctx.URLParam("container_name") + message := ctx.URLParam("message") + db := database.GetInstance().GetMysqlDb() + //生成子查询 + query := db.Model(&model.SysLogs{}) + if startTime == "" { + startTime = time.Date(2000, 0, 1, 0, 0, 0, 0, time.Local).Format("2006-01-02 15:04:05") + } + if endTime == "" { + endTime = time.Now().Format("2006-01-02 15:04:05") + } + query.Where("time >= ? and time <= ?", startTime, endTime) + if containerName != "" { + query.Where("container_name = ?", containerName) + } + //查询,限制1000条日志 + if err := query.Order("time desc").Limit(1000).Find(&logs).Error; err != nil { + ctx.StatusCode(iris.StatusInternalServerError) + ctx.SetErr(errors.New("系统错误,请联系管理员")) + logrus.Errorln("查询容器日志sql失败:", err, "查询参数:", startTime, endTime, containerName) + return + } + //过滤包含message内容 + if message != "" { + for _, log := range logs { + if strings.Contains(log.Message, message) { + resLogs = append(resLogs, log) + } + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, strconv.Itoa(len(resLogs)), resLogs)) + if err != nil { + return + } + } else { + err := ctx.JSON(utils.FormatRes(iris.StatusOK, strconv.Itoa(len(logs)), logs)) + if err != nil { + return + } + } +} + +// 获取日志容器名称列表 +func getSysLogsContainerList(ctx iris.Context) { + var list []string + db := database.GetInstance().GetMysqlDb() + if err := db.Model(&model.SysLogs{}).Distinct("container_name").Find(&list).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", list)) + if err != nil { + return + } +} + +// 手动保存日志 +func saveLogs(ctx iris.Context) { + cPath, _ := os.Getwd() + logPath := path.Join(path.Join(cPath, config.Config.Logs.Nginx)) + fileList, err := os.ReadDir(logPath) + if utils.ErrHandle(ctx, err) { + return + } + var resLogList []model.Logs + var logDealList []model.LogFileDealLog + db := database.GetInstance().GetMysqlDb() + if err1 := db.Find(&logDealList).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + for _, file := range fileList { + if strings.Contains(file.Name(), "access-") { + date := strings.Split(strings.Split(file.Name(), "access-")[1], ".")[0] + if checkDate(date, logDealList) { + continue + } + if err1 := db.Where("time like ?", fmt.Sprintf("%s%%", date)).Delete(&resLogList).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + logList, err := utils.FileRead(path.Join(logPath, file.Name()), true) + if utils.ErrHandle(ctx, err) { + return + } + for _, v := range logList { + c := make(chan model.Logs) + go FormatLog(v, date, c) + resLogItem := <-c + if err1 := db.Create(&resLogItem).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + resLogList = append(resLogList, resLogItem) + } + if err1 := db.Create(&model.LogFileDealLog{ + Date: date, + Success: true, + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + } + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", resLogList)) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 判断日期是否已处理 +func checkDate(date string, list []model.LogFileDealLog) bool { + for _, v := range list { + if v.Date == date && v.Success { + return true + } + } + return false +} + +// 格式化日志内时间格式 +func formatTime(date string, s string) string { + l := strings.Split(s, ":") + return fmt.Sprintf("%s %s:%s:%s", date, l[1], l[2], l[3]) +} + +// 从本地ipLocationList记录中获取IP对应地址 +func getLocalIpAddr(ip string) string { + for _, v := range ipLocationList { + if v.Ip == ip { + return v.Location + } + } + return "" +} + +// 从接口中获取IP对应地址 +func getIpAddr(ip string) (string, error) { + db := database.GetInstance().GetMysqlDb() + location, err := utils.GetIpLocation(ip) + if err != nil { + return "", err + } + ipItem := model.IpsLocation{ + Ip: ip, + Location: location, + Model: gorm.Model{}, + } + if err1 := db.Create(&ipItem).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + UpdateLocalIpList() + return location, nil +} diff --git a/api_iris/service/admin/logsExport.go b/api_iris/service/admin/logsExport.go new file mode 100644 index 0000000..62f38f6 --- /dev/null +++ b/api_iris/service/admin/logsExport.go @@ -0,0 +1,85 @@ +package admin + +import ( + "github.com/kataras/iris/v12" + "github.com/sirupsen/logrus" + "main/database" + "main/jwtSet" + "main/model" + "main/utils" + "strings" +) + +func Logs(party iris.Party) { + party.Post("/", saveLogs) + party.Get("/", jwtSet.Jwt.Serve, checkAdmin, getLogStats) + party.Get("/detail", jwtSet.Jwt.Serve, checkAdmin, getLogDetail) + party.Get("/sys", jwtSet.Jwt.Serve, checkAdmin, getSysLogs) + party.Get("/log-container-list", jwtSet.Jwt.Serve, checkAdmin, getSysLogsContainerList) +} + +// FormatLog 对日志内容切分,格式化数据 +func FormatLog(logItem string, date string, c chan model.Logs) { + var resLogItem model.Logs + resLogItem.Ip = strings.Split(logItem, " ")[0] + resLogItem.Location = GetIpLocation(resLogItem.Ip) + //resLogItem.Location = getLocalIpAddr(resLogItem.Ip) + //if resLogItem.Location == "" { + // resLogItem.Location, _ = getIpAddr(resLogItem.Ip) + //} + resLogItem.Time = formatTime(date, strings.Split(strings.Split(logItem, "[")[1], "]")[0]) + if strings.Split(logItem, "\"")[1] != "" { + if strings.Contains(strings.Split(logItem, "\"")[1], " /") { + resLogItem.Method = strings.Split(strings.Split(logItem, "\"")[1], " ")[0] + resLogItem.Path = strings.Split(strings.Split(logItem, "\"")[1], " ")[1] + } else { + resLogItem.Method = "" + resLogItem.Path = strings.Split(logItem, "\"")[1] + } + resLogItem.Status = strings.Split(strings.Split(logItem, "\" ")[1], " ")[0] + } + resLogItem.UserAgent = strings.Split(logItem, "\"")[5] + c <- resLogItem +} + +// GetIpLocation 获取IP对应的地址 +func GetIpLocation(ip string) string { + if len(ipLocationList) == 0 { + UpdateLocalIpList() + } + location := getLocalIpAddr(ip) + if location == "" { + location, _ = getIpAddr(ip) + } + return location +} + +func UpdateLocalIpList() { + var nullDataList []nullData + db := database.GetInstance().GetMysqlDb() + if err := db.Model(&model.Logs{}). + Distinct("logs.ip", "ips_locations.location"). + Where("ips_locations.location is null"). + Joins("left join ips_locations on logs.ip=ips_locations.ip"). + Scan(&nullDataList).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if len(nullDataList) != 0 { + for _, data := range nullDataList { + location, err := utils.GetIpLocation(data.IP) + if err != nil { + logrus.Errorln(data.IP, "IP地址获取失败", err) + } + if err = db.Create(&model.IpsLocation{ + Ip: data.IP, + Location: location, + }).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + } + } + if err := db.Distinct("ip, location").Find(&ipLocationList).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + logrus.Infoln("更新本地IP地址列表") +} diff --git a/api_iris/service/admin/menus.go b/api_iris/service/admin/menus.go new file mode 100644 index 0000000..c392efb --- /dev/null +++ b/api_iris/service/admin/menus.go @@ -0,0 +1,103 @@ +package admin + +import ( + "errors" + "github.com/kataras/iris/v12" + "github.com/sirupsen/logrus" + "main/database" + "main/model" + "main/utils" +) + +func getMenu(ctx iris.Context) { + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", utils.MenuList)) + if utils.ErrHandle(ctx, err) { + return + } +} + +func addMenu(ctx iris.Context) { + var param model.Menus + err := ctx.ReadJSON(¶m) + if utils.ErrHandle(ctx, err) || utils.DataIsNil(param) { + return + } + if (len(param.MenuId) != 3 && len(param.MenuId) != 5) || param.MenuId[0:3] == "000" { + utils.ErrHandle(ctx, errors.New("菜单ID格式错误")) + return + } + if checkMenuExist(param) { + utils.ErrHandle(ctx, errors.New("菜单ID已存在")) + return + } + db := database.GetInstance().GetMysqlDb() + if err1 := db.Create(¶m).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } + utils.UpdateMenuList() +} + +func updateMenu(ctx iris.Context) { + var param model.Menus + err := ctx.ReadJSON(¶m) + if utils.ErrHandle(ctx, err) || utils.DataIsNil(param) { + return + } + if (len(param.MenuId) != 3 && len(param.MenuId) != 5) || param.MenuId[0:3] == "000" { + utils.ErrHandle(ctx, errors.New("菜单ID格式错误")) + return + } + if !checkMenuIDExist(param) || checkMenuExist(param) { + utils.ErrHandle(ctx, errors.New("菜单ID已存在")) + return + } + db := database.GetInstance().GetMysqlDb() + if err1 := db.Save(¶m).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } + utils.UpdateMenuList() +} + +func deleteMenu(ctx iris.Context) { + menuId := ctx.URLParam("menu_id") + db := database.GetInstance().GetMysqlDb() + utils.UpdateMenuList() + for _, menu := range utils.MenuList { + if menu.MenuId == menuId || menu.MenuId[0:3] == menuId { + if err := db.Delete(&menu).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + } + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } + utils.UpdateMenuList() +} + +func checkMenuExist(menu model.Menus) bool { + for _, m := range utils.MenuList { + if menu.MenuId == m.MenuId && menu.ID != m.ID { + return true + } + } + return false +} + +func checkMenuIDExist(menu model.Menus) bool { + for _, m := range utils.MenuList { + if menu.ID == m.ID { + return true + } + } + return false +} diff --git a/api_iris/service/admin/menusExport.go b/api_iris/service/admin/menusExport.go new file mode 100644 index 0000000..17b8fc9 --- /dev/null +++ b/api_iris/service/admin/menusExport.go @@ -0,0 +1,13 @@ +package admin + +import ( + "github.com/kataras/iris/v12" + "main/jwtSet" +) + +func Menus(party iris.Party) { + party.Get("/", jwtSet.Jwt.Serve, checkAdmin, getMenu) + party.Post("/", jwtSet.Jwt.Serve, checkAdmin, addMenu) + party.Put("/", jwtSet.Jwt.Serve, checkAdmin, updateMenu) + party.Delete("/", jwtSet.Jwt.Serve, checkAdmin, deleteMenu) +} diff --git a/api_iris/service/admin/sysSettings.go b/api_iris/service/admin/sysSettings.go new file mode 100644 index 0000000..5e4f8f8 --- /dev/null +++ b/api_iris/service/admin/sysSettings.go @@ -0,0 +1,106 @@ +package admin + +import ( + "errors" + "github.com/kataras/iris/v12" + "github.com/sirupsen/logrus" + "main/database" + "main/model" + "main/utils" + "strings" +) + +func getSysSettings(ctx iris.Context) { + var res []resSysSettings + var resSysSettingsData []model.SysSettings + var currentList []string + name := ctx.Params().GetString("name") + for _, sysSetting := range utils.SysSettings { + if sysSetting.DType == "option" { + sysSetting.Name = strings.Split(sysSetting.Name, ":")[0] + } + if (name != "" && sysSetting.Name != name) || utils.CheckListItem(currentList, sysSetting.Name) { + continue + } else { + resSysSettingsData = nil + for _, v := range utils.SysSettings { + if strings.Split(v.Name, ":")[0] == sysSetting.Name { + resSysSettingsData = append(resSysSettingsData, v) + } + } + currentList = append(currentList, sysSetting.Name) + res = append(res, resSysSettings{Name: sysSetting.Name, Data: resSysSettingsData}) + } + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", res)) + if utils.ErrHandle(ctx, err) { + return + } +} + +func addSysSettings(ctx iris.Context) { + var params model.SysSettings + err := ctx.ReadJSON(¶ms) + if utils.ErrHandle(ctx, err) { + return + } + db := database.GetInstance().GetMysqlDb() + if !checkExist(params) { + if err1 := db.Create(¶ms).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + utils.UpdateSysSettings() + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } +} + +func delSettings(ctx iris.Context) { + var params model.SysSettings + err := ctx.ReadJSON(¶ms) + if utils.ErrHandle(ctx, err) { + return + } + db := database.GetInstance().GetMysqlDb() + if err1 := db.Delete(¶ms).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + utils.UpdateSysSettings() + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } +} + +func updateSettings(ctx iris.Context) { + var params model.SysSettings + err := ctx.ReadJSON(¶ms) + if utils.ErrHandle(ctx, err) { + return + } + if !checkExist(params) { + if utils.ErrHandle(ctx, errors.New("配置信息不存在")) { + return + } + } + db := database.GetInstance().GetMysqlDb() + if err1 := db.Save(¶ms).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + utils.UpdateSysSettings() + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } +} + +func checkExist(setting model.SysSettings) bool { + for _, sysSetting := range utils.SysSettings { + if setting.Name == sysSetting.Name && setting.Value == sysSetting.Value { + return true + } + } + return false +} diff --git a/api_iris/service/admin/sysSettingsExport.go b/api_iris/service/admin/sysSettingsExport.go new file mode 100644 index 0000000..1aa58d7 --- /dev/null +++ b/api_iris/service/admin/sysSettingsExport.go @@ -0,0 +1,13 @@ +package admin + +import ( + "github.com/kataras/iris/v12" + "main/jwtSet" +) + +func SysSettings(party iris.Party) { + party.Get("/", jwtSet.Jwt.Serve, checkAdmin, getSysSettings) + party.Post("/", jwtSet.Jwt.Serve, checkAdmin, addSysSettings) + party.Delete("/", jwtSet.Jwt.Serve, checkAdmin, delSettings) + party.Put("/", jwtSet.Jwt.Serve, checkAdmin, updateSettings) +} diff --git a/api_iris/service/admin/type.d.go b/api_iris/service/admin/type.d.go new file mode 100644 index 0000000..ac22666 --- /dev/null +++ b/api_iris/service/admin/type.d.go @@ -0,0 +1,27 @@ +package admin + +import "main/model" + +// logs----------------------------------------------------------------------------- +type resIpLocation struct { + Flag bool `json:"flag"` + Data resIpLocationData `json:"data"` +} +type resIpLocationData struct { + Ip string `json:"ip"` + Location string `json:"location"` +} +type nullData struct { + IP string `json:"ip"` + Location string `json:"location"` +} +type resLogStats struct { + Name string `json:"name"` + Value int `json:"value"` +} + +// sysSettings------------------------------------------------------------------------ +type resSysSettings struct { + Name string `json:"name"` + Data []model.SysSettings `json:"data"` +} diff --git a/api_iris/service/admin/user.go b/api_iris/service/admin/user.go new file mode 100644 index 0000000..42cc7a2 --- /dev/null +++ b/api_iris/service/admin/user.go @@ -0,0 +1,66 @@ +package admin + +import ( + "errors" + "github.com/kataras/iris/v12" + "github.com/sirupsen/logrus" + "main/database" + "main/model" + "main/utils" +) + +func getUserList(ctx iris.Context) { + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", utils.UserList)) + if utils.ErrHandle(ctx, err) { + return + } +} + +func updateUserInfo(ctx iris.Context) { + var userinfo model.Userinfo + err := ctx.ReadJSON(&userinfo) + if utils.ErrHandle(ctx, err) { + return + } + db := database.GetInstance().GetMysqlDb() + if err1 := db.Save(&userinfo).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + utils.UpdateUserInfo() + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } +} + +func deleteUser(ctx iris.Context) { + loginUser := utils.GetLoginUser(ctx) + username := ctx.URLParam("username") + if username == loginUser.Username { + logrus.Errorln(loginUser.Username, "无法删除本人") + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("不可删除本人用户")) + return + } + db := database.GetInstance().GetMysqlDb() + var user []model.User + if err := db.Where("username = ?", username).Find(&user).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if len(user) == 0 { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("用户不存在")) + return + } + if err := db.Where("username = ?", username).Delete(&model.User{}).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if err := db.Where("username = ?", username).Delete(&model.Userinfo{}).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + utils.UpdateUserInfo() + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } +} diff --git a/api_iris/service/admin/userExport.go b/api_iris/service/admin/userExport.go new file mode 100644 index 0000000..d6cca69 --- /dev/null +++ b/api_iris/service/admin/userExport.go @@ -0,0 +1,12 @@ +package admin + +import ( + "github.com/kataras/iris/v12" + "main/jwtSet" +) + +func User(party iris.Party) { + party.Get("/", jwtSet.Jwt.Serve, checkAdmin, getUserList) + party.Put("/", jwtSet.Jwt.Serve, checkAdmin, updateUserInfo) + party.Delete("/", jwtSet.Jwt.Serve, checkAdmin, deleteUser) +} diff --git a/api_iris/service/api/backgammon.go b/api_iris/service/api/backgammon.go new file mode 100644 index 0000000..b2e02ba --- /dev/null +++ b/api_iris/service/api/backgammon.go @@ -0,0 +1,313 @@ +package api + +import ( + "errors" + "github.com/kataras/iris/v12" + "github.com/sirupsen/logrus" + "main/database" + "main/model" + "main/utils" + "strconv" + "strings" +) + +// 初始化新棋盘 +var cols = 15 + +func getRooms(ctx iris.Context) { + db := database.GetInstance().GetMysqlDb() + var rooms []typeRoomStatus + if err := db.Model(&model.BackgammonRoom{}).Distinct("room_id").Order("room_id").Scan(&rooms).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + for k, v := range rooms { + roomStatus, err := getRoomStatus(v.RoomId) + if utils.ErrHandle(ctx, err) { + return + } + rooms[k].Player = roomStatus.Player + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", rooms)) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 删除房间 +func delRoom(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + id, err := strconv.Atoi(ctx.Params().Get("id")) + if utils.ErrHandle(ctx, err) { + return + } + roomStatus, err := getRoomStatus(id) + if utils.ErrHandle(ctx, err) { + return + } + db := database.GetInstance().GetMysqlDb() + if roomStatus.Player == username.Username { + if err1 := db.Where("room_id = ?", id).Delete(&model.BackgammonRoom{}).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + } else if strings.Split(roomStatus.Player, ",")[0] == username.Username { + if err1 := db.Create(&model.BackgammonRoom{ + RoomId: roomStatus.RoomId, + Player: strings.Split(roomStatus.Player, ",")[1], + Winner: "", + Current: strings.Split(roomStatus.Player, ",")[1], + PawnStatus: initPawns(), + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + } else if strings.Split(roomStatus.Player, ",")[1] == username.Username { + if err1 := db.Create(&model.BackgammonRoom{ + RoomId: roomStatus.RoomId, + Player: strings.Split(roomStatus.Player, ",")[0], + Winner: "", + Current: strings.Split(roomStatus.Player, ",")[0], + PawnStatus: initPawns(), + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } +} + +func initPawns() string { + var newPawns string + for i := 0; i < cols; i++ { + tmp := "0" + strings.Repeat(",0", cols-1) + if i == 0 { + newPawns = tmp + continue + } + newPawns += ";" + tmp + } + return newPawns +} + +// 新建房间 +func addRoom(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + var currentStatus model.BackgammonRoom + db := database.GetInstance().GetMysqlDb() + if err := db.Order("room_id desc").First(¤tStatus).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + newPawns := initPawns() + if err := db.Create(&model.BackgammonRoom{ + RoomId: currentStatus.RoomId + 1, + Player: username.Username, + Current: username.Username, + Winner: "", + PawnStatus: newPawns, + }).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", currentStatus.RoomId+1)) + if utils.ErrHandle(ctx, err) { + return + } +} + +func joinRoom(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + id, err := strconv.Atoi(ctx.Params().Get("id")) + if utils.ErrHandle(ctx, err) { + return + } + roomStatus, err := getRoomStatus(id) + if utils.ErrHandle(ctx, err) { + return + } + if utils.CheckListItem(strings.Split(roomStatus.Player, ","), username.Username) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("无法重复加入房间")) + return + } + if !utils.CheckListItem(strings.Split(roomStatus.Player, ","), username.Username) && len(strings.Split(roomStatus.Player, ",")) > 2 { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("房间人数已满")) + return + } + roomStatus.Player = roomStatus.Player + "," + username.Username + db := database.GetInstance().GetMysqlDb() + if err1 := db.Model(&model.BackgammonRoom{}).Where("id = ?", roomStatus.ID).Updates(&roomStatus).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } +} + +func getRoomStatus(roomId int) (model.BackgammonRoom, error) { + var status model.BackgammonRoom + db := database.GetInstance().GetMysqlDb() + if err := db.Where("room_id = ?", roomId).Order("id desc").First(&status).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if utils.DataIsNil(status) { + return model.BackgammonRoom{}, errors.New("房间号不存在") + } + return status, nil +} + +// 获取房间棋盘状态 +func getStatus(ctx iris.Context) { + id, err := strconv.Atoi(ctx.Params().Get("id")) + if utils.ErrHandle(ctx, err) { + return + } + roomStatus, err := getRoomStatus(id) + if utils.ErrHandle(ctx, err) { + return + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", roomStatus)) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 新增棋子 +func addPawn(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + id, err := strconv.Atoi(ctx.Params().Get("id")) + if utils.ErrHandle(ctx, err) { + return + } + roomStatus, err := getRoomStatus(id) + if utils.ErrHandle(ctx, err) { + return + } + var tmpT string + if roomStatus.Winner != "" { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("对局已结束")) + return + } + if len(strings.Split(roomStatus.Player, ",")) != 2 { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("当前房间,玩家人数不足")) + return + } + if roomStatus.Current != username.Username { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("现在不是你的回合")) + return + } + if strings.Split(roomStatus.Player, ",")[0] == username.Username { + tmpT = "1" + } else if strings.Split(roomStatus.Player, ",")[1] == username.Username { + tmpT = "-1" + } else { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("非玩家,无法进行游戏")) + return + } + place := strings.Split(ctx.URLParam("place"), ",") + var newPawn []int + for _, i := range place { + tmp, _ := strconv.Atoi(i) + newPawn = append(newPawn, tmp) + } + var pawnStatus [][]string + for _, i := range strings.Split(roomStatus.PawnStatus, ";") { + var tmp []string + for _, j := range strings.Split(i, ",") { + tmp = append(tmp, j) + } + pawnStatus = append(pawnStatus, tmp) + } + if pawnStatus[newPawn[0]][newPawn[1]] != "0" { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("该位置已有棋子")) + return + } + winner := getWinner(pawnStatus, tmpT, [2]int{newPawn[0], newPawn[1]}) + if winner { + roomStatus.Winner = username.Username + } + pawnStatus[newPawn[0]][newPawn[1]] = tmpT + var tmp []string + for _, i := range pawnStatus { + tmp = append(tmp, strings.Join(i, ",")) + } + roomStatus.PawnStatus = strings.Join(tmp, ";") + roomStatus.Current = strings.Replace(strings.Replace(roomStatus.Player, username.Username, "", 1), ",", "", 1) + db := database.GetInstance().GetMysqlDb() + if err1 := db.Create(&model.BackgammonRoom{ + RoomId: roomStatus.RoomId, + Player: roomStatus.Player, + Winner: roomStatus.Winner, + Current: roomStatus.Current, + PawnStatus: roomStatus.PawnStatus, + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", roomStatus)) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 判断当前是否存在winner +func getWinner(pawns [][]string, t string, pawn [2]int) bool { + pawnAround := [8][2]int{{pawn[0] - 1, pawn[1] - 1}, {pawn[0], pawn[1] - 1}, {pawn[0] + 1, pawn[1] - 1}, {pawn[0] - 1, pawn[1]}, + {pawn[0] + 1, pawn[1]}, {pawn[0] - 1, pawn[1] + 1}, {pawn[0], pawn[1] + 1}, {pawn[0] + 1, pawn[1] + 1}} + around := [8]int{0, 0, 0, 0, 0, 0, 0, 0} + for i, p := range pawnAround { + if p[0] >= 0 && p[1] >= 0 && p[0] < cols && p[1] < cols && pawns[p[0]][p[1]] == t { + current := [2]int{p[0], p[1]} + for j := 0; j < 3; j++ { + switch i { + case 0: + current[0]-- + current[1]-- + case 1: + current[1]-- + case 2: + current[0]++ + current[1]-- + case 3: + current[0]-- + case 4: + current[0]++ + case 5: + current[0]-- + current[1]++ + case 6: + current[1]++ + case 7: + current[0]++ + current[1]++ + } + //fmt.Println(current) + if current[0] < 0 || current[0] > cols-1 || current[1] < 0 || current[1] > cols-1 || pawns[current[0]][current[1]] != t { + around[i] = j + 1 + break + } + around[i] = j + 2 + } + } + } + if around[0]+around[7] >= 4 || around[1]+around[6] >= 4 || around[2]+around[5] >= 4 || around[3]+around[4] >= 4 { + return true + } + return false +} diff --git a/api_iris/service/api/backgammonExport.go b/api_iris/service/api/backgammonExport.go new file mode 100644 index 0000000..6a61b66 --- /dev/null +++ b/api_iris/service/api/backgammonExport.go @@ -0,0 +1,15 @@ +package api + +import ( + "github.com/kataras/iris/v12" + "main/jwtSet" +) + +func Backgammon(party iris.Party) { + party.Get("/", getRooms) + party.Post("/", jwtSet.Jwt.Serve, addRoom) + party.Get("/{id:int}", getStatus) + party.Put("/{id:int}", jwtSet.Jwt.Serve, joinRoom) + party.Delete("/{id:int}", jwtSet.Jwt.Serve, delRoom) + party.Post("/{id:int}", jwtSet.Jwt.Serve, addPawn) +} diff --git a/api_iris/service/api/chess.go b/api_iris/service/api/chess.go new file mode 100644 index 0000000..b8b4e06 --- /dev/null +++ b/api_iris/service/api/chess.go @@ -0,0 +1,614 @@ +package api + +import ( + "errors" + "fmt" + "github.com/kataras/iris/v12" + "github.com/sirupsen/logrus" + "io" + "main/database" + "main/model" + "main/utils" + "math/rand" + "net/http" + "net/url" + "sort" + "strconv" + "strings" + "time" +) + +var initChessPawns = model.ChessStatus{ + Current: "", + IsEnd: false, + Status: "0,1,2,3,4,5,6,7,8;-1,-1,-1,-1,-1,-1,-1,-1,-1;-1,9,-1,-1,-1,-1,-1,10,-1;11,-1,12,-1,13,-1,14,-1,15;-1,-1,-1,-1,-1,-1,-1,-1,-1;-1,-1,-1,-1,-1,-1,-1,-1,-1;31,-1,30,-1,29,-1,28,-1,27;-1,26,-1,-1,-1,-1,-1,25,-1;-1,-1,-1,-1,-1,-1,-1,-1,-1;24,23,22,21,20,19,18,17,16", + Winner: "", + ChessId: "", +} + +func addAiChessPlayer(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if username.Username == "" { + return + } + id := ctx.Params().Get("id") + status := getChessRoomStatus(id) + if status.Players != username.Username { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("房主方可添加机器人")) + return + } + status.Players += ",ai--" + id + status.Status = initChessPawns.Status + status.Current = username.Username + status.IsEnd = initChessPawns.IsEnd + status.Winner = initChessPawns.Winner + status.ChessId = initChessPawns.ChessId + db := database.GetInstance().GetMysqlDb() + if err := db.Save(&status).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", status)) + if utils.ErrHandle(ctx, err) { + return + } +} +func updateChessAiStep(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if username.Username == "" { + return + } + id := ctx.Params().Get("id") + status := getChessRoomStatus(id) + if players := strings.Split(status.Players, ","); players[0] != username.Username || players[1] != "ai--"+id { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("房间人员配置错误")) + return + } + if status.Current != "ai--"+id { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("当前非ai回合")) + return + } + stepList := strings.Split(getAiChessStep(chessPawnStatusToFen(chessPawnStrToSlice(status.Status)), "queryall", "b"), "|") + var tmpNum int + if strings.Index(stepList[0], "score:??") != -1 { + rand.NewSource(time.Now().Unix()) + tmpNum = rand.Intn(len(stepList)) + } else { + tmpNum = 0 + } + stepTmp := strings.Split(stepList[tmpNum], ",")[0] + //fmt.Println(stepTmp, chessPawnStatusToFen(chessPawnStrToSlice(status.Status))) + if strings.Index(stepTmp, "move:") == -1 { + ctx.StatusCode(iris.StatusInternalServerError) + ctx.SetErr(errors.New("机器人无合适步骤,对局结束")) + return + } + step := strings.Split(stepTmp, "move:")[1] + tmp := "abcdefghi0123456789" + statusList := chessPawnStrToSlice(status.Status) + start0 := 8 - strings.IndexByte(tmp, step[0]) + end0 := 8 - strings.IndexByte(tmp, step[2]) + start1 := strings.IndexByte(tmp, step[1]) - 9 + end1 := strings.IndexByte(tmp, step[3]) - 9 + statusList[end1][end0] = statusList[start1][start0] + statusList[start1][start0] = "-1" + status.Status = chessPawnSliceToStr(statusList) + status.ChessId = statusList[end1][end0] + status.Current = username.Username + db := database.GetInstance().GetMysqlDb() + if err := db.Save(&status).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + err := ctx.JSON(utils.FormatRes(200, "", status)) + if utils.ErrHandle(ctx, err) { + return + } +} + +func testChess(ctx iris.Context) { + var res [][]string + db := database.GetInstance().GetMysqlDb() + var status []model.ChessStatus + if err := db.Find(&status).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + for _, s := range status { + stepTmp := getAiChessStep(chessPawnStatusToFen(chessPawnStrToSlice(s.Status)), "queryall", "b") + //fmt.Println(s.Status, chessPawnStatusToFen(chessPawnStrToSlice(s.Status))) + step := strings.Split(stepTmp, "move:")[1] + tmp := "abcdefghi0123456789" + //statusList := chessPawnStrToSlice(status.Status) + start0 := strings.IndexByte(tmp, step[0]) + end0 := strings.IndexByte(tmp, step[2]) + start1 := strings.IndexByte(tmp, step[1]) - 9 + end1 := strings.IndexByte(tmp, step[3]) - 9 + statusTmp := chessPawnStrToSlice(s.Status) + start := statusTmp[start1][start0] + end := statusTmp[end1][end0] + res = append(res, []string{start, end}) + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", res)) + if utils.ErrHandle(ctx, err) { + return + } +} + +func resetRoom(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if username.Username == "" { + return + } + id := ctx.Params().Get("id") + status := getChessRoomStatus(id) + if utils.DataIsNil(status) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("房间号不存在")) + return + } + if strings.Split(status.Players, ",")[0] != username.Username { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("房主方可重置房间")) + return + } + db := database.GetInstance().GetMysqlDb() + status.Status = initChessPawns.Status + status.Current = username.Username + status.IsEnd = initChessPawns.IsEnd + status.Winner = initChessPawns.Winner + status.ChessId = initChessPawns.ChessId + if err := db.Save(&status).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", status)) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 新建房间 +func newChessRoom(ctx iris.Context) { + creator := utils.GetLoginUser(ctx) + if creator.Username == "" { + return + } + db := database.GetInstance().GetMysqlDb() + status := model.ChessStatus{ + Players: creator.Username, + Current: creator.Username, + IsEnd: initChessPawns.IsEnd, + Status: initChessPawns.Status, + Winner: initChessPawns.Winner, + ChessId: initChessPawns.ChessId, + } + if err := db.Create(&status).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", status.ID)) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 获取所有房间 +func getChessRooms(ctx iris.Context) { + db := database.GetInstance().GetMysqlDb() + var rooms []model.ChessStatus + if err := db.Find(&rooms).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", rooms)) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 加入房间 +func joinChessRoom(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + id := ctx.Params().Get("id") + status := getChessRoomStatus(id) + if utils.DataIsNil(status) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("房间号不存在")) + return + } + if len(strings.Split(status.Players, ",")) >= 2 { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("房间人数已满")) + return + } + if utils.CheckListItem(strings.Split(status.Players, ","), username.Username) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("不可重复加入房间")) + return + } + status.Players += fmt.Sprintf(",%s", username.Username) + db := database.GetInstance().GetMysqlDb() + if err := db.Save(&status).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if err := db.Create(&model.ChessStatusLog{ + RoomId: status.ID, + Players: status.Players, + Current: status.Current, + IsEnd: status.IsEnd, + Status: status.Status, + Winner: status.Winner, + ChessId: status.ChessId, + Time: status.CreatedAt, + }).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", status)) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 离开房间 +func leaveChessRoom(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + id := ctx.Params().Get("id") + status := getChessRoomStatus(id) + if utils.DataIsNil(status) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("房间号不存在")) + return + } + if !utils.CheckListItem(strings.Split(status.Players, ","), username.Username) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("未加入该房间")) + return + } + for _, v := range strings.Split(status.Players, ",") { + if v != username.Username { + status.Players = v + } + } + db := database.GetInstance().GetMysqlDb() + if status.Players == username.Username || status.Players == "ai--"+id { + if err := db.Delete(&status).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + } else { + status.Current = status.Players + status.ChessId = initChessPawns.ChessId + status.IsEnd = initChessPawns.IsEnd + status.Winner = initChessPawns.Winner + status.Status = initChessPawns.Status + if err := db.Save(&status).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", status)) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 获取房间状态接口 +func getChessRoom(ctx iris.Context) { + id := ctx.Params().Get("id") + status := getChessRoomStatus(id) + if utils.DataIsNil(status) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("房间号不存在")) + return + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", status)) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 更新棋盘状态 +func updateChessStatus(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + id := ctx.Params().Get("id") + pawn := ctx.URLParam("pawn") + des := ctx.URLParam("des") + status := getChessRoomStatus(id) + if utils.DataIsNil(status) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("房间号不存在")) + return + } + if len(strings.Split(status.Players, ",")) < 2 { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("房间人数不足")) + return + } + pawnNum, _ := strconv.Atoi(pawn) + if (strings.Split(status.Players, ",")[0] == username.Username && pawnNum > 15) || (strings.Split(status.Players, ",")[1] == username.Username && pawnNum < 16) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("只能移动自己的棋子")) + return + } + if status.Current != username.Username { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("当前不是你的回合")) + return + } + if checkChessStep(pawn, status.Status, des) { + tmpStatus := chessPawnStrToSlice(status.Status) + locTmp := getPawnLoc(status.Status, pawn) + toTmp0, _ := strconv.Atoi(strings.Split(des, ",")[0]) + toTmp1, _ := strconv.Atoi(strings.Split(des, ",")[1]) + tmpStatus[locTmp[0]][locTmp[1]] = "-1" + tmpStatus[toTmp0][toTmp1] = pawn + status.Status = chessPawnSliceToStr(tmpStatus) + status.ChessId = pawn + for _, v := range strings.Split(status.Players, ",") { + if v != username.Username { + status.Current = v + } + } + if checkWinner(status.Status) == 1 { + status.Winner = username.Username + status.IsEnd = true + status.Current = "" + } + } else { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("当前无法移到该位置")) + return + } + db := database.GetInstance().GetMysqlDb() + if checkWinner(status.Status) == 2 { + for _, v := range strings.Split(status.Players, ",") { + if v != username.Username { + status.Winner = v + status.IsEnd = true + } + } + } + if err := db.Save(&status).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if err := db.Create(&model.ChessStatusLog{ + RoomId: status.ID, + Current: status.Current, + Players: status.Players, + IsEnd: status.IsEnd, + Status: status.Status, + Winner: status.Winner, + ChessId: status.ChessId, + Time: status.CreatedAt, + }).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", status)) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 获取房间状态方法 +func getChessRoomStatus(id string) model.ChessStatus { + db := database.GetInstance().GetMysqlDb() + var status model.ChessStatus + if err := db.Where("id = ?", id).First(&status).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + return status +} + +func checkWinner(status string) int { + if utils.DataIsNil(getPawnLoc(status, "4")) || utils.DataIsNil(getPawnLoc(status, "20")) { + return 1 + } + if checkNullPath(chessPawnStrToSlice(status), getPawnLoc(status, "4"), getPawnLoc(status, "20")) { + return 2 + } + return 0 +} + +// 获取棋子位置 +func getPawnLoc(statusStr string, pawn string) [2]int { + var res [2]int + status := chessPawnStrToSlice(statusStr) + for row, i := range status { + for col, j := range i { + if j == pawn { + res[0] = row + res[1] = col + } + } + } + return res +} + +// 判断棋子是否可移动 +func checkChessStep(pawn string, statusStr string, to string) bool { + locList := getPawnLoc(statusStr, pawn) + status := chessPawnStrToSlice(statusStr) + locTmp0 := locList[0] + locTmp1 := locList[1] + toTmp := strings.Split(to, ",") + toTmp0, _ := strconv.Atoi(toTmp[0]) + toTmp1, _ := strconv.Atoi(toTmp[1]) + //目标位置超出棋盘 + if toTmp0 < 0 || toTmp1 < 0 || toTmp0 > 10 || toTmp1 > 8 { + return false + } + pawnNum, _ := strconv.Atoi(pawn) + toPawnNum, _ := strconv.Atoi(status[toTmp0][toTmp1]) + if (pawnNum < 16 && toPawnNum < 16 && toPawnNum >= 0) || (pawnNum > 15 && toPawnNum > 15) { + return false + } + switch pawn { + case "0", "8", "16", "24": //车:同行列,路径为空 + if (toTmp0 == locTmp0 && toTmp1 != locTmp1) || (toTmp1 == locTmp1 && toTmp0 != locTmp0) && checkNullPath(status, [2]int{locTmp0, locTmp1}, [2]int{toTmp0, toTmp1}) { + return true + } + case "1", "7", "17", "23": //马:日字,前后左右无子 + if (utils.IntAbs(toTmp0-locTmp0) == 1 && utils.IntAbs(toTmp1-locTmp1) == 2 && status[locTmp0][(locTmp1+toTmp1)/2] == "-1") || (utils.IntAbs(toTmp0-locTmp0) == 2 && utils.IntAbs(toTmp1-locTmp1) == 1 && status[(locTmp0+toTmp0)/2][locTmp1] == "-1") { + return true + } + case "2", "6", "18", "22": //象:田字,不过河,中间格子为空 + if utils.IntAbs(toTmp0-locTmp0) == 2 && utils.IntAbs(toTmp1-locTmp1) == 2 && ((locTmp0 < 5 && toTmp0 < 5) || (locTmp0 > 4 && toTmp0 > 4)) && status[(locTmp0+toTmp0)/2][(locTmp1+toTmp1)/2] == "-1" { + return true + } + case "3", "5", "19", "21": //士:斜着走,不超出方格 + if toTmp1 < 3 || toTmp1 > 5 || (locTmp0 < 5 && toTmp0 > 2) || (locTmp0 > 4 && toTmp0 < 7) { + return false + } + if utils.IntAbs(toTmp0-locTmp0) == 1 && utils.IntAbs(toTmp1-locTmp1) == 1 { + return true + } + case "4", "20": //将:走一步,不超出方格 + if toTmp1 < 3 || toTmp1 > 5 || (locTmp0 < 5 && toTmp0 > 2) || (locTmp0 > 4 && toTmp0 < 7) { + return false + } + if utils.IntAbs(toTmp0-locTmp0)+utils.IntAbs(toTmp1-locTmp1) == 1 { + return true + } + case "9", "10", "25", "26": //炮:同行列,路径不为空,目标位置不为空 + if ((toTmp0 == locTmp0 && toTmp1 != locTmp1) || (toTmp1 == locTmp1 && toTmp0 != locTmp0)) && ((!checkNullPath(status, [2]int{locTmp0, locTmp1}, [2]int{toTmp0, toTmp1}) && status[toTmp0][toTmp1] != "-1") || (checkNullPath(status, [2]int{locTmp0, locTmp1}, [2]int{toTmp0, toTmp1}) && status[toTmp0][toTmp1] == "-1")) { + return true + } + case "11", "12", "13", "14", "15": //兵:走一步,过河后可横移 + if (toTmp0 < 5 && toTmp1 == locTmp1 && toTmp0-locTmp0 == 1) || (toTmp0 > 4 && utils.IntAbs(toTmp0-locTmp0+toTmp1-locTmp1) == 1) { + return true + } + case "27", "28", "29", "30", "31": //兵:走一步,过河后可横移 + if (toTmp0 > 4 && toTmp1 == locTmp1 && toTmp0-locTmp0 == -1) || (toTmp0 < 5 && utils.IntAbs(toTmp0-locTmp0+toTmp1-locTmp1) == 1) { + return true + } + default: + return false + } + return false +} + +// 棋盘状态字符串转数组 +func chessPawnStrToSlice(status string) [][]string { + var res [][]string + for _, i := range strings.Split(status, ";") { + var colList []string + for _, j := range strings.Split(i, ",") { + colList = append(colList, j) + } + res = append(res, colList) + } + return res +} + +// 棋盘状态数组转字符串 +func chessPawnSliceToStr(status [][]string) string { + var resList []string + for _, i := range status { + resList = append(resList, strings.Join(i, ",")) + } + res := strings.Join(resList, ";") + return res +} + +func chessPawnStatusToFen(status [][]string) string { + var res []string + for _, i := range status { + resTmp := "" + count := 0 + for col, j := range i { + if j == "-1" && col == len(i)-1 { + resTmp += strconv.Itoa(count + 1) + } else if j == "-1" { + count += 1 + } else { + if count != 0 { + resTmp += strconv.Itoa(count) + count = 0 + } + switch j { + case "0", "8": + resTmp += "R" + case "1", "7": + resTmp += "N" + case "2", "6": + resTmp += "B" + case "3", "5": + resTmp += "A" + case "4": + resTmp += "K" + case "9", "10": + resTmp += "C" + case "11", "12", "13", "14", "15": + resTmp += "P" + case "16", "24": + resTmp += "r" + case "17", "23": + resTmp += "n" + case "18", "22": + resTmp += "b" + case "19", "21": + resTmp += "a" + case "20": + resTmp += "k" + case "25", "26": + resTmp += "c" + case "27", "28", "29", "30", "31": + resTmp += "p" + } + } + } + res = append(res, resTmp) + } + return utils.Reverse(strings.Join(res, "/")) +} + +// 判断路径是否为空 +func checkNullPath(status [][]string, start [2]int, end [2]int) bool { + if start[0] == end[0] { + path := []int{start[1], end[1]} + sort.Ints(path) + for i := path[0] + 1; i < path[1]; i++ { + if status[start[0]][i] != "-1" { + return false + } + } + return true + } + if start[1] == end[1] { + path := []int{start[0], end[0]} + sort.Ints(path) + for i := path[0] + 1; i < path[1]; i++ { + if status[i][start[1]] != "-1" { + return false + } + } + return true + } + return false +} + +func getAiChessStep(fenStatus string, t string, p string) string { + urlPath, err := url.Parse("http://www.chessdb.cn/chessdb.php") + params := url.Values{} + params.Set("action", t) + params.Set("board", fmt.Sprintf("%s %s", fenStatus, p)) + params.Set("showall", "1") + urlPath.RawQuery = params.Encode() + res, err := http.Get(urlPath.String()) + if err != nil { + return err.Error() + } + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + } + }(res.Body) + + body, _ := io.ReadAll(res.Body) + return string(body) +} diff --git a/api_iris/service/api/chessExport.go b/api_iris/service/api/chessExport.go new file mode 100644 index 0000000..764d74d --- /dev/null +++ b/api_iris/service/api/chessExport.go @@ -0,0 +1,19 @@ +package api + +import ( + "github.com/kataras/iris/v12" + "main/jwtSet" +) + +func Chess(party iris.Party) { + party.Post("/", jwtSet.Jwt.Serve, newChessRoom) + party.Get("/", getChessRooms) + party.Get("/{id:int}", getChessRoom) + party.Post("/{id:int}", jwtSet.Jwt.Serve, joinChessRoom) + party.Delete("/{id:int}", jwtSet.Jwt.Serve, leaveChessRoom) + party.Put("/{id:int}", jwtSet.Jwt.Serve, updateChessStatus) + party.Get("/{id:int}/reset", jwtSet.Jwt.Serve, resetRoom) + party.Post("/{id:int}/ai", jwtSet.Jwt.Serve, addAiChessPlayer) + party.Put("/{id:int}/ai", jwtSet.Jwt.Serve, updateChessAiStep) + party.Get("/test", testChess) +} diff --git a/api_iris/service/api/file.go b/api_iris/service/api/file.go new file mode 100644 index 0000000..1b7f2a2 --- /dev/null +++ b/api_iris/service/api/file.go @@ -0,0 +1,319 @@ +package api + +import ( + "errors" + "fmt" + "github.com/kataras/iris/v12" + "github.com/sirupsen/logrus" + "main/database" + "main/model" + "main/utils" + "mime/multipart" + "os" + "path" + "path/filepath" + "strings" + "time" +) + +func getRootFile(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + userPath := fmt.Sprintf("./upload/%s", username.Username) + if !utils.FileIsExist(userPath) { + err := os.MkdirAll(userPath, 0755) + if utils.ErrHandle(ctx, err) { + return + } + } + var files filesRes + fileList, err := os.ReadDir(userPath) + if utils.ErrHandle(ctx, err) { + return + } + for _, file := range fileList { + if file.IsDir() { + files.Dirs = append(files.Dirs, file.Name()) + } else { + var item fileItem + item.Name = file.Name() + f, err := os.Stat(path.Join(userPath, file.Name())) + if utils.ErrHandle(ctx, err) { + return + } + item.Size = f.Size() + item.Type = utils.GetFileType(f.Name()) + files.Files = append(files.Files, item) + } + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", files)) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 获取指定目录下文件 +func getFile(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + filePath := ctx.Params().Get("path") + userPath := fmt.Sprintf("./upload/%s/%s", username.Username, filePath) + if !utils.FileIsExist(userPath) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New(userPath + ":目录不存在")) + return + } + var files filesRes + fileList, err := os.ReadDir(userPath) + if utils.ErrHandle(ctx, err) { + return + } + for _, file := range fileList { + if file.IsDir() { + files.Dirs = append(files.Dirs, file.Name()) + } else { + var item fileItem + item.Name = file.Name() + f, err := os.Stat(path.Join(userPath, file.Name())) + if utils.ErrHandle(ctx, err) { + return + } + item.Size = f.Size() + item.Type = utils.GetFileType(f.Name()) + files.Files = append(files.Files, item) + } + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", files)) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 删除文件或目录 +func deleteFile(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + filePath := ctx.Params().Get("path") + userPath := fmt.Sprintf("./upload/%s/%s", username.Username, filePath) + if !utils.FileIsExist(userPath) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("目录不存在")) + return + } + if info, _ := os.Stat(userPath); info.IsDir() { + err := os.RemoveAll(userPath) + if utils.ErrHandle(ctx, err) { + return + } + } else { + err := os.Remove(userPath) + if utils.ErrHandle(ctx, err) { + return + } + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 上传头像 +func uploadAvatar(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + file, info, err := ctx.FormFile("file") + if utils.ErrHandle(ctx, err) { + return + } + defer func(file multipart.File) { + err = file.Close() + if utils.ErrHandle(ctx, err) { + return + } + }(file) + userPath := fmt.Sprintf("./static/%s", username.Username) + if !utils.FileIsExist(userPath) { + err = os.MkdirAll(userPath, 0755) + if utils.ErrHandle(ctx, err) { + return + } + } + fileType := strings.Split(info.Filename, ".")[len(strings.Split(info.Filename, "."))-1] + avatarName := fmt.Sprintf("./static/%s/avatar-%s.%s", username.Username, time.Now().Format("2006-01-02"), fileType) + _, err = ctx.SaveFormFile(info, avatarName) + if utils.ErrHandle(ctx, err) { + return + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", avatarName[1:])) + if utils.ErrHandle(ctx, err) { + return + } +} + +func uploadDir(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + filePath := ctx.Params().Get("path") + userPath := fmt.Sprintf("./upload/%s/%s", username.Username, filePath) + if utils.FileIsExist(userPath) { + ctx.StatusCode(iris.StatusInternalServerError) + ctx.SetErr(errors.New("目录已存在")) + return + } + err := os.MkdirAll(userPath, 0755) + if utils.ErrHandle(ctx, err) { + return + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 上传文件 +func uploadFile(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + filePath := ctx.Params().Get("path") + file, info, err := ctx.FormFile("file") + if utils.ErrHandle(ctx, err) { + return + } + defer func(file multipart.File) { + err = file.Close() + if utils.ErrHandle(ctx, err) { + return + } + }(file) + userPath := fmt.Sprintf("./upload/%s/%s", username.Username, filePath) + if !utils.FileIsExist(userPath) { + err = os.MkdirAll(userPath, 0755) + if utils.ErrHandle(ctx, err) { + return + } + } + _, err = ctx.SaveFormFile(info, fmt.Sprintf("./upload/%s/%s/%s", username.Username, filePath, info.Filename)) + if utils.ErrHandle(ctx, err) { + return + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 文件下载,转到nginx-upload目录 +func downloadFile(ctx iris.Context) { + authToken := ctx.GetCookie("token") + activeTime := time.Now().Add(-2 * time.Hour) + var userToken model.JwtKeys + db := database.GetInstance().GetMysqlDb() + if err := db.Where("token = ? and created_at >= ?", authToken, activeTime).First(&userToken).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if utils.DataIsNil(userToken.Username) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("未登录")) + return + } + filePath := ctx.Params().Get("path") + userPath := fmt.Sprintf("./upload/%s/%s", userToken.Username, filePath) + if !utils.FileIsExist(userPath) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("文件不存在")) + return + } + if info, _ := os.Stat(userPath); info.IsDir() { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("只可下载文件")) + return + } + ctx.Recorder().Header().Add("X-Accel-Redirect", fmt.Sprintf("/upload/%s/%s", userToken.Username, filePath)) + ctx.Recorder().Header().Add("X-Accel-Charset", "utf-8") + ctx.Recorder().Header().Add("Content-Disposition", "attachment") + ctx.Recorder().Header().Add("Content-Type", "application/octet-stream; charset=utf-8") + return +} + +func getDownloadFileType(ctx iris.Context) { + authToken := ctx.GetCookie("token") + activeTime := time.Now().Add(-2 * time.Hour) + var userToken model.JwtKeys + var res videoM3u8 + db := database.GetInstance().GetMysqlDb() + if err := db.Where("token = ? and created_at >= ?", authToken, activeTime).First(&userToken).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if utils.DataIsNil(userToken.Username) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("未登录")) + return + } + username := userToken.Username + filePath := ctx.Params().Get("path") + userPath := fmt.Sprintf("./upload/%s/%s", username, filePath) + currentPath, _ := filepath.Split(filePath) + filename := strings.TrimSuffix(path.Base(userPath), path.Ext(userPath)) + res.Video = utils.GetFileType(userPath) == "video" + res.M3u8 = utils.FileIsExist(fmt.Sprintf("./upload-video/%s/%s%s/%s.m3u8", username, currentPath, filename, filename)) + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", res)) + if err != nil { + return + } + return +} + +func downloadVideo(ctx iris.Context) { + authToken := ctx.GetCookie("token") + activeTime := time.Now().Add(-2 * time.Hour) + var userToken model.JwtKeys + db := database.GetInstance().GetMysqlDb() + if err := db.Where("token = ? and created_at >= ?", authToken, activeTime).First(&userToken).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if utils.DataIsNil(userToken.Username) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("未登录")) + return + } + username := userToken.Username + filePath := ctx.Params().Get("path") + userPath := fmt.Sprintf("./upload/%s/%s", username, filePath) + currentPath, _ := filepath.Split(filePath) + filename := strings.TrimSuffix(path.Base(userPath), path.Ext(userPath)) + //fmt.Println(filePath, currentPath, filename) + if path.Ext(userPath) != ".ts" && !utils.FileIsExist(userPath) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("文件不存在")) + return + } + if info, err := os.Stat(userPath); err == nil && info.IsDir() { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("只可下载文件")) + return + } + if utils.GetFileType(userPath) == "video" && utils.FileIsExist(fmt.Sprintf("./upload-video/%s/%s%s/%s.m3u8", username, currentPath, filename, filename)) { + ctx.Recorder().Header().Add("X-Accel-Redirect", fmt.Sprintf("/upload-video/%s/%s%s/%s.m3u8", username, currentPath, filename, filename)) + } else if utils.GetFileType(userPath) == "video" { + ctx.Recorder().Header().Add("X-Accel-Redirect", fmt.Sprintf("/upload/%s/%s", username, filePath)) + } else { + tsPath := fmt.Sprintf("./upload-video/%s/%s%s/%s.ts", username, currentPath, filename[:len(filename)-6], filename) + ctx.Recorder().Header().Add("X-Accel-Redirect", tsPath[1:]) + } + ctx.Recorder().Header().Add("X-Accel-Charset", "utf-8") + ctx.Recorder().Header().Add("Content-Disposition", "attachment") + ctx.Recorder().Header().Add("Content-Type", "application/octet-stream; charset=utf-8") + return +} diff --git a/api_iris/service/api/fileExport.go b/api_iris/service/api/fileExport.go new file mode 100644 index 0000000..9ace297 --- /dev/null +++ b/api_iris/service/api/fileExport.go @@ -0,0 +1,18 @@ +package api + +import ( + "github.com/kataras/iris/v12" + "main/jwtSet" +) + +func File(party iris.Party) { + party.Get("/upload", jwtSet.Jwt.Serve, getRootFile) + party.Get("/upload/{path:path}", jwtSet.Jwt.Serve, getFile) + party.Post("/upload/{path:path}", jwtSet.Jwt.Serve, uploadDir) + party.Post("/upload-file/{path:path}", jwtSet.Jwt.Serve, uploadFile) + party.Delete("/upload/{path:path}", jwtSet.Jwt.Serve, deleteFile) + party.Post("/static/avatar", jwtSet.Jwt.Serve, uploadAvatar) + party.Get("/download/{path:path}", downloadFile) + party.Get("/download-video-check/{path:path}", getDownloadFileType) + party.Get("/download-video/{path:path}", downloadVideo) +} diff --git a/api_iris/service/api/init.go b/api_iris/service/api/init.go new file mode 100644 index 0000000..6d888f1 --- /dev/null +++ b/api_iris/service/api/init.go @@ -0,0 +1,13 @@ +package api + +import ( + "github.com/kataras/iris/v12" + "main/utils" +) + +func Apis(ctx iris.Context) { + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", "allApis")) + if err != nil { + return + } +} diff --git a/api_iris/service/api/menus.go b/api_iris/service/api/menus.go new file mode 100644 index 0000000..3a1167c --- /dev/null +++ b/api_iris/service/api/menus.go @@ -0,0 +1,55 @@ +package api + +import ( + "fmt" + "github.com/kataras/iris/v12" + "main/utils" + "regexp" +) + +func getMenu(ctx iris.Context) { + var resTmp []subMenu + var res []resMenu + user := utils.GetLoginUser(ctx) + if utils.DataIsNil(user) { + return + } + if len(utils.MenuList) == 0 { + utils.UpdateMenuList() + } + resTmp = append(resTmp, subMenu{MenuId: "000", Name: "首页", Icon: "bi-emoji-wink", Path: "/", RouteOnly: false}) + for _, menu := range utils.MenuList { + patternWhiteList := fmt.Sprintf("^%s,|,%s,|,%s$|%s", user.Username, user.Username, user.Username, user.Username) + regWhiteList := regexp.MustCompile(patternWhiteList) + patternUserType := fmt.Sprintf("^%s,|,%s,|,%s$|%s", user.Type, user.Type, user.Type, user.Type) + regUserType := regexp.MustCompile(patternUserType) + if match := regWhiteList.MatchString(menu.WhiteList); match || regUserType.MatchString(menu.UserType) { + resTmp = append(resTmp, subMenu{ + MenuId: menu.MenuId, + Name: menu.Name, + Icon: menu.Icon, + Path: menu.Path, + RouteOnly: menu.RouteOnly, + }) + } + } + for _, menu := range resTmp { + if menu.RouteOnly { + res = append(res, resMenu{MenuId: menu.MenuId, Name: menu.Name, Icon: menu.Icon, Path: menu.Path, RouteOnly: menu.RouteOnly}) + continue + } + if len(menu.MenuId) == 3 { + var tmp []subMenu + for _, sub := range resTmp { + if len(sub.MenuId) == 5 && sub.MenuId[0:3] == menu.MenuId && !sub.RouteOnly { + tmp = append(tmp, sub) + } + } + res = append(res, resMenu{MenuId: menu.MenuId, Name: menu.Name, Icon: menu.Icon, Path: menu.Path, Detail: tmp}) + } + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", res)) + if utils.ErrHandle(ctx, err) { + return + } +} diff --git a/api_iris/service/api/menusExport.go b/api_iris/service/api/menusExport.go new file mode 100644 index 0000000..12d1f7e --- /dev/null +++ b/api_iris/service/api/menusExport.go @@ -0,0 +1,10 @@ +package api + +import ( + "github.com/kataras/iris/v12" + "main/jwtSet" +) + +func Menus(party iris.Party) { + party.Get("/", jwtSet.Jwt.Serve, getMenu) +} diff --git a/api_iris/service/api/notes.go b/api_iris/service/api/notes.go new file mode 100644 index 0000000..40d82a2 --- /dev/null +++ b/api_iris/service/api/notes.go @@ -0,0 +1,210 @@ +package api + +import ( + "errors" + "fmt" + "github.com/kataras/iris/v12" + "github.com/sirupsen/logrus" + "main/database" + "main/model" + "main/utils" + "mime/multipart" + "os" + "time" +) + +func getNotesList(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + var notes []model.UserNotes + var res []noteParam + db := database.GetInstance().GetMysqlDb() + if err := db.Where("username = ?", username.Username).Select("id", "content").Find(¬es).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + for _, item := range notes { + if len(item.Content) > 100 { + item.Content = item.Content[:30] + "\n......" + } + res = append(res, noteParam{ + ID: item.ID, + Content: item.Content, + }) + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", res)) + if utils.ErrHandle(ctx, err) { + return + } +} + +func addNote(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + var note noteParam + err := ctx.ReadJSON(¬e) + if utils.ErrHandle(ctx, err) { + return + } + db := database.GetInstance().GetMysqlDb() + if err1 := db.Create(&model.UserNotes{ + Username: username.Username, + Content: note.Content, + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + var newNote model.UserNotes + if err1 := db.Where("username = ?", username.Username).Order("id desc").First(&newNote).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err) + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", newNote.ID)) + if utils.ErrHandle(ctx, err) { + return + } +} + +func getNote(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + db := database.GetInstance().GetMysqlDb() + var note model.UserNotes + if err := db.Where("username = ? and id = ?", username.Username, ctx.Params().Get("id")).Find(¬e).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if utils.DataIsNil(note) { + ctx.StatusCode(iris.StatusInternalServerError) + ctx.SetErr(errors.New("not found")) + return + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", note)) + if utils.ErrHandle(ctx, err) { + return + } +} + +func deleteNote(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + var note model.UserNotes + db := database.GetInstance().GetMysqlDb() + if err := db.Where("username = ? and id = ?", username.Username, ctx.Params().Get("id")).Find(¬e).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if utils.DataIsNil(note) { + ctx.StatusCode(iris.StatusInternalServerError) + ctx.SetErr(errors.New("not found")) + return + } + if err := db.Delete(¬e).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } +} + +func updateNote(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + var note model.UserNotes + db := database.GetInstance().GetMysqlDb() + if err := db.Where("username = ? and id = ?", username.Username, ctx.Params().Get("id")).Find(¬e).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if utils.DataIsNil(note) { + ctx.StatusCode(iris.StatusInternalServerError) + ctx.SetErr(errors.New("not found")) + return + } + var newNote noteParam + err := ctx.ReadJSON(&newNote) + if utils.ErrHandle(ctx, err) { + return + } + if err1 := db.Model(¬e).Updates(model.UserNotes{ + Content: newNote.Content, + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err) + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } +} + +func uploadNoteFile(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + noteId := ctx.Params().Get("id") + userPath := fmt.Sprintf("./upload-note/%s/%s", username.Username, noteId) + if !utils.FileIsExist(userPath) { + err := os.MkdirAll(userPath, 0755) + if utils.ErrHandle(ctx, err) { + return + } + } + file, info, err := ctx.FormFile("file") + if utils.ErrHandle(ctx, err) { + return + } + defer func(file multipart.File) { + err = file.Close() + if utils.ErrHandle(ctx, err) { + return + } + }(file) + filePath := fmt.Sprintf("%s/%s", userPath, info.Filename) + _, err = ctx.SaveFormFile(info, filePath) + if utils.ErrHandle(ctx, err) { + return + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", fmt.Sprintf("/api/notes/%s/file/%s", noteId, info.Filename))) + if utils.ErrHandle(ctx, err) { + return + } +} + +func getNoteFile(ctx iris.Context) { + noteId := ctx.Params().Get("id") + authToken := ctx.GetCookie("token") + activeTime := time.Now().Add(-2 * time.Hour) + var userToken model.JwtKeys + db := database.GetInstance().GetMysqlDb() + if err := db.Where("token = ? and created_at >= ?", authToken, activeTime).First(&userToken).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if utils.DataIsNil(userToken.Username) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("未登录")) + return + } + filename := ctx.Params().Get("filename") + userPath := fmt.Sprintf("./upload-note/%s/%s/%s", userToken.Username, noteId, filename) + if !utils.FileIsExist(userPath) { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("文件不存在")) + return + } + if info, _ := os.Stat(userPath); info.IsDir() { + ctx.StatusCode(iris.StatusBadRequest) + ctx.SetErr(errors.New("只可下载文件")) + return + } + ctx.Recorder().Header().Add("X-Accel-Redirect", userPath[1:]) + ctx.Recorder().Header().Add("X-Accel-Charset", "utf-8") + ctx.Recorder().Header().Add("Content-Disposition", "attachment") + ctx.Recorder().Header().Add("Content-Type", "application/octet-stream; charset=utf-8") + return +} diff --git a/api_iris/service/api/notesExport.go b/api_iris/service/api/notesExport.go new file mode 100644 index 0000000..aa92a36 --- /dev/null +++ b/api_iris/service/api/notesExport.go @@ -0,0 +1,16 @@ +package api + +import ( + "github.com/kataras/iris/v12" + "main/jwtSet" +) + +func Notes(party iris.Party) { + party.Get("/", jwtSet.Jwt.Serve, getNotesList) + party.Post("/", jwtSet.Jwt.Serve, addNote) + party.Get("/{id:int}", jwtSet.Jwt.Serve, getNote) + party.Put("/{id:int}", jwtSet.Jwt.Serve, updateNote) + party.Delete("/{id:int}", jwtSet.Jwt.Serve, deleteNote) + party.Post("/{id:int}/file", jwtSet.Jwt.Serve, uploadNoteFile) + party.Get("/{id:int}/file/{filename:string}", getNoteFile) +} diff --git a/api_iris/service/api/public.go b/api_iris/service/api/public.go new file mode 100644 index 0000000..2801110 --- /dev/null +++ b/api_iris/service/api/public.go @@ -0,0 +1,88 @@ +package api + +import ( + "github.com/kataras/iris/v12" + "github.com/sirupsen/logrus" + "main/utils" + "strconv" + "strings" + "time" +) + +// 获取节假日信息 +func getHolidays(ctx iris.Context) { + yearParam := ctx.URLParam("year") + if yearParam == "" { + yearParam = strconv.Itoa(time.Now().Year()) + } + logrus.Infoln("假期查询年份:", yearParam) + year, err := strconv.Atoi(yearParam) + var res []utils.ResHolidays + if utils.ErrHandle(ctx, err) { + return + } + if utils.ErrHandle(ctx, err) { + return + } + startDate := time.Date(year, 1, 1, 0, 0, 0, 0, time.Local) + for startDate.Year() == year { + isHoliday, name := utils.CheckHoliday(startDate.Format("2006-01-02")) + if startDate.Weekday() == time.Saturday || startDate.Weekday() == time.Sunday { + res = append(res, utils.ResHolidays{ + Date: startDate.Format("2006-01-02"), + HolidayNameCn: "周末", + }) + } else if isHoliday { + res = append(res, utils.ResHolidays{ + Date: startDate.Format("2006-01-02"), + HolidayNameCn: name, + }) + } + startDate = startDate.AddDate(0, 0, 1) + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", res)) + if err != nil { + return + } +} + +// 获取IP归属地 +func getIpLocation(ctx iris.Context) { + var ip string + if ctx.URLParam("ip") != "" { + ip = ctx.URLParam("ip") + } else { + ip = utils.GetRequestIp(ctx) + } + ipLocation, err := utils.GetIpLocation(ip) + if utils.ErrHandle(ctx, err) { + return + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", ipLocationData{ + Ip: ip, + Location: ipLocation, + })) + if err != nil { + return + } +} + +// 邮件发送 +func sendEmail(ctx iris.Context) { + receiver := strings.Split(ctx.URLParam("receiver"), ";") + subject := ctx.URLParam("subject") + content := ctx.URLParam("content") + err := utils.SendEmail(receiver, subject, content) + if utils.ErrHandle(ctx, err) { + logrus.Errorln(err) + return + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", "发送成功")) + if err != nil { + return + } +} + +func getSudokuCheck(ctx iris.Context) { + +} diff --git a/api_iris/service/api/publicExport.go b/api_iris/service/api/publicExport.go new file mode 100644 index 0000000..eab0faa --- /dev/null +++ b/api_iris/service/api/publicExport.go @@ -0,0 +1,10 @@ +package api + +import "github.com/kataras/iris/v12" + +func PublicApis(party iris.Party) { + party.Get("/holidays", getHolidays) + party.Get("/send_email", sendEmail) + party.Get("/get_ip_location", getIpLocation) + party.Get("/get_sudoku_check", getSudokuCheck) +} diff --git a/api_iris/service/api/sudoku.go b/api_iris/service/api/sudoku.go new file mode 100644 index 0000000..5416322 --- /dev/null +++ b/api_iris/service/api/sudoku.go @@ -0,0 +1,170 @@ +package api + +import ( + "errors" + "github.com/kataras/iris/v12" + "github.com/sirupsen/logrus" + "main/database" + "main/model" + "main/utils" + "strings" +) + +func getSudokuList(ctx iris.Context) { + var resSudokuList resSudokuListType + var startingSudokuList []sudokuListType + var newSudokuList []model.Sudoku + db := database.GetInstance().GetMysqlDb() + if err := db.Where("id not in (?)", db.Table("sudoku_statuses").Distinct("sudoku_id").Where("username = ? and deleted_at is null", "admin")).Find(&newSudokuList).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + for _, v := range newSudokuList { + resSudokuList.New = append(resSudokuList.New, sudokuListType{Sudoku: v.Sudoku, SudokuId: v.ID, Complete: false}) + } + queryId := db.Table("sudoku_statuses").Select("max(id)").Where("username = ? and deleted_at is null", "admin").Group("sudoku_id") + if queryId.Error != nil { + logrus.Errorln("sql执行失败:", queryId.Error) + } + queryStatus := db.Table("sudoku_statuses").Where("id in (?)", queryId) + if queryStatus.Error != nil { + logrus.Errorln("sql执行失败:", queryStatus.Error) + } + if err := db.Model(&model.Sudoku{}).Select("sudokus.sudoku, q.*").Joins("left join (?) q on sudokus.id = q.sudoku_id", queryStatus).Scan(&startingSudokuList).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + for _, v := range startingSudokuList { + if v.Complete { + resSudokuList.Complete = append(resSudokuList.Complete, v) + } else { + resSudokuList.Starting = append(resSudokuList.Starting, v) + } + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", resSudokuList)) + if utils.ErrHandle(ctx, err) { + return + } +} + +func addSudokuGame(ctx iris.Context) { + var sudokuParam addSudokuParam + var dbCheck model.Sudoku + var sudokuResult [9][9]string + err := ctx.ReadJSON(&sudokuParam) + if utils.ErrHandle(ctx, err) { + return + } + if sudokuParam.Result != "" { + sudokuResult, err = sudokuStringToList(sudokuParam.Result) + if utils.ErrHandle(ctx, err) { + return + } + } + db := database.GetInstance().GetMysqlDb() + if err1 := db.Where("sudoku = ?", sudokuParam.Sudoku).First(&dbCheck).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + if !utils.DataIsNil(dbCheck) { + utils.ErrHandle(ctx, errors.New("棋盘已存在")) + return + } + err, r := utils.CheckSudoku(sudokuParam.Sudoku) + if utils.ErrHandle(ctx, err) { + return + } + if r.Data.Check || checkSudokuCompleted(sudokuResult) { + if r.Data.Check { + if err1 := db.Create(&model.Sudoku{Sudoku: sudokuParam.Sudoku, Username: "admin", Result: r.Data.Result}).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + } else { + if err1 := db.Create(&model.Sudoku{Sudoku: sudokuParam.Sudoku, Username: "admin", Result: sudokuParam.Result}).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } + } else { + utils.ErrHandle(ctx, errors.New("检测题目无法完成,请上传解题结果")) + return + } +} + +func sudokuStringToList(sudoku string) ([9][9]string, error) { + var res [9][9]string + sudokuRows := strings.Split(sudoku, ";") + if len(sudokuRows) != 9 { + return res, errors.New("棋盘格式错误") + } + for r, row := range sudokuRows { + sudokuCols := strings.Split(row, ",") + if len(sudokuCols) != 9 { + return res, errors.New("棋盘格式错误") + } + for c, col := range sudokuCols { + if len(col) > 1 || strings.Index("123456789", col) == -1 { + return res, errors.New("棋盘格式错误") + } + res[r][c] = col + } + } + return res, nil +} + +func sudokuListToString(sudoku [9][9]string) string { + var res string + for r, _ := range sudoku { + var tmp string + for c, _ := range sudoku[r] { + if tmp == "" { + tmp = sudoku[r][c] + } else { + tmp += "," + sudoku[r][c] + } + } + if res == "" { + res = tmp + } else { + res += ";" + tmp + } + } + return res +} + +// 检查题目是否已完成 +func checkSudokuCompleted(sudoku [9][9]string) bool { + for r, _ := range sudoku { + for c, _ := range sudoku[r] { + if sudoku[r][c] == "" || !checkNum(r, c, sudoku) { + return false + } + } + } + return true +} + +// 检查题目当前位置是否符合要求 +func checkNum(r int, c int, sudoku [9][9]string) bool { + if sudoku[r][c] == "" { + return true + } + for i, n := range sudoku[r] { + if n == sudoku[r][c] && i != c { + return false + } + } + for i, _ := range sudoku { + if sudoku[i][c] == sudoku[r][c] && i != r { + return false + } + } + for row := (r % 3) * 3; row < (r%3)*3+2; row++ { + for col := (c % 3) * 3; col < (c%3)*3+2; col++ { + if sudoku[row][col] == sudoku[r][c] && row != r && col != c { + return false + } + } + } + return true +} diff --git a/api_iris/service/api/sudokuExport.go b/api_iris/service/api/sudokuExport.go new file mode 100644 index 0000000..1cd55a0 --- /dev/null +++ b/api_iris/service/api/sudokuExport.go @@ -0,0 +1,10 @@ +package api + +import ( + "github.com/kataras/iris/v12" +) + +func Sudoku(party iris.Party) { + party.Get("/", getSudokuList) + party.Post("/", addSudokuGame) +} diff --git a/api_iris/service/api/sysSettings.go b/api_iris/service/api/sysSettings.go new file mode 100644 index 0000000..a7ee50b --- /dev/null +++ b/api_iris/service/api/sysSettings.go @@ -0,0 +1,59 @@ +package api + +import ( + "github.com/kataras/iris/v12" + "github.com/sirupsen/logrus" + "main/database" + "main/model" + "main/utils" + "strings" +) + +func getSysIcons(ctx iris.Context) { + var icons []model.SysIcons + db := database.GetInstance().GetMysqlDb() + if err := db.Find(&icons).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", icons)) + if utils.ErrHandle(ctx, err) { + return + } +} + +func addSysIcons(ctx iris.Context) { + var params sysIconsParam + var icons []model.SysIcons + var sysIcons []model.SysIcons + err := ctx.ReadJSON(¶ms) + if utils.ErrHandle(ctx, err) { + return + } + db := database.GetInstance().GetMysqlDb() + if err1 := db.Find(&sysIcons).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + for _, iconStr := range strings.Split(params.Icons, ",") { + icon := strings.Join([]string{"bi", iconStr}, "-") + if !checkIconExist(icon, sysIcons) { + icons = append(icons, model.SysIcons{Icon: icon}) + sysIcons = append(sysIcons, model.SysIcons{Icon: icon}) + } + } + if err1 := db.Create(&icons).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } +} + +func checkIconExist(icon string, sysIcons []model.SysIcons) bool { + for _, sysIcon := range sysIcons { + if sysIcon.Icon == icon { + return true + } + } + return false +} diff --git a/api_iris/service/api/sysSettingsExport.go b/api_iris/service/api/sysSettingsExport.go new file mode 100644 index 0000000..b3cf1ce --- /dev/null +++ b/api_iris/service/api/sysSettingsExport.go @@ -0,0 +1,8 @@ +package api + +import "github.com/kataras/iris/v12" + +func SysSettings(party iris.Party) { + party.Get("/icon", getSysIcons) + party.Post("/icon", addSysIcons) +} diff --git a/api_iris/service/api/sysinfo.go b/api_iris/service/api/sysinfo.go new file mode 100644 index 0000000..9e801fa --- /dev/null +++ b/api_iris/service/api/sysinfo.go @@ -0,0 +1,96 @@ +package api + +import ( + "github.com/kataras/iris/v12" + "github.com/sirupsen/logrus" + "main/database" + "main/model" + "main/utils" + "strconv" + "strings" +) + +func getSystem(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + date := ctx.URLParam("date") + //username := "admin" + db := database.GetInstance().GetMysqlDb() + var systemList []resSystem + var ids []int + if err := db.Model(&model.SysInfo{}).Select("max(id)").Where("date = ? and username = ?", date, username.Username).Group("ip").Scan(&ids).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if err := db.Model(&model.SysInfo{}).Where("id in (?)", ids).Find(&systemList).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", systemList)) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 获取当前系统信息 +func getSysInfo(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + //username := "admin" + date := ctx.URLParam("date") + ip := ctx.URLParam("ip") + db := database.GetInstance().GetMysqlDb() + var sysInfoList []model.SysInfo + var res []resSysInfo + if err := db.Where("username = ? and date = ? and ip = ?", username.Username, date, ip).Find(&sysInfoList).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + for _, sysInfo := range sysInfoList { + var diskList []resDisk + diskLists := strings.Split(sysInfo.DiskPoint, ",") + for i, disk := range diskLists { + if strings.Contains(disk, "docker") { + continue + } + diskPer, err := strconv.ParseFloat(strings.Split(sysInfo.DiskPer, ",")[i], 64) + if utils.ErrHandle(ctx, err) { + return + } + diskList = append(diskList, resDisk{ + Point: disk, + Total: strings.Split(sysInfo.DiskTotal, ",")[i], + Used: strings.Split(sysInfo.DiskUsed, ",")[i], + Per: utils.Round(diskPer, 2), + }) + } + res = append(res, resSysInfo{ + Datetime: sysInfo.Datetime, + CpuPer: utils.Round(sysInfo.CpuPer, 2), + Mem: resMem{ + MemPer: utils.Round(sysInfo.MemPer, 2), + MemUsed: sysInfo.MemUsed, + MemTotal: sysInfo.MemTotal, + }, + Disk: diskList, + Net: resNet{ + Sent: sysInfo.SentSpeed, + Rec: sysInfo.RecSpeed, + }, + }) + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", res)) + if utils.ErrHandle(ctx, err) { + return + } +} + +//func updateSysInfo(ctx iris.Context) { +// date := ctx.URLParam("date") +// crontab.UpdateSysInfo(date) +// err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", fmt.Sprintf("%s 日期数据同步完成", date))) +// if utils.ErrHandle(ctx, err) { +// return +// } +//} diff --git a/api_iris/service/api/sysinfoExport.go b/api_iris/service/api/sysinfoExport.go new file mode 100644 index 0000000..e0f8c01 --- /dev/null +++ b/api_iris/service/api/sysinfoExport.go @@ -0,0 +1,12 @@ +package api + +import ( + "github.com/kataras/iris/v12" + "main/jwtSet" +) + +func SysInfo(party iris.Party) { + party.Get("/server", jwtSet.Jwt.Serve, getSystem) + party.Get("/", jwtSet.Jwt.Serve, getSysInfo) + //party.Get("/update", updateSysInfo) +} diff --git a/api_iris/service/api/type.d.go b/api_iris/service/api/type.d.go new file mode 100644 index 0000000..c5506de --- /dev/null +++ b/api_iris/service/api/type.d.go @@ -0,0 +1,178 @@ +package api + +import "time" + +// user----------------------------------------------------------------------------------------------- +type result struct { + Date string + Password string + ConfirmCode string + UpdatedAt time.Time + Key string + AesKey string +} + +// weather--------------------------------------------------------------------------------------------- +type resWeather struct { + Status int `json:"status"` + Message string `json:"message"` + Data resData `json:"data"` +} +type resData struct { + Forecast24h resDate `json:"forecast_24h"` +} +type resDate struct { + D0 res24h `json:"1"` + D1 res24h `json:"2"` +} +type res24h struct { + Time string `json:"time"` + MaxDegree string `json:"max_degree"` + MinDegree string `json:"min_degree"` + DayWeather string `json:"day_weather"` + DayWindDirection string `json:"day_wind_direction"` + DayWindPower string `json:"day_wind_power"` + NightWeather string `json:"night_weather"` + NightWindPower string `json:"night_wind_power"` + NightWindDirection string `json:"night_wind_direction"` +} +type resLocation struct { + Data map[string]string `json:"data"` + Message string `json:"message"` + Status int `json:"status"` +} + +// backgammon----------------------------------------------------------------------------------------------- +type typeRoomStatus struct { + RoomId int `json:"room_id"` + Player string `json:"player"` +} + +// file----------------------------------------------------------------------------------------------------- +type filesRes struct { + Dirs []string `json:"dirs"` + Files []fileItem `json:"files"` +} +type fileItem struct { + Name string `json:"name"` + Type string `json:"type"` + Size int64 `json:"size"` +} +type videoM3u8 struct { + Video bool `json:"video"` + M3u8 bool `json:"m3u8"` +} + +// notes------------------------------------------------------------------------------------------------------ +type noteParam struct { + ID uint `json:"id"` + Content string `json:"content"` +} + +// sysInfo------------------------------------------------------------------------------------------------------ +type resSysInfo struct { + Datetime string `json:"datetime"` + CpuPer float64 `json:"cpu_per"` + Mem resMem `json:"mem"` + Disk []resDisk `json:"disk"` + Net resNet `json:"net"` +} +type resMem struct { + MemTotal string `json:"mem_total"` + MemUsed string `json:"mem_used"` + MemPer float64 `json:"mem_per"` +} +type resDisk struct { + Point string `json:"point"` + Total string `json:"total"` + Used string `json:"used"` + Per float64 `json:"per"` +} +type resNet struct { + Sent string `json:"sent"` + Rec string `json:"rec"` +} +type resSystem struct { + Hostname string `json:"hostname"` + Ip string `json:"ip"` +} +type sysInfoIDs struct { + IDs []int `json:"ids"` +} + +// yeb---------------------------------------------------------------------------------------------------------- +type paramsBalance struct { + Card string `json:"card"` + Type bool `json:"type"` + Balance float64 `json:"balance"` +} + +type paramSZBalance struct { + Card string `json:"card"` + Type bool `json:"type"` // 1-支出;0-收入 + Amount float64 `json:"amount"` +} + +// 月余额 +type dateLog struct { + Date string `json:"date"` + Duration string `json:"duration"` + Changes float64 `json:"changes"` + Balance float64 `json:"balance"` + Detail []cardLog `json:"detail"` +} + +// 详情 +type cardLog struct { + Card string `json:"card"` + Balance float64 `json:"balance"` + Changes float64 `json:"changes"` +} + +// menus----------------------------------------------------------------------------- +type resMenu struct { + MenuId string `json:"menu_id"` + Name string `json:"name"` + Icon string `json:"icon"` + Path string `json:"path"` + RouteOnly bool `json:"route_only"` + Detail []subMenu `json:"detail"` +} + +type subMenu struct { + MenuId string `json:"menu_id"` + Name string `json:"name"` + Icon string `json:"icon"` + Path string `json:"path"` + RouteOnly bool `json:"route_only"` +} + +type sysIconsParam struct { + Icons string `json:"icons"` +} + +// sudoku----------------------------------------------------------------------------- +type resSudokuListType struct { + New []sudokuListType `json:"new"` + Starting []sudokuListType `json:"starting"` + Complete []sudokuListType `json:"complete"` +} + +type sudokuListType struct { + Sudoku string `json:"sudoku"` + SudokuId uint `json:"sudokuId"` + Username string `json:"username"` + Status string `json:"status"` + Complete bool `json:"complete"` + Id uint `json:"id"` +} +type addSudokuParam struct { + Sudoku string `json:"sudoku"` + Result string `json:"result"` +} + +// public ------------------------------------------------------------------------------ +type ipLocationData struct { + Ip string `json:"ip"` + Location string `json:"location"` +} diff --git a/api_iris/service/api/user.go b/api_iris/service/api/user.go new file mode 100644 index 0000000..09a45cd --- /dev/null +++ b/api_iris/service/api/user.go @@ -0,0 +1,365 @@ +package api + +import ( + "errors" + "fmt" + "github.com/golang-jwt/jwt/v4" + "github.com/jakehl/goid" + "github.com/kataras/iris/v12" + "github.com/sirupsen/logrus" + "main/database" + "main/jwtSet" + "main/model" + "main/service/admin" + "main/utils" + "math/rand" + "strconv" + "strings" + "time" +) + +// 返回加密后用户信息 +func userinfo(ctx iris.Context) { + user := utils.GetLoginUser(ctx) + if utils.DataIsNil(user) { + return + } + if user.Email != "" { + user.Email = fmt.Sprintf("%s****%s", user.Email[0:2], user.Email[len(user.Email)-7:]) + } + if user.Mobile != "" { + user.Mobile = fmt.Sprintf("%s****%s", user.Mobile[0:2], user.Mobile[len(user.Mobile)-4:]) + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", user)) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 更新用户信息 +func updateUserinfo(ctx iris.Context) { + username := utils.GetLoginUser(ctx) + if utils.DataIsNil(username) { + return + } + var params model.Userinfo + err := ctx.ReadJSON(¶ms) + if utils.ErrHandle(ctx, err) { + return + } + db := database.GetInstance().GetMysqlDb() + if strings.Contains(params.Email, "*") { + params.Email = "" + } + if strings.Contains(params.Mobile, "*") { + params.Mobile = "" + } + if err1 := db.Model(&model.Userinfo{}).Where("username = ?", username.Username).Updates(params).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + for i, u := range utils.UserList { + if u.Username == username.Username { + utils.UserList[i] = params + } + } + utils.UpdateUserInfo() + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 用户登录 +func login(ctx iris.Context) (string, error) { + username := ctx.URLParam("username") + password := ctx.URLParam("password") + confirmCode := ctx.URLParam("confirmCode") + auto := ctx.URLParam("auto") + var res result + db := database.GetInstance().GetMysqlDb() + if err := db. + Model(&model.User{}). + Select("users.password, users.confirm_code, users.updated_at, day_keys.key, day_keys.aes_key"). + Joins("left join day_keys on day_keys.user = users.username and day_keys.date = users.date"). + Where("users.username = ?", username). + Order("users.date desc"). + Scan(&res).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if utils.DataIsNil(res) { + return "", errors.New("用户不存在") + } + rsaDePass, err := utils.RsaDecrypt(password, res.Key) + if err != nil { + return "", err + } + if confirmCode == "" { + aesDePass, err := utils.DecryptByAes(res.Password, []byte(res.AesKey)) + if err != nil { + return "", err + } + //fmt.Println(string(aesDePass), rsaDePass) + if string(aesDePass) != rsaDePass { + return "", errors.New("用户信息错误") + } + } else if res.ConfirmCode != confirmCode { + return "", errors.New("验证码错误") + } else if res.ConfirmCode != "" && time.Now().After(res.UpdatedAt.Add(time.Minute*5)) { + return "", errors.New("验证码已过期") + } + priKeys, _ := utils.GetPrivateKeys(username, time.Now().Format("2006-01-02")) + newPwd, _ := utils.EncryptByAes([]byte(rsaDePass), []byte(priKeys.AesKey)) + if err1 := db. + Model(&model.User{}). + Where("username = ?", username). + Updates(map[string]interface{}{ + "password": newPwd, + "date": time.Now().Format("2006-01-02"), + "confirm_code": "", + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + rand1 := rand.New(rand.NewSource(time.Now().UnixNano())) + token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ + "username": username, + "iat": time.Now().Unix(), + "jti": strconv.Itoa(rand1.Int()), + "exp": time.Now().Add(2 * time.Hour).Unix(), + }) + jwtKey := jwtSet.GetJwtKeys().Key + tokenString, err := token.SignedString([]byte(jwtKey)) + if err != nil { + return "", err + } + ctx.SetCookieKV("is_login", "1", iris.CookieHTTPOnly(false), iris.CookieExpires(2*time.Hour)) + ctx.SetCookieKV("token", tokenString, iris.CookieHTTPOnly(false), iris.CookieExpires(2*time.Hour)) + ctx.SetCookieKV("loginTime", strconv.FormatInt(time.Now().UnixMilli(), 10), iris.CookieHTTPOnly(false)) + if err1 := db.Create(&model.UserAction{ + Username: username, + Action: "用户登录", + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + if err1 := db.Create(&model.JwtKeys{ + Username: username, + Date: time.Now().Format("2006-01-02"), + Key: jwtKey, + Token: tokenString, + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + if auto == "true" { + deviceId := goid.NewV4UUID() + location := admin.GetIpLocation(utils.GetRequestIp(ctx)) + ctx.SetCookieKV("deviceId", deviceId.String(), iris.CookieHTTPOnly(false)) + if err1 := db.Create(&model.UserAutoLogin{ + Username: username, + DeviceId: deviceId.String(), + Location: location, + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + } + return tokenString, nil +} + +// 用户注册 +func register(ctx iris.Context) (string, error) { + username := ctx.URLParam("username") + password := ctx.URLParam("password") + priKeys, err := utils.GetPrivateKeys(username, time.Now().Format("2006-01-02")) + if err != nil { + return "", err + } + dePass, err := utils.RsaDecrypt(password, priKeys.Key) + if err != nil { + return "", err + } + enPass, err := utils.EncryptByAes([]byte(dePass), []byte(priKeys.AesKey)) + if err != nil { + return "", err + } + db := database.GetInstance().GetMysqlDb() + var user []model.User + if err1 := db.Where("username = ?", username).Find(&user).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + if len(user) > 0 { + err = errors.New("user exists") + return "", err + } + if err1 := db.Create(&model.User{ + Username: username, + Password: enPass, + Date: time.Now().Format("2006-01-02"), + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + if len(utils.UserList) == 0 { + if err1 := db.Create(&model.Userinfo{ + Username: username, + Type: "admin", + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + } else { + if err1 := db.Create(&model.Userinfo{ + Username: username, + Type: "user", + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + } + utils.UpdateUserInfo() + rand1 := rand.New(rand.NewSource(time.Now().UnixNano())) + token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ + "username": username, + "iat": time.Now().Unix(), + "jti": strconv.Itoa(rand1.Int()), + "exp": time.Now().Add(2 * time.Hour).Unix(), + }) + jwtKey := jwtSet.GetJwtKeys().Key + tokenString, err := token.SignedString([]byte(jwtKey)) + if err != nil { + return "", err + } + ctx.SetCookieKV("is_login", "1", iris.CookieHTTPOnly(false), iris.CookieExpires(2*time.Hour)) + ctx.SetCookieKV("token", tokenString, iris.CookieHTTPOnly(false), iris.CookieExpires(2*time.Hour)) + ctx.SetCookieKV("loginTime", strconv.FormatInt(time.Now().UnixMilli(), 10), iris.CookieHTTPOnly(false)) + if err1 := db.Create(&model.UserAction{ + Username: username, + Action: "用户注册并登录", + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + if err1 := db.Create(&model.JwtKeys{ + Username: username, + Date: time.Now().Format("2006-01-02"), + Key: jwtKey, + Token: tokenString, + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + return tokenString, nil +} + +// 退出登录 +func logout(ctx iris.Context) { + db := database.GetInstance().GetMysqlDb() + username := utils.GetLoginUser(ctx) + auth := ctx.Values().Get("jwt") + deviceId := ctx.GetCookie("deviceId") + if auth == nil { + ctx.StatusCode(iris.StatusUnauthorized) + ctx.SetErr(errors.New("未登录")) + return + } + if err := db.Where("token = ?", auth.(*jwt.Token).Raw).Delete(&model.JwtKeys{}).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + ctx.RemoveCookie("is_login") + ctx.RemoveCookie("token") + if deviceId != "" { + ctx.RemoveCookie("deviceId") + if err := db.Where("device_id = ? and username = ?", deviceId, username).Delete(&model.UserAutoLogin{}).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + } + if err := db.Create(&model.UserAction{ + Username: username.Username, + Action: "用户注销登录", + }).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 重置密码 +func resetPwd(ctx iris.Context) { + username := ctx.URLParam("username") + //var user model.Userinfo + db := database.GetInstance().GetMysqlDb() + user := utils.GetUserInfo(username) + if utils.DataIsNil(user) { + ctx.StatusCode(iris.StatusInternalServerError) + ctx.SetErr(errors.New("用户不存在")) + return + } + confirmCode := utils.NewKey(8) + if err := db.Model(&model.User{}).Where("username = ?", username).Updates(model.User{ConfirmCode: confirmCode}).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + err := utils.SendEmail([]string{user.Email}, "密码重置", fmt.Sprintf("验证码为:%s\n您正在进行密码重置,验证码5分钟内有效,如非本人操作,请忽略本邮件", confirmCode)) + if utils.ErrHandle(ctx, err) { + return + } + if err1 := db.Create(&model.UserAction{ + Username: username, + Action: "获取重置密码验证码", + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", fmt.Sprintf("%s****%s", user.Email[0:2], user.Email[7:]))) + if utils.ErrHandle(ctx, err) { + return + } +} + +func autoLogin(ctx iris.Context) { + var user []model.UserAutoLogin + location := admin.GetIpLocation(utils.GetRequestIp(ctx)) + deviceId := ctx.GetCookie("deviceId") + if deviceId == "" || location == "" { + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "自动登录未设置", false)) + if utils.ErrHandle(ctx, err) { + return + } + return + } + db := database.GetInstance().GetMysqlDb() + if err := db.Where("device_id = ? and location = ?", deviceId, location).Find(&user).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if len(user) == 0 { + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "自动登录未设置", false)) + if utils.ErrHandle(ctx, err) { + return + } + return + } + rand1 := rand.New(rand.NewSource(time.Now().UnixNano())) + token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ + "username": user[0].Username, + "iat": time.Now().Unix(), + "jti": strconv.Itoa(rand1.Int()), + "exp": time.Now().Add(2 * time.Hour).Unix(), + }) + jwtKey := jwtSet.GetJwtKeys().Key + tokenString, err := token.SignedString([]byte(jwtKey)) + if utils.ErrHandle(ctx, err) { + return + } + ctx.SetCookieKV("is_login", "1", iris.CookieHTTPOnly(false), iris.CookieExpires(2*time.Hour)) + ctx.SetCookieKV("token", tokenString, iris.CookieHTTPOnly(false), iris.CookieExpires(2*time.Hour)) + if err1 := db.Create(&model.UserAction{ + Username: user[0].Username, + Action: "用户自动登录", + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + if err1 := db.Create(&model.JwtKeys{ + Username: user[0].Username, + Date: time.Now().Format("2006-01-02"), + Key: jwtKey, + Token: tokenString, + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", tokenString)) + if utils.ErrHandle(ctx, err) { + return + } +} diff --git a/api_iris/service/api/userExport.go b/api_iris/service/api/userExport.go new file mode 100644 index 0000000..a84092a --- /dev/null +++ b/api_iris/service/api/userExport.go @@ -0,0 +1,46 @@ +package api + +import ( + "github.com/kataras/iris/v12" + "main/jwtSet" + "main/utils" +) + +func User(party iris.Party) { + party.Get("/", jwtSet.Jwt.Serve, userinfo) + party.Put("/", jwtSet.Jwt.Serve, updateUserinfo) + party.Post("/", func(context iris.Context) { + token, err := register(context) + if utils.ErrHandle(context, err) { + return + } + err = context.JSON(utils.FormatRes(iris.StatusOK, "", token)) + if err != nil { + return + } + }) + party.Get("/login", func(context iris.Context) { + user := context.URLParam("username") + pubKey, err := utils.GetPublicKey(user) + if utils.ErrHandle(context, err) { + return + } + err = context.JSON(utils.FormatRes(200, "", pubKey)) + if err != nil { + return + } + }) + party.Post("/login", func(ctx iris.Context) { + token, err := login(ctx) + if utils.ErrHandle(ctx, err) { + return + } + err = ctx.JSON(utils.FormatRes(200, "", token)) + if err != nil { + return + } + }) + party.Delete("/login", jwtSet.Jwt.Serve, logout) + party.Get("/reset", resetPwd) + party.Get("/auto", autoLogin) +} diff --git a/api_iris/service/api/weather.go b/api_iris/service/api/weather.go new file mode 100644 index 0000000..326e0cb --- /dev/null +++ b/api_iris/service/api/weather.go @@ -0,0 +1,171 @@ +package api + +import ( + "encoding/json" + "fmt" + "github.com/kataras/iris/v12" + "github.com/sirupsen/logrus" + "io" + "main/database" + "main/model" + "main/utils" + "net/http" + "net/url" + "strings" + "time" +) + +// 获取location +func getLocation(ctx iris.Context) { + param := ctx.URLParam("location") + res, err := http.Get(fmt.Sprintf("https://wis.qq.com/city/like?source=pc&city=%s", param)) + if utils.ErrHandle(ctx, err) { + return + } + defer func(Body io.ReadCloser) { + err = Body.Close() + if utils.ErrHandle(ctx, err) { + return + } + }(res.Body) + body, _ := io.ReadAll(res.Body) + var r resLocation + //解析json结构 + err = json.Unmarshal(body, &r) + if utils.ErrHandle(ctx, err) { + return + } + err = ctx.JSON(utils.FormatRes(200, "", r.Data)) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 获取天气方法 +func getWeather(ctx iris.Context) { + user := utils.GetLoginUser(ctx) + if utils.DataIsNil(user) { + return + } + var info model.Userinfo + db := database.GetInstance().GetMysqlDb() + if err := db.Where("username = ?", user.Username).First(&info).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + var location []string + if info.Location == "" { + location = []string{"北京", "北京", "顺义"} + } else { + info.Location = strings.ReplaceAll(info.Location, " ", "") + location = strings.Split(info.Location, ",") + if len(location) < 3 { + location = append(location, "") + } + } + res, err := getMyWeather(location[0], location[1], location[2]) + if utils.ErrHandle(ctx, err) { + return + } + err = ctx.JSON(utils.FormatRes(200, "", res)) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 获取地点对应地址天气 +func getMyWeather(province string, city string, county string) (string, error) { + var weather []model.Weather + var respWeather string + db := database.GetInstance().GetMysqlDb() + if err := db.Where(&model.Weather{Date: time.Now().Format("2006-01-02"), Location: fmt.Sprintf("%s,%s,%s", province, city, county)}).Find(&weather).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if len(weather) == 0 { + resp, err := http.Get(fmt.Sprintf("https://wis.qq.com/weather/common?"+ + "source=pc&weather_type=forecast_24h&province=%s&city=%s&county=%s", url.QueryEscape(province), url.QueryEscape(city), url.QueryEscape(county))) + if err != nil { + return "", err + } + defer func(Body io.ReadCloser) { + err = Body.Close() + if err != nil { + } + }(resp.Body) + body, err := io.ReadAll(resp.Body) + if err != nil { + fmt.Println(err) + } + var r resWeather + //解析json结构 + err = json.Unmarshal(body, &r) + if err != nil { + //fmt.Println(fmt.Sprintf("https://wis.qq.com/weather/common?"+ + // "source=pc&weather_type=forecast_24h&province=%s&city=%s&county=%s", url.QueryEscape(province), url.QueryEscape(city), url.QueryEscape(county))) + //fmt.Println(resp.Body, "----------", string(body), "----------", err) + return "", err + } + todayWeather := formatWeather(r.Data.Forecast24h.D0) + tomWeather := formatWeather(r.Data.Forecast24h.D1) + respWeather = fmt.Sprintf("%s;%s", todayWeather, tomWeather) + if err1 := db.Create(&model.Weather{ + Date: time.Now().Format("2006-01-02"), + Location: fmt.Sprintf("%s,%s,%s", province, city, county), + Weather: respWeather, + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + } else { + respWeather = weather[0].Weather + } + return respWeather, nil +} + +// 天气格式化 +func formatWeather(data res24h) string { + var weather string + var windPower string + var windDirection string + var degree string + if data.DayWeather == data.NightWeather { + weather = data.DayWeather + } else { + weather = fmt.Sprintf("%s转%s", data.NightWeather, data.DayWeather) + } + if data.DayWindDirection == data.NightWindDirection { + windDirection = data.DayWindDirection + } else { + windDirection = fmt.Sprintf("%s转%s", data.NightWindDirection, data.DayWindDirection) + } + if data.DayWindPower == data.NightWindPower { + windPower = data.DayWindPower + "级" + } else { + windPower = fmt.Sprintf("%s~%s级", data.NightWindPower, data.DayWindPower) + } + degree = fmt.Sprintf("%s~%s℃", data.MinDegree, data.MaxDegree) + return fmt.Sprintf("%s, %s, %s %s", weather, degree, windDirection, windPower) +} + +func testWeather(ctx iris.Context) { + var location []string + loc := ctx.URLParam("location") + fmt.Println(loc) + if loc == "" { + location = []string{"北京", "北京", "顺义"} + fmt.Println(location) + } else { + loc = strings.ReplaceAll(loc, " ", "") + location = strings.Split(loc, ",") + if len(location) < 3 { + location = append(location, "") + } + fmt.Println(location) + } + res, err := getMyWeather(location[0], location[1], location[2]) + if utils.ErrHandle(ctx, err) { + return + } + err = ctx.JSON(utils.FormatRes(200, "", res)) + if utils.ErrHandle(ctx, err) { + return + } +} diff --git a/api_iris/service/api/weatherExport.go b/api_iris/service/api/weatherExport.go new file mode 100644 index 0000000..56d3725 --- /dev/null +++ b/api_iris/service/api/weatherExport.go @@ -0,0 +1,12 @@ +package api + +import ( + "github.com/kataras/iris/v12" + "main/jwtSet" +) + +func Weather(p iris.Party) { + p.Get("/", jwtSet.Jwt.Serve, getWeather) + p.Get("/location", getLocation) + p.Get("/test", testWeather) +} diff --git a/api_iris/service/api/yeb.go b/api_iris/service/api/yeb.go new file mode 100644 index 0000000..dca3d26 --- /dev/null +++ b/api_iris/service/api/yeb.go @@ -0,0 +1,206 @@ +package api + +import ( + "errors" + "github.com/kataras/iris/v12" + "github.com/sirupsen/logrus" + "main/database" + "main/model" + "main/utils" + "time" +) + +func getYeb(ctx iris.Context) { + username := utils.GetLoginUser(ctx).Username + if username == "" { + return + } + var balances []model.Balances + db := database.GetInstance().GetMysqlDb() + if err := db.Where("username = ?", username).Order("card").Find(&balances).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", balances)) + if utils.ErrHandle(ctx, err) { + return + } +} + +func addYeb(ctx iris.Context) { + username := utils.GetLoginUser(ctx).Username + if username == "" { + return + } + var balance paramsBalance + var tmpBalance []model.Balances + err := ctx.ReadJSON(&balance) + if utils.ErrHandle(ctx, err) { + return + } + db := database.GetInstance().GetMysqlDb() + if err1 := db.Where("username = ? and card = ?", username, balance.Card).Find(&tmpBalance).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + if len(tmpBalance) > 0 { + ctx.StatusCode(iris.StatusInternalServerError) + ctx.SetErr(errors.New("card已存在")) + return + } + if err1 := db.Create(&model.Balances{ + Username: username, + Card: balance.Card, + Type: balance.Type, + Balance: balance.Balance, + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + if balance.Type { + balance.Balance = -balance.Balance + } + if err1 := db.Create(&model.BalanceLogs{ + Username: username, + Card: balance.Card, + Date: time.Now().Format("2006-01-02"), + Balance: balance.Balance, + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } +} + +func updateYeb(ctx iris.Context) { + username := utils.GetLoginUser(ctx).Username + if username == "" { + return + } + var params paramsBalance + var balances []model.Balances + err := ctx.ReadJSON(¶ms) + if utils.ErrHandle(ctx, err) { + return + } + db := database.GetInstance().GetMysqlDb() + if err1 := db.Where("username = ? and card = ?", username, params.Card).Find(&balances).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + if len(balances) == 0 { + ctx.StatusCode(iris.StatusInternalServerError) + ctx.SetErr(errors.New("card 不存在")) + return + } + balances[0].Balance = params.Balance + balances[0].Type = params.Type + if err1 := db.Save(&balances[0]).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + //if err1 := db.Model(&balances).Update("balance", params.Balance).Error; err1 != nil { + // logrus.Errorln("sql执行失败:", err1) + //} + if params.Type { + params.Balance = -params.Balance + } + if err1 := db.Create(&model.BalanceLogs{ + Username: username, + Card: params.Card, + Date: time.Now().Format("2006-01-02"), + Balance: params.Balance, + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } +} + +func deleteYeb(ctx iris.Context) { + username := utils.GetLoginUser(ctx).Username + if username == "" { + return + } + var params paramsBalance + var balances []model.Balances + err := ctx.ReadJSON(¶ms) + if utils.ErrHandle(ctx, err) { + return + } + db := database.GetInstance().GetMysqlDb() + if err1 := db.Where("username = ? and card = ?", username, params.Card).Find(&balances).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + if len(balances) == 0 { + ctx.StatusCode(iris.StatusInternalServerError) + ctx.SetErr(errors.New("card 不存在")) + return + } + if err1 := db.Delete(&balances).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + if err1 := db.Create(&model.BalanceLogs{ + Username: username, + Card: params.Card, + Date: time.Now().Format("2006-01-02"), + Balance: 0, + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } +} + +func szYeb(ctx iris.Context) { + username := utils.GetLoginUser(ctx).Username + if username == "" { + return + } + var params paramSZBalance + var balance model.Balances + err := ctx.ReadJSON(¶ms) + if utils.ErrHandle(ctx, err) { + return + } + db := database.GetInstance().GetMysqlDb() + if err1 := db.Where("username = ? and card = ?", username, params.Card).Find(&balance).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + if utils.DataIsNil(balance) { + utils.ErrHandle(ctx, errors.New("card 不存在")) + return + } + if balance.Type == params.Type { + balance.Balance = utils.Round(balance.Balance+params.Amount, 2) + } else { + balance.Balance = utils.Round(balance.Balance-params.Amount, 2) + } + if err1 := db.Save(&balance).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + if balance.Type { + if err1 := db.Create(&model.BalanceLogs{ + Username: username, + Card: params.Card, + Date: time.Now().Format("2006-01-02"), + Balance: -balance.Balance, + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + } else { + if err1 := db.Create(&model.BalanceLogs{ + Username: username, + Card: params.Card, + Date: time.Now().Format("2006-01-02"), + Balance: balance.Balance, + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + } + err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success")) + if utils.ErrHandle(ctx, err) { + return + } +} diff --git a/api_iris/service/api/yebExport.go b/api_iris/service/api/yebExport.go new file mode 100644 index 0000000..dc96439 --- /dev/null +++ b/api_iris/service/api/yebExport.go @@ -0,0 +1,14 @@ +package api + +import ( + "github.com/kataras/iris/v12" + "main/jwtSet" +) + +func Yeb(party iris.Party) { + party.Get("/", jwtSet.Jwt.Serve, getYeb) + party.Post("/", jwtSet.Jwt.Serve, addYeb) + party.Put("/", jwtSet.Jwt.Serve, updateYeb) + party.Delete("/", jwtSet.Jwt.Serve, deleteYeb) + party.Post("/sz", jwtSet.Jwt.Serve, szYeb) +} diff --git a/api_iris/service/api/yebLog.go b/api_iris/service/api/yebLog.go new file mode 100644 index 0000000..32eb16f --- /dev/null +++ b/api_iris/service/api/yebLog.go @@ -0,0 +1,220 @@ +package api + +import ( + "fmt" + "github.com/kataras/iris/v12" + "github.com/sirupsen/logrus" + "main/database" + "main/model" + "main/utils" + "math" + "sort" + "strings" + "time" +) + +// 按月返回余额 +func monthLog(ctx iris.Context) { + var balanceLogs []model.BalanceLogs + var res []dateLog + username := utils.GetLoginUser(ctx).Username + if username == "" { + return + } + db := database.GetInstance().GetMysqlDb() + if err := db.Where("username = ?", username).Order("id").Find(&balanceLogs).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if len(balanceLogs) == 0 { + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", res)) + if err != nil { + return + } + return + } + monthList := getMonthList(balanceLogs) + cardList := getCardList(balanceLogs) + for index, month := range monthList { + var cardLogs []cardLog + var balanceTmp float64 + for _, card := range cardList { + b := getMonthBalance(balanceLogs, month, card) + if index == 0 { + cardLogs = append(cardLogs, cardLog{ + Card: card, + Balance: b, + Changes: 0, + }) + } else { + bTmp := getMonthBalance(balanceLogs, monthList[index-1], card) + cardLogs = append(cardLogs, cardLog{ + Card: card, + Balance: b, + Changes: utils.Round(b-bTmp, 2), + }) + } + balanceTmp += b + } + //cardLogs 排序 + sort.Slice(cardLogs, func(i, j int) bool { + return math.Abs(cardLogs[i].Changes) > math.Abs(cardLogs[j].Changes) + }) + if index == 0 { + res = append(res, dateLog{ + Date: month, + Duration: "", + Changes: 0, + Balance: utils.Round(balanceTmp, 2), + Detail: cardLogs, + }) + } else { + res = append(res, dateLog{ + Date: month, + Duration: fmt.Sprintf("%s~%s", res[index-1].Date, month), + Changes: utils.Round(balanceTmp-res[index-1].Balance, 2), + Balance: utils.Round(balanceTmp, 2), + Detail: cardLogs, + }) + } + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", res)) + if err != nil { + return + } +} + +// 获取时间段内详情 +func detailLog(ctx iris.Context) { + username := utils.GetLoginUser(ctx).Username + if username == "" { + return + } + startDate := ctx.URLParam("start") + endDate := ctx.URLParam("end") + var res []dateLog + var balanceLogs []model.BalanceLogs + db := database.GetInstance().GetMysqlDb() + if err := db.Where("username = ? ", username).Find(&balanceLogs).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if len(balanceLogs) == 0 { + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", res)) + if utils.ErrHandle(ctx, err) { + return + } + return + } + dateList := getDateList(balanceLogs, startDate, endDate) + cardList := getCardList(balanceLogs) + for index, date := range dateList { + var cardLogs []cardLog + var balanceTmp float64 + for _, card := range cardList { + b := getMonthBalance(balanceLogs, date, card) + if index == 0 { + cardLogs = append(cardLogs, cardLog{ + Card: card, + Balance: b, + Changes: 0, + }) + } else { + bTmp := getMonthBalance(balanceLogs, dateList[index-1], card) + cardLogs = append(cardLogs, cardLog{ + Card: card, + Balance: b, + Changes: utils.Round(b-bTmp, 2), + }) + } + balanceTmp += b + } + //cardLogs 排序 + sort.Slice(cardLogs, func(i, j int) bool { + return math.Abs(cardLogs[i].Changes) > math.Abs(cardLogs[j].Changes) + }) + if index == 0 { + res = append(res, dateLog{ + Date: date, + Duration: "", + Changes: 0, + Balance: utils.Round(balanceTmp, 2), + Detail: cardLogs, + }) + } else { + res = append(res, dateLog{ + Date: date, + Duration: fmt.Sprintf("%s~%s", res[index-1].Date, date), + Changes: utils.Round(balanceTmp-res[index-1].Balance, 2), + Balance: utils.Round(balanceTmp, 2), + Detail: cardLogs, + }) + } + } + err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", res)) + if utils.ErrHandle(ctx, err) { + return + } +} + +// 获取数据中card截至month时的余额 +func getMonthBalance(data []model.BalanceLogs, month string, card string) float64 { + var res float64 + for _, v := range data { + if v.Card == card && v.Date <= month { + res = v.Balance + } else if v.Date > month { + break + } + } + return utils.Round(res, 2) +} + +// 获取card列表 +func getCardList(data []model.BalanceLogs) []string { + var resTmp []string + var res []string + for _, v := range data { + card := v.Card + if !utils.CheckListItem(resTmp, card) { + resTmp = append(resTmp, card) + res = append(res, card) + } + } + return res +} + +// 获取月份列表 +func getMonthList(data []model.BalanceLogs) []string { + var resTmp []string + var res []string + for _, v := range data { + month := fmt.Sprintf("%s-%s-01", strings.Split(v.Date, "-")[0], strings.Split(v.Date, "-")[1]) + if !utils.CheckListItem(resTmp, month) { + resTmp = append(resTmp, month) + res = append(res, month) + } + } + t, _ := time.Parse("2006-01-02", data[len(data)-1].Date) + res = append(res, t.AddDate(0, 1, 0).Format("2006-01")+"-01") + return res +} + +// 获取日期列表 +func getDateList(data []model.BalanceLogs, start string, end string) []string { + var resTmp []string + var res []string + res = append(res, start) + for _, v := range data { + date := v.Date + if date <= start { + continue + } else if date >= end { + break + } + if !utils.CheckListItem(resTmp, date) { + resTmp = append(resTmp, date) + res = append(res, date) + } + } + res = append(res, end) + return res +} diff --git a/api_iris/service/api/yebLogExport.go b/api_iris/service/api/yebLogExport.go new file mode 100644 index 0000000..b3c4b3a --- /dev/null +++ b/api_iris/service/api/yebLogExport.go @@ -0,0 +1,11 @@ +package api + +import ( + "github.com/kataras/iris/v12" + "main/jwtSet" +) + +func YebLog(party iris.Party) { + party.Get("/", jwtSet.Jwt.Serve, monthLog) + party.Get("/detail", jwtSet.Jwt.Serve, detailLog) +} diff --git a/api_iris/service/init.go b/api_iris/service/init.go new file mode 100644 index 0000000..b326926 --- /dev/null +++ b/api_iris/service/init.go @@ -0,0 +1,38 @@ +package service + +import ( + "github.com/kataras/iris/v12" + "main/jwtSet" + "main/service/admin" + "main/service/api" + "main/service/test" +) + +func Apis(p iris.Party) { + p.Get("/", api.Apis) + p.PartyFunc("/weather", api.Weather) + p.PartyFunc("/user", api.User) + p.PartyFunc("/yeb", api.Yeb) + p.PartyFunc("/yeb_log", api.YebLog) + p.PartyFunc("/notes", api.Notes) + p.PartyFunc("/file", api.File) + p.PartyFunc("/backgammon", api.Backgammon) + p.PartyFunc("/sys_info", api.SysInfo) + p.PartyFunc("/chess", api.Chess) + p.PartyFunc("/menu", api.Menus) + p.PartyFunc("/sys", api.SysSettings) + p.PartyFunc("/sudoku", api.Sudoku) + p.PartyFunc("/public", api.PublicApis) +} + +func Admin(p iris.Party) { + p.Get("/", jwtSet.Jwt.Serve, admin.Admin) + p.PartyFunc("/logs", admin.Logs) + p.PartyFunc("/menus", admin.Menus) + p.PartyFunc("/user", admin.User) + p.PartyFunc("/sys", admin.SysSettings) +} + +func Test(party iris.Party) { + party.Get("/send", test.TestSend) +} diff --git a/api_iris/service/test/test.go b/api_iris/service/test/test.go new file mode 100644 index 0000000..875c14e --- /dev/null +++ b/api_iris/service/test/test.go @@ -0,0 +1,24 @@ +package test + +import ( + "fmt" + "github.com/kataras/iris/v12" + "main/database" + "main/model" +) + +func TestSend(ctx iris.Context) { + var d []data + db := database.GetInstance().GetMysqlDb() + //query := db.Distinct("ip").Find(&model.Logs{}) + //query.Select("logs.ip,ips_locations.location").Where("ips_locations.location is null").Joins("left join ips_locations on logs.ip=ip_locations.ip").Scan(&d) + //db.Model(&model.IpsLocation{}).Select("a.ip,ips_locations.location").Where("ips_locations.location is null").Joins("right join (?) a on a.ip=ips_locations.ip", query).Scan(&d) + //query.Model(&model.IpsLocation{}).Select("logs.ip,ips_locations.location").Where("ips_locations.location is null").Joins("left join ips_locations on logs.ip=ips_locations.ip").Scan(&d) + db.Model(&model.Logs{}).Distinct("logs.ip", "ips_locations.location").Where("ips_locations.location is null").Joins("left join ips_locations on logs.ip=ips_locations.ip").Scan(&d) + fmt.Println(len(d)) +} + +type data struct { + IP string `json:"ip"` + Location string `json:"location"` +} diff --git a/api_iris/service/ws/websocket.go b/api_iris/service/ws/websocket.go new file mode 100644 index 0000000..9309efd --- /dev/null +++ b/api_iris/service/ws/websocket.go @@ -0,0 +1,71 @@ +package ws + +import ( + websocket1 "github.com/gorilla/websocket" + "github.com/kataras/iris/v12/websocket" + "github.com/kataras/neffos" + "github.com/sirupsen/logrus" + "net/http" + "strconv" +) + +func checkOrigin(_ *http.Request) bool { + return true +} + +func PopItem(l []string, item string) []string { + var tmp []string + for _, v := range l { + if v == item { + continue + } else { + tmp = append(tmp, v) + } + } + return tmp +} + +func SetupWebsocket() *neffos.Server { + Upgrader := websocket1.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, + CheckOrigin: checkOrigin, + } + var userList []string + ws := websocket.New(websocket.GorillaUpgrader(Upgrader), websocket.Events{ + websocket.OnNativeMessage: func(conn *websocket.NSConn, message websocket.Message) error { + mg := websocket.Message{ + Body: []byte(strconv.Itoa(len(userList))), + IsNative: true, + } + conn.Conn.Write(mg) + return nil + }, + }) + ws.OnConnect = func(c *websocket.Conn) error { + userList = append(userList, c.ID()) + mg := websocket.Message{ + Body: []byte(strconv.Itoa(len(userList))), + IsNative: true, + } + c.Write(mg) + for _, cli := range ws.GetConnections() { + cli.Write(mg) + } + return nil + } + ws.OnDisconnect = func(c *websocket.Conn) { + userList = PopItem(userList, c.ID()) + mg := websocket.Message{ + Body: []byte(strconv.Itoa(len(userList))), + IsNative: true, + } + for _, cli := range ws.GetConnections() { + cli.Write(mg) + } + } + ws.OnUpgradeError = func(err error) { + logrus.Errorln("ws初始化失败:", err) + } + return ws +} diff --git a/api_iris/utils/aes.go b/api_iris/utils/aes.go new file mode 100644 index 0000000..8806b56 --- /dev/null +++ b/api_iris/utils/aes.go @@ -0,0 +1,70 @@ +package utils + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "encoding/base64" + "errors" +) + +func pkcs7Padding(data []byte, blockSize int) []byte { + padding := blockSize - len(data)%blockSize + padText := bytes.Repeat([]byte{byte(padding)}, padding) + return append(data, padText...) +} + +func pkcs7UnPadding(data []byte) ([]byte, error) { + length := len(data) + if length == 0 { + return nil, errors.New("加密字符串为空!") + } + unPadding := int(data[length-1]) + return data[:(length - unPadding)], nil +} + +func aesEncrypt(data []byte, key []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + blockSize := block.BlockSize() + encryptBytes := pkcs7Padding(data, blockSize) + encrypted := make([]byte, len(encryptBytes)) + blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) + blockMode.CryptBlocks(encrypted, encryptBytes) + return encrypted, nil +} + +func aesDecrypt(data []byte, key []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + blockSize := block.BlockSize() + blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) + decrypted := make([]byte, len(data)) + blockMode.CryptBlocks(decrypted, data) + //fmt.Println(data, decrypted) + decrypted, err = pkcs7UnPadding(decrypted) + if err != nil { + return nil, err + } + return decrypted, err +} + +func EncryptByAes(data []byte, key []byte) (string, error) { + res, err := aesEncrypt(data, key) + if err != nil { + return "", err + } + return base64.StdEncoding.EncodeToString(res), nil +} + +func DecryptByAes(data string, key []byte) ([]byte, error) { + dataByte, err := base64.StdEncoding.DecodeString(data) + if err != nil { + return nil, err + } + return aesDecrypt(dataByte, key) +} diff --git a/api_iris/utils/holidays.go b/api_iris/utils/holidays.go new file mode 100644 index 0000000..7525502 --- /dev/null +++ b/api_iris/utils/holidays.go @@ -0,0 +1,55 @@ +package utils + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "strconv" +) + +var holidays []ResHolidays + +func CheckHoliday(date string) (bool, string) { + year, err := strconv.Atoi(date[:4]) + if len(holidays) == 0 { + err = getHolidays(year) + if err != nil { + return false, "" + } + } + for _, holiday := range holidays { + if date == holiday.Date { + return true, holiday.HolidayNameCn + } + } + return false, "" +} + +func getHolidays(year int) error { + var res map[string]resHolidaysApi + //var holidays []ResHolidays + data, err := http.Get(fmt.Sprintf("https://api.jiejiariapi.com/v1/holidays/%s", strconv.Itoa(year))) + if err != nil { + return err + } + defer func(Body io.ReadCloser) { + err = Body.Close() + if err != nil { + return + } + }(data.Body) + body, _ := io.ReadAll(data.Body) + //解析json结构 + err = json.Unmarshal(body, &res) + if err != nil { + return err + } + for _, j := range res { + holidays = append(holidays, ResHolidays{ + Date: j.Date, + HolidayNameCn: j.Name, + }) + } + return nil +} diff --git a/api_iris/utils/menus.go b/api_iris/utils/menus.go new file mode 100644 index 0000000..ce2fdb2 --- /dev/null +++ b/api_iris/utils/menus.go @@ -0,0 +1,43 @@ +package utils + +import ( + "github.com/sirupsen/logrus" + "main/database" + "main/model" +) + +var MenuList []model.Menus + +func UpdateMenuList() { + db := database.GetInstance().GetMysqlDb() + if err := db.Order("menu_id").Find(&MenuList).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + logrus.Infoln("更新暂存MenuList列表") + if len(MenuList) == 0 { + logrus.Warningln("数据库MenuList为空,添加初始数据") + if err := db.Create(&model.Menus{ + MenuId: "999", + Name: "系统管理", + Icon: "bi-gear", + Path: "", + UserType: "admin", + WhiteList: "", + }).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if err := db.Create(&model.Menus{ + MenuId: "99901", + Name: "菜单管理", + Icon: "bi-list", + Path: "/admin/menus", + UserType: "admin", + WhiteList: "", + }).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if err := db.Order("menu_id").Find(&MenuList).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + } +} diff --git a/api_iris/utils/rsa.go b/api_iris/utils/rsa.go new file mode 100644 index 0000000..1251bd8 --- /dev/null +++ b/api_iris/utils/rsa.go @@ -0,0 +1,127 @@ +package utils + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/base64" + "github.com/sirupsen/logrus" + "main/database" + "main/model" + "reflect" + "time" +) + +func GetPrivateKeys(user string, date string) (model.DayKeys, error) { + var resKey model.DayKeys + //today := time.Now().Format("2006-01-02") + db := database.GetInstance().GetMysqlDb() + if err := db.Where("date = ? and user = ?", date, user).Order("id").First(&resKey).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if reflect.DeepEqual(resKey, model.DayKeys{}) { + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return model.DayKeys{}, err + } + x509PrivateKey := x509.MarshalPKCS1PrivateKey(privateKey) + base64PrivateKey := base64.StdEncoding.EncodeToString(x509PrivateKey) + resKey = model.DayKeys{ + Date: date, + Key: base64PrivateKey, + AesKey: NewKey(32), + User: user, + } + if err1 := db.Create(&resKey).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1) + } + logrus.Warnln(date + "resKey为空,创建resKey") + return resKey, nil + } else { + if err := db.Where("date = ? and user = ? and id <> ?", date, user, resKey.ID).Delete(&model.DayKeys{}).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + } + logrus.Infoln("resKey存在,返回私钥") + return resKey, nil +} + +func GetPublicKey(user string) (string, error) { + var userLogin model.User + db := database.GetInstance().GetMysqlDb() + if err := db.Where("username = ?", user).First(&userLogin).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if userLogin.Date == "" { + logrus.Infoln(user + ":用户之前未登录系统") + userLogin.Date = time.Now().Format("2006-01-02") + } + sPrivateKey, err := GetPrivateKeys(user, userLogin.Date) + if err != nil { + return "", err + } + deBase64privateKey, err := base64.StdEncoding.DecodeString(sPrivateKey.Key) + if err != nil { + return "", err + } + privateKey, err := x509.ParsePKCS1PrivateKey(deBase64privateKey) + if err != nil { + return "", err + } + publicKey := privateKey.PublicKey + x509PublicKey, err := x509.MarshalPKIXPublicKey(&publicKey) + if err != nil { + return "", err + } + base64PublicKey := base64.StdEncoding.EncodeToString(x509PublicKey) + logrus.Infoln(user + ":获取公钥成功") + return base64PublicKey, nil +} + +func RsaEncrypt(s string, user string) (string, error) { + sPublicKey, err := GetPublicKey(user) + if err != nil { + return "", err + } + deBase64Public, err := base64.StdEncoding.DecodeString(sPublicKey) + if err != nil { + return "", err + } + publicKey, err := x509.ParsePKIXPublicKey(deBase64Public) + if err != nil { + return "", err + } + res, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey.(*rsa.PublicKey), []byte(s)) + if err != nil { + return "", err + } + return base64.StdEncoding.EncodeToString(res), nil +} + +func RsaDecrypt(s string, sPrivateKey string) (string, error) { + //sPrivateKey, err := GetPrivateKeys(user) + //if err != nil { + // return "", err + //} + deBase64Private, err := base64.StdEncoding.DecodeString(sPrivateKey) + if err != nil { + logrus.Errorln("rsa私钥: base64 decode err:", err) + return "", err + } + privateKey, err := x509.ParsePKCS1PrivateKey(deBase64Private) + if err != nil { + logrus.Errorln("rsa私钥: x509 decode err:", err) + return "", err + } + des, err := base64.StdEncoding.DecodeString(s) + if err != nil { + logrus.Errorln("rsa: base64 decode err:", err) + return "", err + } + res, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, des) + if err != nil { + logrus.Errorln("rsa: 解密失败:", err) + return "", err + } + return string(res), nil +} diff --git a/api_iris/utils/sudoku.go b/api_iris/utils/sudoku.go new file mode 100644 index 0000000..14cc363 --- /dev/null +++ b/api_iris/utils/sudoku.go @@ -0,0 +1,34 @@ +package utils + +import ( + "encoding/json" + "fmt" + "github.com/sirupsen/logrus" + "io" + "net/http" + "net/url" +) + +func CheckSudoku(sudoku string) (error, ResSudoku) { + resp, err := http.Get(fmt.Sprintf("https://git-ylsa0.cn/api_ylsa/get_sudoku_check/?sudoku=%s", url.QueryEscape(sudoku))) + if err != nil { + logrus.Errorln("请求get_sudoku_check接口失败:", err) + return err, ResSudoku{} + } + defer func(Body io.ReadCloser) { + err = Body.Close() + if err != nil { + logrus.Errorln(err.Error()) + } + }(resp.Body) + body, _ := io.ReadAll(resp.Body) + var r ResSudoku + //解析json结构 + err = json.Unmarshal(body, &r) + if err != nil { + logrus.Errorln("请求结果json解析失败:", err) + return err, ResSudoku{} + } + logrus.Infoln("数独检查完成") + return nil, r +} diff --git a/api_iris/utils/sysSettings.go b/api_iris/utils/sysSettings.go new file mode 100644 index 0000000..daec43a --- /dev/null +++ b/api_iris/utils/sysSettings.go @@ -0,0 +1,38 @@ +package utils + +import ( + "github.com/sirupsen/logrus" + "main/database" + "main/model" +) + +var SysSettings []model.SysSettings + +func UpdateSysSettings() { + db := database.GetInstance().GetMysqlDb() + if err := db.Order("name").Find(&SysSettings).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if len(SysSettings) == 0 { + if err := db.Create(&model.SysSettings{ + Name: "user_type", + CnName: "系统管理员", + Value: "admin", + DType: "", + }).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if err := db.Create(&model.SysSettings{ + Name: "user_type", + CnName: "系统用户", + Value: "user", + DType: "", + }).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if err := db.Order("name").Find(&SysSettings).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + } + logrus.Infoln("更新系统配置列表") +} diff --git a/api_iris/utils/type.d.go b/api_iris/utils/type.d.go new file mode 100644 index 0000000..50fe983 --- /dev/null +++ b/api_iris/utils/type.d.go @@ -0,0 +1,45 @@ +package utils + +// ResponseBean result结构 +type ResponseBean struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data interface{} `json:"data"` +} + +type ResApiUtils struct { + Flag bool `json:"flag"` + Data interface{} `json:"data"` + Msg string `json:"msg"` +} + +// ResSudoku sudoku -------------------------------------------------------------------- +type ResSudoku struct { + Flag bool `json:"flag"` + Data resSudokuData +} +type resSudokuData struct { + Check bool `json:"check"` + Result string `json:"result"` +} + +// ResHolidays holidays ------------------------------------------------------------------- +type ResHolidays struct { + Date string `json:"date"` + HolidayNameCn string `json:"holiday_name_cn"` +} +type resHolidaysApi struct { + Date string `json:"date"` + Name string `json:"name"` + IsOffDay bool `json:"isOffDay"` +} + +// ipLocation +type resIpLocation struct { + Status string `json:"status"` + Data []resIpLocationData `json:"data"` +} +type resIpLocationData struct { + OriginQuery string `json:"origin_query"` + Location string `json:"location"` +} diff --git a/api_iris/utils/user.go b/api_iris/utils/user.go new file mode 100644 index 0000000..7776ed6 --- /dev/null +++ b/api_iris/utils/user.go @@ -0,0 +1,68 @@ +package utils + +import ( + "errors" + "github.com/golang-jwt/jwt/v4" + "github.com/kataras/iris/v12" + "github.com/sirupsen/logrus" + "main/database" + "main/model" +) + +var UserList []model.Userinfo + +// GetLoginUser 根据token获取用户,返回用户信息 +func GetLoginUser(ctx iris.Context) model.Userinfo { + if ctx.Values().Get("jwt") == nil { + ctx.StatusCode(iris.StatusUnauthorized) + ctx.SetErr(errors.New("未登录")) + logrus.Warningln("请求未携带token信息") + return model.Userinfo{} + } + var tokens []model.JwtKeys + db := database.GetInstance().GetMysqlDb() + auth := ctx.Values().Get("jwt").(*jwt.Token) + if err := db.Where("token = ?", auth.Raw).Find(&tokens).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + if len(tokens) == 0 { + ctx.StatusCode(iris.StatusUnauthorized) + ctx.SetErr(errors.New("未登录")) + logrus.Warningln("token信息无效") + return model.Userinfo{} + } + foobar := auth.Claims.(jwt.MapClaims) + for key, value := range foobar { + if key == "username" { + return GetUserInfo(value.(string)) + } + } + ctx.StatusCode(iris.StatusInternalServerError) + ctx.SetErr(errors.New("系统错误,请联系管理员")) + logrus.Errorln("token存在但获取用户信息失败") + return model.Userinfo{} +} + +// GetUserInfo 根据用户名获取用户信息,先更新本地数据 +func GetUserInfo(username string) model.Userinfo { + if len(UserList) == 0 { + logrus.Warnln("暂存用户列表为空,刷新数据") + UpdateUserInfo() + } + for _, u := range UserList { + if u.Username == username { + return u + } + } + logrus.Warnln("未找到对应的用户信息") + return model.Userinfo{} +} + +// UpdateUserInfo 更新当前用户信息 +func UpdateUserInfo() { + db := database.GetInstance().GetMysqlDb() + if err := db.Find(&UserList).Error; err != nil { + logrus.Errorln("sql执行失败:", err) + } + logrus.Infoln("刷新暂存用户列表") +} diff --git a/api_iris/utils/utils.go b/api_iris/utils/utils.go new file mode 100644 index 0000000..1284fff --- /dev/null +++ b/api_iris/utils/utils.go @@ -0,0 +1,255 @@ +package utils + +import ( + "bufio" + "encoding/json" + "errors" + "fmt" + "github.com/jordan-wright/email" + "github.com/kataras/iris/v12" + "github.com/shopspring/decimal" + "github.com/sirupsen/logrus" + "io" + "main/config" + "main/database" + "main/model" + "math/rand" + "net/http" + "net/smtp" + "os" + "path" + "reflect" + "regexp" + "runtime" + "strings" + "time" + "unsafe" +) + +// FormatRes 格式化result +func FormatRes(code int, msg string, data interface{}) ResponseBean { + return ResponseBean{ + Code: code, + Msg: msg, + Data: data, + } +} + +// GetEnvDefault 获取带默认值环境变量 +func GetEnvDefault(name string, defaultVal string) string { + val, ok := os.LookupEnv(name) + if ok { + return val + } else { + return defaultVal + } +} + +// FileRead 读取文件,返回行列表 +func FileRead(file string, condition bool) ([]string, error) { + f, err := os.Open(file) + var res []string + if err != nil { + return []string{}, err + } + defer func(f *os.File) { + err = f.Close() + if err != nil { + return + } + }(f) + reader := bufio.NewReader(f) + for { + line, _, err := reader.ReadLine() + if err != nil { + break + } + if condition && len(line) > 1 { + res = append(res, string(line)) + } + } + return res, nil +} + +func SendEmail(receiver []string, subject string, content string) error { + emailConfig := config.Config.Email + sender := fmt.Sprintf("ylsa <%s>", emailConfig.Username) + message := email.NewEmail() + message.From = sender + message.To = receiver + message.Subject = subject + message.Text = []byte(content) + logrus.Infoln("发送邮件:", sender, receiver) + err := message.Send("smtp.163.com:25", smtp.PlainAuth("", emailConfig.Username, emailConfig.Password, "smtp.163.com")) + if err != nil { + return err + } + return nil +} + +// DataIsNil 判断数据是否为空 +func DataIsNil(arg interface{}) bool { + if reflect.ValueOf(arg).Kind().String() == "ptr" || reflect.ValueOf(arg).Kind().String() == "slice" { + if reflect.ValueOf(arg).IsValid() { + return true + } + } else { + if reflect.ValueOf(arg).IsZero() { + return true + } + } + return false +} + +// ErrHandle 错误处理 +func ErrHandle(ctx iris.Context, err error) bool { + if err != nil { + pc, _, _, _ := runtime.Caller(1) + f := runtime.FuncForPC(pc).Name() + db := database.GetInstance().GetMysqlDb() + if err1 := db.Create(&model.SysError{ + Username: GetLoginUser(ctx).Username, + Time: time.Now(), + Function: f, + ErrorInfo: err.Error(), + }).Error; err1 != nil { + logrus.Errorln("sql执行失败:", err1.Error()) + } + ctx.StatusCode(iris.StatusInternalServerError) + ctx.SetErr(err) + return true + } + return false +} + +// NewKey 取n位随机数 +func NewKey(n int) string { + const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890()-+*/~" + var src = rand.NewSource(time.Now().UnixNano()) + const ( + // 6 bits to represent a letter index + letterIdBits = 6 + // All 1-bits as many as letterIdBits + letterIdMask = 1<= 0; { + if remain == 0 { + cache, remain = src.Int63(), letterIdMax + } + if idx := int(cache & letterIdMask); idx < len(letters) { + b[i] = letters[idx] + i-- + } + cache >>= letterIdBits + remain-- + } + return *(*string)(unsafe.Pointer(&b)) +} + +// CheckListItem 判断item是否在list内 +func CheckListItem[T comparable](data []T, item T) bool { + for _, value := range data { + if value == item { + return true + } + } + return false +} + +// FileIsExist 判断路径是否存在 +func FileIsExist(path string) bool { + if _, err := os.Stat(path); os.IsNotExist(err) { + return false + } + return true +} + +// Round float取小数 +func Round(num float64, n int32) float64 { + res, _ := decimal.NewFromFloat(num).Round(n).Float64() + return res +} + +// GetFileType 获取文件类型 +func GetFileType(f string) string { + fileSuffix := path.Ext(f) + fileSuffix = strings.ToLower(fileSuffix) + patternFileSuffix := fmt.Sprintf("^%s,|,%s,|,%s$|%s", fileSuffix, fileSuffix, fileSuffix, fileSuffix) + regFileSuffix := regexp.MustCompile(patternFileSuffix) + for _, v := range SysSettings { + if strings.HasPrefix(v.Name, "fileType:") && regFileSuffix.MatchString(v.Value) { + return strings.Split(v.Name, "fileType:")[1] + } + } + //switch fileSuffix { + //case ".png", ".jpg", ".jpeg", ".bmp", ".gif": + // return "image" + //case ".mp4", ".m2v", ".mkv", ".rmvb", ".avi", ".flv", ".mov", ".m4v", ".wmv", ".f4v": + // return "video" + //case ".mp3", ".wav", ".flac": + // return "audio" + //case ".zip", ".rar", ".7z": + // return "zip" + //} + return "doc" +} + +// IntAbs 数字取绝对值 +func IntAbs(i int) int { + if i < 0 { + return -i + } else { + return i + } +} + +// Reverse 字符串反转 +func Reverse(s string) string { + a := []rune(s) + for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 { + a[i], a[j] = a[j], a[i] + } + return string(a) +} + +func GetRequestIp(ctx iris.Context) string { + realIp := ctx.Request().Header.Get("X-Real-IP") + if realIp != "" { + return realIp + } + ip := ctx.RemoteAddr() + return ip +} + +func GetIpLocation(ip string) (string, error) { + var ipLocation resIpLocation + data, err := http.Get(fmt.Sprintf("https://opendata.baidu.com/api.php?query=%s&co=&resource_id=6006&oe=utf8", ip)) + if err != nil { + logrus.Errorln(ip, "ip地址请求失败", err) + return "", err + } + defer func(Body io.ReadCloser) { + err = Body.Close() + if err != nil { + return + } + }(data.Body) + body, err := io.ReadAll(data.Body) + if err != nil { + logrus.Errorln(ip, "获取IP地址请求读取失败:", err) + return "", err + } + err = json.Unmarshal(body, &ipLocation) + if err != nil { + logrus.Errorln(ip, "IP地址请求结果解析失败:", err) + return "", err + } + if len(ipLocation.Data) == 0 { + logrus.Errorln(ip, "ip地址无效:") + return "", errors.New("ip地址无效") + } + return ipLocation.Data[0].Location, nil +} diff --git a/build_file/dev/Dockerfile b/build_file/dev/Dockerfile new file mode 100644 index 0000000..cf133f6 --- /dev/null +++ b/build_file/dev/Dockerfile @@ -0,0 +1,11 @@ +FROM nginx +RUN mkdir -p /data \ + && cd /data \ + && mkdir static \ + && mkdir upload \ + && mkdir upload-video \ + && mkdir upload-note + +COPY default.conf /etc/nginx/conf.d/default.conf +COPY nginx.conf /etc/nginx/nginx.conf +COPY dist /usr/share/nginx/html \ No newline at end of file diff --git a/build_file/dev/build.py b/build_file/dev/build.py new file mode 100644 index 0000000..bb573f4 --- /dev/null +++ b/build_file/dev/build.py @@ -0,0 +1,54 @@ +# coding=utf-8 +import datetime +import os + + +def cp_file(rsc, des): + file_list = os.listdir(rsc) + for i in file_list: + if i in ['venv', '.idea', '.gitignore', 'static', 'migrations', '.git', '__pycache__', 'upload', 'templates', 'logs', 'upload-video', 'build.py', 'move.sh']: + continue + if os.path.isfile(os.path.join(rsc, i)): + os.system('copy /Y %s %s' % (os.path.join(rsc, i), os.path.join(des, i))) + else: + os.makedirs(os.path.join(des, i)) + cp_file(os.path.join(rsc, i), os.path.join(des, i)) + + +# 程序执行会将前后端程序复制到部署文件夹 +if __name__ == '__main__': + os.system('chcp 65001') + build_dir = r'C:\Users\m1582\Documents\vmshare\build' + date = datetime.datetime.today().date().strftime('%Y-%m-%d') + # 公共api + rsc_dir = r'..\..\api_django' + des_dir = r'%s\api_django' % build_dir + if os.path.exists(des_dir): + if not os.path.exists(r'%s\zzzz_bak' % build_dir): + os.makedirs(r'%s\zzzz_bak' % build_dir) + if os.path.exists(r'%s\zzzz_bak\%s_api_django' % (build_dir, date)): + os.system(r'rd /s /q %s\zzzz_bak\%s_api_django' % (build_dir, date)) + os.system(r'move %s %s\zzzz_bak\%s_api_django' % (des_dir, build_dir, date)) + os.makedirs(des_dir) + cp_file(rsc_dir, des_dir) + # 后端 + rsc_dir = r'..\..\api_iris' + des_dir = r'%s\api_iris' % build_dir + if os.path.exists(des_dir): + if not os.path.exists(r'%s\zzzz_bak' % build_dir): + os.makedirs(r'%s\zzzz_bak' % build_dir) + if os.path.exists(r'%s\zzzz_bak\%s_api_iris' % (build_dir, date)): + os.system(r'rd /s /q %s\zzzz_bak\%s_api_iris' % (build_dir, date)) + os.system(r'move %s %s\zzzz_bak\%s_api_iris' % (des_dir, build_dir, date)) + os.makedirs(des_dir) + cp_file(rsc_dir, des_dir) + # 前端 + rsc_dir_web = r'C:\Users\m1582\Documents\project\web_ylsa\web_vue\dist' + if os.path.exists(rsc_dir_web): + if os.path.exists(r'%s\dist' % build_dir): + if os.path.exists(r'%s\zzzz_bak\%s_dist' % (build_dir, date)): + os.system(r'rd /s /q %s\zzzz_bak\%s_dist' % (build_dir, date)) + os.system(r'move %s/dist %s\zzzz_bak\%s_dist' % (build_dir, build_dir, date)) + os.system('move %s %s' % (rsc_dir_web, build_dir)) + # 配置文件 + cp_file(r".\\", build_dir) diff --git a/build_file/dev/default.conf b/build_file/dev/default.conf new file mode 100644 index 0000000..5aa73e8 --- /dev/null +++ b/build_file/dev/default.conf @@ -0,0 +1,86 @@ +upstream api_iris { + server api_iris:8080; +} +upstream api_django { + server api_django:8000; +} +server { + listen 80; + + proxy_read_timeout 600; + client_max_body_size 9000m; + autoindex on; + autoindex_exact_size off; + set_real_ip_from 192.168.0.0/16; + real_ip_header proxy_protocol; + autoindex_localtime on; + charset utf-8; + root /usr/share/nginx/html; + + location /api/ { + proxy_pass http://api_iris/api/; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + location /api_django/ { + proxy_pass http://api_django/api/; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + location /admin/ { + proxy_pass http://api_iris/admin/; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + location /static/ { + alias /data/static/; + } + location /upload/ { + internal; + alias /data/upload/; + sendfile on; + charset utf-8; + } + location /upload-note/ { + internal; + alias /data/upload-note/; + sendfile on; + charset utf-8; + } + location /upload-video/ { + internal; + alias /data/upload-video/; + sendfile on; + add_header Cache-Control no-cache; + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Credentials' 'true'; + types { + application/vnd.apple.mpegurl m3u8; + video/mp2t ts; + } + charset utf-8; + } + + location ^~/ws { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_set_header X-NginX-Proxy true; + proxy_pass http://api_iris/ws; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_redirect off; + } + + location / { + try_files $uri /index.html; + autoindex on; + } +} diff --git a/build_file/dev/docker-compose.yml b/build_file/dev/docker-compose.yml new file mode 100644 index 0000000..1ecd034 --- /dev/null +++ b/build_file/dev/docker-compose.yml @@ -0,0 +1,60 @@ +version: "3" + +services: + api_iris: + image: api_iris:1.0 + container_name: api_iris + restart: always + environment: + - version=dev + healthcheck: + test: + [ + "CMD-SHELL", + "curl -sS 'http://127.0.0.1:8080/api' || kill 1" + ] + interval: 5m + timeout: 10s + retries: 3 + volumes: + - "/mnt/static:/web/api_iris/static/" + - "/mnt/upload:/web/api_iris/upload/" + - "/mnt/upload-note:/web/api_iris/upload-note/" + - "/mnt/upload-video:/web/api_iris/upload-video/" + - "/mnt/log/api_iris:/web/api_iris/logs/" + - "/mnt/log/nginx_vue:/web/api_iris/logs/nginx" + - "/etc/localtime:/etc/localtime" + + api_django: + image: api_django:1.0 + container_name: api_django + restart: always + command: /bin/bash -c "uwsgi --ini uwsgi.ini" + healthcheck: + test: + [ + "CMD-SHELL", + "curl -sS 'http://127.0.0.1:8000/api' || kill 1" + ] + interval: 5m + timeout: 10s + retries: 3 + volumes: + - "/etc/localtime:/etc/localtime" + + web_ylsa: + image: web_ylsa:1.0 + container_name: web_ylsa + restart: always + volumes: + - "/mnt/static:/data/static/" + - "/mnt/upload:/data/upload/" + - "/mnt/upload-note:/data/upload-note/" + - "/mnt/upload-video:/data/upload-video/" + - "/mnt/log/nginx-vue:/var/log/nginx/" + - "/etc/localtime:/etc/localtime" + ports: + - "80:80" + depends_on: + - api_iris + - api_django diff --git a/build_file/dev/move-o.sh b/build_file/dev/move-o.sh new file mode 100644 index 0000000..5cf65ce --- /dev/null +++ b/build_file/dev/move-o.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +if [[ -n $(docker ps -q -f "name=api_iris") ]]; then + cd /data/web/myweb + docker-compose down +fi +cd /data/web +if [[ -d "myweb_$(date +%Y%m%d)" ]]; then + rm -rf myweb_$(date +%Y%m%d) +fi +if [[ -d "myweb" ]]; then + mv myweb myweb_$(date +%Y%m%d) +fi +mkdir myweb +cp /mnt/build/build.zip myweb +cd myweb +unzip build.zip +chmod 755 -R /data/web/myweb +cd /data/web/myweb +sh update.sh diff --git a/build_file/dev/move.sh b/build_file/dev/move.sh new file mode 100644 index 0000000..3e3daea --- /dev/null +++ b/build_file/dev/move.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +if [[ -n $(docker ps -qa -f "name=api_iris") ]]; then + cd /data/web/myweb + docker-compose down +fi +cd /data/web +if [[ -d "myweb_$(date +%Y%m%d)" ]]; then + rm -rf myweb_$(date +%Y%m%d) +fi +if [[ -d "myweb" ]]; then + mv myweb myweb_$(date +%Y%m%d) +fi +mkdir myweb +cp /mnt/build/build.zip myweb +cd myweb +unzip build.zip +chmod 755 -R /data/web/myweb +cd /data/web/myweb +bash update.sh $@ diff --git a/build_file/dev/nginx.conf b/build_file/dev/nginx.conf new file mode 100644 index 0000000..b9c11ab --- /dev/null +++ b/build_file/dev/nginx.conf @@ -0,0 +1,46 @@ +user root; +worker_processes auto; + +error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + map $time_iso8601 $logDate { + '~^(?\d{4}-\d{2}-\d{2})' $ymd; + default 'date-not-found'; + } + + access_log /var/log/nginx/access-$logDate.log main; + open_log_file_cache max=10; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 600; + client_header_timeout 600; + client_body_timeout 600; + + gzip on; + gzip_min_length 1k; + gzip_buffers 4 16k; + gzip_http_version 1.1; + gzip_comp_level 9; + gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php application/javascript application/json; + gzip_disable "MSIE [1-6]\."; + gzip_vary on; + + include /etc/nginx/conf.d/*.conf; +} diff --git a/build_file/dev/update-o.sh b/build_file/dev/update-o.sh new file mode 100644 index 0000000..56cc7ef --- /dev/null +++ b/build_file/dev/update-o.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +if [ "$(sudo docker images -q web_ylsa:1.0 2> /dev/null)" != "" ]; +then + echo web_ylsa------remove + sudo docker rmi web_ylsa:1.0 +fi + +if [ "$(sudo docker images -q api_iris:1.0 2> /dev/null)" != "" ]; +then + echo api_iris------remove + sudo docker rmi api_iris:1.0 +fi + +if [ "$(sudo docker images -q api_django:1.0 2> /dev/null)" != "" ]; +then + echo api_django------remove + sudo docker rmi api_django:1.0 +fi + +echo web_ylsa------building +sudo docker build -t web_ylsa:1.0 . +echo web_ylsa------complete + +echo api_iris------building +sudo docker build -t api_iris:1.0 ./api_iris +echo api_iris------complete + +echo api_django------building +docker build -t api_django:1.0 ./api_django +echo api_django------complete + +docker-compose up -d +echo complete diff --git a/build_file/dev/update.sh b/build_file/dev/update.sh new file mode 100644 index 0000000..419a417 --- /dev/null +++ b/build_file/dev/update.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +if [ $# == 0 ];then + args=("web_vue" "api_iris" "api_django") +else + args=$@ +fi +for arg in ${args[*]};do + if [ $arg == "" ]; then + continue + fi + if [ "$(sudo docker images -q $arg:1.0 2> /dev/null)" != "" ]; + then + echo $arg------remove + sudo docker rmi $arg:1.0 + fi + echo $arg------building + if [ $arg == "web_vue" ];then + sudo docker build -t $arg:1.0 . + else + sudo docker build -t $arg:1.0 ./$arg + fi + echo $arg------complete +done + +docker-compose up -d +echo complete diff --git a/build_file/prd/Dockerfile b/build_file/prd/Dockerfile new file mode 100644 index 0000000..0d23829 --- /dev/null +++ b/build_file/prd/Dockerfile @@ -0,0 +1,12 @@ +FROM nginx +RUN mkdir -p /static/myDocs \ + && cd /static/myDocs \ + && mkdir static \ + && mkdir upload \ + && mkdir upload-video + +#COPY git-ylsa0.cn.key /etc/nginx/git-ylsa0.cn.key +#COPY git-ylsa0.cn_bundle.crt /etc/nginx/git-ylsa0.cn_bundle.crt +COPY default.conf /etc/nginx/conf.d/default.conf +COPY nginx.conf /etc/nginx/nginx.conf +COPY dist /usr/share/nginx/html \ No newline at end of file diff --git a/build_file/prd/build.py b/build_file/prd/build.py new file mode 100644 index 0000000..3a42c62 --- /dev/null +++ b/build_file/prd/build.py @@ -0,0 +1,66 @@ +# coding=utf-8 +import datetime +import os + + +def cp_file(rsc, des): + file_list = os.listdir(rsc) + for i in file_list: + if i in ['venv', '.idea', '.gitignore', 'static', 'migrations', '.git', '__pycache__', 'upload', 'templates', 'logs', 'upload-video', 'build.py', 'move.sh']: + continue + if os.path.isfile(os.path.join(rsc, i)): + os.system('copy /Y %s %s' % (os.path.join(rsc, i), os.path.join(des, i))) + else: + os.makedirs(os.path.join(des, i)) + cp_file(os.path.join(rsc, i), os.path.join(des, i)) + + +# 程序执行会将前后端程序复制到部署文件夹 +if __name__ == '__main__': + os.system('chcp 65001') + build_dir = r'C:\Users\m1582\Documents\vmshare\build-prd' + date = datetime.datetime.today().date().strftime('%Y-%m-%d') + # # 公共api + # rsc_dir = r'..\..\api_django' + # des_dir = r'%s\api_django' % build_dir + # if os.path.exists(des_dir): + # if not os.path.exists(r'%s\zzzz_bak' % build_dir): + # os.makedirs(r'%s\zzzz_bak' % build_dir) + # if os.path.exists(r'%s\zzzz_bak\%s_api_django' % (build_dir, date)): + # os.system(r'rd /s /q %s\zzzz_bak\%s_api_django' % (build_dir, date)) + # os.system(r'move %s %s\zzzz_bak\%s_api_django' % (des_dir, build_dir, date)) + # os.makedirs(des_dir) + # cp_file(rsc_dir, des_dir) + # 后端 + rsc_dir = r'..\..\api_iris' + des_dir = r'%s\api_iris' % build_dir + if os.path.exists(des_dir): + if not os.path.exists(r'%s\zzzz_bak' % build_dir): + os.makedirs(r'%s\zzzz_bak' % build_dir) + if os.path.exists(r'%s\zzzz_bak\%s_api_iris' % (build_dir, date)): + os.system(r'rd /s /q %s\zzzz_bak\%s_api_iris' % (build_dir, date)) + os.system(r'move %s %s\zzzz_bak\%s_api_iris' % (des_dir, build_dir, date)) + os.makedirs(des_dir) + cp_file(rsc_dir, des_dir) + # 后端 + rsc_dir = r'..\..\api_file' + des_dir = r'%s\api_file' % build_dir + if os.path.exists(des_dir): + if not os.path.exists(r'%s\zzzz_bak' % build_dir): + os.makedirs(r'%s\zzzz_bak' % build_dir) + if os.path.exists(r'%s\zzzz_bak\%s_api_file' % (build_dir, date)): + os.system(r'rd /s /q %s\zzzz_bak\%s_api_file' % (build_dir, date)) + os.system(r'move %s %s\zzzz_bak\%s_api_file' % (des_dir, build_dir, date)) + os.makedirs(des_dir) + cp_file(rsc_dir, des_dir) + # 前端 + rsc_dir_web = r'..\..\web_vue\dist' + if os.path.exists(rsc_dir_web): + if os.path.exists(r'%s\dist' % build_dir): + if os.path.exists(r'%s\zzzz_bak\%s_dist' % (build_dir, date)): + os.system(r'rd /s /q %s\zzzz_bak\%s_dist' % (build_dir, date)) + os.system(r'move %s/dist %s\zzzz_bak\%s_dist' % (build_dir, build_dir, date)) + os.system(r'xcopy /E /C /I /H /Y %s %s\dist' % (rsc_dir_web, build_dir)) + os.system(r'rd /s /q %s' % rsc_dir_web) + # 配置文件 + cp_file(r".\\", build_dir) diff --git a/build_file/prd/default.conf b/build_file/prd/default.conf new file mode 100644 index 0000000..98f4f23 --- /dev/null +++ b/build_file/prd/default.conf @@ -0,0 +1,96 @@ +upstream api_iris { + server api_iris:8080; +} +upstream api_file { + server api_file:8081; +} +server { + listen 80; + server_name git-ylsa0.cn; + return 301 https://$server_name$request_uri; +} +server { + listen 443 ssl; + server_name git-ylsa0.cn; + ssl_certificate git-ylsa0.cn_bundle.crt; + ssl_certificate_key git-ylsa0.cn.key; + ssl_session_timeout 5m; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; + ssl_prefer_server_ciphers on; + + proxy_read_timeout 600; + client_max_body_size 9000m; + autoindex on; + autoindex_exact_size off; + autoindex_localtime on; + charset utf-8; + + location /api/ { + proxy_pass http://api_iris/api/; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + location /api_file/ { + proxy_pass http://api_file/api/; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + location /admin/ { + proxy_pass http://api_iris/admin/; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + location /static/ { + alias /static/static/; + } + location /upload/ { + internal; + alias /static/upload/; + sendfile on; + charset utf-8; + } + location /upload-note/ { + internal; + alias /static/upload-note/; + sendfile on; + charset utf-8; + } + location /upload-video/ { + internal; + alias /static/upload-video/; + sendfile on; + add_header Cache-Control no-cache; + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Credentials' 'true'; + types { + application/vnd.apple.mpegurl m3u8; + video/mp2t ts; + } + charset utf-8; + } + + location ^~/ws { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_set_header X-NginX-Proxy true; + proxy_pass http://api_iris/ws; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_redirect off; + } + + location / { + root /usr/share/nginx/html; + try_files $uri /index.html; + autoindex on; + } +} diff --git a/build_file/prd/docker-compose.yml b/build_file/prd/docker-compose.yml new file mode 100644 index 0000000..98e2ef5 --- /dev/null +++ b/build_file/prd/docker-compose.yml @@ -0,0 +1,89 @@ +version: "3" + +services: + api_iris: + image: api_iris:1.0 + container_name: api_iris + restart: always + environment: + - version=prd + healthcheck: + test: + [ + "CMD-SHELL", + "curl -sS 'http://127.0.0.1:8080/api' || kill 1" + ] + interval: 5m + timeout: 10s + retries: 3 + volumes: + - "/data/share/static:/web/api_iris/static/" + - "/data/share/upload:/web/api_iris/upload/" + - "/data/share/upload-note:/web/api_iris/upload-note/" + - "/data/share-video/upload-video:/web/api_iris/upload-video/" + - "/data/share/log/backend-iris:/web/api_iris/logs/" + - "/data/share/log/nginx-vue:/web/api_iris/logs/nginx" + - "/etc/localtime:/etc/localtime" + + api_file: + image: api_file:1.0 + container_name: api_file + restart: always + environment: + - version=prd + healthcheck: + test: + [ + "CMD-SHELL", + "curl -sS 'http://127.0.0.1:8081/api' || kill 1" + ] + interval: 5m + timeout: 10s + retries: 3 + volumes: + - "/data/share/static:/web/api_file/static/" + - "/data/share/upload:/web/api_file/upload/" + - "/data/share/upload-note:/web/api_file/upload-note/" + - "/data/share-video/upload-video:/web/api_file/upload-video/" + - "/data/share/log/backend-iris:/web/api_file/logs/" + - "/data/share/log/nginx-vue:/web/api_file/logs/nginx" + - "/etc/localtime:/etc/localtime" + +# api_django: +# image: api_django:1.0 +# container_name: api_django +# restart: always +# command: /bin/bash -c "uwsgi --ini uwsgi.ini" +# healthcheck: +# test: +# [ +# "CMD-SHELL", +# "curl -sS 'http://127.0.0.1:8000/api' || kill 1" +# ] +# interval: 5m +# timeout: 10s +# retries: 3 +# volumes: +# - "/data/share/log/backend-iris:/web/api_django/logs/" +# - "/data/share/log/nginx-vue:/web/api_django/logs/nginx" +# - "/data/share/log/backend-django:/var/log/uwsgi" +# - "/etc/localtime:/etc/localtime" + + web_vue: + image: web_vue:1.0 + container_name: web_vue + restart: always + volumes: + - "/root/build/git-ylsa0.cn.key:/etc/nginx/git-ylsa0.cn.key" + - "/root/build/git-ylsa0.cn_bundle.crt:/etc/nginx/git-ylsa0.cn_bundle.crt" + - "/data/share/static:/static/static/" + - "/data/share/upload:/static/upload/" + - "/data/share/upload-note:/static/upload-note/" + - "/data/share-video/upload-video:/static/upload-video/" + - "/data/share/log/nginx-vue:/var/log/nginx/" + - "/etc/localtime:/etc/localtime" + ports: + - "80:80" + - "443:443" + depends_on: + - api_iris diff --git a/build_file/prd/git-ylsa0.cn.key b/build_file/prd/git-ylsa0.cn.key new file mode 100644 index 0000000..001139c --- /dev/null +++ b/build_file/prd/git-ylsa0.cn.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAy1gPde5WafiSzPYYBngf8vognefu/DPC0g+YGWBVOxm72D78 +tL9fWUQEqbh6nhSoveEAGJwX/ixudHbFck7pbXmj539Twi9XI/y20yBa88JxuiWl +x/Brn4Uv9GW1AAOUEUgDLOFMHS+hAi/oEQ0vN0EgiXTwHn0hl2vxfZW01w7M9gGJ +E9+NAnM8TAQBUxwJeAioJpi08AWQCJaLRhqVNKjzBZGS1Ffm5n5AHQZVn4UWyUC9 +7HxSkYFs20DdM7tPdlzqvGs56+bcAydt0iuV8MYhHWOUbUIUBrqcJgW9Ov4g3IRR +KOdh65y9gbjTlHnMecKVxeY0aLYjucP2iEWs3wIDAQABAoIBAQCaqkhYMwiXYfKG +40gy43upnIbilF86hURR8nqGErfBVDmEqaQc9tLXWt0RGrhpNN2ET3d7QH8fXTxG +aXCVGWCDXFpG4poawVdOkBGfVKTZc7R9GdWW1k+F7hAjnJinghFFc7i2kkcKjAez +Wizxi022gVr2MnPRPOwn+HqeQxhh2gwk2zd3Uvy1X7bukZwzqbZbB18jLVVAFRVn +yb1tmpXGLWmHR7q96ohdfZoJT1K5a+rGzS7uY1xJb20CW6V5I/ik5wt/exMke3lO +k9V26XOtS92Wco0Bz/+X1GXSekHmEY2NzO+f4M5KrPfN1+aETsoKlcMNrfGyCrfc +JxJXM7cBAoGBANR4K4k9fk2OImCPdy71Ote0+WJO2fnEbnLv36kwaf96Nrkyj97J +4dMSaeV9wOksPJLGQS2fwEVu+cv7VG0ALnM0vVPdcnqX2WK9A472WdN14t84stZx +KJZr/V/LJFnUF0ULv/I9Gfaqt39TlZFq+8hh/FJwQrOdWlYuyV9iyrdxAoGBAPUB +RRIbblVPRpCkY6549x+T/PAV4i9pqFdUdMq19j9dKsffKUx6WXM6Oh0KnnmX+qNi ++Qr1/hoiWK0mYX1yfCExO2wb8vDhBaNLFcH2PhZXOndAL42wBpWZUmLQ9UA7SwFJ +yldgulLp0eERQa4SC+6EKE3Anl7mPx2qC2bhaaFPAoGAINTc7V0aOkO/XiptIU11 +pUVqz90dY7IN2ybj6dH4Xyj0cdO1VZjek24h/PtcputWBNwr0DACVvhaE0In/pvr +kZPRubbbbMQwLEjG1v2MTdfGkfXB9M8RlYTkZzwNxu+2FM5G/9ifPXWN21gAIbNl +asZxZg/Azt5+yhh6t4mdh5ECgYAbOSLlmiquS/q5Q6rXzhUXayglvSi+v+y2l2nK +xGEyNTX1s3QxTT73MLcYVv+43ww4b1zgvOiZGsvjoWofuELWpVPG9WwPG9G2jTdD +sArCDLswWO9vvOleYgTnefRmSLBS2lIjV1ocsGJjsbkskFDHFeQsj4SJYugMccKG +kfFcvQKBgAq749ZtR55zAp/MygSlovhujbMQWOCRjfmpfAPNADKVKkts7Hz0UkYi +vRPA6JMO837DDAyh/5wi9Tplh6jBM7ZR7JNBmk5foY6K9yYWec1CH1HDD1SDPKQY +NiuucE7AE1F9FOrShfXmKfJGJGBwsR7/2K6ox9xNEExzDdrZls41 +-----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/build_file/prd/git-ylsa0.cn_bundle.crt b/build_file/prd/git-ylsa0.cn_bundle.crt new file mode 100644 index 0000000..dd66ca6 --- /dev/null +++ b/build_file/prd/git-ylsa0.cn_bundle.crt @@ -0,0 +1,72 @@ +-----BEGIN CERTIFICATE----- +MIIG5jCCBM6gAwIBAgIQCPyCK2kRd66nPH7hb0N6OzANBgkqhkiG9w0BAQsFADBb +MQswCQYDVQQGEwJDTjElMCMGA1UEChMcVHJ1c3RBc2lhIFRlY2hub2xvZ2llcywg +SW5jLjElMCMGA1UEAxMcVHJ1c3RBc2lhIERWIFRMUyBSU0EgQ0EgMjAyNTAeFw0y +NTA0MTgwMDAwMDBaFw0yNTA3MTcyMzU5NTlaMBcxFTATBgNVBAMTDGdpdC15bHNh +MC5jbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMtYD3XuVmn4ksz2 +GAZ4H/L6IJ3n7vwzwtIPmBlgVTsZu9g+/LS/X1lEBKm4ep4UqL3hABicF/4sbnR2 +xXJO6W15o+d/U8IvVyP8ttMgWvPCcbolpcfwa5+FL/RltQADlBFIAyzhTB0voQIv +6BENLzdBIIl08B59IZdr8X2VtNcOzPYBiRPfjQJzPEwEAVMcCXgIqCaYtPAFkAiW +i0YalTSo8wWRktRX5uZ+QB0GVZ+FFslAvex8UpGBbNtA3TO7T3Zc6rxrOevm3AMn +bdIrlfDGIR1jlG1CFAa6nCYFvTr+INyEUSjnYeucvYG405R5zHnClcXmNGi2I7nD +9ohFrN8CAwEAAaOCAugwggLkMB8GA1UdIwQYMBaAFLQSKKW0wB2fKXFpPNkRlkp1 +aVDAMB0GA1UdDgQWBBTlGvVqmm0T+17WOjXf+bRDROQk0DApBgNVHREEIjAgggxn +aXQteWxzYTAuY26CEHd3dy5naXQteWxzYTAuY24wPgYDVR0gBDcwNTAzBgZngQwB +AgEwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BTMA4G +A1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIweQYI +KwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j +b20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9UcnVz +dEFzaWFEVlRMU1JTQUNBMjAyNS5jcnQwDAYDVR0TAQH/BAIwADCCAX0GCisGAQQB +1nkCBAIEggFtBIIBaQFnAHYAEvFONL1TckyEBhnDjz96E/jntWKHiJxtMAWE6+WG +JjoAAAGWRkwfqQAABAMARzBFAiBBHS9jqaLJlyrLkjN5qogGnCaDOnreIYpKp9c9 +kOyBNwIhAOjKYVSKBpT+NzNuK0ECAqNqHI2O8lbcb+suYSnJsLsNAHUApELFBklg +YVSPD9TqnPt6LSZFTYepfy/fRVn2J086hFQAAAGWRkwf8gAABAMARjBEAiEA4FcC ++E8K4SGch61oF/EOB70aENJOY75yXTbi0D+zbPICHyAzxg7zR9w6buULxO66A1k3 +3PAi+yWCuqFLuKAegi8AdgDM+w9qhXEJZf6Vm1PO6bJ8IumFXA2XjbapflTA/kwN +sAAAAZZGTB+tAAAEAwBHMEUCIB8ymVM5uZHirHtgqFrEjDgSP4UZ+Ux1bFP5aPYX +QdgBAiEAiq8gn/BQ2ZQHcmMmXieuf3GsVMzhFLm+P6u+kMq7+yswDQYJKoZIhvcN +AQELBQADggIBACDD9/XVOBHMrUoja7p4DtiF4OVckXXWRTa+mTC8M8ZorbKBv/Mk +i1jxoe2HX49yLy01aP5PKVAkMV8oxJpDbq0W6gDm9ALzE1Fhh225v0AcLPcUIslG +wFWjaTVlx0UcozErcbBSJKC13EN2LiEfyAt3lOnmpLhVC25IYrMWJXTamp985Klz +9N71Ttw1PzELWRAQZwYenf57ikhqPR6VGbhkx1blVBqugmvaLPXQjspwq2YVdwEI +3y60dGmwuNvld4pH2xHQvLkaZiQdQ90NgzxqZannHgY4S8yfsDBzjpHZrYYOynRm +LpK4dTJwYHoQ+qwUmK9aEgZ9PEX0FJpEl1IauhP/4iIazY2clS/9VNHfsWrrdng4 +ZaAD4obpA12GmjvjTeqd5oNVkIi9PSCkkwPFoI/PO6iYvreHtxD2z494UoeGMTHO +OzYv1MsnuKgwPIF+R7cnq/1sdIw8/cC0Z88VQCQdZd3C0Sx7Ee54KEXTZQBHIZuZ +mVlKKf98jISSGTuZGeQox8P5PON+/BcRbYXtWvSbu0zzWBxa2jZngkEWNhMFX4zY +BBti+nUpGdWnb1NHk4/bs6YiQwiL2CQdhgWmput0/fV0A0IKXaHOZgAw915O9i57 +Hu5lAvOtGZ6IEVjNlSXXrcIeTP/DD7oOSPcQ3C3I+7SE8qAVGyPJP2WU +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFnjCCBIagAwIBAgIQCSYyO0lk42hGFRLe8aXVLDANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0yNTAxMDgwMDAwMDBaFw0zNTAxMDcyMzU5NTlaMFsxCzAJBgNVBAYTAkNO +MSUwIwYDVQQKExxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSUwIwYDVQQD +ExxUcnVzdEFzaWEgRFYgVExTIFJTQSBDQSAyMDI1MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA0fuEmuBIsN6ZZVq+gRobMorOGIilTCIfQrxNpR8FUZ9R +/GfbiekbiIKphQXEZ7N1uBnn6tXUuZ32zl6jPkZpHzN/Bmgk1BWSIzVc0npMzrWq +/hrbk5+KddXJdsNpeG1+Q8lc8uVMBrztnxaPb7Rh7yQCsMrcO4hgVaqLJWkVvEfW +ULtoCHQnNaj4IroG6VxQf1oArQ8bPbwpI02lieSahRa78FQuXdoGVeQcrkhtVjZs +ON98vq5fPWZX2LFv7e5J6P9IHbzvOl8yyQjv+2/IOwhNSkaXX3bI+//bqF9XW/p7 ++gsUmHiK5YsvLjmXcvDmoDEGrXMzgX31Zl2nJ+umpRbLjwP8rxYIUsKoEwEdFoto +Aid59UEBJyw/GibwXQ5xTyKD/N6C8SFkr1+myOo4oe1UB+YgvRu6qSxIABo5kYdX +FodLP4IgoVJdeUFs1Usa6bxYEO6EgMf5lCWt9hGZszvXYZwvyZGq3ogNXM7eKyi2 +20WzJXYMmi9TYFq2Fa95aZe4wki6YhDhhOO1g0sjITGVaB73G+JOCI9yJhv6+REN +D40ZpboUHE8JNgMVWbG1isAMVCXqiADgXtuC+tmJWPEH9cR6OuJLEpwOzPfgAbnn +2MRu7Tsdr8jPjTPbD0FxblX1ydW3RG30vwLF5lkTTRkHG9epMgpPMdYP7nY/08MC +AwEAAaOCAVYwggFSMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFLQSKKW0 +wB2fKXFpPNkRlkp1aVDAMB8GA1UdIwQYMBaAFE4iVCAYlebjbuYP+vq5Eu0GF485 +MA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw +dgYIKwYBBQUHAQEEajBoMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy +dC5jb20wQAYIKwYBBQUHMAKGNGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9E +aWdpQ2VydEdsb2JhbFJvb3RHMi5jcnQwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDov +L2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFsUm9vdEcyLmNybDARBgNV +HSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQELBQADggEBAJ4a3svh316GY2+Z7EYx +mBIsOwjJSnyoEfzx2T699ctLLrvuzS79Mg3pPjxSLlUgyM8UzrFc5tgVU3dZ1sFQ +I4RM+ysJdvIAX/7Yx1QbooVdKhkdi9X7QN7yVkjqwM3fY3WfQkRTzhIkM7mYIQbR +r+y2Vkju61BLqh7OCRpPMiudjEpP1kEtRyGs2g0aQpEIqKBzxgitCXSayO1hoO6/ +71ts801OzYlqYW9OQQQ2GCJyFbD6XHDjdpn+bWUxTKWaMY0qedSCbHE3Kl2QEF0C +ynZ7SbC03yR+gKZQDeTXrNP1kk5Qhe7jSXgw+nhbspe0q/M1ZcNCz+sPxeOwdCcC +gJE= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/build_file/prd/nginx.conf b/build_file/prd/nginx.conf new file mode 100644 index 0000000..b9c11ab --- /dev/null +++ b/build_file/prd/nginx.conf @@ -0,0 +1,46 @@ +user root; +worker_processes auto; + +error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + map $time_iso8601 $logDate { + '~^(?\d{4}-\d{2}-\d{2})' $ymd; + default 'date-not-found'; + } + + access_log /var/log/nginx/access-$logDate.log main; + open_log_file_cache max=10; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 600; + client_header_timeout 600; + client_body_timeout 600; + + gzip on; + gzip_min_length 1k; + gzip_buffers 4 16k; + gzip_http_version 1.1; + gzip_comp_level 9; + gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php application/javascript application/json; + gzip_disable "MSIE [1-6]\."; + gzip_vary on; + + include /etc/nginx/conf.d/*.conf; +} diff --git a/build_file/prd/update.sh b/build_file/prd/update.sh new file mode 100644 index 0000000..0570951 --- /dev/null +++ b/build_file/prd/update.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +if [ $# == 0 ];then + args=("web_vue" "api_iris" "api_file") +else + args=$@ +fi +for arg in ${args[*]};do + if [[ $arg != "web_vue" && $arg != "api_iris" && $arg != "api_file" ]]; then + continue + fi + if [ "$(sudo docker images -q $arg:1.0 2> /dev/null)" != "" ]; + then + echo $arg------remove + sudo docker rmi $arg:1.0 + fi + echo $arg------building + if [ $arg == "web_vue" ];then + sudo docker build -t $arg:1.0 . + else + sudo docker build -t $arg:1.0 ./$arg + fi + echo $arg------complete +done + +docker-compose up -d +echo complete diff --git a/web_vue/.vscode/extensions.json b/web_vue/.vscode/extensions.json new file mode 100644 index 0000000..a7cea0b --- /dev/null +++ b/web_vue/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["Vue.volar"] +} diff --git a/web_vue/README.md b/web_vue/README.md new file mode 100644 index 0000000..e69de29 diff --git a/web_vue/index.html b/web_vue/index.html new file mode 100644 index 0000000..21be348 --- /dev/null +++ b/web_vue/index.html @@ -0,0 +1,13 @@ + + + + + + + ylsa + + + + + + diff --git a/web_vue/package-lock.json b/web_vue/package-lock.json new file mode 100644 index 0000000..88fbde3 --- /dev/null +++ b/web_vue/package-lock.json @@ -0,0 +1,2661 @@ +{ + "name": "web_vue", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "web_vue", + "version": "0.0.0", + "dependencies": { + "@element-plus/icons-vue": "^2.3.1", + "@types/node": "^20.14.10", + "@vitejs/plugin-vue": "^5.2.1", + "animate.css": "^4.1.1", + "axios": "1.8.2", + "bootstrap-icons": "^1.11.3", + "echarts": "^5.5.1", + "element-plus": "^2.9.0", + "hls.js": "^1.5.20", + "jsencrypt": "^3.3.2", + "lodash": "^4.17.21", + "luxon": "^3.5.0", + "md-editor-v3": "^4.21.1", + "vue": "^3.4.21", + "vue-request": "^2.0.4", + "vue-router": "^4.4.0", + "vue-video-player": "^6.0.0" + }, + "devDependencies": { + "@types/luxon": "^3.4.2", + "typescript": "^5.2.2", + "vite": "6.2.3", + "vue-tsc": "^2.0.6" + } + }, + "node_modules/@babel/parser": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", + "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.24.8", + "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.24.8.tgz", + "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==", + "peer": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@codemirror/autocomplete": { + "version": "6.18.1", + "resolved": "https://registry.npmmirror.com/@codemirror/autocomplete/-/autocomplete-6.18.1.tgz", + "integrity": "sha512-iWHdj/B1ethnHRTwZj+C1obmmuCzquH29EbcKr0qIjA9NfDeBDJ7vs+WOHsFeLeflE4o+dHfYndJloMKHUkWUA==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + }, + "peerDependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.7.1", + "resolved": "https://registry.npmmirror.com/@codemirror/commands/-/commands-6.7.1.tgz", + "integrity": "sha512-llTrboQYw5H4THfhN4U3qCnSZ1SOJ60ohhz+SzU0ADGtwlc533DtklQP0vSFaQuCPDn3BPpOd1GbbnUtwNjsrw==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/lang-angular": { + "version": "0.1.3", + "resolved": "https://registry.npmmirror.com/@codemirror/lang-angular/-/lang-angular-0.1.3.tgz", + "integrity": "sha512-xgeWGJQQl1LyStvndWtruUvb4SnBZDAu/gvFH/ZU+c0W25tQR8e5hq7WTwiIY2dNxnf+49mRiGI/9yxIwB6f5w==", + "dependencies": { + "@codemirror/lang-html": "^6.0.0", + "@codemirror/lang-javascript": "^6.1.2", + "@codemirror/language": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.3.3" + } + }, + "node_modules/@codemirror/lang-cpp": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/@codemirror/lang-cpp/-/lang-cpp-6.0.2.tgz", + "integrity": "sha512-6oYEYUKHvrnacXxWxYa6t4puTlbN3dgV662BDfSH8+MfjQjVmP697/KYTDOqpxgerkvoNm7q5wlFMBeX8ZMocg==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/cpp": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-css": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/@codemirror/lang-css/-/lang-css-6.3.0.tgz", + "integrity": "sha512-CyR4rUNG9OYcXDZwMPvJdtb6PHbBDKUc/6Na2BIwZ6dKab1JQqKa4di+RNRY9Myn7JB81vayKwJeQ7jEdmNVDA==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.2", + "@lezer/css": "^1.1.7" + } + }, + "node_modules/@codemirror/lang-go": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/@codemirror/lang-go/-/lang-go-6.0.1.tgz", + "integrity": "sha512-7fNvbyNylvqCphW9HD6WFnRpcDjr+KXX/FgqXy5H5ZS0eC5edDljukm/yNgYkwTsgp2busdod50AOTIy6Jikfg==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/go": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-html": { + "version": "6.4.9", + "resolved": "https://registry.npmmirror.com/@codemirror/lang-html/-/lang-html-6.4.9.tgz", + "integrity": "sha512-aQv37pIMSlueybId/2PVSP6NPnmurFDVmZwzc7jszd2KAF8qd4VBbvNYPXWQq90WIARjsdVkPbw29pszmHws3Q==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/lang-css": "^6.0.0", + "@codemirror/lang-javascript": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/css": "^1.1.0", + "@lezer/html": "^1.3.0" + } + }, + "node_modules/@codemirror/lang-java": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/@codemirror/lang-java/-/lang-java-6.0.1.tgz", + "integrity": "sha512-OOnmhH67h97jHzCuFaIEspbmsT98fNdhVhmA3zCxW0cn7l8rChDhZtwiwJ/JOKXgfm4J+ELxQihxaI7bj7mJRg==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/java": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-javascript": { + "version": "6.2.2", + "resolved": "https://registry.npmmirror.com/@codemirror/lang-javascript/-/lang-javascript-6.2.2.tgz", + "integrity": "sha512-VGQfY+FCc285AhWuwjYxQyUQcYurWlxdKYT4bqwr3Twnd5wP5WSeu52t4tvvuWmljT4EmgEgZCqSieokhtY8hg==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/javascript": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-json": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/@codemirror/lang-json/-/lang-json-6.0.1.tgz", + "integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/json": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-less": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/@codemirror/lang-less/-/lang-less-6.0.2.tgz", + "integrity": "sha512-EYdQTG22V+KUUk8Qq582g7FMnCZeEHsyuOJisHRft/mQ+ZSZ2w51NupvDUHiqtsOy7It5cHLPGfHQLpMh9bqpQ==", + "dependencies": { + "@codemirror/lang-css": "^6.2.0", + "@codemirror/language": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-liquid": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/@codemirror/lang-liquid/-/lang-liquid-6.2.1.tgz", + "integrity": "sha512-J1Mratcm6JLNEiX+U2OlCDTysGuwbHD76XwuL5o5bo9soJtSbz2g6RU3vGHFyS5DC8rgVmFSzi7i6oBftm7tnA==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/lang-html": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.3.1" + } + }, + "node_modules/@codemirror/lang-markdown": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/@codemirror/lang-markdown/-/lang-markdown-6.3.0.tgz", + "integrity": "sha512-lYrI8SdL/vhd0w0aHIEvIRLRecLF7MiiRfzXFZY94dFwHqC9HtgxgagJ8fyYNBldijGatf9wkms60d8SrAj6Nw==", + "dependencies": { + "@codemirror/autocomplete": "^6.7.1", + "@codemirror/lang-html": "^6.0.0", + "@codemirror/language": "^6.3.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.2.1", + "@lezer/markdown": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-php": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/@codemirror/lang-php/-/lang-php-6.0.1.tgz", + "integrity": "sha512-ublojMdw/PNWa7qdN5TMsjmqkNuTBD3k6ndZ4Z0S25SBAiweFGyY68AS3xNcIOlb6DDFDvKlinLQ40vSLqf8xA==", + "dependencies": { + "@codemirror/lang-html": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/php": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-python": { + "version": "6.1.6", + "resolved": "https://registry.npmmirror.com/@codemirror/lang-python/-/lang-python-6.1.6.tgz", + "integrity": "sha512-ai+01WfZhWqM92UqjnvorkxosZ2aq2u28kHvr+N3gu012XqY2CThD67JPMHnGceRfXPDBmn1HnyqowdpF57bNg==", + "dependencies": { + "@codemirror/autocomplete": "^6.3.2", + "@codemirror/language": "^6.8.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.2.1", + "@lezer/python": "^1.1.4" + } + }, + "node_modules/@codemirror/lang-rust": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/@codemirror/lang-rust/-/lang-rust-6.0.1.tgz", + "integrity": "sha512-344EMWFBzWArHWdZn/NcgkwMvZIWUR1GEBdwG8FEp++6o6vT6KL9V7vGs2ONsKxxFUPXKI0SPcWhyYyl2zPYxQ==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/rust": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-sass": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/@codemirror/lang-sass/-/lang-sass-6.0.2.tgz", + "integrity": "sha512-l/bdzIABvnTo1nzdY6U+kPAC51czYQcOErfzQ9zSm9D8GmNPD0WTW8st/CJwBTPLO8jlrbyvlSEcN20dc4iL0Q==", + "dependencies": { + "@codemirror/lang-css": "^6.2.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.2", + "@lezer/sass": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-sql": { + "version": "6.8.0", + "resolved": "https://registry.npmmirror.com/@codemirror/lang-sql/-/lang-sql-6.8.0.tgz", + "integrity": "sha512-aGLmY4OwGqN3TdSx3h6QeA1NrvaYtF7kkoWR/+W7/JzB0gQtJ+VJxewlnE3+VImhA4WVlhmkJr109PefOOhjLg==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-vue": { + "version": "0.1.3", + "resolved": "https://registry.npmmirror.com/@codemirror/lang-vue/-/lang-vue-0.1.3.tgz", + "integrity": "sha512-QSKdtYTDRhEHCfo5zOShzxCmqKJvgGrZwDQSdbvCRJ5pRLWBS7pD/8e/tH44aVQT6FKm0t6RVNoSUWHOI5vNug==", + "dependencies": { + "@codemirror/lang-html": "^6.0.0", + "@codemirror/lang-javascript": "^6.1.2", + "@codemirror/language": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.3.1" + } + }, + "node_modules/@codemirror/lang-wast": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/@codemirror/lang-wast/-/lang-wast-6.0.2.tgz", + "integrity": "sha512-Imi2KTpVGm7TKuUkqyJ5NRmeFWF7aMpNiwHnLQe0x9kmrxElndyH0K6H/gXtWwY6UshMRAhpENsgfpSwsgmC6Q==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-xml": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/@codemirror/lang-xml/-/lang-xml-6.1.0.tgz", + "integrity": "sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/xml": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-yaml": { + "version": "6.1.1", + "resolved": "https://registry.npmmirror.com/@codemirror/lang-yaml/-/lang-yaml-6.1.1.tgz", + "integrity": "sha512-HV2NzbK9bbVnjWxwObuZh5FuPCowx51mEfoFT9y3y+M37fA3+pbxx4I7uePuygFzDsAmCTwQSc/kXh/flab4uw==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.2.0", + "@lezer/yaml": "^1.0.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.10.3", + "resolved": "https://registry.npmmirror.com/@codemirror/language/-/language-6.10.3.tgz", + "integrity": "sha512-kDqEU5sCP55Oabl6E7m5N+vZRoc0iWqgDVhEKifcHzPzjqCegcO4amfrYVL9PmPZpl4G0yjkpTpUO/Ui8CzO8A==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.1.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/language-data": { + "version": "6.5.1", + "resolved": "https://registry.npmmirror.com/@codemirror/language-data/-/language-data-6.5.1.tgz", + "integrity": "sha512-0sWxeUSNlBr6OmkqybUTImADFUP0M3P0IiSde4nc24bz/6jIYzqYSgkOSLS+CBIoW1vU8Q9KUWXscBXeoMVC9w==", + "dependencies": { + "@codemirror/lang-angular": "^0.1.0", + "@codemirror/lang-cpp": "^6.0.0", + "@codemirror/lang-css": "^6.0.0", + "@codemirror/lang-go": "^6.0.0", + "@codemirror/lang-html": "^6.0.0", + "@codemirror/lang-java": "^6.0.0", + "@codemirror/lang-javascript": "^6.0.0", + "@codemirror/lang-json": "^6.0.0", + "@codemirror/lang-less": "^6.0.0", + "@codemirror/lang-liquid": "^6.0.0", + "@codemirror/lang-markdown": "^6.0.0", + "@codemirror/lang-php": "^6.0.0", + "@codemirror/lang-python": "^6.0.0", + "@codemirror/lang-rust": "^6.0.0", + "@codemirror/lang-sass": "^6.0.0", + "@codemirror/lang-sql": "^6.0.0", + "@codemirror/lang-vue": "^0.1.1", + "@codemirror/lang-wast": "^6.0.0", + "@codemirror/lang-xml": "^6.0.0", + "@codemirror/lang-yaml": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/legacy-modes": "^6.4.0" + } + }, + "node_modules/@codemirror/legacy-modes": { + "version": "6.4.1", + "resolved": "https://registry.npmmirror.com/@codemirror/legacy-modes/-/legacy-modes-6.4.1.tgz", + "integrity": "sha512-vdg3XY7OAs5uLDx2Iw+cGfnwtd7kM+Et/eMsqAGTfT/JKiVBQZXosTzjEbWAi/FrY6DcQIz8mQjBozFHZEUWQA==", + "dependencies": { + "@codemirror/language": "^6.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.8.2", + "resolved": "https://registry.npmmirror.com/@codemirror/lint/-/lint-6.8.2.tgz", + "integrity": "sha512-PDFG5DjHxSEjOXk9TQYYVjZDqlZTFaDBfhQixHnQOEVDDNHUbEh/hstAjcQJaA6FQdZTD1hquXTK0rVBLADR1g==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/search": { + "version": "6.5.6", + "resolved": "https://registry.npmmirror.com/@codemirror/search/-/search-6.5.6.tgz", + "integrity": "sha512-rpMgcsh7o0GuCDUXKPvww+muLA1pDJaFrpq/CCHtpQJYz8xopu4D1hPcKRoDD0YlF8gZaqTNIRa4VRBWyhyy7Q==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.4.1", + "resolved": "https://registry.npmmirror.com/@codemirror/state/-/state-6.4.1.tgz", + "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==" + }, + "node_modules/@codemirror/view": { + "version": "6.34.1", + "resolved": "https://registry.npmmirror.com/@codemirror/view/-/view-6.34.1.tgz", + "integrity": "sha512-t1zK/l9UiRqwUNPm+pdIT0qzJlzuVckbTEMVNFhfWkGiBQClstzg+78vedCvLSX0xJEZ6lwZbPpnljL7L6iwMQ==", + "dependencies": { + "@codemirror/state": "^6.4.0", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, + "node_modules/@ctrl/tinycolor": { + "version": "3.6.1", + "resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", + "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/@element-plus/icons-vue": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz", + "integrity": "sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==", + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", + "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.2.tgz", + "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", + "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.2.tgz", + "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", + "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", + "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", + "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", + "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", + "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", + "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", + "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", + "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", + "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", + "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", + "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", + "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", + "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", + "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", + "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", + "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", + "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", + "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", + "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", + "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", + "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.6.4", + "resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.6.4.tgz", + "integrity": "sha512-a4IowK4QkXl4SCWTGUR0INAfEOX3wtsYw3rKK5InQEHMGObkR8Xk44qYQD9P4r6HHw0iIfK6GUKECmY8sTkqRA==", + "dependencies": { + "@floating-ui/utils": "^0.2.4" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.7", + "resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.6.7.tgz", + "integrity": "sha512-wmVfPG5o2xnKDU4jx/m4w5qva9FWHcnZ8BvzEe90D/RpwsJaTAVYPEPdQ8sbr/N8zZTAHlZUTQdqg8ZUbzHmng==", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.4" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.4", + "resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.4.tgz", + "integrity": "sha512-dWO2pw8hhi+WrXq1YJy2yCuWoL20PddgGaqTgVe4cOS9Q6qklXCiA1tJEqX6BEwRNSCP84/afac9hd4MS+zEUA==" + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, + "node_modules/@lezer/common": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/@lezer/common/-/common-1.2.3.tgz", + "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==" + }, + "node_modules/@lezer/cpp": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/@lezer/cpp/-/cpp-1.1.2.tgz", + "integrity": "sha512-macwKtyeUO0EW86r3xWQCzOV9/CF8imJLpJlPv3sDY57cPGeUZ8gXWOWNlJr52TVByMV3PayFQCA5SHEERDmVQ==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/css": { + "version": "1.1.9", + "resolved": "https://registry.npmmirror.com/@lezer/css/-/css-1.1.9.tgz", + "integrity": "sha512-TYwgljcDv+YrV0MZFFvYFQHCfGgbPMR6nuqLabBdmZoFH3EP1gvw8t0vae326Ne3PszQkbXfVBjCnf3ZVCr0bA==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/go": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/@lezer/go/-/go-1.0.0.tgz", + "integrity": "sha512-co9JfT3QqX1YkrMmourYw2Z8meGC50Ko4d54QEcQbEYpvdUvN4yb0NBZdn/9ertgvjsySxHsKzH3lbm3vqJ4Jw==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/highlight": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/@lezer/highlight/-/highlight-1.2.1.tgz", + "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/html": { + "version": "1.3.10", + "resolved": "https://registry.npmmirror.com/@lezer/html/-/html-1.3.10.tgz", + "integrity": "sha512-dqpT8nISx/p9Do3AchvYGV3qYc4/rKr3IBZxlHmpIKam56P47RSHkSF5f13Vu9hebS1jM0HmtJIwLbWz1VIY6w==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/java": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/@lezer/java/-/java-1.1.3.tgz", + "integrity": "sha512-yHquUfujwg6Yu4Fd1GNHCvidIvJwi/1Xu2DaKl/pfWIA2c1oXkVvawH3NyXhCaFx4OdlYBVX5wvz2f7Aoa/4Xw==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/javascript": { + "version": "1.4.19", + "resolved": "https://registry.npmmirror.com/@lezer/javascript/-/javascript-1.4.19.tgz", + "integrity": "sha512-j44kbR1QL26l6dMunZ1uhKBFteVGLVCBGNUD2sUaMnic+rbTviVuoK0CD1l9FTW31EueWvFFswCKMH7Z+M3JRA==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.1.3", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/json": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/@lezer/json/-/json-1.0.2.tgz", + "integrity": "sha512-xHT2P4S5eeCYECyKNPhr4cbEL9tc8w83SPwRC373o9uEdrvGKTZoJVAGxpOsZckMlEh9W23Pc72ew918RWQOBQ==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/@lezer/lr/-/lr-1.4.2.tgz", + "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/markdown": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/@lezer/markdown/-/markdown-1.3.1.tgz", + "integrity": "sha512-DGlzU/i8DC8k0uz1F+jeePrkATl0jWakauTzftMQOcbaMkHbNSRki/4E2tOzJWsVpoKYhe7iTJ03aepdwVUXUA==", + "dependencies": { + "@lezer/common": "^1.0.0", + "@lezer/highlight": "^1.0.0" + } + }, + "node_modules/@lezer/php": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/@lezer/php/-/php-1.0.2.tgz", + "integrity": "sha512-GN7BnqtGRpFyeoKSEqxvGvhJQiI4zkgmYnDk/JIyc7H7Ifc1tkPnUn/R2R8meH3h/aBf5rzjvU8ZQoyiNDtDrA==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.1.0" + } + }, + "node_modules/@lezer/python": { + "version": "1.1.14", + "resolved": "https://registry.npmmirror.com/@lezer/python/-/python-1.1.14.tgz", + "integrity": "sha512-ykDOb2Ti24n76PJsSa4ZoDF0zH12BSw1LGfQXCYJhJyOGiFTfGaX0Du66Ze72R+u/P35U+O6I9m8TFXov1JzsA==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/rust": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/@lezer/rust/-/rust-1.0.2.tgz", + "integrity": "sha512-Lz5sIPBdF2FUXcWeCu1//ojFAZqzTQNRga0aYv6dYXqJqPfMdCAI0NzajWUd4Xijj1IKJLtjoXRPMvTKWBcqKg==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/sass": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/@lezer/sass/-/sass-1.0.7.tgz", + "integrity": "sha512-8HLlOkuX/SMHOggI2DAsXUw38TuURe+3eQ5hiuk9QmYOUyC55B1dYEIMkav5A4IELVaW4e1T4P9WRiI5ka4mdw==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/xml": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/@lezer/xml/-/xml-1.0.5.tgz", + "integrity": "sha512-VFouqOzmUWfIg+tfmpcdV33ewtK+NSwd4ngSe1aG7HFb4BN0ExyY1b8msp+ndFrnlG4V4iC8yXacjFtrwERnaw==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/yaml": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/@lezer/yaml/-/yaml-1.0.3.tgz", + "integrity": "sha512-GuBLekbw9jDBDhGur82nuwkxKQ+a3W5H0GfaAthDXcAu+XdpS43VlnxA9E9hllkpSP5ellRDKjLLj7Lu9Wr6xA==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.4.0" + } + }, + "node_modules/@popperjs/core": { + "name": "@sxzz/popperjs-es", + "version": "2.11.7", + "resolved": "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz", + "integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.39.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.39.0.tgz", + "integrity": "sha512-lGVys55Qb00Wvh8DMAocp5kIcaNzEFTmGhfFd88LfaogYTRKrdxgtlO5H6S49v2Nd8R2C6wLOal0qv6/kCkOwA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.39.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.39.0.tgz", + "integrity": "sha512-It9+M1zE31KWfqh/0cJLrrsCPiF72PoJjIChLX+rEcujVRCb4NLQ5QzFkzIZW8Kn8FTbvGQBY5TkKBau3S8cCQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.39.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.39.0.tgz", + "integrity": "sha512-lXQnhpFDOKDXiGxsU9/l8UEGGM65comrQuZ+lDcGUx+9YQ9dKpF3rSEGepyeR5AHZ0b5RgiligsBhWZfSSQh8Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.39.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.39.0.tgz", + "integrity": "sha512-mKXpNZLvtEbgu6WCkNij7CGycdw9cJi2k9v0noMb++Vab12GZjFgUXD69ilAbBh034Zwn95c2PNSz9xM7KYEAQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.39.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.39.0.tgz", + "integrity": "sha512-jivRRlh2Lod/KvDZx2zUR+I4iBfHcu2V/BA2vasUtdtTN2Uk3jfcZczLa81ESHZHPHy4ih3T/W5rPFZ/hX7RtQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.39.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.39.0.tgz", + "integrity": "sha512-8RXIWvYIRK9nO+bhVz8DwLBepcptw633gv/QT4015CpJ0Ht8punmoHU/DuEd3iw9Hr8UwUV+t+VNNuZIWYeY7Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.39.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.39.0.tgz", + "integrity": "sha512-mz5POx5Zu58f2xAG5RaRRhp3IZDK7zXGk5sdEDj4o96HeaXhlUwmLFzNlc4hCQi5sGdR12VDgEUqVSHer0lI9g==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.39.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.39.0.tgz", + "integrity": "sha512-+YDwhM6gUAyakl0CD+bMFpdmwIoRDzZYaTWV3SDRBGkMU/VpIBYXXEvkEcTagw/7VVkL2vA29zU4UVy1mP0/Yw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.39.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.39.0.tgz", + "integrity": "sha512-EKf7iF7aK36eEChvlgxGnk7pdJfzfQbNvGV/+l98iiMwU23MwvmV0Ty3pJ0p5WQfm3JRHOytSIqD9LB7Bq7xdQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.39.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.39.0.tgz", + "integrity": "sha512-vYanR6MtqC7Z2SNr8gzVnzUul09Wi1kZqJaek3KcIlI/wq5Xtq4ZPIZ0Mr/st/sv/NnaPwy/D4yXg5x0B3aUUA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.39.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.39.0.tgz", + "integrity": "sha512-NMRUT40+h0FBa5fb+cpxtZoGAggRem16ocVKIv5gDB5uLDgBIwrIsXlGqYbLwW8YyO3WVTk1FkFDjMETYlDqiw==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.39.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.39.0.tgz", + "integrity": "sha512-0pCNnmxgduJ3YRt+D+kJ6Ai/r+TaePu9ZLENl+ZDV/CdVczXl95CbIiwwswu4L+K7uOIGf6tMo2vm8uadRaICQ==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.39.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.39.0.tgz", + "integrity": "sha512-t7j5Zhr7S4bBtksT73bO6c3Qa2AV/HqiGlj9+KB3gNF5upcVkx+HLgxTm8DK4OkzsOYqbdqbLKwvGMhylJCPhQ==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.39.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.39.0.tgz", + "integrity": "sha512-m6cwI86IvQ7M93MQ2RF5SP8tUjD39Y7rjb1qjHgYh28uAPVU8+k/xYWvxRO3/tBN2pZkSMa5RjnPuUIbrwVxeA==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.39.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.39.0.tgz", + "integrity": "sha512-iRDJd2ebMunnk2rsSBYlsptCyuINvxUfGwOUldjv5M4tpa93K8tFMeYGpNk2+Nxl+OBJnBzy2/JCscGeO507kA==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.39.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.39.0.tgz", + "integrity": "sha512-t9jqYw27R6Lx0XKfEFe5vUeEJ5pF3SGIM6gTfONSMb7DuG6z6wfj2yjcoZxHg129veTqU7+wOhY6GX8wmf90dA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.39.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.39.0.tgz", + "integrity": "sha512-ThFdkrFDP55AIsIZDKSBWEt/JcWlCzydbZHinZ0F/r1h83qbGeenCt/G/wG2O0reuENDD2tawfAj2s8VK7Bugg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.39.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.39.0.tgz", + "integrity": "sha512-jDrLm6yUtbOg2TYB3sBF3acUnAwsIksEYjLeHL+TJv9jg+TmTwdyjnDex27jqEMakNKf3RwwPahDIt7QXCSqRQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.39.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.39.0.tgz", + "integrity": "sha512-6w9uMuza+LbLCVoNKL5FSLE7yvYkq9laSd09bwS0tMjkwXrmib/4KmoJcrKhLWHvw19mwU+33ndC69T7weNNjQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.39.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.39.0.tgz", + "integrity": "sha512-yAkUOkIKZlK5dl7u6dg897doBgLXmUHhIINM2c+sND3DZwnrdQkkSiDh7N75Ll4mM4dxSkYfXqU9fW3lLkMFug==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==" + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==" + }, + "node_modules/@types/lodash": { + "version": "4.17.7", + "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.7.tgz", + "integrity": "sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==" + }, + "node_modules/@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/luxon": { + "version": "3.4.2", + "resolved": "https://registry.npmmirror.com/@types/luxon/-/luxon-3.4.2.tgz", + "integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==", + "dev": true + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmmirror.com/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==" + }, + "node_modules/@types/node": { + "version": "20.14.10", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.14.10.tgz", + "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/video.js": { + "version": "7.3.58", + "resolved": "https://registry.npmmirror.com/@types/video.js/-/video.js-7.3.58.tgz", + "integrity": "sha512-1CQjuSrgbv1/dhmcfQ83eVyYbvGyqhTvb2Opxr0QCV+iJ4J6/J+XWQ3Om59WiwCd1MN3rDUHasx5XRrpUtewYQ==", + "peer": true + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.16", + "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz", + "integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==" + }, + "node_modules/@vavt/util": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/@vavt/util/-/util-2.1.0.tgz", + "integrity": "sha512-YIfAvArSFVXmWvoF+DEGD0FhkhVNcCtVWWkfYtj76eSrwHh/wuEEFhiEubg1XLNM3tChO8FH8xJCT/hnizjgFQ==" + }, + "node_modules/@videojs-player/vue": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/@videojs-player/vue/-/vue-1.0.0.tgz", + "integrity": "sha512-WonTezRfKu3fYdQLt/ta+nuKH6gMZUv8l40Jke/j4Lae7IqeO/+lLAmBnh3ni88bwR+vkFXIlZ2Ci7VKInIYJg==", + "peerDependencies": { + "@types/video.js": "7.x", + "video.js": "7.x", + "vue": "3.x" + } + }, + "node_modules/@videojs/http-streaming": { + "version": "2.16.3", + "resolved": "https://registry.npmmirror.com/@videojs/http-streaming/-/http-streaming-2.16.3.tgz", + "integrity": "sha512-91CJv5PnFBzNBvyEjt+9cPzTK/xoVixARj2g7ZAvItA+5bx8VKdk5RxCz/PP2kdzz9W+NiDUMPkdmTsosmy69Q==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@videojs/vhs-utils": "3.0.5", + "aes-decrypter": "3.1.3", + "global": "^4.4.0", + "m3u8-parser": "4.8.0", + "mpd-parser": "^0.22.1", + "mux.js": "6.0.1", + "video.js": "^6 || ^7" + }, + "engines": { + "node": ">=8", + "npm": ">=5" + }, + "peerDependencies": { + "video.js": "^6 || ^7" + } + }, + "node_modules/@videojs/vhs-utils": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/@videojs/vhs-utils/-/vhs-utils-3.0.5.tgz", + "integrity": "sha512-PKVgdo8/GReqdx512F+ombhS+Bzogiofy1LgAj4tN8PfdBx3HSS7V5WfJotKTqtOWGwVfSWsrYN/t09/DSryrw==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "global": "^4.4.0", + "url-toolkit": "^2.2.1" + }, + "engines": { + "node": ">=8", + "npm": ">=5" + } + }, + "node_modules/@videojs/xhr": { + "version": "2.6.0", + "resolved": "https://registry.npmmirror.com/@videojs/xhr/-/xhr-2.6.0.tgz", + "integrity": "sha512-7J361GiN1tXpm+gd0xz2QWr3xNWBE+rytvo8J3KuggFaLg+U37gZQ2BuPLcnkfGffy2e+ozY70RHC8jt7zjA6Q==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.5.5", + "global": "~4.4.0", + "is-function": "^1.0.1" + } + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.2.1.tgz", + "integrity": "sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ==", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@volar/language-core": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.3.0.tgz", + "integrity": "sha512-pvhL24WUh3VDnv7Yw5N1sjhPtdx7q9g+Wl3tggmnkMcyK8GcCNElF2zHiKznryn0DiUGk+eez/p2qQhz+puuHw==", + "dev": true, + "dependencies": { + "@volar/source-map": "2.3.0" + } + }, + "node_modules/@volar/source-map": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.3.0.tgz", + "integrity": "sha512-G/228aZjAOGhDjhlyZ++nDbKrS9uk+5DMaEstjvzglaAw7nqtDyhnQAsYzUg6BMP9BtwZ59RIw5HGePrutn00Q==", + "dev": true, + "dependencies": { + "muggle-string": "^0.4.0" + } + }, + "node_modules/@volar/typescript": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.3.0.tgz", + "integrity": "sha512-PtUwMM87WsKVeLJN33GSTUjBexlKfKgouWlOUIv7pjrOnTwhXHZNSmpc312xgXdTjQPpToK6KXSIcKu9sBQ5LQ==", + "dev": true, + "dependencies": { + "@volar/language-core": "2.3.0", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.4.29", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.29.tgz", + "integrity": "sha512-TFKiRkKKsRCKvg/jTSSKK7mYLJEQdUiUfykbG49rubC9SfDyvT2JrzTReopWlz2MxqeLyxh9UZhvxEIBgAhtrg==", + "dependencies": { + "@babel/parser": "^7.24.7", + "@vue/shared": "3.4.29", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.4.29", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.29.tgz", + "integrity": "sha512-A6+iZ2fKIEGnfPJejdB7b1FlJzgiD+Y/sxxKwJWg1EbJu6ZPgzaPQQ51ESGNv0CP6jm6Z7/pO6Ia8Ze6IKrX7w==", + "dependencies": { + "@vue/compiler-core": "3.4.29", + "@vue/shared": "3.4.29" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.4.29", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.29.tgz", + "integrity": "sha512-zygDcEtn8ZimDlrEQyLUovoWgKQic6aEQqRXce2WXBvSeHbEbcAsXyCk9oG33ZkyWH4sl9D3tkYc1idoOkdqZQ==", + "dependencies": { + "@babel/parser": "^7.24.7", + "@vue/compiler-core": "3.4.29", + "@vue/compiler-dom": "3.4.29", + "@vue/compiler-ssr": "3.4.29", + "@vue/shared": "3.4.29", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.10", + "postcss": "^8.4.38", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.4.29", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.29.tgz", + "integrity": "sha512-rFbwCmxJ16tDp3N8XCx5xSQzjhidYjXllvEcqX/lopkoznlNPz3jyy0WGJCyhAaVQK677WWFt3YO/WUEkMMUFQ==", + "dependencies": { + "@vue/compiler-dom": "3.4.29", + "@vue/shared": "3.4.29" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.3", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.3.tgz", + "integrity": "sha512-0MiMsFma/HqA6g3KLKn+AGpL1kgKhFWszC9U29NfpWK5LE7bjeXxySWJrOJ77hBz+TBrBQ7o4QJqbPbqbs8rJw==" + }, + "node_modules/@vue/language-core": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.0.21.tgz", + "integrity": "sha512-vjs6KwnCK++kIXT+eI63BGpJHfHNVJcUCr3RnvJsccT3vbJnZV5IhHR2puEkoOkIbDdp0Gqi1wEnv3hEd3WsxQ==", + "dev": true, + "dependencies": { + "@volar/language-core": "~2.3.0-alpha.15", + "@vue/compiler-dom": "^3.4.0", + "@vue/shared": "^3.4.0", + "computeds": "^0.0.1", + "minimatch": "^9.0.3", + "path-browserify": "^1.0.1", + "vue-template-compiler": "^2.7.14" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/reactivity": { + "version": "3.4.29", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.29.tgz", + "integrity": "sha512-w8+KV+mb1a8ornnGQitnMdLfE0kXmteaxLdccm2XwdFxXst4q/Z7SEboCV5SqJNpZbKFeaRBBJBhW24aJyGINg==", + "dependencies": { + "@vue/shared": "3.4.29" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.4.29", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.29.tgz", + "integrity": "sha512-s8fmX3YVR/Rk5ig0ic0NuzTNjK2M7iLuVSZyMmCzN/+Mjuqqif1JasCtEtmtoJWF32pAtUjyuT2ljNKNLeOmnQ==", + "dependencies": { + "@vue/reactivity": "3.4.29", + "@vue/shared": "3.4.29" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.4.29", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.29.tgz", + "integrity": "sha512-gI10atCrtOLf/2MPPMM+dpz3NGulo9ZZR9d1dWo4fYvm+xkfvRrw1ZmJ7mkWtiJVXSsdmPbcK1p5dZzOCKDN0g==", + "dependencies": { + "@vue/reactivity": "3.4.29", + "@vue/runtime-core": "3.4.29", + "@vue/shared": "3.4.29", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.4.29", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.29.tgz", + "integrity": "sha512-HMLCmPI2j/k8PVkSBysrA2RxcxC5DgBiCdj7n7H2QtR8bQQPqKAe8qoaxLcInzouBmzwJ+J0x20ygN/B5mYBng==", + "dependencies": { + "@vue/compiler-ssr": "3.4.29", + "@vue/shared": "3.4.29" + }, + "peerDependencies": { + "vue": "3.4.29" + } + }, + "node_modules/@vue/shared": { + "version": "3.4.29", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.29.tgz", + "integrity": "sha512-hQ2gAQcBO/CDpC82DCrinJNgOHI2v+FA7BDW4lMSPeBpQ7sRe2OLHWe5cph1s7D8DUQAwRt18dBDfJJ220APEA==" + }, + "node_modules/@vueuse/core": { + "version": "9.13.0", + "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-9.13.0.tgz", + "integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==", + "dependencies": { + "@types/web-bluetooth": "^0.0.16", + "@vueuse/metadata": "9.13.0", + "@vueuse/shared": "9.13.0", + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/core/node_modules/vue-demi": { + "version": "0.14.8", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.8.tgz", + "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@vueuse/metadata": { + "version": "9.13.0", + "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz", + "integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "9.13.0", + "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.13.0.tgz", + "integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==", + "dependencies": { + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared/node_modules/vue-demi": { + "version": "0.14.8", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.8.tgz", + "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "peer": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/aes-decrypter": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/aes-decrypter/-/aes-decrypter-3.1.3.tgz", + "integrity": "sha512-VkG9g4BbhMBy+N5/XodDeV6F02chEk9IpgRTq/0bS80y4dzy79VH2Gtms02VXomf3HmyRe3yyJYkJ990ns+d6A==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@videojs/vhs-utils": "^3.0.5", + "global": "^4.4.0", + "pkcs7": "^1.0.4" + } + }, + "node_modules/animate.css": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/animate.css/-/animate.css-4.1.1.tgz", + "integrity": "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ==" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/async-validator": { + "version": "4.2.5", + "resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz", + "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.8.2", + "resolved": "https://registry.npmmirror.com/axios/-/axios-1.8.2.tgz", + "integrity": "sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/bootstrap-icons": { + "version": "1.11.3", + "resolved": "https://registry.npmmirror.com/bootstrap-icons/-/bootstrap-icons-1.11.3.tgz", + "integrity": "sha512-+3lpHrCw/it2/7lBL15VR0HEumaBss0+f/Lb6ZvHISn1mlK83jjFpooTLsMWbIjJMDjDjOExMsTxnXSIT4k4ww==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ] + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/codemirror": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/codemirror/-/codemirror-6.0.1.tgz", + "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/computeds": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz", + "integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==", + "dev": true + }, + "node_modules/copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" + }, + "node_modules/cssfilter": { + "version": "0.0.10", + "resolved": "https://registry.npmmirror.com/cssfilter/-/cssfilter-0.0.10.tgz", + "integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==" + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==", + "peer": true + }, + "node_modules/echarts": { + "version": "5.5.1", + "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.5.1.tgz", + "integrity": "sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==", + "dependencies": { + "tslib": "2.3.0", + "zrender": "5.6.0" + } + }, + "node_modules/element-plus": { + "version": "2.9.0", + "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.9.0.tgz", + "integrity": "sha512-ccOFXKsauo2dtokAr4OX7gZsb7TuAoVxA2zGRZo5o2yyDDBLBaZxOoFQPoxITSLcHbBfQuNDGK5Iag5hnyKkZA==", + "dependencies": { + "@ctrl/tinycolor": "^3.4.1", + "@element-plus/icons-vue": "^2.3.1", + "@floating-ui/dom": "^1.0.1", + "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7", + "@types/lodash": "^4.14.182", + "@types/lodash-es": "^4.17.6", + "@vueuse/core": "^9.1.0", + "async-validator": "^4.2.5", + "dayjs": "^1.11.13", + "escape-html": "^1.0.3", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "lodash-unified": "^1.0.2", + "memoize-one": "^6.0.0", + "normalize-wheel-es": "^1.2.0" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.2.tgz", + "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.2", + "@esbuild/android-arm": "0.25.2", + "@esbuild/android-arm64": "0.25.2", + "@esbuild/android-x64": "0.25.2", + "@esbuild/darwin-arm64": "0.25.2", + "@esbuild/darwin-x64": "0.25.2", + "@esbuild/freebsd-arm64": "0.25.2", + "@esbuild/freebsd-x64": "0.25.2", + "@esbuild/linux-arm": "0.25.2", + "@esbuild/linux-arm64": "0.25.2", + "@esbuild/linux-ia32": "0.25.2", + "@esbuild/linux-loong64": "0.25.2", + "@esbuild/linux-mips64el": "0.25.2", + "@esbuild/linux-ppc64": "0.25.2", + "@esbuild/linux-riscv64": "0.25.2", + "@esbuild/linux-s390x": "0.25.2", + "@esbuild/linux-x64": "0.25.2", + "@esbuild/netbsd-arm64": "0.25.2", + "@esbuild/netbsd-x64": "0.25.2", + "@esbuild/openbsd-arm64": "0.25.2", + "@esbuild/openbsd-x64": "0.25.2", + "@esbuild/sunos-x64": "0.25.2", + "@esbuild/win32-arm64": "0.25.2", + "@esbuild/win32-ia32": "0.25.2", + "@esbuild/win32-x64": "0.25.2" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/global": { + "version": "4.4.0", + "resolved": "https://registry.npmmirror.com/global/-/global-4.4.0.tgz", + "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", + "peer": true, + "dependencies": { + "min-document": "^2.19.0", + "process": "^0.11.10" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/hls.js": { + "version": "1.5.20", + "resolved": "https://registry.npmmirror.com/hls.js/-/hls.js-1.5.20.tgz", + "integrity": "sha512-uu0VXUK52JhihhnN/MVVo1lvqNNuhoxkonqgO3IpjvQiGpJBdIXMGkofjQb/j9zvV7a1SW8U9g1FslWx/1HOiQ==" + }, + "node_modules/individual": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/individual/-/individual-2.0.0.tgz", + "integrity": "sha512-pWt8hBCqJsUWI/HtcfWod7+N9SgAqyPEaF7JQjwzjn5vGrpg6aQ5qeAFQ7dx//UH4J1O+7xqew+gCeeFt6xN/g==", + "peer": true + }, + "node_modules/is-function": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", + "peer": true + }, + "node_modules/jsencrypt": { + "version": "3.3.2", + "resolved": "https://registry.npmmirror.com/jsencrypt/-/jsencrypt-3.3.2.tgz", + "integrity": "sha512-arQR1R1ESGdAxY7ZheWr12wCaF2yF47v5qpB76TtV64H1pyGudk9Hvw8Y9tb/FiTIaaTRUyaSnm5T/Y53Ghm/A==" + }, + "node_modules/keycode": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/keycode/-/keycode-2.2.1.tgz", + "integrity": "sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg==", + "peer": true + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "node_modules/lodash-unified": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/lodash-unified/-/lodash-unified-1.0.3.tgz", + "integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==", + "peerDependencies": { + "@types/lodash-es": "*", + "lodash": "*", + "lodash-es": "*" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" + }, + "node_modules/luxon": { + "version": "3.5.0", + "resolved": "https://registry.npmmirror.com/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/m3u8-parser": { + "version": "4.8.0", + "resolved": "https://registry.npmmirror.com/m3u8-parser/-/m3u8-parser-4.8.0.tgz", + "integrity": "sha512-UqA2a/Pw3liR6Df3gwxrqghCP17OpPlQj6RBPLYygf/ZSQ4MoSgvdvhvt35qV+3NaaA0FSZx93Ix+2brT1U7cA==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@videojs/vhs-utils": "^3.0.5", + "global": "^4.4.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmmirror.com/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-it-image-figures": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/markdown-it-image-figures/-/markdown-it-image-figures-2.1.1.tgz", + "integrity": "sha512-mwXSQ2nPeVUzCMIE3HlLvjRioopiqyJLNph0pyx38yf9mpqFDhNGnMpAXF9/A2Xv0oiF2cVyg9xwfF0HNAz05g==", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "markdown-it": "*" + } + }, + "node_modules/markdown-it-sub": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/markdown-it-sub/-/markdown-it-sub-2.0.0.tgz", + "integrity": "sha512-iCBKgwCkfQBRg2vApy9vx1C1Tu6D8XYo8NvevI3OlwzBRmiMtsJ2sXupBgEA7PPxiDwNni3qIUkhZ6j5wofDUA==" + }, + "node_modules/markdown-it-sup": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/markdown-it-sup/-/markdown-it-sup-2.0.0.tgz", + "integrity": "sha512-5VgmdKlkBd8sgXuoDoxMpiU+BiEt3I49GItBzzw7Mxq9CxvnhE/k09HFli09zgfFDRixDQDfDxi0mgBCXtaTvA==" + }, + "node_modules/md-editor-v3": { + "version": "4.21.1", + "resolved": "https://registry.npmmirror.com/md-editor-v3/-/md-editor-v3-4.21.1.tgz", + "integrity": "sha512-887rjL0jJBdu8yA7jHU472gEaLlJ4kH8POzG/qYRLtIN72RuAOuKTiDfJVdnrSqMgpIrXYgVgVt36luQpo/zMA==", + "dependencies": { + "@codemirror/lang-markdown": "^6.2.5", + "@codemirror/language-data": "^6.5.1", + "@types/markdown-it": "^14.0.1", + "@vavt/util": "^2.1.0", + "codemirror": "^6.0.1", + "copy-to-clipboard": "^3.3.3", + "lru-cache": "^10.2.0", + "markdown-it": "^14.0.0", + "markdown-it-image-figures": "^2.1.1", + "markdown-it-sub": "^2.0.0", + "markdown-it-sup": "^2.0.0", + "medium-zoom": "^1.1.0", + "xss": "^1.0.15" + }, + "peerDependencies": { + "vue": "^3.2.47" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==" + }, + "node_modules/medium-zoom": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/medium-zoom/-/medium-zoom-1.1.0.tgz", + "integrity": "sha512-ewyDsp7k4InCUp3jRmwHBRFGyjBimKps/AJLjRSox+2q/2H4p/PNpQf+pwONWlJiOudkBXtbdmVbFjqyybfTmQ==" + }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmmirror.com/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==", + "peer": true, + "dependencies": { + "dom-walk": "^0.1.0" + } + }, + "node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mpd-parser": { + "version": "0.22.1", + "resolved": "https://registry.npmmirror.com/mpd-parser/-/mpd-parser-0.22.1.tgz", + "integrity": "sha512-fwBebvpyPUU8bOzvhX0VQZgSohncbgYwUyJJoTSNpmy7ccD2ryiCvM7oRkn/xQH5cv73/xU7rJSNCLjdGFor0Q==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@videojs/vhs-utils": "^3.0.5", + "@xmldom/xmldom": "^0.8.3", + "global": "^4.4.0" + }, + "bin": { + "mpd-to-m3u8-json": "bin/parse.js" + } + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true + }, + "node_modules/mux.js": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/mux.js/-/mux.js-6.0.1.tgz", + "integrity": "sha512-22CHb59rH8pWGcPGW5Og7JngJ9s+z4XuSlYvnxhLuc58cA1WqGDQPzuG8I+sPm1/p0CdgpzVTaKW408k5DNn8w==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.11.2", + "global": "^4.4.0" + }, + "bin": { + "muxjs-transmux": "bin/transmux.js" + }, + "engines": { + "node": ">=8", + "npm": ">=5" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/normalize-wheel-es": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz", + "integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==" + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/pkcs7": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/pkcs7/-/pkcs7-1.0.4.tgz", + "integrity": "sha512-afRERtHn54AlwaF2/+LFszyAANTCggGilmcmILUzEjvs3XgFZT+xE6+QWQcAGmu4xajy+Xtj7acLOPdx5/eXWQ==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.5.5" + }, + "bin": { + "pkcs7": "bin/cli.js" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmmirror.com/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "peer": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "peer": true + }, + "node_modules/rollup": { + "version": "4.39.0", + "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.39.0.tgz", + "integrity": "sha512-thI8kNc02yNvnmJp8dr3fNWJ9tCONDhp6TV35X6HkKGGs9E6q7YWCHbe5vKiTa7TAiNcFEmXKj3X/pG2b3ci0g==", + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.39.0", + "@rollup/rollup-android-arm64": "4.39.0", + "@rollup/rollup-darwin-arm64": "4.39.0", + "@rollup/rollup-darwin-x64": "4.39.0", + "@rollup/rollup-freebsd-arm64": "4.39.0", + "@rollup/rollup-freebsd-x64": "4.39.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.39.0", + "@rollup/rollup-linux-arm-musleabihf": "4.39.0", + "@rollup/rollup-linux-arm64-gnu": "4.39.0", + "@rollup/rollup-linux-arm64-musl": "4.39.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.39.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.39.0", + "@rollup/rollup-linux-riscv64-gnu": "4.39.0", + "@rollup/rollup-linux-riscv64-musl": "4.39.0", + "@rollup/rollup-linux-s390x-gnu": "4.39.0", + "@rollup/rollup-linux-x64-gnu": "4.39.0", + "@rollup/rollup-linux-x64-musl": "4.39.0", + "@rollup/rollup-win32-arm64-msvc": "4.39.0", + "@rollup/rollup-win32-ia32-msvc": "4.39.0", + "@rollup/rollup-win32-x64-msvc": "4.39.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/rust-result": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/rust-result/-/rust-result-1.0.0.tgz", + "integrity": "sha512-6cJzSBU+J/RJCF063onnQf0cDUOHs9uZI1oroSGnHOph+CQTIJ5Pp2hK5kEQq1+7yE/EEWfulSNXAQ2jikPthA==", + "peer": true, + "dependencies": { + "individual": "^2.0.0" + } + }, + "node_modules/safe-json-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/safe-json-parse/-/safe-json-parse-4.0.0.tgz", + "integrity": "sha512-RjZPPHugjK0TOzFrLZ8inw44s9bKox99/0AZW9o/BEQVrJfhI+fIHMErnPyRa89/yRXUUr93q+tiN6zhoVV4wQ==", + "peer": true, + "dependencies": { + "rust-result": "^1.0.0" + } + }, + "node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/style-mod": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/style-mod/-/style-mod-4.1.2.tgz", + "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==" + }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" + }, + "node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + }, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "devOptional": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==" + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/url-toolkit": { + "version": "2.2.5", + "resolved": "https://registry.npmmirror.com/url-toolkit/-/url-toolkit-2.2.5.tgz", + "integrity": "sha512-mtN6xk+Nac+oyJ/PrI7tzfmomRVNFIWKUbG8jdYFt52hxbiReFAXIjYskvu64/dvuW71IcB7lV8l0HvZMac6Jg==", + "peer": true + }, + "node_modules/video.js": { + "version": "7.21.6", + "resolved": "https://registry.npmmirror.com/video.js/-/video.js-7.21.6.tgz", + "integrity": "sha512-m41TbODrUCToVfK1aljVd296CwDQnCRewpIm5tTXMuV87YYSGw1H+VDOaV45HlpcWSsTWWLF++InDgGJfthfUw==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@videojs/http-streaming": "2.16.3", + "@videojs/vhs-utils": "^3.0.4", + "@videojs/xhr": "2.6.0", + "aes-decrypter": "3.1.3", + "global": "^4.4.0", + "keycode": "^2.2.0", + "m3u8-parser": "4.8.0", + "mpd-parser": "0.22.1", + "mux.js": "6.0.1", + "safe-json-parse": "4.0.0", + "videojs-font": "3.2.0", + "videojs-vtt.js": "^0.15.5" + } + }, + "node_modules/videojs-font": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/videojs-font/-/videojs-font-3.2.0.tgz", + "integrity": "sha512-g8vHMKK2/JGorSfqAZQUmYYNnXmfec4MLhwtEFS+mMs2IDY398GLysy6BH6K+aS1KMNu/xWZ8Sue/X/mdQPliA==", + "peer": true + }, + "node_modules/videojs-vtt.js": { + "version": "0.15.5", + "resolved": "https://registry.npmmirror.com/videojs-vtt.js/-/videojs-vtt.js-0.15.5.tgz", + "integrity": "sha512-yZbBxvA7QMYn15Lr/ZfhhLPrNpI/RmCSCqgIff57GC2gIrV5YfyzLfLyZMj0NnZSAz8syB4N0nHXpZg9MyrMOQ==", + "peer": true, + "dependencies": { + "global": "^4.3.1" + } + }, + "node_modules/vite": { + "version": "6.2.3", + "resolved": "https://registry.npmmirror.com/vite/-/vite-6.2.3.tgz", + "integrity": "sha512-IzwM54g4y9JA/xAeBPNaDXiBF8Jsgl3VBQ2YQ/wOY6fyW3xMdSoltIV3Bo59DErdqdE6RxUfv8W69DvUorE4Eg==", + "dependencies": { + "esbuild": "^0.25.0", + "postcss": "^8.5.3", + "rollup": "^4.30.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", + "dev": true + }, + "node_modules/vue": { + "version": "3.4.29", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.29.tgz", + "integrity": "sha512-8QUYfRcYzNlYuzKPfge1UWC6nF9ym0lx7mpGVPJYNhddxEf3DD0+kU07NTL0sXuiT2HuJuKr/iEO8WvXvT0RSQ==", + "dependencies": { + "@vue/compiler-dom": "3.4.29", + "@vue/compiler-sfc": "3.4.29", + "@vue/runtime-dom": "3.4.29", + "@vue/server-renderer": "3.4.29", + "@vue/shared": "3.4.29" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-request": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/vue-request/-/vue-request-2.0.4.tgz", + "integrity": "sha512-+Tu5rDy6ItF9UdD21Mmbjiq5Pq6NZSN9juH72hNQTMn1whHh4KZPTKWVLK2YS4nzbuEnPs+82G91AA2Fgd93mg==", + "dependencies": { + "vue-demi": "latest" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^2.0.0 || >=3.0.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/vue-request/node_modules/vue-demi": { + "version": "0.14.8", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.8.tgz", + "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/vue-router": { + "version": "4.4.0", + "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.4.0.tgz", + "integrity": "sha512-HB+t2p611aIZraV2aPSRNXf0Z/oLZFrlygJm+sZbdJaW6lcFqEDQwnzUBXn+DApw+/QzDU/I9TeWx9izEjTmsA==", + "dependencies": { + "@vue/devtools-api": "^6.5.1" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/vue-template-compiler": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz", + "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==", + "dev": true, + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/vue-tsc": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.0.21.tgz", + "integrity": "sha512-E6x1p1HaHES6Doy8pqtm7kQern79zRtIewkf9fiv7Y43Zo4AFDS5hKi+iHi2RwEhqRmuiwliB1LCEFEGwvxQnw==", + "dev": true, + "dependencies": { + "@volar/typescript": "~2.3.0-alpha.15", + "@vue/language-core": "2.0.21", + "semver": "^7.5.4" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/vue-video-player": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/vue-video-player/-/vue-video-player-6.0.0.tgz", + "integrity": "sha512-WP47OtefsjMEReRCIKIL3tRRgH/PyNm8ELjsbYgr/WWrYAj5Ih9Adzkzp+ylYOI/v57jJ4O7O4XkbXBCmsTqNw==", + "dependencies": { + "@videojs-player/vue": "1.x" + }, + "peerDependencies": { + "@types/video.js": "7.x", + "video.js": "7.x", + "vue": "3.x" + } + }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmmirror.com/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" + }, + "node_modules/xss": { + "version": "1.0.15", + "resolved": "https://registry.npmmirror.com/xss/-/xss-1.0.15.tgz", + "integrity": "sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==", + "dependencies": { + "commander": "^2.20.3", + "cssfilter": "0.0.10" + }, + "bin": { + "xss": "bin/xss" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/zrender": { + "version": "5.6.0", + "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.6.0.tgz", + "integrity": "sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==", + "dependencies": { + "tslib": "2.3.0" + } + } + } +} diff --git a/web_vue/package.json b/web_vue/package.json new file mode 100644 index 0000000..ff81be5 --- /dev/null +++ b/web_vue/package.json @@ -0,0 +1,36 @@ +{ + "name": "web_vue", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@element-plus/icons-vue": "^2.3.1", + "@types/node": "^20.14.10", + "@vitejs/plugin-vue": "^5.2.1", + "animate.css": "^4.1.1", + "axios": "1.8.2", + "bootstrap-icons": "^1.11.3", + "echarts": "^5.5.1", + "element-plus": "^2.9.0", + "hls.js": "^1.5.20", + "jsencrypt": "^3.3.2", + "lodash": "^4.17.21", + "luxon": "^3.5.0", + "md-editor-v3": "^4.21.1", + "vue": "^3.4.21", + "vue-request": "^2.0.4", + "vue-router": "^4.4.0", + "vue-video-player": "^6.0.0" + }, + "devDependencies": { + "@types/luxon": "^3.4.2", + "typescript": "^5.2.2", + "vite": "6.2.3", + "vue-tsc": "^2.0.6" + } +} diff --git a/web_vue/public/docker_icon.svg b/web_vue/public/docker_icon.svg new file mode 100644 index 0000000..21190ce --- /dev/null +++ b/web_vue/public/docker_icon.svg @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/web_vue/public/favicon.ico b/web_vue/public/favicon.ico new file mode 100644 index 0000000..c5d3d7d Binary files /dev/null and b/web_vue/public/favicon.ico differ diff --git a/web_vue/public/nginx_icon.svg b/web_vue/public/nginx_icon.svg new file mode 100644 index 0000000..cff95ef --- /dev/null +++ b/web_vue/public/nginx_icon.svg @@ -0,0 +1 @@ +file_type_nginx \ No newline at end of file diff --git a/web_vue/public/vite.svg b/web_vue/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/web_vue/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web_vue/src/App.vue b/web_vue/src/App.vue new file mode 100644 index 0000000..fc33154 --- /dev/null +++ b/web_vue/src/App.vue @@ -0,0 +1,69 @@ + + + + + + + + + + diff --git a/web_vue/src/assets/css/normalize.css b/web_vue/src/assets/css/normalize.css new file mode 100644 index 0000000..89b1c6c --- /dev/null +++ b/web_vue/src/assets/css/normalize.css @@ -0,0 +1,350 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ + +/* Document + ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in iOS. + */ + +html { + line-height: 1.15; /* 1 */ + text-size-adjust: 100%; +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers. + */ + +body { + margin: 0; + font-size: 16px; +} + +/** + * Render the `main` element consistently in IE. + */ + +main { + display: block; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Remove the gray background on active links in IE 10. + */ + +a { + background-color: transparent; +} + +/** + * 1. Remove the bottom border in Chrome 57- + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + /*text-decoration: underline dotted; !* 2 *!*/ +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove the border on images inside links in IE 10. + */ + +img { + border-style: none; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers. + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { /* 1 */ + text-transform: none; +} + +/** + * Correct the inability to style clickable types in iOS and Safari. + */ + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + vertical-align: baseline; +} + +/** + * Remove the default vertical scrollbar in IE 10+. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10. + * 2. Remove the padding in IE 10. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in Edge, IE 10+, and Firefox. + */ + +details { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Misc + ========================================================================== */ + +/** + * Add the correct display in IE 10+. + */ + +template { + display: none; +} + +/** + * Add the correct display in IE 10. + */ + +[hidden] { + display: none; +} \ No newline at end of file diff --git a/web_vue/src/assets/css/style.css b/web_vue/src/assets/css/style.css new file mode 100644 index 0000000..722d0ba --- /dev/null +++ b/web_vue/src/assets/css/style.css @@ -0,0 +1,44 @@ +body { + font-family: '微软雅黑', serif; + font-weight: normal; +} +.flex { + display: flex; + align-items: center; +} +.around { + justify-content: space-around; +} +.between { + justify-content: space-between; +} +.menu { + height: calc(100vh - 100px); + overflow-x: hidden; + overflow-y: auto; +} +.link:hover { + cursor: pointer; +} +.space-inline-5 { + width: 5rem; + display: inline-block; +} +input { + padding: 0 5px !important; +} +.el-main { + padding: 5px !important; +} +.cursor-pointer { + cursor: pointer; +} +.center { + text-align: center; +} +.btn-action-div { + padding: 1rem 2rem; +} +div[aria-hidden='true'] { + display: none !important; +} \ No newline at end of file diff --git a/web_vue/src/assets/image/avatar.png b/web_vue/src/assets/image/avatar.png new file mode 100644 index 0000000..e16488e Binary files /dev/null and b/web_vue/src/assets/image/avatar.png differ diff --git a/web_vue/src/assets/image/background.jpg b/web_vue/src/assets/image/background.jpg new file mode 100644 index 0000000..7850546 Binary files /dev/null and b/web_vue/src/assets/image/background.jpg differ diff --git a/web_vue/src/assets/image/background.webp b/web_vue/src/assets/image/background.webp new file mode 100644 index 0000000..77ff4c2 Binary files /dev/null and b/web_vue/src/assets/image/background.webp differ diff --git a/web_vue/src/assets/image/black.png b/web_vue/src/assets/image/black.png new file mode 100644 index 0000000..5678043 Binary files /dev/null and b/web_vue/src/assets/image/black.png differ diff --git a/web_vue/src/assets/image/chess.webp b/web_vue/src/assets/image/chess.webp new file mode 100644 index 0000000..68fbd67 Binary files /dev/null and b/web_vue/src/assets/image/chess.webp differ diff --git a/web_vue/src/assets/image/logo.png b/web_vue/src/assets/image/logo.png new file mode 100644 index 0000000..f61572e Binary files /dev/null and b/web_vue/src/assets/image/logo.png differ diff --git a/web_vue/src/assets/image/white.png b/web_vue/src/assets/image/white.png new file mode 100644 index 0000000..9434456 Binary files /dev/null and b/web_vue/src/assets/image/white.png differ diff --git a/web_vue/src/components/charts/yl-line.vue b/web_vue/src/components/charts/yl-line.vue new file mode 100644 index 0000000..a62d915 --- /dev/null +++ b/web_vue/src/components/charts/yl-line.vue @@ -0,0 +1,102 @@ + + + + + + + + diff --git a/web_vue/src/components/yl-action-div.vue b/web_vue/src/components/yl-action-div.vue new file mode 100644 index 0000000..051b4bd --- /dev/null +++ b/web_vue/src/components/yl-action-div.vue @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/web_vue/src/components/yl-hls-player.vue b/web_vue/src/components/yl-hls-player.vue new file mode 100644 index 0000000..e368620 --- /dev/null +++ b/web_vue/src/components/yl-hls-player.vue @@ -0,0 +1,110 @@ + + + + + + + + + diff --git a/web_vue/src/components/yl-icon-selector.vue b/web_vue/src/components/yl-icon-selector.vue new file mode 100644 index 0000000..bfa6ad2 --- /dev/null +++ b/web_vue/src/components/yl-icon-selector.vue @@ -0,0 +1,83 @@ + + + + + + + + + + + { + iconSelector.selected = item; + $emit('update', iconSelector.selected); + iconSelector.visible = false; + } + " /> + + + + + diff --git a/web_vue/src/components/yl-layout/header.vue b/web_vue/src/components/yl-layout/header.vue new file mode 100644 index 0000000..827df45 --- /dev/null +++ b/web_vue/src/components/yl-layout/header.vue @@ -0,0 +1,140 @@ + + + + + + + + + + + {{ key ? "明" : "今" }}日天气:{{ item }} + + + + + + + + + + + + + + + + + diff --git a/web_vue/src/components/yl-layout/layout.vue b/web_vue/src/components/yl-layout/layout.vue new file mode 100644 index 0000000..1e47bef --- /dev/null +++ b/web_vue/src/components/yl-layout/layout.vue @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + @ ylsa + + + + + + + diff --git a/web_vue/src/components/yl-layout/menu.vue b/web_vue/src/components/yl-layout/menu.vue new file mode 100644 index 0000000..54621e6 --- /dev/null +++ b/web_vue/src/components/yl-layout/menu.vue @@ -0,0 +1,53 @@ + + + + + + + + + {{ item.name }} + + + + {{ m.name }} + + + + + {{ item.name }} + + + + + + diff --git a/web_vue/src/components/yl-layout/yl-userinfo.vue b/web_vue/src/components/yl-layout/yl-userinfo.vue new file mode 100644 index 0000000..cb605a7 --- /dev/null +++ b/web_vue/src/components/yl-layout/yl-userinfo.vue @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 保存 + + + + + diff --git a/web_vue/src/main.ts b/web_vue/src/main.ts new file mode 100644 index 0000000..0e9fa61 --- /dev/null +++ b/web_vue/src/main.ts @@ -0,0 +1,24 @@ +import { createApp } from "vue"; +import "./assets/css/normalize.css"; +import "./assets/css/style.css"; +import ElementPlus from "element-plus"; +import zhCn from "element-plus/es/locale/lang/zh-cn"; +import "element-plus/dist/index.css"; +import "animate.css"; +import "bootstrap-icons/font/bootstrap-icons.css"; +import App from "./App.vue"; +import router from "@/router/router.ts"; +import { config } from "md-editor-v3"; + +config({ + markdownItPlugins(plugins) { + return plugins.map((item) => { + if (item.type === "taskList") { + return { ...item, options: { ...item.options, enabled: true } }; + } + return item; + }); + }, +}); + +createApp(App).use(ElementPlus, { locale: zhCn }).use(router).mount("#app"); diff --git a/web_vue/src/pages/account-bill/bill-component.vue b/web_vue/src/pages/account-bill/bill-component.vue new file mode 100644 index 0000000..f7a4d61 --- /dev/null +++ b/web_vue/src/pages/account-bill/bill-component.vue @@ -0,0 +1,86 @@ + + + + + + + + + + + 显示余额: + + + + + + + {{ data[currentIndex].duration || data[currentIndex].date }} : + {{ + data[currentIndex].changes === 0 + ? "无变动" + : data[currentIndex].changes > 0 + ? `收入 ${account.show ? data[currentIndex].changes : "*****"}` + : `支出 ${account.show ? -data[currentIndex].changes : "*****"}` + }}{{ + data[currentIndex].changes === 0 + ? "无变动" + : data[currentIndex].changes > 0 + ? `收入 ${account.show ? data[currentIndex].changes : "*****"}` + : `支出 ${account.show ? -data[currentIndex].changes : "*****"}` + }}--------- + + + + + {{ account.show ? scope.row.balance : "*****" }} + + + + {{ + scope.row.changes === 0 + ? "-----" + : scope.row.changes > 0 + ? `收入 ${account.show ? scope.row.changes : "*****"}` + : `支出 ${account.show ? -scope.row.changes : "*****"}` + }} + + + + + + + diff --git a/web_vue/src/pages/account-bill/index.vue b/web_vue/src/pages/account-bill/index.vue new file mode 100644 index 0000000..72d2c99 --- /dev/null +++ b/web_vue/src/pages/account-bill/index.vue @@ -0,0 +1,20 @@ + + + + + + + diff --git a/web_vue/src/pages/account-bill/month.vue b/web_vue/src/pages/account-bill/month.vue new file mode 100644 index 0000000..7532499 --- /dev/null +++ b/web_vue/src/pages/account-bill/month.vue @@ -0,0 +1,32 @@ + + + + + 返回月视图 + + + + + diff --git a/web_vue/src/pages/account/index.vue b/web_vue/src/pages/account/index.vue new file mode 100644 index 0000000..b077a4a --- /dev/null +++ b/web_vue/src/pages/account/index.vue @@ -0,0 +1,262 @@ + + + + + 新增项目 + + + + + { + editYe = true; + editData = { + card: scope.row.card, + type: scope.row.type, + balance: scope.row.balance, + }; + } + " + > + {{ account.show ? scope.row.balance : "******" }} + + + + ¥ + + + + + + + + + + + { + editYe = false; + updateType(t, scope.row); + } + " + /> + + + + + deleteCardFunc(scope.row.card)" + > + + + + + + 显示余额: + + + 合计:{{ + sum(data?.data.map((e) => (e.type ? -e.balance : e.balance))).toFixed( + 2, + ) + }} + + + + + + + + + + + + + + + ¥ + + + + + + + + diff --git a/web_vue/src/pages/account/yl-account-sz.vue b/web_vue/src/pages/account/yl-account-sz.vue new file mode 100644 index 0000000..a0a17e3 --- /dev/null +++ b/web_vue/src/pages/account/yl-account-sz.vue @@ -0,0 +1,87 @@ + + + + + + + 收入 + 支出 + + + + + + {{ item.card }} + + + + + + ¥ + + + + 提交 + + + + + diff --git a/web_vue/src/pages/error/index.vue b/web_vue/src/pages/error/index.vue new file mode 100644 index 0000000..9ac5162 --- /dev/null +++ b/web_vue/src/pages/error/index.vue @@ -0,0 +1,13 @@ + + + + + + router.back()">返回 + + + + + diff --git a/web_vue/src/pages/files/index.vue b/web_vue/src/pages/files/index.vue new file mode 100644 index 0000000..31e462d --- /dev/null +++ b/web_vue/src/pages/files/index.vue @@ -0,0 +1,231 @@ + + + + + + (useFileStore.path = [])" + > + toPath(index)">{{ + item + }} + + + + + 新建文件夹 上传文件 + + + + + r.type === 'dir' + ? (useFileStore.path = useFileStore.path.concat([r.name])) + : 1 + " + > + + + + {{ scope.row.name }} + + + + + + + (imgPreview = { + visible: true, + src: `/api_file/file/download/${useFileStore.path + .concat([scope.row.name]) + .join('/')}`, + }) + " + > + + funcDownloadFile(scope.row.name)" + v-if="scope.row.type !== 'dir'" + > + funcDelFile(scope.row.name)" + > + + + + + + + + 确认 + + + + + + diff --git a/web_vue/src/pages/files/store.ts b/web_vue/src/pages/files/store.ts new file mode 100644 index 0000000..6dbc280 --- /dev/null +++ b/web_vue/src/pages/files/store.ts @@ -0,0 +1,5 @@ +import { reactive } from "vue"; + +export const useFileStore = reactive<{ path: string[] }>({ + path: [], +}); diff --git a/web_vue/src/pages/files/yl-video.vue b/web_vue/src/pages/files/yl-video.vue new file mode 100644 index 0000000..6c1b27c --- /dev/null +++ b/web_vue/src/pages/files/yl-video.vue @@ -0,0 +1,37 @@ + + + + 返回 + + + + + + + + + diff --git a/web_vue/src/pages/games/yl-backgammon-room.vue b/web_vue/src/pages/games/yl-backgammon-room.vue new file mode 100644 index 0000000..43718a6 --- /dev/null +++ b/web_vue/src/pages/games/yl-backgammon-room.vue @@ -0,0 +1,152 @@ + + + + + 退出 + 加入对战 + + + winner:{{ _.get(roomStatus, "data.winner", "") }} + + + + + {{ _.get(roomStatus, "data.player", "").split(",")[0] }} + + + + add(col, index)" + > + + + + {{ _.get(roomStatus, "data.player", "").split(",")[1] }} + + + + + diff --git a/web_vue/src/pages/games/yl-backgammon.vue b/web_vue/src/pages/games/yl-backgammon.vue new file mode 100644 index 0000000..7d34719 --- /dev/null +++ b/web_vue/src/pages/games/yl-backgammon.vue @@ -0,0 +1,68 @@ + + + + + 新建房间 + + + + toRoom(room.room_id as number)"> + {{ room.player.split(",")[0] }} vs {{ room.player.split(",")[1] }} + + + + + + diff --git a/web_vue/src/pages/games/yl-chess-room.vue b/web_vue/src/pages/games/yl-chess-room.vue new file mode 100644 index 0000000..28a1413 --- /dev/null +++ b/web_vue/src/pages/games/yl-chess-room.vue @@ -0,0 +1,285 @@ + + + + + 退出房间 + 加入房间 + + 重置房间 + + 添加机器人 + + + + winner: {{ _.get(data, "data.winner", "") }} + + + + {{ _.get(data, "data.players", "").split(",")[0] || "" }} + + + + + current !== '-1' && current !== '' && current !== col + ? updatePawn(i, j) + : current === col || + (Number(col) > 15 && + _.get(data, 'data.players', '').split(',')[0] === + userinfo.username) || + (Number(col) < 16 && + _.get(data, 'data.players', '').split(',')[1] === + userinfo.username) + ? (current = '') + : (current = col) + " + > + + {{ col !== "-1" ? pawns[Number(col)] : "" }} + + + + + + {{ _.get(data, "data.players", "").split(",")[1] || "" }} + + + + + diff --git a/web_vue/src/pages/games/yl-chess.vue b/web_vue/src/pages/games/yl-chess.vue new file mode 100644 index 0000000..ebb18ea --- /dev/null +++ b/web_vue/src/pages/games/yl-chess.vue @@ -0,0 +1,73 @@ + + + + + 刷新 + 新建房间 + + + + no.{{ item.ID }} + + {{ item.players?.split(",")[0] || "-" }} vs + {{ item.players?.split(",")[1] || "-" }} + + + + + + diff --git a/web_vue/src/pages/home/index.vue b/web_vue/src/pages/home/index.vue new file mode 100644 index 0000000..1ca3dbe --- /dev/null +++ b/web_vue/src/pages/home/index.vue @@ -0,0 +1,70 @@ + + + + + welcome,{{ userinfo.userinfo.nickname || userinfo.username }} + + + + + + + + + + + + + + + + + + + + diff --git a/web_vue/src/pages/login/index.vue b/web_vue/src/pages/login/index.vue new file mode 100644 index 0000000..8df8733 --- /dev/null +++ b/web_vue/src/pages/login/index.vue @@ -0,0 +1,49 @@ + + + + + + + + + + + + diff --git a/web_vue/src/pages/login/login-data.vue b/web_vue/src/pages/login/login-data.vue new file mode 100644 index 0000000..27e9bd1 --- /dev/null +++ b/web_vue/src/pages/login/login-data.vue @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + diff --git a/web_vue/src/pages/login/login.vue b/web_vue/src/pages/login/login.vue new file mode 100644 index 0000000..c9553f5 --- /dev/null +++ b/web_vue/src/pages/login/login.vue @@ -0,0 +1,134 @@ + + + + + 用户登录 + + + + + + + 转到注册 + {{ loading ? "登录中。。。" : "登录" }} + + + + + diff --git a/web_vue/src/pages/login/register.vue b/web_vue/src/pages/login/register.vue new file mode 100644 index 0000000..e2b1c60 --- /dev/null +++ b/web_vue/src/pages/login/register.vue @@ -0,0 +1,107 @@ + + + + + 用户注册 + + + 返回登录 + + {{ loading ? "注册中。。。" : "注册" }} + + + + + diff --git a/web_vue/src/pages/login/reset-pwd.vue b/web_vue/src/pages/login/reset-pwd.vue new file mode 100644 index 0000000..f038910 --- /dev/null +++ b/web_vue/src/pages/login/reset-pwd.vue @@ -0,0 +1,190 @@ + + + + 忘记密码 ? + + + + + + + + 验证码已发送邮箱:{{ email }}, 5分钟内有效 + + + + 验证码: + + + + {{ leftTime > 0 ? `${leftTime}s 后` : "" + }}重新发送 + + + + + + + + + + + + + + 上一步 + 下一步 + 完成 + + + + + diff --git a/web_vue/src/pages/notes/store.ts b/web_vue/src/pages/notes/store.ts new file mode 100644 index 0000000..72fba61 --- /dev/null +++ b/web_vue/src/pages/notes/store.ts @@ -0,0 +1,45 @@ +import { reactive } from "vue"; + +export const useNotes: { + id: string; + content: string; + preview: boolean; + toolbars: any[]; +} = reactive({ + id: "my-editor", + content: "", + preview: true, + toolbars: [ + "bold", + "underline", + "italic", + "-", + "title", + "strikeThrough", + "sub", + "sup", + "quote", + "unorderedList", + "orderedList", + "task", + "-", + "codeRow", + "code", + "link", + "image", + "table", + "mermaid", + "katex", + "-", + "revoke", + "next", + "save", + 0, + "=", + "pageFullscreen", + "fullscreen", + "preview", + "previewOnly", + "catalog", + ], +}); diff --git a/web_vue/src/pages/notes/yl-note-detail.vue b/web_vue/src/pages/notes/yl-note-detail.vue new file mode 100644 index 0000000..49fb0f2 --- /dev/null +++ b/web_vue/src/pages/notes/yl-note-detail.vue @@ -0,0 +1,134 @@ + + + + + + 返回 + + + (useNotes.preview = false)" + > + + + + + (useNotes.preview = true)"> + + + + + + + + + diff --git a/web_vue/src/pages/notes/yl-note.vue b/web_vue/src/pages/notes/yl-note.vue new file mode 100644 index 0000000..bdbabfc --- /dev/null +++ b/web_vue/src/pages/notes/yl-note.vue @@ -0,0 +1,102 @@ + + + + + 新建笔记 + + + + toDelete(item.id)" + > + + + + + + diff --git a/web_vue/src/pages/sys-info/yl-cpu-chart.vue b/web_vue/src/pages/sys-info/yl-cpu-chart.vue new file mode 100644 index 0000000..2a031e0 --- /dev/null +++ b/web_vue/src/pages/sys-info/yl-cpu-chart.vue @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/web_vue/src/pages/sys-info/yl-disk-chart.vue b/web_vue/src/pages/sys-info/yl-disk-chart.vue new file mode 100644 index 0000000..b953611 --- /dev/null +++ b/web_vue/src/pages/sys-info/yl-disk-chart.vue @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/web_vue/src/pages/sys-info/yl-mem-chart.vue b/web_vue/src/pages/sys-info/yl-mem-chart.vue new file mode 100644 index 0000000..99a0b21 --- /dev/null +++ b/web_vue/src/pages/sys-info/yl-mem-chart.vue @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/web_vue/src/pages/sys-info/yl-net-chart.vue b/web_vue/src/pages/sys-info/yl-net-chart.vue new file mode 100644 index 0000000..8ed75bd --- /dev/null +++ b/web_vue/src/pages/sys-info/yl-net-chart.vue @@ -0,0 +1,25 @@ + + + + + + + + + diff --git a/web_vue/src/pages/sys-info/yl-sys-info.vue b/web_vue/src/pages/sys-info/yl-sys-info.vue new file mode 100644 index 0000000..03ddd3f --- /dev/null +++ b/web_vue/src/pages/sys-info/yl-sys-info.vue @@ -0,0 +1,122 @@ + + + + + + 服务器列表 + + + {{ item.hostname }}--{{ item.ip }} + + + + + + 日期: + + + + + + + + + + + + + diff --git a/web_vue/src/pages/sys-log/store.ts b/web_vue/src/pages/sys-log/store.ts new file mode 100644 index 0000000..948484e --- /dev/null +++ b/web_vue/src/pages/sys-log/store.ts @@ -0,0 +1,45 @@ +import { reactive } from "vue"; + +export const columns = [ + { + title: "时间", + dataIndex: "time", + key: "time", + width: 150, + align: "center", + }, + { + title: "方法", + dataIndex: "method", + key: "method", + width: 100, + align: "center", + }, + { + title: "路径", + dataIndex: "path", + key: "path", + align: "center", + overflow: true, + }, + { + title: "返回码", + dataIndex: "status", + key: "status", + width: 100, + align: "center", + }, + { + title: "userAgent", + dataIndex: "user_agent", + key: "user_agent", + width: "450", + align: "center", + overflow: true, + }, +]; + +export const sysLog = reactive({ + date: "", + ip: "", +}); diff --git a/web_vue/src/pages/sys-log/yl-docker-log.vue b/web_vue/src/pages/sys-log/yl-docker-log.vue new file mode 100644 index 0000000..99b758e --- /dev/null +++ b/web_vue/src/pages/sys-log/yl-docker-log.vue @@ -0,0 +1,117 @@ + + + + + + + + + (time = ['', ''])" + start-placeholder="开始时间" + end-placeholder="结束时间" + /> + + + 查询 + + + + + + + + + + + + + + + diff --git a/web_vue/src/pages/sys-log/yl-nginx-log-detail.vue b/web_vue/src/pages/sys-log/yl-nginx-log-detail.vue new file mode 100644 index 0000000..a5e0026 --- /dev/null +++ b/web_vue/src/pages/sys-log/yl-nginx-log-detail.vue @@ -0,0 +1,107 @@ + + + + 详情日期:{{ sysLog.date }} + + + + + + IP地址:{{ detailLine[ipIndex].name }} + + 地址:{{ detailLine[ipIndex].detail[0].location }} + + + (page = p)" + /> + + + + + + + + diff --git a/web_vue/src/pages/sys-log/yl-nginx-log.vue b/web_vue/src/pages/sys-log/yl-nginx-log.vue new file mode 100644 index 0000000..97e2bf0 --- /dev/null +++ b/web_vue/src/pages/sys-log/yl-nginx-log.vue @@ -0,0 +1,38 @@ + + + + + + + + + + + diff --git a/web_vue/src/pages/sys-log/yl-sys-log.vue b/web_vue/src/pages/sys-log/yl-sys-log.vue new file mode 100644 index 0000000..a364f35 --- /dev/null +++ b/web_vue/src/pages/sys-log/yl-sys-log.vue @@ -0,0 +1,91 @@ + + + + + 系统运行日志 + + + + + + + {{ scope.item.label }} + + + + + + + + + + + + + + + diff --git a/web_vue/src/pages/sys-menu/index.vue b/web_vue/src/pages/sys-menu/index.vue new file mode 100644 index 0000000..979d39f --- /dev/null +++ b/web_vue/src/pages/sys-menu/index.vue @@ -0,0 +1,214 @@ + + + + + + run()" + > 刷新 + { + dialog.visible = true; + dialog.title = '添加菜单'; + dialogData = { ...blackMenu }; + } + " + >添加菜单 + + + e.menu_id)) : [] + " + >全部展开全部折叠 + + + { + if (ex) { + expandList.push(row.menu_id); + } else { + expandList = expandList.filter((e) => e !== row.menu_id); + } + } + " + row-key="menu_id" + stripe + > + + + + + + + + + + + + + + + + + { + dialog.title = '添加菜单'; + dialog.visible = true; + dialogData = { + ...blackMenu, + menu_id: getId(scope.row), + icon: scope.row.icon, + }; + } + " + >新增 + { + dialog.title = '修改菜单'; + dialog.visible = true; + dialogData = { ...scope.row }; + } + " + >编辑 + deleteMenuItem(scope.row.menu_id)" + > + 删除 + + + + + + + + + + + diff --git a/web_vue/src/pages/sys-menu/menu-edit.vue b/web_vue/src/pages/sys-menu/menu-edit.vue new file mode 100644 index 0000000..374963f --- /dev/null +++ b/web_vue/src/pages/sys-menu/menu-edit.vue @@ -0,0 +1,255 @@ + + + + + + + + 一级菜单 + 二级菜单 + + + + + + + + + + + + + + + + + + + + + (icon = value)" /> + + + + + + + + + + + + + + + + 确认 + + + + + + diff --git a/web_vue/src/pages/sys-settings/yl-sys-settings.vue b/web_vue/src/pages/sys-settings/yl-sys-settings.vue new file mode 100644 index 0000000..a64d2b8 --- /dev/null +++ b/web_vue/src/pages/sys-settings/yl-sys-settings.vue @@ -0,0 +1,127 @@ + + + + + 新增配置 + + + + + + + + {{ item }} + {{ scope.row.value }} + + + + + + 编辑 + 删除 + + + + + + + + + + 字符串 + 选项 + + + + + + + + (modal.data.value = v.join(','))" + v-else-if="modal.data.d_type == 'option'" + /> + + + + + + + + 保存 + + + + + diff --git a/web_vue/src/pages/sys-user/yl-sys-user.vue b/web_vue/src/pages/sys-user/yl-sys-user.vue new file mode 100644 index 0000000..323a7fc --- /dev/null +++ b/web_vue/src/pages/sys-user/yl-sys-user.vue @@ -0,0 +1,116 @@ + + + + + 刷新 + + + + + + + + + + + + + + + + { + show = true; + tmpUserinfo = _.cloneDeep(scope.row); + } + " + >编辑 + toDeleteUserinfo(scope.row.username)" + > + 删除 + + + + + + + + + + diff --git a/web_vue/src/requests/request.ts b/web_vue/src/requests/request.ts new file mode 100644 index 0000000..15f27d9 --- /dev/null +++ b/web_vue/src/requests/request.ts @@ -0,0 +1,53 @@ +import axios from "axios"; +import { userinfo } from "@/store/store.ts"; +import { getCookie } from "@/utils/cookie.ts"; + +// 自定义请求方法 +export const myRequest = async ( + api: string, + token: string = "", + data: object = {}, + method: "get" | "post" | "put" | "delete" = "get", +) => { + // 未获取到token,返回登录 + if (!getCookie("token")) { + userinfo.login = false; + userinfo.token = ""; + } + // 调用请求 + if (method === "post") { + const res = await axios.post(api, data, { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + }); + return res.data; + } else if (method === "get") { + const res = await axios.get(api, { + params: data, + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + }); + return res.data; + } else if (method === "put") { + const res = await axios.put(api, data, { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + }); + return res.data; + } else if (method === "delete") { + const res = await axios.delete(api, { + data: data, + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + }); + return res.data; + } else return Promise.reject("暂不支持的请求"); +}; diff --git a/web_vue/src/router/router.ts b/web_vue/src/router/router.ts new file mode 100644 index 0000000..0673fc9 --- /dev/null +++ b/web_vue/src/router/router.ts @@ -0,0 +1,50 @@ +import { createRouter, createWebHashHistory } from "vue-router"; +import Home from "@/pages/home/index.vue"; +import YlSysMenu from "@/pages/sys-menu/index.vue"; +import YlAccount from "@/pages/account/index.vue"; +import YlAccountBill from "@/pages/account-bill/index.vue"; +import YlAccountBillDetail from "@/pages/account-bill/month.vue"; +import YlFile from "@/pages/files/index.vue"; +import ErrorPage from "@/pages/error/index.vue"; +import YlVideo from "@/pages/files/yl-video.vue"; +import YlSysInfo from "@/pages/sys-info/yl-sys-info.vue"; +import YlSysLog from "@/pages/sys-log/yl-sys-log.vue"; +import YlChess from "@/pages/games/yl-chess.vue"; +import YlChessRoom from "@/pages/games/yl-chess-room.vue"; +import YlBackgammon from "@/pages/games/yl-backgammon.vue"; +import YlBackgammonRoom from "@/pages/games/yl-backgammon-room.vue"; +import YlNote from "@/pages/notes/yl-note.vue"; +import YlNoteDetail from "@/pages/notes/yl-note-detail.vue"; +import YlSysSettings from "@/pages/sys-settings/yl-sys-settings.vue"; +import YlSysUser from "@/pages/sys-user/yl-sys-user.vue"; + +export const routes = [ + { path: "/", component: Home }, + { path: "/:catchAll(.*)", component: ErrorPage }, + // { path: "/admin/user", component: Home }, + { path: "/admin/menus", component: YlSysMenu }, + { path: "/account", component: YlAccount }, + { path: "/bill", component: YlAccountBill }, + { path: "/bill/:date", component: YlAccountBillDetail }, + { path: "/file", component: YlFile }, + { path: "/file/video", component: YlVideo }, + { path: "/sys-info", component: YlSysInfo }, + { path: "/sys-log", component: YlSysLog }, + { path: "/chess", component: YlChess }, + { path: "/chess/:id", component: YlChessRoom }, + { path: "/backgammon", component: YlBackgammon }, + { path: "/backgammon/:id", component: YlBackgammonRoom }, + { path: "/notes", component: YlNote }, + { path: "/notes/:id", component: YlNoteDetail }, + { path: "/sys-settings", component: YlSysSettings }, + { path: "/sys-user", component: YlSysUser }, +]; + +const router = createRouter({ + history: createWebHashHistory(), + routes: routes.filter((route) => + ["/", "/:catchAll(.*)"].includes(route.path), + ), +}); + +export default router; diff --git a/web_vue/src/store/store.ts b/web_vue/src/store/store.ts new file mode 100644 index 0000000..2a9996c --- /dev/null +++ b/web_vue/src/store/store.ts @@ -0,0 +1,43 @@ +import { reactive } from "vue"; +//登录页面 +export const loginData = reactive({ + login: true, + username: "", + password: "", + repeatPass: "", + autoLogin: false, +}); +//用户登录信息 +export const userinfo: { + login: boolean; + username: string; + token: string; + type: string; + userinfo: userinfo; +} = reactive({ + login: false, + username: "", + token: "", + type: "user", + userinfo: {}, +}); +// websocket +export const message = reactive({ + online: 0, +}); +// 菜单 +export const menuData: { + data: sysMenu[]; + default: string; +} = reactive({ + data: [], + default: "", +}); +// 图标选择 +export const iconSelector = reactive({ + visible: false, + iconList: [], + selected: "", +}); +//余额表及账单 +export const account = reactive({ show: false }); diff --git a/web_vue/src/type.d.ts b/web_vue/src/type.d.ts new file mode 100644 index 0000000..8e048e0 --- /dev/null +++ b/web_vue/src/type.d.ts @@ -0,0 +1,96 @@ +// store.ts------------------------------------------------- +interface sysMenu { + name: string; + icon: string; + path: string; + menu_id: string; + route_only: boolean; + detail?: sysMenu[]; +} + +interface userinfo { + avatar?: string; + email?: string; + location?: string; + mobile?: string; + nickname?: string; + type?: string; + username?: string; +} + +// sys-menu------------------------------------------------- +interface sysMenuList { + id?: number; + menu_id: string; + name: string; + path: string; + icon: string; + route_only: boolean; + user_type: string; + white_list: string; + children?: sysMenuList[]; +} +interface sysSettings { + name: string; + cn_name: string; + value: string; + d_type: "string" | "option"; +} +interface typeUserList { + username: string; + nickname: string; + avatar: string; + mobile: string; + email: string; + type: string; +} + +interface accountBillData { + date: string; + balance: number; + changes: number; + duration: string; + detail: accountBillDetailData[]; +} +interface accountBillDetailData { + card: string; + balance: number; + changes: number; +} + +// files------------------------------------------------- +interface fileList { + name: string; + type: string; + size?: number; +} + +// sys-info----------------------------------------------- +interface resSystem { + data: { hostname: string; ip: string }[]; +} +interface resSysInfo { + data: { + datetime: string; + cpu_per: number; + disk: { point: string; total: string; used: string; per: number }[]; + mem: { mem_total: string; mem_used: string; mem_per: number }; + net: { sent: string; rec: string }; + }[]; +} + +// yl-chess----------------------------------------------------- +interface chess { + ID: number; + players: string; + current: string; + is_end: string; + status: string; + winner: string; + chess_id: string; +} +// yl-backgammon------------------------------------------------ +interface roomStatus { + room_id: number; + player: string; +} diff --git a/web_vue/src/utils/cookie.ts b/web_vue/src/utils/cookie.ts new file mode 100644 index 0000000..e4b3f2b --- /dev/null +++ b/web_vue/src/utils/cookie.ts @@ -0,0 +1,13 @@ +// 获取页面cookie +export const getCookie = (name: string) => { + const strCookie = document.cookie; + const arrCookie = strCookie.split("; "); + let i: number; + for (i = 0; i < arrCookie.length; i++) { + const arr = arrCookie[i].split("="); + if (arr[0] === name) { + return arr.filter((_, index) => index !== 0).join("="); + } + } + return ""; +}; diff --git a/web_vue/src/utils/mebu.vue b/web_vue/src/utils/mebu.vue new file mode 100644 index 0000000..a0b0c5f --- /dev/null +++ b/web_vue/src/utils/mebu.vue @@ -0,0 +1,73 @@ + + + + + diff --git a/web_vue/src/utils/menu.ts b/web_vue/src/utils/menu.ts new file mode 100644 index 0000000..c39cdcd --- /dev/null +++ b/web_vue/src/utils/menu.ts @@ -0,0 +1,66 @@ +import { useRequest } from "vue-request"; +import { myRequest } from "@/requests/request.ts"; +import { menuData, userinfo } from "@/store/store.ts"; +import { ref, watch } from "vue"; +import router, { routes } from "@/router/router.ts"; +import _ from "lodash"; + +export const updateMenu = () => { + const currentPath = ref("/"); + const { data: menuList } = useRequest( + () => myRequest(`/api/menu`, userinfo.token), + { + refreshDeps: [() => userinfo.token], + ready: () => Boolean(userinfo.token), + }, + ); + // 没有菜单信息,返回首页 + if (!menuList.value) { + currentPath.value = window.location.href.split("#")[1]; + router.push("/"); + } + // 获取菜单后更新路由 + watch(menuList, () => { + router.clearRoutes(); + menuData.data = []; + _.get(menuList, "value.data", [ + { name: "首页", icon: "home", path: "/" }, + ]).map((e: sysMenu) => { + menuData.data.push({ + name: e.name, + icon: e.icon, + path: e.path, + menu_id: e.menu_id, + route_only: e.route_only, + detail: e.detail, + }); + // 菜单是最后登录的页面 + if (e.path === currentPath.value) { + menuData.default = e.menu_id; + } + // 菜单存在二级菜单,添加二级菜单路由 + if (e.detail && e.detail.length > 0) { + e.detail.map((m) => { + if (routes.filter((item) => item.path === m.path).length > 0) { + router.addRoute(routes.filter((item) => item.path === m.path)[0]); + } + if (m.path === currentPath.value) { + menuData.default = m.menu_id; + } + }); + } else { + if (routes.filter((item) => item.path === e.path).length > 0) { + router.addRoute(routes.filter((item) => item.path === e.path)[0]); + } + } + }); + // router.addRoute(routes.filter((item) => item.path === "/234")[0]); + // 路由添加全局匹配页面 + router.addRoute(routes.filter((item) => item.path === "/:catchAll(.*)")[0]); + if (menuList.value.data.length > 0) { + router.push(currentPath.value); + } else { + router.push("/"); + } + }); +}; diff --git a/web_vue/src/utils/mouse.ts b/web_vue/src/utils/mouse.ts new file mode 100644 index 0000000..cbaa189 --- /dev/null +++ b/web_vue/src/utils/mouse.ts @@ -0,0 +1,22 @@ +import { ref, onMounted, onUnmounted } from "vue"; + +// 按照惯例,组合式函数名以“use”开头 +export function useMouse() { + // 被组合式函数封装和管理的状态 + const x = ref(0); + const y = ref(0); + + // 组合式函数可以随时更改其状态。 + function update(event: MouseEvent) { + x.value = event.pageX; + y.value = event.pageY; + } + + // 一个组合式函数也可以挂靠在所属组件的生命周期上 + // 来启动和卸载副作用 + onMounted(() => window.addEventListener("mousemove", update)); + onUnmounted(() => window.removeEventListener("mousemove", update)); + + // 通过返回值暴露所管理的状态 + return { x, y }; +} diff --git a/web_vue/src/utils/utils.ts b/web_vue/src/utils/utils.ts new file mode 100644 index 0000000..ba18bad --- /dev/null +++ b/web_vue/src/utils/utils.ts @@ -0,0 +1,58 @@ +import jsEncrypt from "jsencrypt"; +import _ from "lodash"; +// rsa非对称加密 +export const rsaEncrypt = (pubKey: string, data: string) => { + const encrypt = new jsEncrypt(); + encrypt.setPublicKey(pubKey); + return encrypt.encrypt(data); +}; +// 格式化接口返回的错误信息 +export const formatError = (res: any) => _.get(res, "response.data.msg", ""); + +//数字单位转换,s:带单位字符串,数字和单位以空格分割;d:计划转换的单位,默认MB;t:测试使用,打印s +export const transNum = (s: string, d?: string, t?: string) => { + if (t) { + console.log(s); + } + // 未传参返回0 + if (!s) { + return 0; + } + let n = Number(s.split(" ")[0]); + const list = ["B", "KB", "MB", "GB", "TB"]; + // 数字单位由d转换为MB + if (d && d !== "MB") { + let a = list.indexOf(d); + if (a < 2) { + while (2 - a) { + n *= 1024; + a += 1; + } + } else if (a > 2) { + while (a - 2) { + n /= 1024; + a -= 1; + } + } + } + // 单位由s转换为d + const b = s.split(" ")[1]; + if (b) { + if (b.indexOf("KB") !== -1) { + return Number((n / 1024).toFixed(3)); + } else if (b.indexOf("MB") !== -1) { + return Number(n.toFixed(3)); + } else if (b.indexOf("GB") !== -1) { + return Number((n * 1024).toFixed(3)); + } else if (b.indexOf("TB") !== -1) { + return Number((n * 1024 * 1024).toFixed(3)); + } else return Number((n / 1024 / 1024).toFixed(3)); + } + return 0; +}; +// 获取设备类型 +export const isMobile = () => { + return !!window.navigator.userAgent.match( + /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i, + ); +}; diff --git a/web_vue/src/vite-env.d.ts b/web_vue/src/vite-env.d.ts new file mode 100644 index 0000000..7752b97 --- /dev/null +++ b/web_vue/src/vite-env.d.ts @@ -0,0 +1,13 @@ +/// +declare module "*.vue" { + import { DefineComponent } from "vue"; + const component: DefineComponent<{}, {}, any>; + export default component; +} + +declare module "lodash"; +declare module "@kangc/v-md-editor"; +declare module "@kangc/v-md-editor/lib/theme/vuepress.js"; +declare module "@kangc/v-md-editor/lib/plugins/emoji/index"; +declare module "prismjs"; +declare module "prismjs"; diff --git a/web_vue/tsconfig.json b/web_vue/tsconfig.json new file mode 100644 index 0000000..3547146 --- /dev/null +++ b/web_vue/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "baseUrl": "./", + "paths": { + "@": ["src"], + "@/*": ["src/*"] + }, + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve", + "jsxImportSource": "vue", + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/web_vue/tsconfig.node.json b/web_vue/tsconfig.node.json new file mode 100644 index 0000000..97ede7e --- /dev/null +++ b/web_vue/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "strict": true + }, + "include": ["vite.config.ts"] +} diff --git a/web_vue/vite.config.ts b/web_vue/vite.config.ts new file mode 100644 index 0000000..c1f4d32 --- /dev/null +++ b/web_vue/vite.config.ts @@ -0,0 +1,38 @@ +import { defineConfig, splitVendorChunkPlugin } from "vite"; +import vue from "@vitejs/plugin-vue"; +import { resolve } from "path"; +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [vue(), splitVendorChunkPlugin()], + resolve: { + alias: { + "@": resolve(__dirname, "src"), + }, + }, + server: { + host: "0.0.0.0", + proxy: { + "/api_django": { + target: "http://localhost:8000/api/", + rewrite: (path) => path.replace(/^\/api_django/, ""), + changeOrigin: true, + }, + "/api": { + target: "http://localhost:8080", + changeOrigin: true, + }, + "/api_file": { + target: "http://localhost:8081", + changeOrigin: true, + }, + "/admin": { + target: "http://localhost:8080", + changeOrigin: true, + }, + "/static": { + target: "https://git-ylsa0.cn", + changeOrigin: true, + }, + }, + }, +});