提交 v1.3.0 beta
This commit is contained in:
parent
4ff4d37442
commit
112435199a
@ -49,7 +49,7 @@ https://next.typesafe.cn/ 账号:test 密码:test
|
||||
## 问题反馈
|
||||
|
||||
- Issues
|
||||
- 微信群 加我微信拉你进群
|
||||
- 微信群 加我微信拉你进群 (请备注 next-terminal)
|
||||
|
||||
<img src="wx.png" width="300" height="auto"/>
|
||||
|
||||
|
4
build.sh
4
build.sh
@ -1,5 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
cp build/resources/logo.png web/src/images/logo.png
|
||||
cp build/resources/logo-with-name.png web/src/images/logo-with-name.png
|
||||
cp build/resources/favicon.ico web/public/favicon.ico
|
||||
|
||||
rm -rf server/resource/build
|
||||
echo "clean build history"
|
||||
|
||||
|
20
go.mod
20
go.mod
@ -5,7 +5,7 @@ go 1.17
|
||||
require (
|
||||
github.com/glebarez/sqlite v1.3.5
|
||||
github.com/gliderlabs/ssh v0.3.3
|
||||
github.com/gofrs/uuid v4.2.0+incompatible
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
|
||||
github.com/labstack/echo/v4 v4.7.0
|
||||
@ -17,10 +17,13 @@ require (
|
||||
github.com/pkg/sftp v1.13.4
|
||||
github.com/pquerna/otp v1.3.0
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/shirou/gopsutil/v3 v3.22.9
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.10.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb
|
||||
go.uber.org/zap v1.23.0
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
|
||||
golang.org/x/text v0.3.7
|
||||
@ -36,33 +39,40 @@ require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||
github.com/glebarez/go-sqlite v1.14.8 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.4 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||
github.com/spf13/afero v1.8.1 // indirect
|
||||
github.com/spf13/cast v1.4.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/subosito/gotenv v1.2.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
github.com/tklauser/numcpus v0.4.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.1 // indirect
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/ini.v1 v1.66.4 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.14.6 // indirect
|
||||
modernc.org/mathutil v1.4.1 // indirect
|
||||
modernc.org/memory v1.0.5 // indirect
|
||||
|
43
go.sum
43
go.sum
@ -65,6 +65,8 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV
|
||||
github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
@ -133,12 +135,12 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
|
||||
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
@ -189,6 +191,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
@ -283,6 +287,8 @@ github.com/labstack/echo/v4 v4.7.0 h1:8wHgZhoE9OT1NSLw6sfrX7ZGpWMtO5Zlfr68+BIo18
|
||||
github.com/labstack/echo/v4 v4.7.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
|
||||
github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o=
|
||||
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
|
||||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||
@ -339,6 +345,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/pquerna/otp v1.3.0 h1:oJV/SkzR33anKXwQU3Of42rL4wbrffP4uvUf1SvS5Xs=
|
||||
github.com/pquerna/otp v1.3.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
@ -362,6 +370,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sagikazarmark/crypt v0.4.0/go.mod h1:ALv2SRj7GxYV4HO9elxH9nS6M9gW+xDNxqmyJ6RfDFM=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/shirou/gopsutil/v3 v3.22.9 h1:yibtJhIVEMcdw+tCTbOPiF1VcsuDeTE4utJ8Dm4c5eA=
|
||||
github.com/shirou/gopsutil/v3 v3.22.9/go.mod h1:bBYl1kjgEJpWpxeHmLI+dVHWtyAwfcmSBLDsp2TNT8A=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
@ -381,16 +391,25 @@ github.com/spf13/viper v1.10.1 h1:nuJZuYpG7gTj/XqiUwg8bA0cp1+M2mC3J4g5luUYBKk=
|
||||
github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw=
|
||||
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
|
||||
github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o=
|
||||
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb h1:Ywfo8sUltxogBpFuMOFRrrSifO788kAFxmvVw31PtQQ=
|
||||
github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb/go.mod h1:ikPs9bRWicNw3S7XpJ8sK/smGwU9WcSVU3dy9qahYBM=
|
||||
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/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
|
||||
@ -400,6 +419,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||
go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs=
|
||||
@ -411,9 +432,15 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
|
||||
go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@ -552,6 +579,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -579,6 +607,7 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -608,8 +637,9 @@ golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@ -853,8 +883,9 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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.3.2 h1:QJryWiqQ91EvZ0jZL48NOpdlPdMjdip1hQ8bTgo4H7I=
|
||||
gorm.io/driver/mysql v1.3.2/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U=
|
||||
gorm.io/gorm v1.22.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
|
70
server/api/abi/abi.go
Normal file
70
server/api/abi/abi.go
Normal file
@ -0,0 +1,70 @@
|
||||
package abi
|
||||
|
||||
import (
|
||||
"github.com/labstack/echo/v4"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/common/nt"
|
||||
"next-terminal/server/dto"
|
||||
"next-terminal/server/global/cache"
|
||||
"next-terminal/server/model"
|
||||
)
|
||||
|
||||
type Abi struct {
|
||||
}
|
||||
|
||||
func (r *Abi) Fail(c echo.Context, code int, message string) error {
|
||||
return c.JSON(200, maps.Map{
|
||||
"code": code,
|
||||
"message": message,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Abi) FailWithData(c echo.Context, code int, message string, data interface{}) error {
|
||||
return c.JSON(200, maps.Map{
|
||||
"code": code,
|
||||
"message": message,
|
||||
"data": data,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Abi) Success(c echo.Context, data interface{}) error {
|
||||
return c.JSON(200, maps.Map{
|
||||
"code": 1,
|
||||
"message": "success",
|
||||
"data": data,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Abi) GetToken(c echo.Context) string {
|
||||
token := c.Request().Header.Get(nt.Token)
|
||||
if len(token) > 0 {
|
||||
return token
|
||||
}
|
||||
return c.QueryParam(nt.Token)
|
||||
}
|
||||
|
||||
func (r *Abi) GetCurrentAccount(c echo.Context) (*model.User, bool) {
|
||||
token := r.GetToken(c)
|
||||
get, b := cache.TokenManager.Get(token)
|
||||
if b {
|
||||
return get.(dto.Authorization).User, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (r *Abi) HasPermission(c echo.Context, owner string) bool {
|
||||
// 检测是否登录
|
||||
account, found := r.GetCurrentAccount(c)
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
// 检测是否为管理人员
|
||||
if nt.TypeAdmin == account.Type {
|
||||
return true
|
||||
}
|
||||
// 检测是否为所有者
|
||||
if owner == account.ID {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
@ -2,6 +2,8 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/maps"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -23,7 +25,7 @@ func (api AccessGatewayApi) AccessGatewayCreateEndpoint(c echo.Context) error {
|
||||
}
|
||||
|
||||
item.ID = utils.UUID()
|
||||
item.Created = utils.NowJsonTime()
|
||||
item.Created = common.NowJsonTime()
|
||||
|
||||
if err := repository.GatewayRepository.Create(context.TODO(), &item); err != nil {
|
||||
return err
|
||||
@ -38,11 +40,14 @@ func (api AccessGatewayApi) AccessGatewayAllEndpoint(c echo.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var simpleGateways = make([]model.AccessGatewayForPage, 0)
|
||||
for i := 0; i < len(gateways); i++ {
|
||||
simpleGateways = append(simpleGateways, model.AccessGatewayForPage{ID: gateways[i].ID, Name: gateways[i].Name})
|
||||
items := make([]maps.Map, len(gateways))
|
||||
for i, e := range gateways {
|
||||
items[i] = maps.Map{
|
||||
"id": e.ID,
|
||||
"name": e.Name,
|
||||
}
|
||||
return Success(c, simpleGateways)
|
||||
}
|
||||
return Success(c, items)
|
||||
}
|
||||
|
||||
func (api AccessGatewayApi) AccessGatewayPagingEndpoint(c echo.Context) error {
|
||||
@ -66,7 +71,7 @@ func (api AccessGatewayApi) AccessGatewayPagingEndpoint(c echo.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
|
@ -3,17 +3,17 @@ package api
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/nt"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"next-terminal/server/config"
|
||||
"next-terminal/server/constant"
|
||||
"next-terminal/server/dto"
|
||||
"next-terminal/server/global/cache"
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/service"
|
||||
"next-terminal/server/totp"
|
||||
"next-terminal/server/utils"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
@ -50,7 +50,7 @@ func (api AccountApi) LoginEndpoint(c echo.Context) error {
|
||||
return FailWithData(c, -1, "您输入的账号或密码不正确", count)
|
||||
}
|
||||
|
||||
if user.Status == constant.StatusDisabled {
|
||||
if user.Status == nt.StatusDisabled {
|
||||
return Fail(c, -1, "该账户已停用")
|
||||
}
|
||||
|
||||
@ -64,11 +64,24 @@ func (api AccountApi) LoginEndpoint(c echo.Context) error {
|
||||
return FailWithData(c, -1, "您输入的账号或密码不正确", count)
|
||||
}
|
||||
|
||||
// 账号密码正确,需要进行两步验证
|
||||
if user.TOTPSecret != "" && user.TOTPSecret != "-" {
|
||||
return Fail(c, 0, "")
|
||||
if loginAccount.TOTP == "" {
|
||||
return Fail(c, 100, "")
|
||||
} else {
|
||||
if !common.Validate(loginAccount.TOTP, user.TOTPSecret) {
|
||||
count++
|
||||
cache.LoginFailedKeyManager.Set(loginFailCountKey, count, cache.LoginLockExpiration)
|
||||
// 保存登录日志
|
||||
if err := service.UserService.SaveLoginLog(c.RealIP(), c.Request().UserAgent(), loginAccount.Username, false, loginAccount.Remember, "", "双因素认证授权码不正确"); err != nil {
|
||||
return err
|
||||
}
|
||||
return FailWithData(c, -1, "您输入双因素认证授权码不正确", count)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
token, err := api.LoginSuccess(loginAccount, user)
|
||||
token, err := api.LoginSuccess(loginAccount, user, c.RealIP())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -80,12 +93,17 @@ func (api AccountApi) LoginEndpoint(c echo.Context) error {
|
||||
return Success(c, token)
|
||||
}
|
||||
|
||||
func (api AccountApi) LoginSuccess(loginAccount dto.LoginAccount, user model.User) (string, error) {
|
||||
func (api AccountApi) LoginSuccess(loginAccount dto.LoginAccount, user model.User, ip string) (string, error) {
|
||||
// 判断当前时间是否允许该用户登录
|
||||
if err := service.LoginPolicyService.Check(user.ID, ip); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
token := utils.LongUUID()
|
||||
|
||||
authorization := dto.Authorization{
|
||||
Token: token,
|
||||
Type: constant.LoginToken,
|
||||
Type: nt.LoginToken,
|
||||
Remember: loginAccount.Remember,
|
||||
User: &user,
|
||||
}
|
||||
@ -97,75 +115,12 @@ func (api AccountApi) LoginSuccess(loginAccount dto.LoginAccount, user model.Use
|
||||
cache.TokenManager.Set(token, authorization, cache.NotRememberExpiration)
|
||||
}
|
||||
|
||||
b := true
|
||||
// 修改登录状态
|
||||
err := repository.UserRepository.Update(context.TODO(), &model.User{Online: true, ID: user.ID})
|
||||
err := repository.UserRepository.Update(context.TODO(), &model.User{Online: &b, ID: user.ID})
|
||||
return token, err
|
||||
}
|
||||
|
||||
func (api AccountApi) LoginWithTotpEndpoint(c echo.Context) error {
|
||||
var loginAccount dto.LoginAccount
|
||||
if err := c.Bind(&loginAccount); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 存储登录失败次数信息
|
||||
loginFailCountKey := c.RealIP() + loginAccount.Username
|
||||
v, ok := cache.LoginFailedKeyManager.Get(loginFailCountKey)
|
||||
if !ok {
|
||||
v = 1
|
||||
}
|
||||
count := v.(int)
|
||||
if count >= 5 {
|
||||
return Fail(c, -1, "登录失败次数过多,请等待5分钟后再试")
|
||||
}
|
||||
|
||||
user, err := repository.UserRepository.FindByUsername(context.TODO(), loginAccount.Username)
|
||||
if err != nil {
|
||||
count++
|
||||
cache.LoginFailedKeyManager.Set(loginFailCountKey, count, cache.LoginLockExpiration)
|
||||
// 保存登录日志
|
||||
if err := service.UserService.SaveLoginLog(c.RealIP(), c.Request().UserAgent(), loginAccount.Username, false, loginAccount.Remember, "", "账号或密码不正确"); err != nil {
|
||||
return err
|
||||
}
|
||||
return FailWithData(c, -1, "您输入的账号或密码不正确", count)
|
||||
}
|
||||
|
||||
if user.Status == constant.StatusDisabled {
|
||||
return Fail(c, -1, "该账户已停用")
|
||||
}
|
||||
|
||||
if err := utils.Encoder.Match([]byte(user.Password), []byte(loginAccount.Password)); err != nil {
|
||||
count++
|
||||
cache.LoginFailedKeyManager.Set(loginFailCountKey, count, cache.LoginLockExpiration)
|
||||
// 保存登录日志
|
||||
if err := service.UserService.SaveLoginLog(c.RealIP(), c.Request().UserAgent(), loginAccount.Username, false, loginAccount.Remember, "", "账号或密码不正确"); err != nil {
|
||||
return err
|
||||
}
|
||||
return FailWithData(c, -1, "您输入的账号或密码不正确", count)
|
||||
}
|
||||
|
||||
if !totp.Validate(loginAccount.TOTP, user.TOTPSecret) {
|
||||
count++
|
||||
cache.LoginFailedKeyManager.Set(loginFailCountKey, count, cache.LoginLockExpiration)
|
||||
// 保存登录日志
|
||||
if err := service.UserService.SaveLoginLog(c.RealIP(), c.Request().UserAgent(), loginAccount.Username, false, loginAccount.Remember, "", "双因素认证授权码不正确"); err != nil {
|
||||
return err
|
||||
}
|
||||
return FailWithData(c, -1, "您输入双因素认证授权码不正确", count)
|
||||
}
|
||||
|
||||
token, err := api.LoginSuccess(loginAccount, user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 保存登录日志
|
||||
if err := service.UserService.SaveLoginLog(c.RealIP(), c.Request().UserAgent(), loginAccount.Username, true, loginAccount.Remember, token, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, token)
|
||||
}
|
||||
|
||||
func (api AccountApi) LogoutEndpoint(c echo.Context) error {
|
||||
token := GetToken(c)
|
||||
service.UserService.Logout(token)
|
||||
@ -183,7 +138,7 @@ func (api AccountApi) ConfirmTOTPEndpoint(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if !totp.Validate(confirmTOTP.TOTP, confirmTOTP.Secret) {
|
||||
if !common.Validate(confirmTOTP.TOTP, confirmTOTP.Secret) {
|
||||
return Fail(c, -1, "TOTP 验证失败,请重试")
|
||||
}
|
||||
|
||||
@ -202,7 +157,7 @@ func (api AccountApi) ConfirmTOTPEndpoint(c echo.Context) error {
|
||||
func (api AccountApi) ReloadTOTPEndpoint(c echo.Context) error {
|
||||
account, _ := GetCurrentAccount(c)
|
||||
|
||||
key, err := totp.NewTOTP(totp.GenerateOpts{
|
||||
key, err := common.NewTOTP(common.GenerateOpts{
|
||||
Issuer: c.Request().Host,
|
||||
AccountName: account.Username,
|
||||
})
|
||||
@ -275,54 +230,63 @@ type AccountInfo struct {
|
||||
Nickname string `json:"nickname"`
|
||||
Type string `json:"type"`
|
||||
EnableTotp bool `json:"enableTotp"`
|
||||
Roles []string `json:"roles"`
|
||||
Menus []string `json:"menus"`
|
||||
}
|
||||
|
||||
func (api AccountApi) InfoEndpoint(c echo.Context) error {
|
||||
account, _ := GetCurrentAccount(c)
|
||||
if strings.EqualFold("anonymous", account.Type) {
|
||||
return Success(c, account)
|
||||
}
|
||||
|
||||
user, err := repository.UserRepository.FindById(context.TODO(), account.ID)
|
||||
user, err := service.UserService.FindById(account.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var menus []string
|
||||
if service.UserService.IsSuperAdmin(account.ID) {
|
||||
menus = service.MenuService.GetMenus()
|
||||
} else {
|
||||
roles, err := service.RoleService.GetRolesByUserId(account.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, role := range roles {
|
||||
items := service.RoleService.GetMenuListByRole(role)
|
||||
menus = append(menus, items...)
|
||||
}
|
||||
}
|
||||
|
||||
info := AccountInfo{
|
||||
Id: user.ID,
|
||||
Username: user.Username,
|
||||
Nickname: user.Nickname,
|
||||
Type: user.Type,
|
||||
EnableTotp: user.TOTPSecret != "" && user.TOTPSecret != "-",
|
||||
Roles: user.Roles,
|
||||
Menus: menus,
|
||||
}
|
||||
return Success(c, info)
|
||||
}
|
||||
|
||||
func (api AccountApi) AccountAssetEndpoint(c echo.Context) error {
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
name := c.QueryParam("name")
|
||||
protocol := c.QueryParam("protocol")
|
||||
tags := c.QueryParam("tags")
|
||||
owner := c.QueryParam("owner")
|
||||
sharer := c.QueryParam("sharer")
|
||||
userGroupId := c.QueryParam("userGroupId")
|
||||
ip := c.QueryParam("ip")
|
||||
|
||||
order := c.QueryParam("order")
|
||||
field := c.QueryParam("field")
|
||||
func (api AccountApi) MenuEndpoint(c echo.Context) error {
|
||||
account, _ := GetCurrentAccount(c)
|
||||
|
||||
items, total, err := repository.AssetRepository.Find(context.TODO(), pageIndex, pageSize, name, protocol, tags, account, owner, sharer, userGroupId, ip, order, field)
|
||||
if service.UserService.IsSuperAdmin(account.ID) {
|
||||
items := service.MenuService.GetMenus()
|
||||
return Success(c, items)
|
||||
}
|
||||
roles, err := service.RoleService.GetRolesByUserId(account.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range items {
|
||||
items[i].IP = ""
|
||||
items[i].Port = 0
|
||||
var menus []string
|
||||
for _, role := range roles {
|
||||
items := service.RoleService.GetMenuListByRole(role)
|
||||
menus = append(menus, items...)
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
return Success(c, menus)
|
||||
}
|
||||
|
||||
func (api AccountApi) AccountStorageEndpoint(c echo.Context) error {
|
||||
@ -364,3 +328,11 @@ func (api AccountApi) AccessTokenGenEndpoint(c echo.Context) error {
|
||||
}
|
||||
return Success(c, nil)
|
||||
}
|
||||
|
||||
func (api AccountApi) AccessTokenDelEndpoint(c echo.Context) error {
|
||||
account, _ := GetCurrentAccount(c)
|
||||
if err := service.AccessTokenService.DelAccessToken(context.Background(), account.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, nil)
|
||||
}
|
||||
|
@ -1,25 +1,23 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"next-terminal/server/constant"
|
||||
"github.com/labstack/echo/v4"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/common/nt"
|
||||
"next-terminal/server/dto"
|
||||
"next-terminal/server/global/cache"
|
||||
"next-terminal/server/model"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type Map map[string]interface{}
|
||||
|
||||
func Fail(c echo.Context, code int, message string) error {
|
||||
return c.JSON(200, Map{
|
||||
return c.JSON(200, maps.Map{
|
||||
"code": code,
|
||||
"message": message,
|
||||
})
|
||||
}
|
||||
|
||||
func FailWithData(c echo.Context, code int, message string, data interface{}) error {
|
||||
return c.JSON(200, Map{
|
||||
return c.JSON(200, maps.Map{
|
||||
"code": code,
|
||||
"message": message,
|
||||
"data": data,
|
||||
@ -27,7 +25,7 @@ func FailWithData(c echo.Context, code int, message string, data interface{}) er
|
||||
}
|
||||
|
||||
func Success(c echo.Context, data interface{}) error {
|
||||
return c.JSON(200, Map{
|
||||
return c.JSON(200, maps.Map{
|
||||
"code": 1,
|
||||
"message": "success",
|
||||
"data": data,
|
||||
@ -35,11 +33,11 @@ func Success(c echo.Context, data interface{}) error {
|
||||
}
|
||||
|
||||
func GetToken(c echo.Context) string {
|
||||
token := c.Request().Header.Get(constant.Token)
|
||||
token := c.Request().Header.Get(nt.Token)
|
||||
if len(token) > 0 {
|
||||
return token
|
||||
}
|
||||
return c.QueryParam(constant.Token)
|
||||
return c.QueryParam(nt.Token)
|
||||
}
|
||||
|
||||
func GetCurrentAccount(c echo.Context) (*model.User, bool) {
|
||||
@ -50,20 +48,3 @@ func GetCurrentAccount(c echo.Context) (*model.User, bool) {
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func HasPermission(c echo.Context, owner string) bool {
|
||||
// 检测是否登录
|
||||
account, found := GetCurrentAccount(c)
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
// 检测是否为管理人员
|
||||
if constant.TypeAdmin == account.Type {
|
||||
return true
|
||||
}
|
||||
// 检测是否为所有者
|
||||
if owner == account.ID {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -5,10 +5,12 @@ import (
|
||||
"context"
|
||||
"encoding/csv"
|
||||
"errors"
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/common/nt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"next-terminal/server/constant"
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/service"
|
||||
@ -75,13 +77,13 @@ func (assetApi AssetApi) AssetImportEndpoint(c echo.Context) error {
|
||||
Protocol: record[1],
|
||||
IP: record[2],
|
||||
Port: port,
|
||||
AccountType: constant.Custom,
|
||||
AccountType: nt.Custom,
|
||||
Username: record[4],
|
||||
Password: record[5],
|
||||
PrivateKey: record[6],
|
||||
Passphrase: record[7],
|
||||
Description: record[8],
|
||||
Created: utils.NowJsonTime(),
|
||||
Created: common.NowJsonTime(),
|
||||
Owner: account.ID,
|
||||
Active: true,
|
||||
}
|
||||
@ -114,22 +116,18 @@ func (assetApi AssetApi) AssetPagingEndpoint(c echo.Context) error {
|
||||
name := c.QueryParam("name")
|
||||
protocol := c.QueryParam("protocol")
|
||||
tags := c.QueryParam("tags")
|
||||
owner := c.QueryParam("owner")
|
||||
sharer := c.QueryParam("sharer")
|
||||
userGroupId := c.QueryParam("userGroupId")
|
||||
ip := c.QueryParam("ip")
|
||||
active := c.QueryParam("active")
|
||||
|
||||
order := c.QueryParam("order")
|
||||
field := c.QueryParam("field")
|
||||
|
||||
account, _ := GetCurrentAccount(c)
|
||||
|
||||
items, total, err := repository.AssetRepository.Find(context.TODO(), pageIndex, pageSize, name, protocol, tags, account, owner, sharer, userGroupId, ip, order, field)
|
||||
items, total, err := repository.AssetRepository.Find(context.Background(), pageIndex, pageSize, name, protocol, tags, ip, active, order, field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
@ -137,16 +135,22 @@ func (assetApi AssetApi) AssetPagingEndpoint(c echo.Context) error {
|
||||
|
||||
func (assetApi AssetApi) AssetAllEndpoint(c echo.Context) error {
|
||||
protocol := c.QueryParam("protocol")
|
||||
items, _ := repository.AssetRepository.FindByProtocol(context.TODO(), protocol)
|
||||
assets, err := repository.AssetRepository.FindByProtocol(context.TODO(), protocol)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
items := make([]maps.Map, len(assets))
|
||||
for i, e := range assets {
|
||||
items[i] = maps.Map{
|
||||
"id": e.ID,
|
||||
"name": e.Name,
|
||||
}
|
||||
}
|
||||
return Success(c, items)
|
||||
}
|
||||
|
||||
func (assetApi AssetApi) AssetUpdateEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
if err := assetApi.PreCheckAssetPermission(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m := echo.Map{}
|
||||
if err := c.Bind(&m); err != nil {
|
||||
return err
|
||||
@ -161,9 +165,6 @@ func (assetApi AssetApi) AssetDeleteEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
split := strings.Split(id, ",")
|
||||
for i := range split {
|
||||
if err := assetApi.PreCheckAssetPermission(c, split[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := service.AssetService.DeleteById(split[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -174,9 +175,6 @@ func (assetApi AssetApi) AssetDeleteEndpoint(c echo.Context) error {
|
||||
|
||||
func (assetApi AssetApi) AssetGetEndpoint(c echo.Context) (err error) {
|
||||
id := c.Param("id")
|
||||
if err := assetApi.PreCheckAssetPermission(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var item model.Asset
|
||||
if item, err = service.AssetService.FindByIdAndDecrypt(context.TODO(), id); err != nil {
|
||||
@ -202,20 +200,17 @@ func (assetApi AssetApi) AssetTcpingEndpoint(c echo.Context) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
active, err := service.AssetService.CheckStatus(item.AccessGatewayId, item.IP, item.Port)
|
||||
|
||||
if item.Active != active {
|
||||
if err := repository.AssetRepository.UpdateActiveById(context.TODO(), active, item.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
active, err := service.AssetService.CheckStatus(&item, item.IP, item.Port)
|
||||
|
||||
var message = ""
|
||||
if err != nil {
|
||||
message = err.Error()
|
||||
}
|
||||
if err := repository.AssetRepository.UpdateActiveById(context.TODO(), active, message, item.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"active": active,
|
||||
"message": message,
|
||||
})
|
||||
@ -232,25 +227,9 @@ func (assetApi AssetApi) AssetTagsEndpoint(c echo.Context) (err error) {
|
||||
func (assetApi AssetApi) AssetChangeOwnerEndpoint(c echo.Context) (err error) {
|
||||
id := c.Param("id")
|
||||
|
||||
if err := assetApi.PreCheckAssetPermission(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
owner := c.QueryParam("owner")
|
||||
if err := repository.AssetRepository.UpdateById(context.TODO(), &model.Asset{Owner: owner}, id); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (assetApi AssetApi) PreCheckAssetPermission(c echo.Context, id string) error {
|
||||
item, err := repository.AssetRepository.FindById(context.TODO(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !HasPermission(c, item.Owner) {
|
||||
return errors.New("permission denied")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
136
server/api/authorised.go
Normal file
136
server/api/authorised.go
Normal file
@ -0,0 +1,136 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/labstack/echo/v4"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/dto"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/service"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type AuthorisedApi struct {
|
||||
}
|
||||
|
||||
func (api AuthorisedApi) Selected(c echo.Context) error {
|
||||
userId := c.QueryParam("userId")
|
||||
userGroupId := c.QueryParam("userGroupId")
|
||||
assetId := c.QueryParam("assetId")
|
||||
key := c.QueryParam("key")
|
||||
|
||||
items, err := repository.AuthorisedRepository.FindAll(context.Background(), userId, userGroupId, assetId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var result = make([]string, 0)
|
||||
switch key {
|
||||
case "userId":
|
||||
for _, item := range items {
|
||||
result = append(result, item.UserId)
|
||||
}
|
||||
case "userGroupId":
|
||||
for _, item := range items {
|
||||
result = append(result, item.UserGroupId)
|
||||
}
|
||||
case "assetId":
|
||||
for _, item := range items {
|
||||
result = append(result, item.AssetId)
|
||||
}
|
||||
}
|
||||
|
||||
return Success(c, result)
|
||||
}
|
||||
|
||||
func (api AuthorisedApi) Delete(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
if err := repository.AuthorisedRepository.DeleteById(context.Background(), id); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (api AuthorisedApi) PagingAsset(c echo.Context) error {
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
assetName := c.QueryParam("assetName")
|
||||
userId := c.QueryParam("userId")
|
||||
userGroupId := c.QueryParam("userGroupId")
|
||||
|
||||
items, total, err := repository.AuthorisedRepository.FindAssetPage(context.Background(), pageIndex, pageSize, assetName, userId, userGroupId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
|
||||
func (api AuthorisedApi) PagingUser(c echo.Context) error {
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
userName := c.QueryParam("userName")
|
||||
assetId := c.QueryParam("assetId")
|
||||
|
||||
items, total, err := repository.AuthorisedRepository.FindUserPage(context.Background(), pageIndex, pageSize, userName, assetId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
|
||||
func (api AuthorisedApi) PagingUserGroup(c echo.Context) error {
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
userGroupName := c.QueryParam("userGroupName")
|
||||
assetId := c.QueryParam("assetId")
|
||||
|
||||
items, total, err := repository.AuthorisedRepository.FindUserGroupPage(context.Background(), pageIndex, pageSize, userGroupName, assetId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
|
||||
func (api AuthorisedApi) AuthorisedAssets(c echo.Context) error {
|
||||
var item dto.AuthorisedAsset
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := service.AuthorisedService.AuthorisedAssets(context.Background(), &item); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, nil)
|
||||
}
|
||||
|
||||
func (api AuthorisedApi) AuthorisedUsers(c echo.Context) error {
|
||||
var item dto.AuthorisedUser
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := service.AuthorisedService.AuthorisedUsers(context.Background(), &item); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, nil)
|
||||
}
|
||||
|
||||
func (api AuthorisedApi) AuthorisedUserGroups(c echo.Context) error {
|
||||
var item dto.AuthorisedUserGroup
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := service.AuthorisedService.AuthorisedUserGroups(context.Background(), &item); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, nil)
|
||||
}
|
15
server/api/branding.go
Normal file
15
server/api/branding.go
Normal file
@ -0,0 +1,15 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"next-terminal/server/branding"
|
||||
"next-terminal/server/common/maps"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func Branding(c echo.Context) error {
|
||||
return Success(c, maps.Map{
|
||||
"name": branding.Name,
|
||||
"copyright": branding.Copyright,
|
||||
})
|
||||
}
|
@ -2,7 +2,8 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/maps"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -24,7 +25,7 @@ func (api CommandApi) CommandCreateEndpoint(c echo.Context) error {
|
||||
account, _ := GetCurrentAccount(c)
|
||||
item.Owner = account.ID
|
||||
item.ID = utils.UUID()
|
||||
item.Created = utils.NowJsonTime()
|
||||
item.Created = common.NowJsonTime()
|
||||
|
||||
if err := repository.CommandRepository.Create(context.TODO(), &item); err != nil {
|
||||
return err
|
||||
@ -34,8 +35,7 @@ func (api CommandApi) CommandCreateEndpoint(c echo.Context) error {
|
||||
}
|
||||
|
||||
func (api CommandApi) CommandAllEndpoint(c echo.Context) error {
|
||||
account, _ := GetCurrentAccount(c)
|
||||
items, err := repository.CommandRepository.FindByUser(context.TODO(), account)
|
||||
items, err := repository.CommandRepository.FindAll(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -47,17 +47,16 @@ func (api CommandApi) CommandPagingEndpoint(c echo.Context) error {
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
name := c.QueryParam("name")
|
||||
content := c.QueryParam("content")
|
||||
account, _ := GetCurrentAccount(c)
|
||||
|
||||
order := c.QueryParam("order")
|
||||
field := c.QueryParam("field")
|
||||
|
||||
items, total, err := repository.CommandRepository.Find(context.TODO(), pageIndex, pageSize, name, content, order, field, account)
|
||||
items, total, err := repository.CommandRepository.Find(context.TODO(), pageIndex, pageSize, name, content, order, field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
@ -65,9 +64,6 @@ func (api CommandApi) CommandPagingEndpoint(c echo.Context) error {
|
||||
|
||||
func (api CommandApi) CommandUpdateEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
if err := api.PreCheckCommandPermission(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var item model.Command
|
||||
if err := c.Bind(&item); err != nil {
|
||||
@ -85,9 +81,6 @@ func (api CommandApi) CommandDeleteEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
split := strings.Split(id, ",")
|
||||
for i := range split {
|
||||
if err := api.PreCheckCommandPermission(c, split[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := repository.CommandRepository.DeleteById(context.TODO(), split[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -97,11 +90,6 @@ func (api CommandApi) CommandDeleteEndpoint(c echo.Context) error {
|
||||
|
||||
func (api CommandApi) CommandGetEndpoint(c echo.Context) (err error) {
|
||||
id := c.Param("id")
|
||||
|
||||
if err := api.PreCheckCommandPermission(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var item model.Command
|
||||
if item, err = repository.CommandRepository.FindById(context.TODO(), id); err != nil {
|
||||
return err
|
||||
@ -111,26 +99,9 @@ func (api CommandApi) CommandGetEndpoint(c echo.Context) (err error) {
|
||||
|
||||
func (api CommandApi) CommandChangeOwnerEndpoint(c echo.Context) (err error) {
|
||||
id := c.Param("id")
|
||||
|
||||
if err := api.PreCheckCommandPermission(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
owner := c.QueryParam("owner")
|
||||
if err := repository.CommandRepository.UpdateById(context.TODO(), &model.Command{Owner: owner}, id); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (api CommandApi) PreCheckCommandPermission(c echo.Context, id string) error {
|
||||
item, err := repository.CommandRepository.FindById(context.TODO(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !HasPermission(c, item.Owner) {
|
||||
return errors.New("permission denied")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -3,12 +3,13 @@ package api
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/common/nt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"next-terminal/server/config"
|
||||
"next-terminal/server/constant"
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/service"
|
||||
@ -20,7 +21,7 @@ import (
|
||||
type CredentialApi struct{}
|
||||
|
||||
func (api CredentialApi) CredentialAllEndpoint(c echo.Context) error {
|
||||
items, err := repository.CredentialRepository.FindByUser(context.TODO())
|
||||
items, err := repository.CredentialRepository.FindByAll(context.TODO())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -35,10 +36,10 @@ func (api CredentialApi) CredentialCreateEndpoint(c echo.Context) error {
|
||||
account, _ := GetCurrentAccount(c)
|
||||
item.Owner = account.ID
|
||||
item.ID = utils.UUID()
|
||||
item.Created = utils.NowJsonTime()
|
||||
item.Created = common.NowJsonTime()
|
||||
|
||||
switch item.Type {
|
||||
case constant.Custom:
|
||||
case nt.Custom:
|
||||
item.PrivateKey = "-"
|
||||
item.Passphrase = "-"
|
||||
if item.Username == "" {
|
||||
@ -47,7 +48,7 @@ func (api CredentialApi) CredentialCreateEndpoint(c echo.Context) error {
|
||||
if item.Password == "" {
|
||||
item.Password = "-"
|
||||
}
|
||||
case constant.PrivateKey:
|
||||
case nt.PrivateKey:
|
||||
item.Password = "-"
|
||||
if item.Username == "" {
|
||||
item.Username = "-"
|
||||
@ -79,13 +80,12 @@ func (api CredentialApi) CredentialPagingEndpoint(c echo.Context) error {
|
||||
order := c.QueryParam("order")
|
||||
field := c.QueryParam("field")
|
||||
|
||||
account, _ := GetCurrentAccount(c)
|
||||
items, total, err := repository.CredentialRepository.Find(context.TODO(), pageIndex, pageSize, name, order, field, account)
|
||||
items, total, err := repository.CredentialRepository.Find(context.TODO(), pageIndex, pageSize, name, order, field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
@ -94,17 +94,13 @@ func (api CredentialApi) CredentialPagingEndpoint(c echo.Context) error {
|
||||
func (api CredentialApi) CredentialUpdateEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
|
||||
if err := api.PreCheckCredentialPermission(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var item model.Credential
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch item.Type {
|
||||
case constant.Custom:
|
||||
case nt.Custom:
|
||||
item.PrivateKey = "-"
|
||||
item.Passphrase = "-"
|
||||
if item.Username == "" {
|
||||
@ -120,7 +116,7 @@ func (api CredentialApi) CredentialUpdateEndpoint(c echo.Context) error {
|
||||
}
|
||||
item.Password = base64.StdEncoding.EncodeToString(encryptedCBC)
|
||||
}
|
||||
case constant.PrivateKey:
|
||||
case nt.PrivateKey:
|
||||
item.Password = "-"
|
||||
if item.Username == "" {
|
||||
item.Username = "-"
|
||||
@ -161,9 +157,6 @@ func (api CredentialApi) CredentialDeleteEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
split := strings.Split(id, ",")
|
||||
for i := range split {
|
||||
if err := api.PreCheckCredentialPermission(c, split[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := repository.CredentialRepository.DeleteById(context.TODO(), split[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -174,44 +167,21 @@ func (api CredentialApi) CredentialDeleteEndpoint(c echo.Context) error {
|
||||
|
||||
func (api CredentialApi) CredentialGetEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
if err := api.PreCheckCredentialPermission(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
item, err := service.CredentialService.FindByIdAndDecrypt(context.TODO(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !HasPermission(c, item.Owner) {
|
||||
return errors.New("permission denied")
|
||||
}
|
||||
|
||||
return Success(c, item)
|
||||
}
|
||||
|
||||
func (api CredentialApi) CredentialChangeOwnerEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
|
||||
if err := api.PreCheckCredentialPermission(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
owner := c.QueryParam("owner")
|
||||
if err := repository.CredentialRepository.UpdateById(context.TODO(), &model.Credential{Owner: owner}, id); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (api CredentialApi) PreCheckCredentialPermission(c echo.Context, id string) error {
|
||||
item, err := repository.CredentialRepository.FindById(context.TODO(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !HasPermission(c, item.Owner) {
|
||||
return errors.New("permission denied")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -4,13 +4,13 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"next-terminal/server/common/guacamole"
|
||||
"next-terminal/server/common/nt"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"next-terminal/server/config"
|
||||
"next-terminal/server/constant"
|
||||
"next-terminal/server/global/session"
|
||||
"next-terminal/server/guacd"
|
||||
"next-terminal/server/log"
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
@ -34,6 +34,8 @@ const (
|
||||
)
|
||||
|
||||
var UpGrader = websocket.Upgrader{
|
||||
ReadBufferSize: 4096,
|
||||
WriteBufferSize: 4096,
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
@ -46,7 +48,7 @@ type GuacamoleApi struct {
|
||||
func (api GuacamoleApi) Guacamole(c echo.Context) error {
|
||||
ws, err := UpGrader.Upgrade(c.Response().Writer, c.Request(), nil)
|
||||
if err != nil {
|
||||
log.Errorf("升级为WebSocket协议失败:%v", err.Error())
|
||||
log.Warn("升级为WebSocket协议失败", log.NamedError("err", err))
|
||||
return err
|
||||
}
|
||||
ctx := context.TODO()
|
||||
@ -58,7 +60,7 @@ func (api GuacamoleApi) Guacamole(c echo.Context) error {
|
||||
intWidth, _ := strconv.Atoi(width)
|
||||
intHeight, _ := strconv.Atoi(height)
|
||||
|
||||
configuration := guacd.NewConfiguration()
|
||||
configuration := guacamole.NewConfiguration()
|
||||
|
||||
propertyMap := repository.PropertyRepository.FindAllMap(ctx)
|
||||
|
||||
@ -74,14 +76,14 @@ func (api GuacamoleApi) Guacamole(c echo.Context) error {
|
||||
if s.AccessGatewayId != "" && s.AccessGatewayId != "-" {
|
||||
g, err := service.GatewayService.GetGatewayById(s.AccessGatewayId)
|
||||
if err != nil {
|
||||
utils.Disconnect(ws, AccessGatewayUnAvailable, "获取接入网关失败:"+err.Error())
|
||||
guacamole.Disconnect(ws, AccessGatewayUnAvailable, "获取接入网关失败:"+err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
defer g.CloseSshTunnel(s.ID)
|
||||
exposedIP, exposedPort, err := g.OpenSshTunnel(s.ID, s.IP, s.Port)
|
||||
if err != nil {
|
||||
utils.Disconnect(ws, AccessGatewayCreateError, "创建SSH隧道失败:"+err.Error())
|
||||
guacamole.Disconnect(ws, AccessGatewayCreateError, "创建SSH隧道失败:"+err.Error())
|
||||
return nil
|
||||
}
|
||||
s.IP = exposedIP
|
||||
@ -108,12 +110,12 @@ func (api GuacamoleApi) Guacamole(c echo.Context) error {
|
||||
|
||||
addr := config.GlobalCfg.Guacd.Hostname + ":" + strconv.Itoa(config.GlobalCfg.Guacd.Port)
|
||||
asset := fmt.Sprintf("%s:%s", configuration.GetParameter("hostname"), configuration.GetParameter("port"))
|
||||
log.Debugf("[%v] 新建 guacd 会话, guacd=%v, asset=%v", sessionId, addr, asset)
|
||||
log.Debug("新建 guacd 会话", log.String("sessionId", sessionId), log.String("addr", addr), log.String("asset", asset))
|
||||
|
||||
guacdTunnel, err := guacd.NewTunnel(addr, configuration)
|
||||
guacdTunnel, err := guacamole.NewTunnel(addr, configuration)
|
||||
if err != nil {
|
||||
utils.Disconnect(ws, NewTunnelError, err.Error())
|
||||
log.Printf("[%v] 建立连接失败: %v", sessionId, err.Error())
|
||||
guacamole.Disconnect(ws, NewTunnelError, err.Error())
|
||||
log.Error("建立连接失败", log.String("sessionId", sessionId), log.NamedError("err", err))
|
||||
return err
|
||||
}
|
||||
|
||||
@ -125,11 +127,11 @@ func (api GuacamoleApi) Guacamole(c echo.Context) error {
|
||||
GuacdTunnel: guacdTunnel,
|
||||
}
|
||||
|
||||
if configuration.Protocol == constant.SSH {
|
||||
if configuration.Protocol == nt.SSH {
|
||||
nextTerminal, err := CreateNextTerminalBySession(s)
|
||||
if err != nil {
|
||||
utils.Disconnect(ws, NewSshClientError, "建立SSH客户端失败: "+err.Error())
|
||||
log.Printf("[%v] 建立 ssh 客户端失败: %v", sessionId, err.Error())
|
||||
guacamole.Disconnect(ws, NewSshClientError, "建立SSH客户端失败: "+err.Error())
|
||||
log.Debug("建立 ssh 客户端失败", log.String("sessionId", sessionId), log.NamedError("err", err))
|
||||
return err
|
||||
}
|
||||
nextSession.NextTerminal = nextTerminal
|
||||
@ -141,15 +143,15 @@ func (api GuacamoleApi) Guacamole(c echo.Context) error {
|
||||
ConnectionId: guacdTunnel.UUID,
|
||||
Width: intWidth,
|
||||
Height: intHeight,
|
||||
Status: constant.Connecting,
|
||||
Recording: configuration.GetParameter(guacd.RecordingPath),
|
||||
Status: nt.Connecting,
|
||||
Recording: configuration.GetParameter(guacamole.RecordingPath),
|
||||
}
|
||||
if sess.Recording == "" {
|
||||
// 未录屏时无需审计
|
||||
sess.Reviewed = true
|
||||
}
|
||||
// 创建新会话
|
||||
log.Debugf("[%v] 新建会话成功: %v", sessionId, sess.ConnectionId)
|
||||
log.Debug("新建会话成功", log.String("sessionId", sessionId))
|
||||
if err := repository.SessionRepository.UpdateById(ctx, &sess, sessionId); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -161,7 +163,7 @@ func (api GuacamoleApi) Guacamole(c echo.Context) error {
|
||||
for {
|
||||
_, message, err := ws.ReadMessage()
|
||||
if err != nil {
|
||||
log.Debugf("[%v] WebSocket已关闭, %v", sessionId, err.Error())
|
||||
log.Debug("WebSocket已关闭", log.String("sessionId", sessionId), log.NamedError("err", err))
|
||||
// guacdTunnel.Read() 会阻塞,所以要先把guacdTunnel客户端关闭,才能退出Guacd循环
|
||||
_ = guacdTunnel.Close()
|
||||
|
||||
@ -176,23 +178,22 @@ func (api GuacamoleApi) Guacamole(c echo.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (api GuacamoleApi) setAssetConfig(attributes map[string]string, s model.Session, configuration *guacd.Configuration) {
|
||||
func (api GuacamoleApi) setAssetConfig(attributes map[string]string, s model.Session, configuration *guacamole.Configuration) {
|
||||
for key, value := range attributes {
|
||||
if guacd.DrivePath == key {
|
||||
if guacamole.DrivePath == key {
|
||||
// 忽略该参数
|
||||
continue
|
||||
}
|
||||
if guacd.EnableDrive == key && value == "true" {
|
||||
storageId := attributes[guacd.DrivePath]
|
||||
if guacamole.EnableDrive == key && value == "true" {
|
||||
storageId := attributes[guacamole.DrivePath]
|
||||
if storageId == "" || storageId == "-" {
|
||||
// 默认空间ID和用户ID相同
|
||||
storageId = s.Creator
|
||||
}
|
||||
realPath := path.Join(service.StorageService.GetBaseDrivePath(), storageId)
|
||||
configuration.SetParameter(guacd.EnableDrive, "true")
|
||||
configuration.SetParameter(guacd.DriveName, "Filesystem")
|
||||
configuration.SetParameter(guacd.DrivePath, realPath)
|
||||
log.Debugf("[%v] 会话 %v:%v 映射目录地址为 %v", s.ID, s.IP, s.Port, realPath)
|
||||
configuration.SetParameter(guacamole.EnableDrive, "true")
|
||||
configuration.SetParameter(guacamole.DriveName, "Filesystem")
|
||||
configuration.SetParameter(guacamole.DrivePath, realPath)
|
||||
} else {
|
||||
configuration.SetParameter(key, value)
|
||||
}
|
||||
@ -202,7 +203,7 @@ func (api GuacamoleApi) setAssetConfig(attributes map[string]string, s model.Ses
|
||||
func (api GuacamoleApi) GuacamoleMonitor(c echo.Context) error {
|
||||
ws, err := UpGrader.Upgrade(c.Response().Writer, c.Request(), nil)
|
||||
if err != nil {
|
||||
log.Errorf("升级为WebSocket协议失败:%v", err.Error())
|
||||
log.Warn("升级为WebSocket协议失败", log.NamedError("err", err))
|
||||
return err
|
||||
}
|
||||
ctx := context.TODO()
|
||||
@ -212,12 +213,12 @@ func (api GuacamoleApi) GuacamoleMonitor(c echo.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if s.Status != constant.Connected {
|
||||
utils.Disconnect(ws, AssetNotActive, "会话离线")
|
||||
if s.Status != nt.Connected {
|
||||
guacamole.Disconnect(ws, AssetNotActive, "会话离线")
|
||||
return nil
|
||||
}
|
||||
connectionId := s.ConnectionId
|
||||
configuration := guacd.NewConfiguration()
|
||||
configuration := guacamole.NewConfiguration()
|
||||
configuration.ConnectionID = connectionId
|
||||
sessionId = s.ID
|
||||
configuration.SetParameter("width", strconv.Itoa(s.Width))
|
||||
@ -225,13 +226,10 @@ func (api GuacamoleApi) GuacamoleMonitor(c echo.Context) error {
|
||||
configuration.SetParameter("dpi", "96")
|
||||
|
||||
addr := config.GlobalCfg.Guacd.Hostname + ":" + strconv.Itoa(config.GlobalCfg.Guacd.Port)
|
||||
asset := fmt.Sprintf("%s:%s", configuration.GetParameter("hostname"), configuration.GetParameter("port"))
|
||||
log.Debugf("[%v] 新建 guacd 会话, guacd=%v, asset=%v", sessionId, addr, asset)
|
||||
|
||||
guacdTunnel, err := guacd.NewTunnel(addr, configuration)
|
||||
guacdTunnel, err := guacamole.NewTunnel(addr, configuration)
|
||||
if err != nil {
|
||||
utils.Disconnect(ws, NewTunnelError, err.Error())
|
||||
log.Printf("[%v] 建立连接失败: %v", sessionId, err.Error())
|
||||
guacamole.Disconnect(ws, NewTunnelError, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
@ -246,12 +244,11 @@ func (api GuacamoleApi) GuacamoleMonitor(c echo.Context) error {
|
||||
// 要监控会话
|
||||
forObsSession := session.GlobalSessionManager.GetById(sessionId)
|
||||
if forObsSession == nil {
|
||||
utils.Disconnect(ws, NotFoundSession, "获取会话失败")
|
||||
guacamole.Disconnect(ws, NotFoundSession, "获取会话失败")
|
||||
return nil
|
||||
}
|
||||
nextSession.ID = utils.UUID()
|
||||
forObsSession.Observer.Add(nextSession)
|
||||
log.Debugf("[%v:%v] 观察者[%v]加入会话[%v]", sessionId, connectionId, nextSession.ID, s.ConnectionId)
|
||||
|
||||
guacamoleHandler := NewGuacamoleHandler(ws, guacdTunnel)
|
||||
guacamoleHandler.Start()
|
||||
@ -260,13 +257,11 @@ func (api GuacamoleApi) GuacamoleMonitor(c echo.Context) error {
|
||||
for {
|
||||
_, message, err := ws.ReadMessage()
|
||||
if err != nil {
|
||||
log.Debugf("[%v:%v] WebSocket已关闭, %v", sessionId, connectionId, err.Error())
|
||||
// guacdTunnel.Read() 会阻塞,所以要先把guacdTunnel客户端关闭,才能退出Guacd循环
|
||||
_ = guacdTunnel.Close()
|
||||
|
||||
observerId := nextSession.ID
|
||||
forObsSession.Observer.Del(observerId)
|
||||
log.Debugf("[%v:%v] 观察者[%v]退出会话", sessionId, connectionId, observerId)
|
||||
return nil
|
||||
}
|
||||
_, err = guacdTunnel.WriteAndFlush(message)
|
||||
@ -277,12 +272,12 @@ func (api GuacamoleApi) GuacamoleMonitor(c echo.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (api GuacamoleApi) setConfig(propertyMap map[string]string, s model.Session, configuration *guacd.Configuration) {
|
||||
if propertyMap[guacd.EnableRecording] == "true" {
|
||||
configuration.SetParameter(guacd.RecordingPath, path.Join(config.GlobalCfg.Guacd.Recording, s.ID))
|
||||
configuration.SetParameter(guacd.CreateRecordingPath, "true")
|
||||
func (api GuacamoleApi) setConfig(propertyMap map[string]string, s model.Session, configuration *guacamole.Configuration) {
|
||||
if propertyMap[guacamole.EnableRecording] == "true" {
|
||||
configuration.SetParameter(guacamole.RecordingPath, path.Join(config.GlobalCfg.Guacd.Recording, s.ID))
|
||||
configuration.SetParameter(guacamole.CreateRecordingPath, "true")
|
||||
} else {
|
||||
configuration.SetParameter(guacd.RecordingPath, "")
|
||||
configuration.SetParameter(guacamole.RecordingPath, "")
|
||||
}
|
||||
|
||||
configuration.Protocol = s.Protocol
|
||||
@ -295,18 +290,18 @@ func (api GuacamoleApi) setConfig(propertyMap map[string]string, s model.Session
|
||||
configuration.SetParameter("ignore-cert", "true")
|
||||
configuration.SetParameter("create-drive-path", "true")
|
||||
configuration.SetParameter("resize-method", "reconnect")
|
||||
configuration.SetParameter(guacd.EnableWallpaper, propertyMap[guacd.EnableWallpaper])
|
||||
configuration.SetParameter(guacd.EnableTheming, propertyMap[guacd.EnableTheming])
|
||||
configuration.SetParameter(guacd.EnableFontSmoothing, propertyMap[guacd.EnableFontSmoothing])
|
||||
configuration.SetParameter(guacd.EnableFullWindowDrag, propertyMap[guacd.EnableFullWindowDrag])
|
||||
configuration.SetParameter(guacd.EnableDesktopComposition, propertyMap[guacd.EnableDesktopComposition])
|
||||
configuration.SetParameter(guacd.EnableMenuAnimations, propertyMap[guacd.EnableMenuAnimations])
|
||||
configuration.SetParameter(guacd.DisableBitmapCaching, propertyMap[guacd.DisableBitmapCaching])
|
||||
configuration.SetParameter(guacd.DisableOffscreenCaching, propertyMap[guacd.DisableOffscreenCaching])
|
||||
configuration.SetParameter(guacd.ColorDepth, propertyMap[guacd.ColorDepth])
|
||||
configuration.SetParameter(guacd.ForceLossless, propertyMap[guacd.ForceLossless])
|
||||
configuration.SetParameter(guacd.PreConnectionId, propertyMap[guacd.PreConnectionId])
|
||||
configuration.SetParameter(guacd.PreConnectionBlob, propertyMap[guacd.PreConnectionBlob])
|
||||
configuration.SetParameter(guacamole.EnableWallpaper, propertyMap[guacamole.EnableWallpaper])
|
||||
configuration.SetParameter(guacamole.EnableTheming, propertyMap[guacamole.EnableTheming])
|
||||
configuration.SetParameter(guacamole.EnableFontSmoothing, propertyMap[guacamole.EnableFontSmoothing])
|
||||
configuration.SetParameter(guacamole.EnableFullWindowDrag, propertyMap[guacamole.EnableFullWindowDrag])
|
||||
configuration.SetParameter(guacamole.EnableDesktopComposition, propertyMap[guacamole.EnableDesktopComposition])
|
||||
configuration.SetParameter(guacamole.EnableMenuAnimations, propertyMap[guacamole.EnableMenuAnimations])
|
||||
configuration.SetParameter(guacamole.DisableBitmapCaching, propertyMap[guacamole.DisableBitmapCaching])
|
||||
configuration.SetParameter(guacamole.DisableOffscreenCaching, propertyMap[guacamole.DisableOffscreenCaching])
|
||||
configuration.SetParameter(guacamole.ColorDepth, propertyMap[guacamole.ColorDepth])
|
||||
configuration.SetParameter(guacamole.ForceLossless, propertyMap[guacamole.ForceLossless])
|
||||
configuration.SetParameter(guacamole.PreConnectionId, propertyMap[guacamole.PreConnectionId])
|
||||
configuration.SetParameter(guacamole.PreConnectionBlob, propertyMap[guacamole.PreConnectionBlob])
|
||||
case "ssh":
|
||||
if len(s.PrivateKey) > 0 && s.PrivateKey != "-" {
|
||||
configuration.SetParameter("username", s.Username)
|
||||
@ -317,11 +312,11 @@ func (api GuacamoleApi) setConfig(propertyMap map[string]string, s model.Session
|
||||
configuration.SetParameter("password", s.Password)
|
||||
}
|
||||
|
||||
configuration.SetParameter(guacd.FontSize, propertyMap[guacd.FontSize])
|
||||
configuration.SetParameter(guacd.FontName, propertyMap[guacd.FontName])
|
||||
configuration.SetParameter(guacd.ColorScheme, propertyMap[guacd.ColorScheme])
|
||||
configuration.SetParameter(guacd.Backspace, propertyMap[guacd.Backspace])
|
||||
configuration.SetParameter(guacd.TerminalType, propertyMap[guacd.TerminalType])
|
||||
configuration.SetParameter(guacamole.FontSize, propertyMap[guacamole.FontSize])
|
||||
configuration.SetParameter(guacamole.FontName, propertyMap[guacamole.FontName])
|
||||
configuration.SetParameter(guacamole.ColorScheme, propertyMap[guacamole.ColorScheme])
|
||||
configuration.SetParameter(guacamole.Backspace, propertyMap[guacamole.Backspace])
|
||||
configuration.SetParameter(guacamole.TerminalType, propertyMap[guacamole.TerminalType])
|
||||
case "vnc":
|
||||
configuration.SetParameter("username", s.Username)
|
||||
configuration.SetParameter("password", s.Password)
|
||||
@ -329,17 +324,17 @@ func (api GuacamoleApi) setConfig(propertyMap map[string]string, s model.Session
|
||||
configuration.SetParameter("username", s.Username)
|
||||
configuration.SetParameter("password", s.Password)
|
||||
|
||||
configuration.SetParameter(guacd.FontSize, propertyMap[guacd.FontSize])
|
||||
configuration.SetParameter(guacd.FontName, propertyMap[guacd.FontName])
|
||||
configuration.SetParameter(guacd.ColorScheme, propertyMap[guacd.ColorScheme])
|
||||
configuration.SetParameter(guacd.Backspace, propertyMap[guacd.Backspace])
|
||||
configuration.SetParameter(guacd.TerminalType, propertyMap[guacd.TerminalType])
|
||||
configuration.SetParameter(guacamole.FontSize, propertyMap[guacamole.FontSize])
|
||||
configuration.SetParameter(guacamole.FontName, propertyMap[guacamole.FontName])
|
||||
configuration.SetParameter(guacamole.ColorScheme, propertyMap[guacamole.ColorScheme])
|
||||
configuration.SetParameter(guacamole.Backspace, propertyMap[guacamole.Backspace])
|
||||
configuration.SetParameter(guacamole.TerminalType, propertyMap[guacamole.TerminalType])
|
||||
case "kubernetes":
|
||||
configuration.SetParameter(guacd.FontSize, propertyMap[guacd.FontSize])
|
||||
configuration.SetParameter(guacd.FontName, propertyMap[guacd.FontName])
|
||||
configuration.SetParameter(guacd.ColorScheme, propertyMap[guacd.ColorScheme])
|
||||
configuration.SetParameter(guacd.Backspace, propertyMap[guacd.Backspace])
|
||||
configuration.SetParameter(guacd.TerminalType, propertyMap[guacd.TerminalType])
|
||||
configuration.SetParameter(guacamole.FontSize, propertyMap[guacamole.FontSize])
|
||||
configuration.SetParameter(guacamole.FontName, propertyMap[guacamole.FontName])
|
||||
configuration.SetParameter(guacamole.ColorScheme, propertyMap[guacamole.ColorScheme])
|
||||
configuration.SetParameter(guacamole.Backspace, propertyMap[guacamole.Backspace])
|
||||
configuration.SetParameter(guacamole.TerminalType, propertyMap[guacamole.TerminalType])
|
||||
default:
|
||||
|
||||
}
|
||||
|
@ -2,22 +2,19 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"next-terminal/server/guacd"
|
||||
"next-terminal/server/log"
|
||||
"next-terminal/server/utils"
|
||||
"next-terminal/server/common/guacamole"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type GuacamoleHandler struct {
|
||||
ws *websocket.Conn
|
||||
tunnel *guacd.Tunnel
|
||||
tunnel *guacamole.Tunnel
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func NewGuacamoleHandler(ws *websocket.Conn, tunnel *guacd.Tunnel) *GuacamoleHandler {
|
||||
func NewGuacamoleHandler(ws *websocket.Conn, tunnel *guacamole.Tunnel) *GuacamoleHandler {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
return &GuacamoleHandler{
|
||||
ws: ws,
|
||||
@ -36,7 +33,7 @@ func (r GuacamoleHandler) Start() {
|
||||
default:
|
||||
instruction, err := r.tunnel.Read()
|
||||
if err != nil {
|
||||
utils.Disconnect(r.ws, TunnelClosed, "远程连接已关闭")
|
||||
guacamole.Disconnect(r.ws, TunnelClosed, "远程连接已关闭")
|
||||
return
|
||||
}
|
||||
if len(instruction) == 0 {
|
||||
@ -44,7 +41,6 @@ func (r GuacamoleHandler) Start() {
|
||||
}
|
||||
err = r.ws.WriteMessage(websocket.TextMessage, instruction)
|
||||
if err != nil {
|
||||
log.Debugf("WebSocket写入失败,即将关闭Guacd连接...")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,8 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/maps"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -23,7 +24,7 @@ func (api JobApi) JobCreateEndpoint(c echo.Context) error {
|
||||
}
|
||||
|
||||
item.ID = utils.UUID()
|
||||
item.Created = utils.NowJsonTime()
|
||||
item.Created = common.NowJsonTime()
|
||||
|
||||
if err := service.JobService.Create(context.TODO(), &item); err != nil {
|
||||
return err
|
||||
@ -45,7 +46,7 @@ func (api JobApi) JobPagingEndpoint(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
@ -110,13 +111,17 @@ func (api JobApi) JobGetEndpoint(c echo.Context) error {
|
||||
|
||||
func (api JobApi) JobGetLogsEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
|
||||
items, err := repository.JobLogRepository.FindByJobId(context.TODO(), id)
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
items, total, err := repository.JobLogRepository.FindByJobId(context.TODO(), id, pageIndex, pageSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, items)
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
|
||||
func (api JobApi) JobDeleteLogsEndpoint(c echo.Context) error {
|
||||
|
@ -2,6 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"next-terminal/server/common/maps"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -26,7 +27,7 @@ func (api LoginLogApi) LoginLogPagingEndpoint(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
|
143
server/api/login_policy.go
Normal file
143
server/api/login_policy.go
Normal file
@ -0,0 +1,143 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/service"
|
||||
"next-terminal/server/utils"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type LoginPolicyApi struct{}
|
||||
|
||||
func (api LoginPolicyApi) PagingEndpoint(c echo.Context) error {
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
name := c.QueryParam("name")
|
||||
userId := c.QueryParam("userId")
|
||||
|
||||
order := c.QueryParam("order")
|
||||
field := c.QueryParam("field")
|
||||
|
||||
items, total, err := repository.LoginPolicyRepository.Find(context.TODO(), pageIndex, pageSize, name, userId, order, field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
|
||||
func (api LoginPolicyApi) GetEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
|
||||
item, err := service.LoginPolicyService.FindById(context.Background(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, item)
|
||||
}
|
||||
|
||||
func (api LoginPolicyApi) CreateEndpoint(c echo.Context) error {
|
||||
var item model.LoginPolicy
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
item.ID = utils.UUID()
|
||||
|
||||
if err := service.LoginPolicyService.Create(context.Background(), &item); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (api LoginPolicyApi) DeleteEndpoint(c echo.Context) error {
|
||||
ids := c.Param("id")
|
||||
split := strings.Split(ids, ",")
|
||||
if err := service.LoginPolicyService.DeleteByIds(context.Background(), split); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, nil)
|
||||
}
|
||||
|
||||
func (api LoginPolicyApi) UpdateEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
var item model.LoginPolicy
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := service.LoginPolicyService.UpdateById(context.Background(), &item, id); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (api LoginPolicyApi) GetUserPageEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
username := c.QueryParam("username")
|
||||
nickname := c.QueryParam("nickname")
|
||||
mail := c.QueryParam("mail")
|
||||
|
||||
order := c.QueryParam("order")
|
||||
field := c.QueryParam("field")
|
||||
|
||||
items, total, err := repository.UserRepository.Find(context.TODO(), pageIndex, pageSize, username, nickname, mail, "", id, order, field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
|
||||
func (api LoginPolicyApi) BindEndpoint(c echo.Context) error {
|
||||
var items []model.LoginPolicyUserRef
|
||||
if err := c.Bind(&items); err != nil {
|
||||
return err
|
||||
}
|
||||
id := c.Param("id")
|
||||
if err := service.LoginPolicyService.Bind(context.Background(), id, items); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (api LoginPolicyApi) UnbindEndpoint(c echo.Context) error {
|
||||
var items []model.LoginPolicyUserRef
|
||||
if err := c.Bind(&items); err != nil {
|
||||
return err
|
||||
}
|
||||
id := c.Param("id")
|
||||
if err := service.LoginPolicyService.Unbind(context.Background(), id, items); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (api LoginPolicyApi) GetUserIdEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
refs, err := repository.LoginPolicyUserRefRepository.FindByLoginPolicyId(context.Background(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var ids = make([]string, 0)
|
||||
for _, ref := range refs {
|
||||
ids = append(ids, ref.UserId)
|
||||
}
|
||||
|
||||
return Success(c, ids)
|
||||
}
|
@ -2,10 +2,11 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"next-terminal/server/constant"
|
||||
"next-terminal/server/common/nt"
|
||||
"next-terminal/server/dto"
|
||||
"next-terminal/server/global/stat"
|
||||
"next-terminal/server/repository"
|
||||
"time"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
@ -14,21 +15,27 @@ type OverviewApi struct{}
|
||||
|
||||
func (api OverviewApi) OverviewCounterEndPoint(c echo.Context) error {
|
||||
var (
|
||||
countUser int64
|
||||
countOnlineSession int64
|
||||
credential int64
|
||||
asset int64
|
||||
totalUser int64
|
||||
onlineUser int64
|
||||
countOfflineSession int64
|
||||
totalAsset int64
|
||||
activeAsset int64
|
||||
failLoginCount int64
|
||||
)
|
||||
countUser, _ = repository.UserRepository.CountOnlineUser(context.TODO())
|
||||
countOnlineSession, _ = repository.SessionRepository.CountOnlineSession(context.TODO())
|
||||
credential, _ = repository.CredentialRepository.Count(context.TODO())
|
||||
asset, _ = repository.AssetRepository.Count(context.TODO())
|
||||
totalUser, _ = repository.UserRepository.Count(context.TODO())
|
||||
onlineUser, _ = repository.UserRepository.CountOnlineUser(context.TODO())
|
||||
countOfflineSession, _ = repository.SessionRepository.CountOfflineSession(context.TODO())
|
||||
totalAsset, _ = repository.AssetRepository.Count(context.TODO())
|
||||
activeAsset, _ = repository.AssetRepository.CountByActive(context.TODO(), true)
|
||||
failLoginCount, _ = repository.LoginLogRepository.CountByState(context.TODO(), "0")
|
||||
|
||||
counter := dto.Counter{
|
||||
User: countUser,
|
||||
OnlineSession: countOnlineSession,
|
||||
Credential: credential,
|
||||
Asset: asset,
|
||||
TotalUser: totalUser,
|
||||
OnlineUser: onlineUser,
|
||||
OfflineSession: countOfflineSession,
|
||||
TotalAsset: totalAsset,
|
||||
ActiveAsset: activeAsset,
|
||||
FailLoginCount: failLoginCount,
|
||||
}
|
||||
|
||||
return Success(c, counter)
|
||||
@ -43,11 +50,11 @@ func (api OverviewApi) OverviewAssetEndPoint(c echo.Context) error {
|
||||
kubernetes int64
|
||||
)
|
||||
|
||||
ssh, _ = repository.AssetRepository.CountByProtocol(context.TODO(), constant.SSH)
|
||||
rdp, _ = repository.AssetRepository.CountByProtocol(context.TODO(), constant.RDP)
|
||||
vnc, _ = repository.AssetRepository.CountByProtocol(context.TODO(), constant.VNC)
|
||||
telnet, _ = repository.AssetRepository.CountByProtocol(context.TODO(), constant.Telnet)
|
||||
kubernetes, _ = repository.AssetRepository.CountByProtocol(context.TODO(), constant.K8s)
|
||||
ssh, _ = repository.AssetRepository.CountByProtocol(context.TODO(), nt.SSH)
|
||||
rdp, _ = repository.AssetRepository.CountByProtocol(context.TODO(), nt.RDP)
|
||||
vnc, _ = repository.AssetRepository.CountByProtocol(context.TODO(), nt.VNC)
|
||||
telnet, _ = repository.AssetRepository.CountByProtocol(context.TODO(), nt.Telnet)
|
||||
kubernetes, _ = repository.AssetRepository.CountByProtocol(context.TODO(), nt.K8s)
|
||||
|
||||
m := echo.Map{
|
||||
"ssh": ssh,
|
||||
@ -55,14 +62,168 @@ func (api OverviewApi) OverviewAssetEndPoint(c echo.Context) error {
|
||||
"vnc": vnc,
|
||||
"telnet": telnet,
|
||||
"kubernetes": kubernetes,
|
||||
"all": ssh + rdp + vnc + telnet + kubernetes,
|
||||
}
|
||||
return Success(c, m)
|
||||
}
|
||||
|
||||
func (api OverviewApi) OverviewAccessEndPoint(c echo.Context) error {
|
||||
access, err := repository.SessionRepository.OverviewAccess(context.TODO())
|
||||
func (api OverviewApi) OverviewDateCounterEndPoint(c echo.Context) error {
|
||||
d := c.QueryParam("d")
|
||||
var days = 7
|
||||
if d == "month" {
|
||||
days = 30
|
||||
}
|
||||
now := time.Now()
|
||||
lastDate := now.AddDate(0, 0, -days)
|
||||
// 最近一月登录次数
|
||||
loginLogCounters, err := repository.LoginLogRepository.CountWithGroupByLoginTime(context.TODO(), lastDate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, access)
|
||||
// 最近一月活跃用户
|
||||
userCounters, err := repository.LoginLogRepository.CountWithGroupByLoginTimeAndUsername(context.TODO(), lastDate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 最近一月活跃资产
|
||||
sessionCounters, err := repository.SessionRepository.CountWithGroupByLoginTime(context.TODO(), lastDate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var counters []dto.DateCounter
|
||||
for i := 0; i < days; i++ {
|
||||
day := lastDate.AddDate(0, 0, i).Format("2006-01-02")
|
||||
|
||||
var exist = false
|
||||
for _, counter := range loginLogCounters {
|
||||
if counter.Date == day {
|
||||
exist = true
|
||||
counters = append(counters, dto.DateCounter{
|
||||
Type: "登录次数",
|
||||
Date: day,
|
||||
Value: counter.Value,
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !exist {
|
||||
counters = append(counters, dto.DateCounter{
|
||||
Type: "登录次数",
|
||||
Date: day,
|
||||
Value: 0,
|
||||
})
|
||||
}
|
||||
|
||||
exist = false
|
||||
for _, counter := range userCounters {
|
||||
if counter.Date == day {
|
||||
exist = true
|
||||
counters = append(counters, dto.DateCounter{
|
||||
Type: "活跃用户",
|
||||
Date: day,
|
||||
Value: counter.Value,
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !exist {
|
||||
counters = append(counters, dto.DateCounter{
|
||||
Type: "活跃用户",
|
||||
Date: day,
|
||||
Value: 0,
|
||||
})
|
||||
}
|
||||
|
||||
exist = false
|
||||
for _, counter := range sessionCounters {
|
||||
if counter.Date == day {
|
||||
exist = true
|
||||
counters = append(counters, dto.DateCounter{
|
||||
Type: "活跃资产",
|
||||
Date: day,
|
||||
Value: counter.Value,
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !exist {
|
||||
counters = append(counters, dto.DateCounter{
|
||||
Type: "活跃资产",
|
||||
Date: day,
|
||||
Value: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return Success(c, counters)
|
||||
}
|
||||
|
||||
func (api OverviewApi) OverviewPS(c echo.Context) error {
|
||||
//memoryStat, err := mem.VirtualMemory()
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//avgStat, err := load.Avg()
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//
|
||||
//cpuCount, err := cpu.Counts(true)
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//
|
||||
//percent, err := cpu.Percent(time.Second, false)
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//
|
||||
//var bytesRead uint64 = 0
|
||||
//var bytesWrite uint64 = 0
|
||||
//
|
||||
//diskIO, err := disk.IOCounters()
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//for _, v := range diskIO {
|
||||
// bytesRead += v.ReadBytes
|
||||
// bytesWrite += v.WriteBytes
|
||||
//}
|
||||
//
|
||||
//var bytesSent uint64 = 0
|
||||
//var bytesRecv uint64 = 0
|
||||
//netIO, err := net.IOCounters(true)
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//for _, v := range netIO {
|
||||
// bytesSent += v.BytesSent
|
||||
// bytesRecv += v.BytesRecv
|
||||
//}
|
||||
|
||||
//return Success(c, Map{
|
||||
// "mem": Map{
|
||||
// "total": memoryStat.Total,
|
||||
// "usedPercent": memoryStat.UsedPercent,
|
||||
// },
|
||||
// "cpu": Map{
|
||||
// "count": cpuCount,
|
||||
// "loadAvg": avgStat,
|
||||
// "usedPercent": percent[0],
|
||||
// },
|
||||
// "diskIO": Map{
|
||||
// "bytesRead": bytesRead,
|
||||
// "bytesWrite": bytesWrite,
|
||||
// },
|
||||
// "netIO": Map{
|
||||
// "bytesSent": bytesSent,
|
||||
// "bytesRecv": bytesRecv,
|
||||
// },
|
||||
//})
|
||||
|
||||
return Success(c, stat.SystemLoad)
|
||||
}
|
||||
|
@ -1,51 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"next-terminal/server/dto"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/service"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type ResourceSharerApi struct{}
|
||||
|
||||
func (api ResourceSharerApi) RSGetSharersEndPoint(c echo.Context) error {
|
||||
resourceId := c.QueryParam("resourceId")
|
||||
resourceType := c.QueryParam("resourceType")
|
||||
userId := c.QueryParam("userId")
|
||||
userGroupId := c.QueryParam("userGroupId")
|
||||
userIds, err := repository.ResourceSharerRepository.Find(context.TODO(), resourceId, resourceType, userId, userGroupId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, userIds)
|
||||
}
|
||||
|
||||
func (api ResourceSharerApi) ResourceRemoveByUserIdAssignEndPoint(c echo.Context) error {
|
||||
var ru dto.RU
|
||||
if err := c.Bind(&ru); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := repository.ResourceSharerRepository.DeleteByUserIdAndResourceTypeAndResourceIdIn(context.TODO(), ru.UserGroupId, ru.UserId, ru.ResourceType, ru.ResourceIds); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (api ResourceSharerApi) ResourceAddByUserIdAssignEndPoint(c echo.Context) error {
|
||||
var ru dto.RU
|
||||
if err := c.Bind(&ru); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := service.UserService.AddSharerResources(context.TODO(), ru.UserGroupId, ru.UserId, ru.StrategyId, ru.ResourceType, ru.ResourceIds); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, "")
|
||||
}
|
100
server/api/role.go
Normal file
100
server/api/role.go
Normal file
@ -0,0 +1,100 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/service"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/utils"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type RoleApi struct{}
|
||||
|
||||
func (api RoleApi) AllEndpoint(c echo.Context) error {
|
||||
items, err := repository.RoleRepository.FindAll(context.TODO())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, items)
|
||||
}
|
||||
|
||||
func (api RoleApi) PagingEndpoint(c echo.Context) error {
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
name := c.QueryParam("name")
|
||||
_type := c.QueryParam("type")
|
||||
|
||||
order := c.QueryParam("order")
|
||||
field := c.QueryParam("field")
|
||||
|
||||
items, total, err := repository.RoleRepository.Find(context.TODO(), pageIndex, pageSize, name, _type, order, field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
|
||||
func (api RoleApi) GetEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
|
||||
item, err := service.RoleService.FindById(context.Background(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, item)
|
||||
}
|
||||
|
||||
func (api RoleApi) CreateEndpoint(c echo.Context) error {
|
||||
var item model.Role
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
item.ID = utils.UUID()
|
||||
item.Created = common.NowJsonTime()
|
||||
item.Deletable = true
|
||||
item.Modifiable = true
|
||||
item.Type = "new"
|
||||
|
||||
if err := service.RoleService.Create(context.Background(), &item); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (api RoleApi) DeleteEndpoint(c echo.Context) error {
|
||||
ids := c.Param("id")
|
||||
split := strings.Split(ids, ",")
|
||||
if err := service.RoleService.DeleteByIds(context.Background(), split, false); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, nil)
|
||||
}
|
||||
|
||||
func (api RoleApi) UpdateEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
var item model.Role
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := service.RoleService.UpdateById(context.Background(), &item, id, false); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (api RoleApi) TreeMenus(c echo.Context) error {
|
||||
return Success(c, service.MenuService.GetTreeMenus())
|
||||
}
|
@ -2,7 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"next-terminal/server/common/maps"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -54,7 +54,7 @@ func (api SecurityApi) SecurityPagingEndpoint(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
|
@ -13,9 +13,10 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"next-terminal/server/constant"
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/common/nt"
|
||||
"next-terminal/server/global/session"
|
||||
"next-terminal/server/log"
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/service"
|
||||
@ -44,10 +45,10 @@ func (api SessionApi) SessionPagingEndpoint(c echo.Context) error {
|
||||
}
|
||||
|
||||
for i := 0; i < len(items); i++ {
|
||||
if status == constant.Disconnected && len(items[i].Recording) > 0 {
|
||||
if status == nt.Disconnected && len(items[i].Recording) > 0 {
|
||||
|
||||
var recording string
|
||||
if items[i].Mode == constant.Native || items[i].Mode == constant.Terminal {
|
||||
if items[i].Mode == nt.Native || items[i].Mode == nt.Terminal {
|
||||
recording = items[i].Recording
|
||||
} else {
|
||||
recording = items[i].Recording + "/recording"
|
||||
@ -63,7 +64,7 @@ func (api SessionApi) SessionPagingEndpoint(c echo.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
@ -71,7 +72,7 @@ func (api SessionApi) SessionPagingEndpoint(c echo.Context) error {
|
||||
|
||||
func (api SessionApi) SessionDeleteEndpoint(c echo.Context) error {
|
||||
sessionIds := strings.Split(c.Param("id"), ",")
|
||||
err := repository.SessionRepository.DeleteByIds(context.TODO(), sessionIds)
|
||||
err := service.SessionService.DeleteByIds(context.TODO(), sessionIds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -115,8 +116,8 @@ func (api SessionApi) SessionConnectEndpoint(c echo.Context) error {
|
||||
|
||||
s := model.Session{}
|
||||
s.ID = sessionId
|
||||
s.Status = constant.Connected
|
||||
s.ConnectedTime = utils.NowJsonTime()
|
||||
s.Status = nt.Connected
|
||||
s.ConnectedTime = common.NowJsonTime()
|
||||
|
||||
if err := repository.SessionRepository.UpdateById(context.TODO(), &s, sessionId); err != nil {
|
||||
return err
|
||||
@ -170,10 +171,10 @@ func (api SessionApi) SessionCreateEndpoint(c echo.Context) error {
|
||||
assetId := c.QueryParam("assetId")
|
||||
mode := c.QueryParam("mode")
|
||||
|
||||
if mode == constant.Native {
|
||||
mode = constant.Native
|
||||
if mode == nt.Native {
|
||||
mode = nt.Native
|
||||
} else {
|
||||
mode = constant.Guacd
|
||||
mode = nt.Guacd
|
||||
}
|
||||
|
||||
user, _ := GetCurrentAccount(c)
|
||||
@ -221,6 +222,10 @@ func (api SessionApi) SessionUploadEndpoint(c echo.Context) error {
|
||||
remoteDir := c.QueryParam("dir")
|
||||
remoteFile := path.Join(remoteDir, filename)
|
||||
|
||||
// 记录日志
|
||||
account, _ := GetCurrentAccount(c)
|
||||
_ = service.StorageLogService.Save(context.Background(), s.AssetId, sessionId, account.ID, nt.StorageLogActionUpload, remoteFile)
|
||||
|
||||
if "ssh" == s.Protocol {
|
||||
nextSession := session.GlobalSessionManager.GetById(sessionId)
|
||||
if nextSession == nil {
|
||||
@ -313,6 +318,11 @@ func (api SessionApi) SessionDownloadEndpoint(c echo.Context) error {
|
||||
return errors.New("禁止操作")
|
||||
}
|
||||
file := c.QueryParam("file")
|
||||
|
||||
// 记录日志
|
||||
account, _ := GetCurrentAccount(c)
|
||||
_ = service.StorageLogService.Save(context.Background(), s.AssetId, sessionId, account.ID, nt.StorageLogActionDownload, file)
|
||||
|
||||
// 获取带后缀的文件名称
|
||||
filenameWithSuffix := path.Base(file)
|
||||
if "ssh" == s.Protocol {
|
||||
@ -360,7 +370,6 @@ func (api SessionApi) SessionLsEndpoint(c echo.Context) error {
|
||||
if nextSession.NextTerminal.SftpClient == nil {
|
||||
sftpClient, err := sftp.NewClient(nextSession.NextTerminal.SshClient)
|
||||
if err != nil {
|
||||
log.Errorf("创建sftp客户端失败:%v", err.Error())
|
||||
return err
|
||||
}
|
||||
nextSession.NextTerminal.SftpClient = sftpClient
|
||||
@ -374,18 +383,13 @@ func (api SessionApi) SessionLsEndpoint(c echo.Context) error {
|
||||
var files = make([]service.File, 0)
|
||||
for i := range fileInfos {
|
||||
|
||||
// 忽略隐藏文件
|
||||
if strings.HasPrefix(fileInfos[i].Name(), ".") {
|
||||
continue
|
||||
}
|
||||
|
||||
file := service.File{
|
||||
Name: fileInfos[i].Name(),
|
||||
Path: path.Join(remoteDir, fileInfos[i].Name()),
|
||||
IsDir: fileInfos[i].IsDir(),
|
||||
Mode: fileInfos[i].Mode().String(),
|
||||
IsLink: fileInfos[i].Mode()&os.ModeSymlink == os.ModeSymlink,
|
||||
ModTime: utils.NewJsonTime(fileInfos[i].ModTime()),
|
||||
ModTime: common.NewJsonTime(fileInfos[i].ModTime()),
|
||||
Size: fileInfos[i].Size(),
|
||||
}
|
||||
|
||||
@ -415,6 +419,11 @@ func (api SessionApi) SessionMkDirEndpoint(c echo.Context) error {
|
||||
return errors.New("禁止操作")
|
||||
}
|
||||
remoteDir := c.QueryParam("dir")
|
||||
|
||||
// 记录日志
|
||||
account, _ := GetCurrentAccount(c)
|
||||
_ = service.StorageLogService.Save(context.Background(), s.AssetId, sessionId, account.ID, nt.StorageLogActionMkdir, remoteDir)
|
||||
|
||||
if "ssh" == s.Protocol {
|
||||
nextSession := session.GlobalSessionManager.GetById(sessionId)
|
||||
if nextSession == nil {
|
||||
@ -445,6 +454,11 @@ func (api SessionApi) SessionRmEndpoint(c echo.Context) error {
|
||||
}
|
||||
// 文件夹或者文件
|
||||
file := c.FormValue("file")
|
||||
|
||||
// 记录日志
|
||||
account, _ := GetCurrentAccount(c)
|
||||
_ = service.StorageLogService.Save(context.Background(), s.AssetId, sessionId, account.ID, nt.StorageLogActionRm, file)
|
||||
|
||||
if "ssh" == s.Protocol {
|
||||
nextSession := session.GlobalSessionManager.GetById(sessionId)
|
||||
if nextSession == nil {
|
||||
@ -502,6 +516,11 @@ func (api SessionApi) SessionRenameEndpoint(c echo.Context) error {
|
||||
}
|
||||
oldName := c.QueryParam("oldName")
|
||||
newName := c.QueryParam("newName")
|
||||
|
||||
// 记录日志
|
||||
account, _ := GetCurrentAccount(c)
|
||||
_ = service.StorageLogService.Save(context.Background(), s.AssetId, sessionId, account.ID, nt.StorageLogActionRename, oldName)
|
||||
|
||||
if "ssh" == s.Protocol {
|
||||
nextSession := session.GlobalSessionManager.GetById(sessionId)
|
||||
if nextSession == nil {
|
||||
@ -533,15 +552,13 @@ func (api SessionApi) SessionRecordingEndpoint(c echo.Context) error {
|
||||
}
|
||||
|
||||
var recording string
|
||||
if s.Mode == constant.Native || s.Mode == constant.Terminal {
|
||||
if s.Mode == nt.Native || s.Mode == nt.Terminal {
|
||||
recording = s.Recording
|
||||
} else {
|
||||
recording = s.Recording + "/recording"
|
||||
}
|
||||
_ = repository.SessionRepository.UpdateReadByIds(context.TODO(), true, []string{sessionId})
|
||||
|
||||
log.Debugf("读取录屏文件:%v,是否存在: %v, 是否为文件: %v", recording, utils.FileExists(recording), utils.IsFile(recording))
|
||||
|
||||
http.ServeFile(c.Response(), c.Request(), recording)
|
||||
return nil
|
||||
}
|
||||
@ -570,8 +587,8 @@ func (api SessionApi) SessionStatsEndpoint(c echo.Context) error {
|
||||
if nextSession == nil {
|
||||
return errors.New("获取会话失败")
|
||||
}
|
||||
sshClient := nextSession.NextTerminal.SshClient
|
||||
stats, err := GetAllStats(sshClient)
|
||||
|
||||
stats, err := GetAllStats(nextSession)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
package api
|
||||
|
||||
func DealCommand(enterKeys []rune) {
|
||||
println(string(enterKeys))
|
||||
|
||||
}
|
@ -3,6 +3,8 @@ package api
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"next-terminal/server/common/taskrunner"
|
||||
"next-terminal/server/global/session"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@ -70,40 +72,56 @@ type Stat struct {
|
||||
CPU CPU `json:"cpu"`
|
||||
}
|
||||
|
||||
func GetAllStats(client *ssh.Client) (*Stat, error) {
|
||||
func GetAllStats(nextSession *session.Session) (*Stat, error) {
|
||||
client := nextSession.NextTerminal.SshClient
|
||||
start := time.Now()
|
||||
stats := &Stat{}
|
||||
|
||||
stats := &Stat{
|
||||
Uptime: nextSession.Uptime,
|
||||
Hostname: nextSession.Hostname,
|
||||
}
|
||||
if stats.Uptime == 0 {
|
||||
if err := getUptime(client, stats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nextSession.Uptime = stats.Uptime
|
||||
}
|
||||
|
||||
if stats.Hostname == "" {
|
||||
if err := getHostname(client, stats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := getLoad(client, stats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := getMem(client, stats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := getFileSystems(client, stats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := getInterfaces(client, stats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := getInterfaceInfo(client, stats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := getCPU(client, stats); err != nil {
|
||||
return nil, err
|
||||
nextSession.Hostname = stats.Hostname
|
||||
}
|
||||
|
||||
runner := taskrunner.Runner{}
|
||||
|
||||
runner.Add(func() error {
|
||||
return getLoad(client, stats)
|
||||
})
|
||||
runner.Add(func() error {
|
||||
return getMem(client, stats)
|
||||
})
|
||||
runner.Add(func() error {
|
||||
return getFileSystems(client, stats)
|
||||
})
|
||||
runner.Add(func() error {
|
||||
return getInterfaces(client, stats)
|
||||
})
|
||||
runner.Add(func() error {
|
||||
return getInterfaceInfo(client, stats)
|
||||
})
|
||||
runner.Add(func() error {
|
||||
return getCPU(client, stats)
|
||||
})
|
||||
runner.Wait()
|
||||
cost := time.Since(start)
|
||||
fmt.Printf("%s: %v\n", "GetAllStats", cost)
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func getHostname(client *ssh.Client, stat *Stat) (err error) {
|
||||
//defer utils.TimeWatcher("getHostname")
|
||||
defer utils.TimeWatcher("getHostname")
|
||||
hostname, err := utils.RunCommand(client, "/bin/hostname -f")
|
||||
if err != nil {
|
||||
return
|
||||
@ -113,7 +131,7 @@ func getHostname(client *ssh.Client, stat *Stat) (err error) {
|
||||
}
|
||||
|
||||
func getUptime(client *ssh.Client, stat *Stat) (err error) {
|
||||
//defer utils.TimeWatcher("getUptime")
|
||||
defer utils.TimeWatcher("getUptime")
|
||||
uptime, err := utils.RunCommand(client, "/bin/cat /proc/uptime")
|
||||
if err != nil {
|
||||
return
|
||||
@ -132,7 +150,7 @@ func getUptime(client *ssh.Client, stat *Stat) (err error) {
|
||||
}
|
||||
|
||||
func getLoad(client *ssh.Client, stat *Stat) (err error) {
|
||||
//defer utils.TimeWatcher("getLoad")
|
||||
defer utils.TimeWatcher("getLoad")
|
||||
line, err := utils.RunCommand(client, "/bin/cat /proc/loadavg")
|
||||
if err != nil {
|
||||
return
|
||||
@ -154,7 +172,7 @@ func getLoad(client *ssh.Client, stat *Stat) (err error) {
|
||||
}
|
||||
|
||||
func getMem(client *ssh.Client, stat *Stat) (err error) {
|
||||
//defer utils.TimeWatcher("getMem")
|
||||
defer utils.TimeWatcher("getMem")
|
||||
lines, err := utils.RunCommand(client, "/bin/cat /proc/meminfo")
|
||||
if err != nil {
|
||||
return
|
||||
@ -192,7 +210,7 @@ func getMem(client *ssh.Client, stat *Stat) (err error) {
|
||||
}
|
||||
|
||||
func getFileSystems(client *ssh.Client, stat *Stat) (err error) {
|
||||
//defer utils.TimeWatcher("getFileSystems")
|
||||
defer utils.TimeWatcher("getFileSystems")
|
||||
lines, err := utils.RunCommand(client, "/bin/df -B1")
|
||||
if err != nil {
|
||||
return
|
||||
@ -228,7 +246,7 @@ func getFileSystems(client *ssh.Client, stat *Stat) (err error) {
|
||||
}
|
||||
|
||||
func getInterfaces(client *ssh.Client, stats *Stat) (err error) {
|
||||
//defer utils.TimeWatcher("getInterfaces")
|
||||
defer utils.TimeWatcher("getInterfaces")
|
||||
var lines string
|
||||
lines, err = utils.RunCommand(client, "/bin/ip -o addr")
|
||||
if err != nil {
|
||||
@ -273,16 +291,16 @@ func getInterfaces(client *ssh.Client, stats *Stat) (err error) {
|
||||
}
|
||||
|
||||
func getInterfaceInfo(client *ssh.Client, stats *Stat) (err error) {
|
||||
//defer utils.TimeWatcher("getInterfaceInfo")
|
||||
lines, err := utils.RunCommand(client, "/bin/cat /proc/net/dev")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer utils.TimeWatcher("getInterfaceInfo")
|
||||
|
||||
if stats.Network == nil {
|
||||
return
|
||||
} // should have been here already
|
||||
|
||||
lines, err := utils.RunCommand(client, "/bin/cat /proc/net/dev")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
scanner := bufio.NewScanner(strings.NewReader(lines))
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
@ -345,7 +363,7 @@ func parseCPUFields(fields []string, stat *cpuRaw) {
|
||||
var preCPU cpuRaw
|
||||
|
||||
func getCPU(client *ssh.Client, stats *Stat) (err error) {
|
||||
//defer utils.TimeWatcher("getCPU")
|
||||
defer utils.TimeWatcher("getCPU")
|
||||
lines, err := utils.RunCommand(client, "/bin/cat /proc/stat")
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -3,12 +3,14 @@ package api
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/common/nt"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"next-terminal/server/constant"
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/service"
|
||||
@ -44,7 +46,7 @@ func (api StorageApi) StoragePagingEndpoint(c echo.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
@ -59,7 +61,7 @@ func (api StorageApi) StorageCreateEndpoint(c echo.Context) error {
|
||||
account, _ := GetCurrentAccount(c)
|
||||
|
||||
item.ID = utils.UUID()
|
||||
item.Created = utils.NowJsonTime()
|
||||
item.Created = common.NowJsonTime()
|
||||
item.Owner = account.ID
|
||||
// 创建对应的目录文件夹
|
||||
drivePath := service.StorageService.GetBaseDrivePath()
|
||||
@ -147,7 +149,7 @@ func (api StorageApi) PermissionCheck(c echo.Context, id string) error {
|
||||
return err
|
||||
}
|
||||
account, _ := GetCurrentAccount(c)
|
||||
if account.Type != constant.TypeAdmin {
|
||||
if account.Type != nt.TypeAdmin {
|
||||
if storage.Owner != account.ID {
|
||||
return errors.New("您没有权限访问此地址 :(")
|
||||
}
|
||||
|
48
server/api/storage_log.go
Normal file
48
server/api/storage_log.go
Normal file
@ -0,0 +1,48 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/labstack/echo/v4"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/repository"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type StorageLogApi struct {
|
||||
}
|
||||
|
||||
func (api StorageLogApi) PagingEndpoint(c echo.Context) error {
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
assetId := c.QueryParam("assetId")
|
||||
userId := c.QueryParam("userId")
|
||||
action := c.QueryParam("action")
|
||||
|
||||
order := c.QueryParam("order")
|
||||
field := c.QueryParam("field")
|
||||
|
||||
items, total, err := repository.StorageLogRepository.Find(context.TODO(), pageIndex, pageSize, assetId, userId, action, order, field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
|
||||
func (api StorageLogApi) DeleteEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
if err := repository.StorageLogRepository.DeleteById(context.Background(), id); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, nil)
|
||||
}
|
||||
|
||||
func (api StorageLogApi) ClearEndpoint(c echo.Context) error {
|
||||
if err := repository.StorageLogRepository.DeleteAll(context.Background()); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, nil)
|
||||
}
|
@ -2,7 +2,8 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/maps"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -36,7 +37,7 @@ func (api StrategyApi) StrategyPagingEndpoint(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
@ -48,7 +49,7 @@ func (api StrategyApi) StrategyCreateEndpoint(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
item.ID = utils.UUID()
|
||||
item.Created = utils.NowJsonTime()
|
||||
item.Created = common.NowJsonTime()
|
||||
|
||||
if err := repository.StrategyRepository.Create(context.TODO(), &item); err != nil {
|
||||
return err
|
||||
@ -80,3 +81,12 @@ func (api StrategyApi) StrategyUpdateEndpoint(c echo.Context) error {
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (api StrategyApi) GetEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
strategy, err := repository.StrategyRepository.FindById(context.Background(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, strategy)
|
||||
}
|
||||
|
83
server/api/tenant.go
Normal file
83
server/api/tenant.go
Normal file
@ -0,0 +1,83 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/maps"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/utils"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type TenantApi struct{}
|
||||
|
||||
func (api TenantApi) AllEndpoint(c echo.Context) error {
|
||||
items, err := repository.TenantRepository.FindAll(context.TODO())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, items)
|
||||
}
|
||||
|
||||
func (api TenantApi) PagingEndpoint(c echo.Context) error {
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
name := c.QueryParam("name")
|
||||
|
||||
order := c.QueryParam("order")
|
||||
field := c.QueryParam("field")
|
||||
|
||||
items, total, err := repository.TenantRepository.Find(context.TODO(), pageIndex, pageSize, name, order, field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
|
||||
func (api TenantApi) CreateEndpoint(c echo.Context) error {
|
||||
var item model.Tenant
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
item.ID = utils.UUID()
|
||||
item.Created = common.NowJsonTime()
|
||||
|
||||
if err := repository.TenantRepository.Create(context.TODO(), &item); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (api TenantApi) DeleteEndpoint(c echo.Context) error {
|
||||
ids := c.Param("id")
|
||||
split := strings.Split(ids, ",")
|
||||
for i := range split {
|
||||
id := split[i]
|
||||
if err := repository.TenantRepository.DeleteById(context.TODO(), id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return Success(c, nil)
|
||||
}
|
||||
|
||||
func (api TenantApi) UpdateEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
var item model.Tenant
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := repository.TenantRepository.UpdateById(context.TODO(), &item, id); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
@ -4,20 +4,18 @@ import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"next-terminal/server/common/nt"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"next-terminal/server/common/guacamole"
|
||||
"next-terminal/server/common/term"
|
||||
"next-terminal/server/config"
|
||||
"next-terminal/server/constant"
|
||||
"next-terminal/server/dto"
|
||||
"next-terminal/server/global/session"
|
||||
"next-terminal/server/guacd"
|
||||
"next-terminal/server/log"
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/service"
|
||||
"next-terminal/server/term"
|
||||
"next-terminal/server/utils"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
@ -38,7 +36,6 @@ type WebTerminalApi struct {
|
||||
func (api WebTerminalApi) SshEndpoint(c echo.Context) error {
|
||||
ws, err := UpGrader.Upgrade(c.Response().Writer, c.Request(), nil)
|
||||
if err != nil {
|
||||
log.Errorf("升级为WebSocket协议失败:%v", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
@ -86,7 +83,7 @@ func (api WebTerminalApi) SshEndpoint(c echo.Context) error {
|
||||
|
||||
recording := ""
|
||||
var isRecording = false
|
||||
property, err := repository.PropertyRepository.FindByName(ctx, guacd.EnableRecording)
|
||||
property, err := repository.PropertyRepository.FindByName(ctx, guacamole.EnableRecording)
|
||||
if err == nil && property.Value == "true" {
|
||||
isRecording = true
|
||||
}
|
||||
@ -102,8 +99,8 @@ func (api WebTerminalApi) SshEndpoint(c echo.Context) error {
|
||||
|
||||
var xterm = "xterm-256color"
|
||||
var nextTerminal *term.NextTerminal
|
||||
if "true" == attributes[constant.SocksProxyEnable] {
|
||||
nextTerminal, err = term.NewNextTerminalUseSocks(ip, port, username, password, privateKey, passphrase, rows, cols, recording, xterm, true, attributes[constant.SocksProxyHost], attributes[constant.SocksProxyPort], attributes[constant.SocksProxyUsername], attributes[constant.SocksProxyPassword])
|
||||
if "true" == attributes[nt.SocksProxyEnable] {
|
||||
nextTerminal, err = term.NewNextTerminalUseSocks(ip, port, username, password, privateKey, passphrase, rows, cols, recording, xterm, true, attributes[nt.SocksProxyHost], attributes[nt.SocksProxyPort], attributes[nt.SocksProxyUsername], attributes[nt.SocksProxyPassword])
|
||||
} else {
|
||||
nextTerminal, err = term.NewNextTerminal(ip, port, username, password, privateKey, passphrase, rows, cols, recording, xterm, true)
|
||||
}
|
||||
@ -120,20 +117,19 @@ func (api WebTerminalApi) SshEndpoint(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
sess := model.Session{
|
||||
sessionForUpdate := model.Session{
|
||||
ConnectionId: sessionId,
|
||||
Width: cols,
|
||||
Height: rows,
|
||||
Status: constant.Connecting,
|
||||
Status: nt.Connecting,
|
||||
Recording: recording,
|
||||
}
|
||||
if sess.Recording == "" {
|
||||
if sessionForUpdate.Recording == "" {
|
||||
// 未录屏时无需审计
|
||||
sess.Reviewed = true
|
||||
sessionForUpdate.Reviewed = true
|
||||
}
|
||||
// 创建新会话
|
||||
log.Debugf("创建新会话 %v", sess.ConnectionId)
|
||||
if err := repository.SessionRepository.UpdateById(ctx, &sess, sessionId); err != nil {
|
||||
if err := repository.SessionRepository.UpdateById(ctx, &sessionForUpdate, sessionId); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -152,7 +148,7 @@ func (api WebTerminalApi) SshEndpoint(c echo.Context) error {
|
||||
}
|
||||
session.GlobalSessionManager.Add(nextSession)
|
||||
|
||||
termHandler := NewTermHandler(sessionId, isRecording, ws, nextTerminal)
|
||||
termHandler := NewTermHandler(s.Creator, s.AssetId, sessionId, isRecording, ws, nextTerminal)
|
||||
termHandler.Start()
|
||||
defer termHandler.Stop()
|
||||
|
||||
@ -160,14 +156,12 @@ func (api WebTerminalApi) SshEndpoint(c echo.Context) error {
|
||||
_, message, err := ws.ReadMessage()
|
||||
if err != nil {
|
||||
// web socket会话关闭后主动关闭ssh会话
|
||||
log.Debugf("WebSocket已关闭")
|
||||
service.SessionService.CloseSessionById(sessionId, Normal, "用户正常退出")
|
||||
break
|
||||
}
|
||||
|
||||
msg, err := dto.ParseMessage(string(message))
|
||||
if err != nil {
|
||||
log.Warnf("消息解码失败: %v, 原始字符串:%v", err, string(message))
|
||||
continue
|
||||
}
|
||||
|
||||
@ -175,31 +169,28 @@ func (api WebTerminalApi) SshEndpoint(c echo.Context) error {
|
||||
case Resize:
|
||||
decodeString, err := base64.StdEncoding.DecodeString(msg.Content)
|
||||
if err != nil {
|
||||
log.Warnf("Base64解码失败: %v,原始字符串:%v", err, msg.Content)
|
||||
continue
|
||||
}
|
||||
var winSize dto.WindowSize
|
||||
err = json.Unmarshal(decodeString, &winSize)
|
||||
if err != nil {
|
||||
log.Warnf("解析SSH会话窗口大小失败: %v,原始字符串:%v", err, msg.Content)
|
||||
continue
|
||||
}
|
||||
if err := nextTerminal.WindowChange(winSize.Rows, winSize.Cols); err != nil {
|
||||
log.Warnf("更改SSH会话窗口大小失败: %v", err)
|
||||
if err := termHandler.WindowChange(winSize.Rows, winSize.Cols); err != nil {
|
||||
}
|
||||
_ = repository.SessionRepository.UpdateWindowSizeById(ctx, winSize.Rows, winSize.Cols, sessionId)
|
||||
case Data:
|
||||
input := []byte(msg.Content)
|
||||
_, err := nextTerminal.Write(input)
|
||||
err := termHandler.Write(input)
|
||||
if err != nil {
|
||||
service.SessionService.CloseSessionById(sessionId, TunnelClosed, "远程连接已关闭")
|
||||
}
|
||||
case Ping:
|
||||
_, _, err := nextTerminal.SshClient.Conn.SendRequest("helloworld1024@foxmail.com", true, nil)
|
||||
err := termHandler.SendRequest()
|
||||
if err != nil {
|
||||
service.SessionService.CloseSessionById(sessionId, TunnelClosed, "远程连接已关闭")
|
||||
} else {
|
||||
_ = termHandler.WriteMessage(dto.NewMessage(Ping, ""))
|
||||
_ = termHandler.SendMessageToWebSocket(dto.NewMessage(Ping, ""))
|
||||
}
|
||||
|
||||
}
|
||||
@ -210,7 +201,6 @@ func (api WebTerminalApi) SshEndpoint(c echo.Context) error {
|
||||
func (api WebTerminalApi) SshMonitorEndpoint(c echo.Context) error {
|
||||
ws, err := UpGrader.Upgrade(c.Response().Writer, c.Request(), nil)
|
||||
if err != nil {
|
||||
log.Errorf("升级为WebSocket协议失败:%v", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
@ -238,12 +228,10 @@ func (api WebTerminalApi) SshMonitorEndpoint(c echo.Context) error {
|
||||
WebSocket: ws,
|
||||
}
|
||||
nextSession.Observer.Add(obSession)
|
||||
log.Debugf("会话 %v 观察者 %v 进入", sessionId, obId)
|
||||
|
||||
for {
|
||||
_, _, err := ws.ReadMessage()
|
||||
if err != nil {
|
||||
log.Debugf("会话 %v 观察者 %v 退出", sessionId, obId)
|
||||
nextSession.Observer.Del(obId)
|
||||
break
|
||||
}
|
||||
@ -253,16 +241,16 @@ func (api WebTerminalApi) SshMonitorEndpoint(c echo.Context) error {
|
||||
|
||||
func (api WebTerminalApi) permissionCheck(c echo.Context, assetId string) error {
|
||||
user, _ := GetCurrentAccount(c)
|
||||
if constant.TypeUser == user.Type {
|
||||
// 检测是否有访问权限
|
||||
assetIds, err := repository.ResourceSharerRepository.FindAssetIdsByUserId(context.TODO(), user.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !utils.Contains(assetIds, assetId) {
|
||||
return errors.New("您没有权限访问此资产")
|
||||
}
|
||||
if nt.TypeUser == user.Type {
|
||||
// 检测是否有访问权限 TODO
|
||||
//assetIds, err := repository.ResourceSharerRepository.FindAssetIdsByUserId(context.TODO(), user.ID)
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//
|
||||
//if !utils.Contains(assetIds, assetId) {
|
||||
// return errors.New("您没有权限访问此资产")
|
||||
//}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"next-terminal/server/common/term"
|
||||
"next-terminal/server/dto"
|
||||
"next-terminal/server/global/session"
|
||||
"next-terminal/server/term"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type TermHandler struct {
|
||||
@ -23,11 +23,13 @@ type TermHandler struct {
|
||||
dataChan chan rune
|
||||
tick *time.Ticker
|
||||
mutex sync.Mutex
|
||||
buf bytes.Buffer
|
||||
}
|
||||
|
||||
func NewTermHandler(sessionId string, isRecording bool, ws *websocket.Conn, nextTerminal *term.NextTerminal) *TermHandler {
|
||||
func NewTermHandler(userId, assetId, sessionId string, isRecording bool, ws *websocket.Conn, nextTerminal *term.NextTerminal) *TermHandler {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
tick := time.NewTicker(time.Millisecond * time.Duration(60))
|
||||
|
||||
return &TermHandler{
|
||||
sessionId: sessionId,
|
||||
isRecording: isRecording,
|
||||
@ -46,6 +48,7 @@ func (r *TermHandler) Start() {
|
||||
}
|
||||
|
||||
func (r *TermHandler) Stop() {
|
||||
// 会话结束时记录最后一个命令
|
||||
r.tick.Stop()
|
||||
r.cancel()
|
||||
}
|
||||
@ -68,15 +71,13 @@ func (r *TermHandler) readFormTunnel() {
|
||||
}
|
||||
|
||||
func (r *TermHandler) writeToWebsocket() {
|
||||
var buf []byte
|
||||
for {
|
||||
select {
|
||||
case <-r.ctx.Done():
|
||||
return
|
||||
case <-r.tick.C:
|
||||
if len(buf) > 0 {
|
||||
s := string(buf)
|
||||
if err := r.WriteMessage(dto.NewMessage(Data, s)); err != nil {
|
||||
s := r.buf.String()
|
||||
if err := r.SendMessageToWebSocket(dto.NewMessage(Data, s)); err != nil {
|
||||
return
|
||||
}
|
||||
// 录屏
|
||||
@ -85,21 +86,35 @@ func (r *TermHandler) writeToWebsocket() {
|
||||
}
|
||||
// 监控
|
||||
SendObData(r.sessionId, s)
|
||||
buf = []byte{}
|
||||
}
|
||||
r.buf.Reset()
|
||||
case data := <-r.dataChan:
|
||||
if data != utf8.RuneError {
|
||||
p := make([]byte, utf8.RuneLen(data))
|
||||
utf8.EncodeRune(p, data)
|
||||
buf = append(buf, p...)
|
||||
r.buf.Write(p)
|
||||
} else {
|
||||
buf = append(buf, []byte("@")...)
|
||||
r.buf.Write([]byte("@"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *TermHandler) WriteMessage(msg dto.Message) error {
|
||||
func (r *TermHandler) Write(input []byte) error {
|
||||
// 正常的字符输入
|
||||
_, err := r.nextTerminal.Write(input)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *TermHandler) WindowChange(h int, w int) error {
|
||||
return r.nextTerminal.WindowChange(h, w)
|
||||
}
|
||||
|
||||
func (r *TermHandler) SendRequest() error {
|
||||
_, _, err := r.nextTerminal.SshClient.Conn.SendRequest("helloworld1024@foxmail.com", true, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *TermHandler) SendMessageToWebSocket(msg dto.Message) error {
|
||||
if r.webSocket == nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -1,66 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/manifoldco/promptui"
|
||||
)
|
||||
|
||||
type pepper struct {
|
||||
Name string
|
||||
HeatUnit int
|
||||
Peppers int
|
||||
}
|
||||
|
||||
func main() {
|
||||
peppers := []pepper{
|
||||
{Name: "Bell Pepper", HeatUnit: 0, Peppers: 0},
|
||||
{Name: "Banana Pepper", HeatUnit: 100, Peppers: 1},
|
||||
{Name: "Poblano", HeatUnit: 1000, Peppers: 2},
|
||||
{Name: "Jalapeño", HeatUnit: 3500, Peppers: 3},
|
||||
{Name: "Aleppo", HeatUnit: 10000, Peppers: 4},
|
||||
{Name: "Tabasco", HeatUnit: 30000, Peppers: 5},
|
||||
{Name: "Malagueta", HeatUnit: 50000, Peppers: 6},
|
||||
{Name: "Habanero", HeatUnit: 100000, Peppers: 7},
|
||||
{Name: "Red Savina Habanero", HeatUnit: 350000, Peppers: 8},
|
||||
{Name: "Dragon’s Breath", HeatUnit: 855000, Peppers: 9},
|
||||
}
|
||||
|
||||
templates := &promptui.SelectTemplates{
|
||||
Label: "{{ . }}?",
|
||||
Active: "\U0001F336 {{ .Name | cyan }} ({{ .HeatUnit | red }})",
|
||||
Inactive: " {{ .Name | cyan }} ({{ .HeatUnit | red }})",
|
||||
Selected: "\U0001F336 {{ .Name | red | cyan }}",
|
||||
Details: `
|
||||
--------- Pepper ----------
|
||||
{{ "Name:" | faint }} {{ .Name }}/
|
||||
{{ "Heat Unit:" | faint }} {{ .HeatUnit }}
|
||||
{{ "Peppers:" | faint }} {{ .Peppers }}`,
|
||||
}
|
||||
|
||||
searcher := func(input string, index int) bool {
|
||||
pepper := peppers[index]
|
||||
name := strings.Replace(strings.ToLower(pepper.Name), " ", "", -1)
|
||||
input = strings.Replace(strings.ToLower(input), " ", "", -1)
|
||||
|
||||
return strings.Contains(name, input)
|
||||
}
|
||||
|
||||
prompt := promptui.Select{
|
||||
Label: "Spicy Level",
|
||||
Items: peppers,
|
||||
Templates: templates,
|
||||
Size: 4,
|
||||
Searcher: searcher,
|
||||
}
|
||||
|
||||
i, _, err := prompt.Run()
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Prompt failed %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("You choose number %d: %s\n", i+1, peppers[i].Name)
|
||||
}
|
@ -2,21 +2,19 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"next-terminal/server/common/maps"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/service"
|
||||
"next-terminal/server/utils"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type UserApi struct{}
|
||||
|
||||
func (userApi UserApi) UserCreateEndpoint(c echo.Context) (err error) {
|
||||
func (userApi UserApi) CreateEndpoint(c echo.Context) (err error) {
|
||||
var item model.User
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
@ -29,7 +27,7 @@ func (userApi UserApi) UserCreateEndpoint(c echo.Context) (err error) {
|
||||
return Success(c, item)
|
||||
}
|
||||
|
||||
func (userApi UserApi) UserPagingEndpoint(c echo.Context) error {
|
||||
func (userApi UserApi) PagingEndpoint(c echo.Context) error {
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
username := c.QueryParam("username")
|
||||
@ -38,25 +36,26 @@ func (userApi UserApi) UserPagingEndpoint(c echo.Context) error {
|
||||
|
||||
order := c.QueryParam("order")
|
||||
field := c.QueryParam("field")
|
||||
online := c.QueryParam("online")
|
||||
|
||||
items, total, err := repository.UserRepository.Find(context.TODO(), pageIndex, pageSize, username, nickname, mail, order, field)
|
||||
items, total, err := repository.UserRepository.Find(context.TODO(), pageIndex, pageSize, username, nickname, mail, online, "", order, field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
|
||||
func (userApi UserApi) UserUpdateEndpoint(c echo.Context) error {
|
||||
func (userApi UserApi) UpdateEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
|
||||
account, _ := GetCurrentAccount(c)
|
||||
if account.ID == id {
|
||||
return Fail(c, -1, "cannot modify itself")
|
||||
}
|
||||
//account, _ := GetCurrentAccount(c)
|
||||
//if account.ID == id {
|
||||
// return Fail(c, -1, "cannot modify itself")
|
||||
//}
|
||||
|
||||
var item model.User
|
||||
if err := c.Bind(&item); err != nil {
|
||||
@ -70,7 +69,7 @@ func (userApi UserApi) UserUpdateEndpoint(c echo.Context) error {
|
||||
return Success(c, nil)
|
||||
}
|
||||
|
||||
func (userApi UserApi) UserUpdateStatusEndpoint(c echo.Context) error {
|
||||
func (userApi UserApi) UpdateStatusEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
status := c.QueryParam("status")
|
||||
account, _ := GetCurrentAccount(c)
|
||||
@ -85,7 +84,7 @@ func (userApi UserApi) UserUpdateStatusEndpoint(c echo.Context) error {
|
||||
return Success(c, nil)
|
||||
}
|
||||
|
||||
func (userApi UserApi) UserDeleteEndpoint(c echo.Context) error {
|
||||
func (userApi UserApi) DeleteEndpoint(c echo.Context) error {
|
||||
ids := c.Param("id")
|
||||
account, found := GetCurrentAccount(c)
|
||||
if !found {
|
||||
@ -105,10 +104,10 @@ func (userApi UserApi) UserDeleteEndpoint(c echo.Context) error {
|
||||
return Success(c, nil)
|
||||
}
|
||||
|
||||
func (userApi UserApi) UserGetEndpoint(c echo.Context) error {
|
||||
func (userApi UserApi) GetEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
|
||||
item, err := repository.UserRepository.FindById(context.TODO(), id)
|
||||
item, err := service.UserService.FindById(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -116,49 +115,41 @@ func (userApi UserApi) UserGetEndpoint(c echo.Context) error {
|
||||
return Success(c, item)
|
||||
}
|
||||
|
||||
func (userApi UserApi) UserChangePasswordEndpoint(c echo.Context) error {
|
||||
func (userApi UserApi) ChangePasswordEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
password := c.FormValue("password")
|
||||
if password == "" {
|
||||
return Fail(c, -1, "请输入密码")
|
||||
}
|
||||
|
||||
user, err := repository.UserRepository.FindById(context.TODO(), id)
|
||||
if err != nil {
|
||||
ids := strings.Split(id, ",")
|
||||
if err := service.UserService.ChangePassword(ids, password); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
passwd, err := utils.Encoder.Encode([]byte(password))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u := &model.User{
|
||||
Password: string(passwd),
|
||||
ID: id,
|
||||
}
|
||||
if err := repository.UserRepository.Update(context.TODO(), u); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if user.Mail != "" {
|
||||
subject := "密码修改通知"
|
||||
text := fmt.Sprintf(`您好,%s。
|
||||
管理员已将你的密码修改为:%s。
|
||||
`, user.Username, password)
|
||||
go service.MailService.SendMail(user.Mail, subject, text)
|
||||
}
|
||||
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (userApi UserApi) UserResetTotpEndpoint(c echo.Context) error {
|
||||
func (userApi UserApi) ResetTotpEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
u := &model.User{
|
||||
TOTPSecret: "-",
|
||||
ID: id,
|
||||
}
|
||||
if err := repository.UserRepository.Update(context.TODO(), u); err != nil {
|
||||
ids := strings.Split(id, ",")
|
||||
if err := service.UserService.ResetTotp(ids); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (userApi UserApi) AllEndpoint(c echo.Context) error {
|
||||
users, err := repository.UserRepository.FindAll(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
items := make([]maps.Map, len(users))
|
||||
for i, user := range users {
|
||||
items[i] = maps.Map{
|
||||
"id": user.ID,
|
||||
"nickname": user.Nickname,
|
||||
}
|
||||
}
|
||||
return Success(c, items)
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/model"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -15,7 +17,7 @@ import (
|
||||
type UserGroupApi struct{}
|
||||
|
||||
func (userGroupApi UserGroupApi) UserGroupCreateEndpoint(c echo.Context) error {
|
||||
var item dto.UserGroup
|
||||
var item model.UserGroup
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -40,7 +42,7 @@ func (userGroupApi UserGroupApi) UserGroupPagingEndpoint(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
@ -49,7 +51,7 @@ func (userGroupApi UserGroupApi) UserGroupPagingEndpoint(c echo.Context) error {
|
||||
func (userGroupApi UserGroupApi) UserGroupUpdateEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
|
||||
var item dto.UserGroup
|
||||
var item model.UserGroup
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -82,7 +84,7 @@ func (userGroupApi UserGroupApi) UserGroupGetEndpoint(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
members, err := repository.UserGroupMemberRepository.FindUserIdsByUserGroupId(context.TODO(), id)
|
||||
members, err := repository.UserGroupMemberRepository.FindByUserGroupId(context.TODO(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -90,8 +92,17 @@ func (userGroupApi UserGroupApi) UserGroupGetEndpoint(c echo.Context) error {
|
||||
userGroup := dto.UserGroup{
|
||||
Id: item.ID,
|
||||
Name: item.Name,
|
||||
Created: item.Created,
|
||||
Members: members,
|
||||
}
|
||||
|
||||
return Success(c, userGroup)
|
||||
}
|
||||
|
||||
func (userGroupApi UserGroupApi) UserGroupAllEndpoint(c echo.Context) error {
|
||||
userGroups, err := repository.UserGroupRepository.FindAll(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, userGroups)
|
||||
}
|
49
server/api/worker/asset.go
Normal file
49
server/api/worker/asset.go
Normal file
@ -0,0 +1,49 @@
|
||||
package worker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/labstack/echo/v4"
|
||||
"next-terminal/server/api/abi"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/service"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type WorkAssetApi struct {
|
||||
abi.Abi
|
||||
}
|
||||
|
||||
func (api WorkAssetApi) PagingEndpoint(c echo.Context) error {
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
name := c.QueryParam("name")
|
||||
protocol := c.QueryParam("protocol")
|
||||
tags := c.QueryParam("tags")
|
||||
|
||||
order := c.QueryParam("order")
|
||||
field := c.QueryParam("field")
|
||||
account, _ := api.GetCurrentAccount(c)
|
||||
|
||||
items, total, err := service.WorkerService.FindMyAssetPaging(pageIndex, pageSize, name, protocol, tags, account.ID, order, field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range items {
|
||||
items[i].IP = ""
|
||||
items[i].Port = 0
|
||||
}
|
||||
|
||||
return api.Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
|
||||
func (api WorkAssetApi) TagsEndpoint(c echo.Context) (err error) {
|
||||
account, _ := api.GetCurrentAccount(c)
|
||||
var items []string
|
||||
if items, err = service.WorkerService.FindMyAssetTags(context.TODO(), account.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
return api.Success(c, items)
|
||||
}
|
132
server/api/worker/command.go
Normal file
132
server/api/worker/command.go
Normal file
@ -0,0 +1,132 @@
|
||||
package worker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"next-terminal/server/common/nt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"next-terminal/server/api/abi"
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/utils"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type WorkCommandApi struct {
|
||||
abi.Abi
|
||||
}
|
||||
|
||||
func (api WorkCommandApi) CommandCreateEndpoint(c echo.Context) error {
|
||||
var item model.Command
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
account, _ := api.GetCurrentAccount(c)
|
||||
item.Owner = account.ID
|
||||
item.ID = utils.UUID()
|
||||
item.Created = common.NowJsonTime()
|
||||
|
||||
if err := repository.CommandRepository.Create(context.TODO(), &item); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return api.Success(c, item)
|
||||
}
|
||||
|
||||
func (api WorkCommandApi) CommandAllEndpoint(c echo.Context) error {
|
||||
account, _ := api.GetCurrentAccount(c)
|
||||
userId := account.ID
|
||||
items, err := repository.CommandRepository.FindByUserId(context.Background(), userId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return api.Success(c, items)
|
||||
}
|
||||
|
||||
func (api WorkCommandApi) CommandPagingEndpoint(c echo.Context) error {
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
name := c.QueryParam("name")
|
||||
content := c.QueryParam("content")
|
||||
order := c.QueryParam("order")
|
||||
field := c.QueryParam("field")
|
||||
|
||||
account, _ := api.GetCurrentAccount(c)
|
||||
userId := account.ID
|
||||
|
||||
items, total, err := repository.CommandRepository.WorkerFind(context.TODO(), pageIndex, pageSize, name, content, order, field, userId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return api.Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
|
||||
func (api WorkCommandApi) CommandUpdateEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
|
||||
if !api.checkPermission(c, id) {
|
||||
return nt.ErrPermissionDenied
|
||||
}
|
||||
|
||||
var item model.Command
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := repository.CommandRepository.UpdateById(context.TODO(), &item, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return api.Success(c, nil)
|
||||
}
|
||||
|
||||
func (api WorkCommandApi) CommandDeleteEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
split := strings.Split(id, ",")
|
||||
for i := range split {
|
||||
if !api.checkPermission(c, id) {
|
||||
return nt.ErrPermissionDenied
|
||||
}
|
||||
if err := repository.CommandRepository.DeleteById(context.TODO(), split[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return api.Success(c, nil)
|
||||
}
|
||||
|
||||
func (api WorkCommandApi) CommandGetEndpoint(c echo.Context) (err error) {
|
||||
id := c.Param("id")
|
||||
if !api.checkPermission(c, id) {
|
||||
return nt.ErrPermissionDenied
|
||||
}
|
||||
var item model.Command
|
||||
if item, err = repository.CommandRepository.FindById(context.TODO(), id); err != nil {
|
||||
return err
|
||||
}
|
||||
return api.Success(c, item)
|
||||
}
|
||||
|
||||
func (api WorkCommandApi) checkPermission(c echo.Context, commandId string) bool {
|
||||
command, err := repository.CommandRepository.FindById(context.Background(), commandId)
|
||||
if err != nil {
|
||||
if errors.Is(gorm.ErrRecordNotFound, err) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
account, _ := api.GetCurrentAccount(c)
|
||||
userId := account.ID
|
||||
|
||||
return command.Owner == userId
|
||||
}
|
@ -3,16 +3,12 @@ package app
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
|
||||
"next-terminal/server/log"
|
||||
|
||||
"next-terminal/server/cli"
|
||||
"next-terminal/server/branding"
|
||||
"next-terminal/server/config"
|
||||
"next-terminal/server/constant"
|
||||
"next-terminal/server/service"
|
||||
"next-terminal/server/sshd"
|
||||
"next-terminal/server/task"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
@ -66,7 +62,12 @@ func (app App) InitDBData() (err error) {
|
||||
if err := service.StorageService.InitStorages(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := service.MenuService.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := service.RoleService.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
// 修复数据
|
||||
if err := service.AssetService.FixSshMode(); err != nil {
|
||||
return err
|
||||
@ -93,7 +94,7 @@ func (app App) ReloadData() error {
|
||||
|
||||
func Run() error {
|
||||
|
||||
fmt.Printf(constant.AppBanner, constant.AppVersion)
|
||||
fmt.Printf(branding.Hi)
|
||||
|
||||
if err := app.InitDBData(); err != nil {
|
||||
panic(err)
|
||||
@ -108,13 +109,10 @@ func Run() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
log.Fatal(http.ListenAndServe("localhost:8099", nil))
|
||||
}()
|
||||
fmt.Printf("当前配置为: %v\n", string(jsonBytes))
|
||||
}
|
||||
|
||||
_cli := cli.NewCli()
|
||||
_cli := service.NewCli()
|
||||
|
||||
if config.GlobalCfg.ResetPassword != "" {
|
||||
return _cli.ResetPassword(config.GlobalCfg.ResetPassword)
|
||||
@ -127,6 +125,9 @@ func Run() error {
|
||||
return _cli.ChangeEncryptionKey(config.GlobalCfg.EncryptionKey, config.GlobalCfg.NewEncryptionKey)
|
||||
}
|
||||
|
||||
ticker := task.NewTicker()
|
||||
ticker.SetupTicker()
|
||||
|
||||
if config.GlobalCfg.Sshd.Enable {
|
||||
go sshd.Sshd.Serve()
|
||||
}
|
||||
|
@ -1,145 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"next-terminal/server/api"
|
||||
"next-terminal/server/constant"
|
||||
"next-terminal/server/dto"
|
||||
"next-terminal/server/global/cache"
|
||||
"next-terminal/server/global/security"
|
||||
"next-terminal/server/utils"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func ErrorHandler(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
|
||||
if err := next(c); err != nil {
|
||||
|
||||
if he, ok := err.(*echo.HTTPError); ok {
|
||||
message := fmt.Sprintf("%v", he.Message)
|
||||
return api.Fail(c, he.Code, message)
|
||||
}
|
||||
|
||||
return api.Fail(c, 0, err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func TcpWall(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
|
||||
return func(c echo.Context) error {
|
||||
securities := security.GlobalSecurityManager.Values()
|
||||
if len(securities) == 0 {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
ip := c.RealIP()
|
||||
|
||||
for _, s := range securities {
|
||||
if strings.Contains(s.IP, "/") {
|
||||
// CIDR
|
||||
_, ipNet, err := net.ParseCIDR(s.IP)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if !ipNet.Contains(net.ParseIP(ip)) {
|
||||
continue
|
||||
}
|
||||
} else if strings.Contains(s.IP, "-") {
|
||||
// 范围段
|
||||
split := strings.Split(s.IP, "-")
|
||||
if len(split) < 2 {
|
||||
continue
|
||||
}
|
||||
start := split[0]
|
||||
end := split[1]
|
||||
intReqIP := utils.IpToInt(ip)
|
||||
if intReqIP < utils.IpToInt(start) || intReqIP > utils.IpToInt(end) {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// IP
|
||||
if s.IP != ip {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if s.Rule == constant.AccessRuleAllow {
|
||||
return next(c)
|
||||
}
|
||||
if s.Rule == constant.AccessRuleReject {
|
||||
if c.Request().Header.Get("X-Requested-With") != "" || c.Request().Header.Get(constant.Token) != "" {
|
||||
return api.Fail(c, 0, "您的访问请求被拒绝 :(")
|
||||
} else {
|
||||
return c.HTML(666, "您的访问请求被拒绝 :(")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
|
||||
var anonymousUrls = []string{"/login", "/static", "/favicon.ico", "/logo.svg", "/asciinema"}
|
||||
|
||||
func Auth(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
|
||||
return func(c echo.Context) error {
|
||||
|
||||
uri := c.Request().RequestURI
|
||||
if uri == "/" || strings.HasPrefix(uri, "/#") {
|
||||
return next(c)
|
||||
}
|
||||
// 路由拦截 - 登录身份、资源权限判断等
|
||||
for i := range anonymousUrls {
|
||||
if strings.HasPrefix(uri, anonymousUrls[i]) {
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
|
||||
token := api.GetToken(c)
|
||||
if token == "" {
|
||||
return api.Fail(c, 401, "您的登录信息已失效,请重新登录后再试。")
|
||||
}
|
||||
|
||||
v, found := cache.TokenManager.Get(token)
|
||||
if !found {
|
||||
return api.Fail(c, 401, "您的登录信息已失效,请重新登录后再试。")
|
||||
}
|
||||
|
||||
authorization := v.(dto.Authorization)
|
||||
|
||||
if strings.EqualFold(constant.LoginToken, authorization.Type) {
|
||||
if authorization.Remember {
|
||||
// 记住登录有效期两周
|
||||
cache.TokenManager.Set(token, authorization, cache.RememberMeExpiration)
|
||||
} else {
|
||||
cache.TokenManager.Set(token, authorization, cache.NotRememberExpiration)
|
||||
}
|
||||
}
|
||||
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
|
||||
func Admin(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
|
||||
account, found := api.GetCurrentAccount(c)
|
||||
if !found {
|
||||
return api.Fail(c, 401, "您的登录信息已失效,请重新登录后再试。")
|
||||
}
|
||||
|
||||
if account.Type != constant.TypeAdmin {
|
||||
return api.Fail(c, 403, "permission denied")
|
||||
}
|
||||
|
||||
return next(c)
|
||||
}
|
||||
}
|
138
server/app/middleware/auth.go
Normal file
138
server/app/middleware/auth.go
Normal file
@ -0,0 +1,138 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"next-terminal/server/common/nt"
|
||||
"strings"
|
||||
|
||||
"next-terminal/server/api"
|
||||
"next-terminal/server/dto"
|
||||
"next-terminal/server/global/cache"
|
||||
"next-terminal/server/service"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/ucarion/urlpath"
|
||||
)
|
||||
|
||||
var anonymousUrls = []string{"/login", "/static", "/favicon.ico", "/logo.svg", "/branding"}
|
||||
|
||||
var allowUrls = []urlpath.Path{
|
||||
urlpath.New("/account/info"),
|
||||
urlpath.New("/share-sessions/:id"),
|
||||
urlpath.New("/sessions"),
|
||||
urlpath.New("/sessions/:id/tunnel"),
|
||||
urlpath.New("/sessions/:id/connect"),
|
||||
urlpath.New("/sessions/:id/resize"),
|
||||
urlpath.New("/sessions/:id/stats"),
|
||||
urlpath.New("/sessions/:id/ls"),
|
||||
urlpath.New("/sessions/:id/download"),
|
||||
urlpath.New("/sessions/:id/upload"),
|
||||
urlpath.New("/sessions/:id/edit"),
|
||||
urlpath.New("/sessions/:id/mkdir"),
|
||||
urlpath.New("/sessions/:id/rm"),
|
||||
urlpath.New("/sessions/:id/rename"),
|
||||
urlpath.New("/sessions/:id/ssh"),
|
||||
}
|
||||
|
||||
func Auth(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
|
||||
return func(c echo.Context) error {
|
||||
|
||||
uri := c.Request().RequestURI
|
||||
if uri == "/" || strings.HasPrefix(uri, "/#") {
|
||||
return next(c)
|
||||
}
|
||||
// 路由拦截 - 登录身份、资源权限判断等
|
||||
for i := range anonymousUrls {
|
||||
if strings.HasPrefix(uri, anonymousUrls[i]) {
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
|
||||
token := api.GetToken(c)
|
||||
if token == "" {
|
||||
return api.Fail(c, 401, "您的登录信息已失效,请重新登录后再试。")
|
||||
}
|
||||
|
||||
v, found := cache.TokenManager.Get(token)
|
||||
if !found {
|
||||
return api.Fail(c, 401, "您的登录信息已失效,请重新登录后再试。")
|
||||
}
|
||||
|
||||
authorization := v.(dto.Authorization)
|
||||
|
||||
if strings.EqualFold(nt.LoginToken, authorization.Type) {
|
||||
if authorization.Remember {
|
||||
// 记住登录有效期两周
|
||||
cache.TokenManager.Set(token, authorization, cache.RememberMeExpiration)
|
||||
} else {
|
||||
cache.TokenManager.Set(token, authorization, cache.NotRememberExpiration)
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasPrefix(uri, "/account") {
|
||||
return next(c)
|
||||
}
|
||||
if strings.HasPrefix(uri, "/worker") {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
// 放行接入相关接口
|
||||
uri = strings.Split(uri, "?")[0]
|
||||
for _, url := range allowUrls {
|
||||
_, ok := url.Match(uri)
|
||||
if ok {
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
|
||||
account, _ := api.GetCurrentAccount(c)
|
||||
|
||||
if service.UserService.IsSuperAdmin(account.ID) {
|
||||
return next(c)
|
||||
}
|
||||
var roles []string
|
||||
v, ok := cache.UserRolesManager.Get(account.ID)
|
||||
if ok {
|
||||
roles = v.([]string)
|
||||
if len(roles) == 0 {
|
||||
roles, _ = service.RoleService.GetRolesByUserId(account.ID)
|
||||
cache.UserRolesManager.SetDefault(account.ID, roles)
|
||||
}
|
||||
} else {
|
||||
roles, _ = service.RoleService.GetRolesByUserId(account.ID)
|
||||
cache.UserRolesManager.SetDefault(account.ID, roles)
|
||||
}
|
||||
|
||||
urlPath := c.Request().URL.Path
|
||||
|
||||
for _, role := range roles {
|
||||
menus := service.RoleService.GetMenuListByRole(role)
|
||||
for _, menu := range menus {
|
||||
permissions := service.MenuService.GetPermissionByMenu(menu)
|
||||
for _, perm := range permissions {
|
||||
_, ok := perm.Match(urlPath)
|
||||
if ok {
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return api.Fail(c, 403, "permission denied")
|
||||
}
|
||||
}
|
||||
|
||||
func Admin(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
|
||||
account, found := api.GetCurrentAccount(c)
|
||||
if !found {
|
||||
return api.Fail(c, 401, "您的登录信息已失效,请重新登录后再试。")
|
||||
}
|
||||
|
||||
if account.Type != nt.TypeAdmin {
|
||||
return api.Fail(c, 403, "permission denied.")
|
||||
}
|
||||
|
||||
return next(c)
|
||||
}
|
||||
}
|
28
server/app/middleware/error_handler.go
Normal file
28
server/app/middleware/error_handler.go
Normal file
@ -0,0 +1,28 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"next-terminal/server/log"
|
||||
|
||||
"next-terminal/server/api"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func ErrorHandler(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
|
||||
if err := next(c); err != nil {
|
||||
|
||||
fmt.Printf("%+v\n", err)
|
||||
log.Error("api error", log.NamedError("err", err))
|
||||
if he, ok := err.(*echo.HTTPError); ok {
|
||||
message := fmt.Sprintf("%v", he.Message)
|
||||
return api.Fail(c, he.Code, message)
|
||||
}
|
||||
|
||||
return api.Fail(c, -1, err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
72
server/app/middleware/tcpwall.go
Normal file
72
server/app/middleware/tcpwall.go
Normal file
@ -0,0 +1,72 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net"
|
||||
"next-terminal/server/common/nt"
|
||||
"strings"
|
||||
|
||||
"next-terminal/server/api"
|
||||
"next-terminal/server/global/security"
|
||||
"next-terminal/server/utils"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func TcpWall(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
|
||||
return func(c echo.Context) error {
|
||||
securities := security.GlobalSecurityManager.Values()
|
||||
if len(securities) == 0 {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
ip := c.RealIP()
|
||||
|
||||
var pass = true
|
||||
|
||||
for _, s := range securities {
|
||||
ipGroups := strings.Split(s.IP, ",")
|
||||
for _, ipGroup := range ipGroups {
|
||||
if strings.Contains(ipGroup, "/") {
|
||||
// CIDR
|
||||
_, ipNet, err := net.ParseCIDR(ipGroup)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if !ipNet.Contains(net.ParseIP(ip)) {
|
||||
continue
|
||||
}
|
||||
} else if strings.Contains(ipGroup, "-") {
|
||||
// 范围段
|
||||
split := strings.Split(ipGroup, "-")
|
||||
if len(split) < 2 {
|
||||
continue
|
||||
}
|
||||
start := split[0]
|
||||
end := split[1]
|
||||
intReqIP := utils.IpToInt(ip)
|
||||
if intReqIP < utils.IpToInt(start) || intReqIP > utils.IpToInt(end) {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// IP
|
||||
if ipGroup != ip {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
pass = s.Rule == nt.AccessRuleAllow
|
||||
}
|
||||
}
|
||||
|
||||
if !pass {
|
||||
if c.Request().Header.Get("X-Requested-With") != "" || c.Request().Header.Get(nt.Token) != "" {
|
||||
return api.Fail(c, -1, "您的访问请求被拒绝 :(")
|
||||
} else {
|
||||
return c.HTML(666, "您的访问请求被拒绝 :(")
|
||||
}
|
||||
}
|
||||
|
||||
return next(c)
|
||||
}
|
||||
}
|
@ -6,6 +6,8 @@ import (
|
||||
"os"
|
||||
|
||||
"next-terminal/server/api"
|
||||
"next-terminal/server/api/worker"
|
||||
mw "next-terminal/server/app/middleware"
|
||||
"next-terminal/server/config"
|
||||
"next-terminal/server/log"
|
||||
"next-terminal/server/resource"
|
||||
@ -48,7 +50,7 @@ func setupRoutes() *echo.Echo {
|
||||
fileServer := http.FileServer(http.FS(fsys))
|
||||
handler := WrapHandler(fileServer)
|
||||
e.GET("/", handler)
|
||||
e.GET("/asciinema.html", handler)
|
||||
e.GET("/branding", api.Branding)
|
||||
e.GET("/favicon.ico", handler)
|
||||
e.GET("/static/*", handler)
|
||||
|
||||
@ -58,9 +60,11 @@ func setupRoutes() *echo.Echo {
|
||||
AllowOrigins: []string{"*"},
|
||||
AllowMethods: []string{http.MethodGet, http.MethodHead, http.MethodPut, http.MethodPatch, http.MethodPost, http.MethodDelete},
|
||||
}))
|
||||
e.Use(ErrorHandler)
|
||||
e.Use(TcpWall)
|
||||
e.Use(Auth)
|
||||
e.Use(mw.ErrorHandler)
|
||||
e.Use(mw.TcpWall)
|
||||
e.Use(mw.Auth)
|
||||
//e.Use(RBAC)
|
||||
e.Use(middleware.Gzip())
|
||||
|
||||
accountApi := new(api.AccountApi)
|
||||
guacamoleApi := new(api.GuacamoleApi)
|
||||
@ -71,7 +75,6 @@ func setupRoutes() *echo.Echo {
|
||||
CommandApi := new(api.CommandApi)
|
||||
CredentialApi := new(api.CredentialApi)
|
||||
SessionApi := new(api.SessionApi)
|
||||
ResourceSharerApi := new(api.ResourceSharerApi)
|
||||
LoginLogApi := new(api.LoginLogApi)
|
||||
PropertyApi := new(api.PropertyApi)
|
||||
OverviewApi := new(api.OverviewApi)
|
||||
@ -81,14 +84,17 @@ func setupRoutes() *echo.Echo {
|
||||
StrategyApi := new(api.StrategyApi)
|
||||
AccessGatewayApi := new(api.AccessGatewayApi)
|
||||
BackupApi := new(api.BackupApi)
|
||||
TenantApi := new(api.TenantApi)
|
||||
RoleApi := new(api.RoleApi)
|
||||
LoginPolicyApi := new(api.LoginPolicyApi)
|
||||
StorageLogApi := new(api.StorageLogApi)
|
||||
AuthorisedApi := new(api.AuthorisedApi)
|
||||
|
||||
e.POST("/login", accountApi.LoginEndpoint)
|
||||
e.POST("/loginWithTotp", accountApi.LoginWithTotpEndpoint)
|
||||
|
||||
account := e.Group("/account")
|
||||
{
|
||||
account.GET("/info", accountApi.InfoEndpoint)
|
||||
account.GET("/assets", accountApi.AccountAssetEndpoint)
|
||||
account.GET("/storage", accountApi.AccountStorageEndpoint)
|
||||
account.POST("/logout", accountApi.LogoutEndpoint)
|
||||
account.POST("/change-password", accountApi.ChangePasswordEndpoint)
|
||||
@ -97,30 +103,54 @@ func setupRoutes() *echo.Echo {
|
||||
account.POST("/confirm-totp", accountApi.ConfirmTOTPEndpoint)
|
||||
account.GET("/access-token", accountApi.AccessTokenGetEndpoint)
|
||||
account.POST("/access-token", accountApi.AccessTokenGenEndpoint)
|
||||
account.DELETE("/access-token", accountApi.AccessTokenDelEndpoint)
|
||||
}
|
||||
|
||||
users := e.Group("/users", Admin)
|
||||
_worker := e.Group("/worker")
|
||||
{
|
||||
users.POST("", UserApi.UserCreateEndpoint)
|
||||
users.GET("/paging", UserApi.UserPagingEndpoint)
|
||||
users.PUT("/:id", UserApi.UserUpdateEndpoint)
|
||||
users.PATCH("/:id/status", UserApi.UserUpdateStatusEndpoint)
|
||||
users.DELETE("/:id", UserApi.UserDeleteEndpoint)
|
||||
users.GET("/:id", UserApi.UserGetEndpoint)
|
||||
users.POST("/:id/change-password", UserApi.UserChangePasswordEndpoint)
|
||||
users.POST("/:id/reset-totp", UserApi.UserResetTotpEndpoint)
|
||||
commands := _worker.Group("/commands")
|
||||
{
|
||||
workerCommandApi := new(worker.WorkCommandApi)
|
||||
commands.GET("", workerCommandApi.CommandAllEndpoint)
|
||||
commands.GET("/paging", workerCommandApi.CommandPagingEndpoint)
|
||||
commands.POST("", workerCommandApi.CommandCreateEndpoint)
|
||||
commands.PUT("/:id", workerCommandApi.CommandUpdateEndpoint)
|
||||
commands.DELETE("/:id", workerCommandApi.CommandDeleteEndpoint)
|
||||
commands.GET("/:id", workerCommandApi.CommandGetEndpoint)
|
||||
}
|
||||
|
||||
userGroups := e.Group("/user-groups", Admin)
|
||||
assets := _worker.Group("/assets")
|
||||
{
|
||||
workAssetApi := new(worker.WorkAssetApi)
|
||||
assets.GET("/paging", workAssetApi.PagingEndpoint)
|
||||
assets.GET("/tags", workAssetApi.TagsEndpoint)
|
||||
}
|
||||
}
|
||||
|
||||
users := e.Group("/users", mw.Admin)
|
||||
{
|
||||
users.GET("", UserApi.AllEndpoint)
|
||||
users.GET("/paging", UserApi.PagingEndpoint)
|
||||
users.POST("", UserApi.CreateEndpoint)
|
||||
users.PUT("/:id", UserApi.UpdateEndpoint)
|
||||
users.PATCH("/:id/status", UserApi.UpdateStatusEndpoint)
|
||||
users.DELETE("/:id", UserApi.DeleteEndpoint)
|
||||
users.GET("/:id", UserApi.GetEndpoint)
|
||||
users.POST("/:id/change-password", UserApi.ChangePasswordEndpoint)
|
||||
users.POST("/:id/reset-totp", UserApi.ResetTotpEndpoint)
|
||||
}
|
||||
|
||||
userGroups := e.Group("/user-groups", mw.Admin)
|
||||
{
|
||||
userGroups.POST("", UserGroupApi.UserGroupCreateEndpoint)
|
||||
userGroups.GET("", UserGroupApi.UserGroupAllEndpoint)
|
||||
userGroups.GET("/paging", UserGroupApi.UserGroupPagingEndpoint)
|
||||
userGroups.PUT("/:id", UserGroupApi.UserGroupUpdateEndpoint)
|
||||
userGroups.DELETE("/:id", UserGroupApi.UserGroupDeleteEndpoint)
|
||||
userGroups.GET("/:id", UserGroupApi.UserGroupGetEndpoint)
|
||||
}
|
||||
|
||||
assets := e.Group("/assets", Admin)
|
||||
assets := e.Group("/assets", mw.Admin)
|
||||
{
|
||||
assets.GET("", AssetApi.AssetAllEndpoint)
|
||||
assets.POST("", AssetApi.AssetCreateEndpoint)
|
||||
@ -135,7 +165,7 @@ func setupRoutes() *echo.Echo {
|
||||
|
||||
e.GET("/tags", AssetApi.AssetTagsEndpoint)
|
||||
|
||||
commands := e.Group("/commands")
|
||||
commands := e.Group("/commands", mw.Admin)
|
||||
{
|
||||
commands.GET("", CommandApi.CommandAllEndpoint)
|
||||
commands.GET("/paging", CommandApi.CommandPagingEndpoint)
|
||||
@ -143,12 +173,12 @@ func setupRoutes() *echo.Echo {
|
||||
commands.PUT("/:id", CommandApi.CommandUpdateEndpoint)
|
||||
commands.DELETE("/:id", CommandApi.CommandDeleteEndpoint)
|
||||
commands.GET("/:id", CommandApi.CommandGetEndpoint)
|
||||
commands.POST("/:id/change-owner", CommandApi.CommandChangeOwnerEndpoint, Admin)
|
||||
commands.POST("/:id/change-owner", CommandApi.CommandChangeOwnerEndpoint, mw.Admin)
|
||||
}
|
||||
|
||||
credentials := e.Group("/credentials", Admin)
|
||||
credentials := e.Group("/credentials", mw.Admin)
|
||||
{
|
||||
credentials.GET("", CredentialApi.CredentialAllEndpoint)
|
||||
//credentials.GET("", CredentialApi.CredentialAllEndpoint)
|
||||
credentials.GET("/paging", CredentialApi.CredentialPagingEndpoint)
|
||||
credentials.POST("", CredentialApi.CredentialCreateEndpoint)
|
||||
credentials.PUT("/:id", CredentialApi.CredentialUpdateEndpoint)
|
||||
@ -159,15 +189,15 @@ func setupRoutes() *echo.Echo {
|
||||
|
||||
sessions := e.Group("/sessions")
|
||||
{
|
||||
sessions.GET("/paging", Admin(SessionApi.SessionPagingEndpoint))
|
||||
sessions.POST("/:id/disconnect", Admin(SessionApi.SessionDisconnectEndpoint))
|
||||
sessions.DELETE("/:id", Admin(SessionApi.SessionDeleteEndpoint))
|
||||
sessions.GET("/:id/recording", Admin(SessionApi.SessionRecordingEndpoint))
|
||||
sessions.GET("/:id", Admin(SessionApi.SessionGetEndpoint))
|
||||
sessions.POST("/:id/reviewed", Admin(SessionApi.SessionReviewedEndpoint))
|
||||
sessions.POST("/:id/unreviewed", Admin(SessionApi.SessionUnViewedEndpoint))
|
||||
sessions.POST("/clear", Admin(SessionApi.SessionClearEndpoint))
|
||||
sessions.POST("/reviewed", Admin(SessionApi.SessionReviewedAllEndpoint))
|
||||
sessions.GET("/paging", mw.Admin(SessionApi.SessionPagingEndpoint))
|
||||
sessions.POST("/:id/disconnect", mw.Admin(SessionApi.SessionDisconnectEndpoint))
|
||||
sessions.DELETE("/:id", mw.Admin(SessionApi.SessionDeleteEndpoint))
|
||||
sessions.GET("/:id/recording", mw.Admin(SessionApi.SessionRecordingEndpoint))
|
||||
sessions.GET("/:id", mw.Admin(SessionApi.SessionGetEndpoint))
|
||||
sessions.POST("/:id/reviewed", mw.Admin(SessionApi.SessionReviewedEndpoint))
|
||||
sessions.POST("/:id/unreviewed", mw.Admin(SessionApi.SessionUnViewedEndpoint))
|
||||
sessions.POST("/clear", mw.Admin(SessionApi.SessionClearEndpoint))
|
||||
sessions.POST("/reviewed", mw.Admin(SessionApi.SessionReviewedAllEndpoint))
|
||||
|
||||
sessions.POST("", SessionApi.SessionCreateEndpoint)
|
||||
sessions.POST("/:id/connect", SessionApi.SessionConnectEndpoint)
|
||||
@ -187,34 +217,35 @@ func setupRoutes() *echo.Echo {
|
||||
sessions.POST("/:id/rename", SessionApi.SessionRenameEndpoint)
|
||||
}
|
||||
|
||||
resourceSharers := e.Group("/resource-sharers", Admin)
|
||||
{
|
||||
resourceSharers.GET("", ResourceSharerApi.RSGetSharersEndPoint)
|
||||
resourceSharers.POST("/remove-resources", ResourceSharerApi.ResourceRemoveByUserIdAssignEndPoint)
|
||||
resourceSharers.POST("/add-resources", ResourceSharerApi.ResourceAddByUserIdAssignEndPoint)
|
||||
}
|
||||
|
||||
loginLogs := e.Group("login-logs", Admin)
|
||||
loginLogs := e.Group("login-logs", mw.Admin)
|
||||
{
|
||||
loginLogs.GET("/paging", LoginLogApi.LoginLogPagingEndpoint)
|
||||
loginLogs.DELETE("/:id", LoginLogApi.LoginLogDeleteEndpoint)
|
||||
loginLogs.POST("/clear", LoginLogApi.LoginLogClearEndpoint)
|
||||
}
|
||||
|
||||
properties := e.Group("properties", Admin)
|
||||
storageLogs := e.Group("storage-logs", mw.Admin)
|
||||
{
|
||||
storageLogs.GET("/paging", StorageLogApi.PagingEndpoint)
|
||||
storageLogs.DELETE("/:id", StorageLogApi.DeleteEndpoint)
|
||||
storageLogs.POST("/clear", StorageLogApi.ClearEndpoint)
|
||||
}
|
||||
|
||||
properties := e.Group("properties", mw.Admin)
|
||||
{
|
||||
properties.GET("", PropertyApi.PropertyGetEndpoint)
|
||||
properties.PUT("", PropertyApi.PropertyUpdateEndpoint)
|
||||
}
|
||||
|
||||
overview := e.Group("overview", Admin)
|
||||
overview := e.Group("overview", mw.Admin)
|
||||
{
|
||||
overview.GET("/counter", OverviewApi.OverviewCounterEndPoint)
|
||||
overview.GET("/asset", OverviewApi.OverviewAssetEndPoint)
|
||||
overview.GET("/access", OverviewApi.OverviewAccessEndPoint)
|
||||
overview.GET("/date-counter", OverviewApi.OverviewDateCounterEndPoint)
|
||||
overview.GET("/ps", OverviewApi.OverviewPS)
|
||||
}
|
||||
|
||||
jobs := e.Group("/jobs", Admin)
|
||||
jobs := e.Group("/jobs", mw.Admin)
|
||||
{
|
||||
jobs.POST("", JobApi.JobCreateEndpoint)
|
||||
jobs.GET("/paging", JobApi.JobPagingEndpoint)
|
||||
@ -223,11 +254,12 @@ func setupRoutes() *echo.Echo {
|
||||
jobs.POST("/:id/exec", JobApi.JobExecEndpoint)
|
||||
jobs.DELETE("/:id", JobApi.JobDeleteEndpoint)
|
||||
jobs.GET("/:id", JobApi.JobGetEndpoint)
|
||||
jobs.GET("/:id/logs", JobApi.JobGetLogsEndpoint)
|
||||
|
||||
jobs.GET("/:id/logs/paging", JobApi.JobGetLogsEndpoint)
|
||||
jobs.DELETE("/:id/logs", JobApi.JobDeleteLogsEndpoint)
|
||||
}
|
||||
|
||||
securities := e.Group("/securities", Admin)
|
||||
securities := e.Group("/securities", mw.Admin)
|
||||
{
|
||||
securities.POST("", SecurityApi.SecurityCreateEndpoint)
|
||||
securities.GET("/paging", SecurityApi.SecurityPagingEndpoint)
|
||||
@ -238,12 +270,12 @@ func setupRoutes() *echo.Echo {
|
||||
|
||||
storages := e.Group("/storages")
|
||||
{
|
||||
storages.GET("/paging", StorageApi.StoragePagingEndpoint, Admin)
|
||||
storages.POST("", StorageApi.StorageCreateEndpoint, Admin)
|
||||
storages.DELETE("/:id", StorageApi.StorageDeleteEndpoint, Admin)
|
||||
storages.PUT("/:id", StorageApi.StorageUpdateEndpoint, Admin)
|
||||
storages.GET("/shares", StorageApi.StorageSharesEndpoint, Admin)
|
||||
storages.GET("/:id", StorageApi.StorageGetEndpoint, Admin)
|
||||
storages.GET("/paging", StorageApi.StoragePagingEndpoint, mw.Admin)
|
||||
storages.POST("", StorageApi.StorageCreateEndpoint, mw.Admin)
|
||||
storages.DELETE("/:id", StorageApi.StorageDeleteEndpoint, mw.Admin)
|
||||
storages.PUT("/:id", StorageApi.StorageUpdateEndpoint, mw.Admin)
|
||||
storages.GET("/shares", StorageApi.StorageSharesEndpoint, mw.Admin)
|
||||
storages.GET("/:id", StorageApi.StorageGetEndpoint, mw.Admin)
|
||||
|
||||
storages.POST("/:storageId/ls", StorageApi.StorageLsEndpoint)
|
||||
storages.GET("/:storageId/download", StorageApi.StorageDownloadEndpoint)
|
||||
@ -254,16 +286,17 @@ func setupRoutes() *echo.Echo {
|
||||
storages.POST("/:storageId/edit", StorageApi.StorageEditEndpoint)
|
||||
}
|
||||
|
||||
strategies := e.Group("/strategies", Admin)
|
||||
strategies := e.Group("/strategies", mw.Admin)
|
||||
{
|
||||
strategies.GET("", StrategyApi.StrategyAllEndpoint)
|
||||
strategies.GET("/paging", StrategyApi.StrategyPagingEndpoint)
|
||||
strategies.POST("", StrategyApi.StrategyCreateEndpoint)
|
||||
strategies.DELETE("/:id", StrategyApi.StrategyDeleteEndpoint)
|
||||
strategies.PUT("/:id", StrategyApi.StrategyUpdateEndpoint)
|
||||
strategies.GET("/:id", StrategyApi.GetEndpoint)
|
||||
}
|
||||
|
||||
accessGateways := e.Group("/access-gateways", Admin)
|
||||
accessGateways := e.Group("/access-gateways", mw.Admin)
|
||||
{
|
||||
accessGateways.GET("", AccessGatewayApi.AccessGatewayAllEndpoint)
|
||||
accessGateways.POST("", AccessGatewayApi.AccessGatewayCreateEndpoint)
|
||||
@ -273,11 +306,57 @@ func setupRoutes() *echo.Echo {
|
||||
accessGateways.GET("/:id", AccessGatewayApi.AccessGatewayGetEndpoint)
|
||||
}
|
||||
|
||||
backup := e.Group("/backup", Admin)
|
||||
backup := e.Group("/backup", mw.Admin)
|
||||
{
|
||||
backup.GET("/export", BackupApi.BackupExportEndpoint)
|
||||
backup.POST("/import", BackupApi.BackupImportEndpoint)
|
||||
}
|
||||
|
||||
tenants := e.Group("/tenants", mw.Admin)
|
||||
{
|
||||
tenants.GET("", TenantApi.AllEndpoint)
|
||||
tenants.GET("/paging", TenantApi.PagingEndpoint)
|
||||
tenants.POST("", TenantApi.CreateEndpoint)
|
||||
tenants.DELETE("/:id", TenantApi.DeleteEndpoint)
|
||||
tenants.PUT("/:id", TenantApi.UpdateEndpoint)
|
||||
}
|
||||
|
||||
roles := e.Group("/roles", mw.Admin)
|
||||
{
|
||||
roles.GET("", RoleApi.AllEndpoint)
|
||||
roles.GET("/paging", RoleApi.PagingEndpoint)
|
||||
roles.GET("/:id", RoleApi.GetEndpoint)
|
||||
roles.POST("", RoleApi.CreateEndpoint)
|
||||
roles.DELETE("/:id", RoleApi.DeleteEndpoint)
|
||||
roles.PUT("/:id", RoleApi.UpdateEndpoint)
|
||||
}
|
||||
|
||||
loginPolicies := e.Group("/login-policies", mw.Admin)
|
||||
{
|
||||
loginPolicies.GET("/paging", LoginPolicyApi.PagingEndpoint)
|
||||
loginPolicies.GET("/:id", LoginPolicyApi.GetEndpoint)
|
||||
loginPolicies.GET("/:id/users/paging", LoginPolicyApi.GetUserPageEndpoint)
|
||||
loginPolicies.GET("/:id/users/id", LoginPolicyApi.GetUserIdEndpoint)
|
||||
loginPolicies.POST("", LoginPolicyApi.CreateEndpoint)
|
||||
loginPolicies.DELETE("/:id", LoginPolicyApi.DeleteEndpoint)
|
||||
loginPolicies.PUT("/:id", LoginPolicyApi.UpdateEndpoint)
|
||||
loginPolicies.POST("/:id/bind", LoginPolicyApi.BindEndpoint)
|
||||
loginPolicies.POST("/:id/unbind", LoginPolicyApi.UnbindEndpoint)
|
||||
}
|
||||
|
||||
authorised := e.Group("/authorised", mw.Admin)
|
||||
{
|
||||
authorised.GET("/assets/paging", AuthorisedApi.PagingAsset)
|
||||
authorised.GET("/users/paging", AuthorisedApi.PagingUser)
|
||||
authorised.GET("/user-groups/paging", AuthorisedApi.PagingUserGroup)
|
||||
authorised.GET("/selected", AuthorisedApi.Selected)
|
||||
authorised.POST("/assets", AuthorisedApi.AuthorisedAssets)
|
||||
authorised.POST("/users", AuthorisedApi.AuthorisedUsers)
|
||||
authorised.POST("/user-groups", AuthorisedApi.AuthorisedUserGroups)
|
||||
authorised.DELETE("/:id", AuthorisedApi.Delete)
|
||||
}
|
||||
|
||||
e.GET("/menus", RoleApi.TreeMenus, mw.Admin)
|
||||
|
||||
return e
|
||||
}
|
||||
|
13
server/branding/branding.go
Normal file
13
server/branding/branding.go
Normal file
@ -0,0 +1,13 @@
|
||||
package branding
|
||||
|
||||
var Name = "Next Terminal"
|
||||
var Copyright = "Copyright © 2020-2022 dushixiang, All Rights Reserved."
|
||||
var Banner = ` ___ ___
|
||||
/\__\ /\ \
|
||||
/:| _|_ \:\ \
|
||||
/::|/\__\ /::\__\
|
||||
\/|::/ / /:/\/__/
|
||||
|:/ / \/__/
|
||||
\/__/ `
|
||||
var Version = `v1.3.0-beta1`
|
||||
var Hi = Banner + Version
|
@ -1,12 +1,16 @@
|
||||
package guacd
|
||||
package guacamole
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -288,3 +292,12 @@ func (opt *Tunnel) Close() error {
|
||||
opt.IsOpen = false
|
||||
return opt.conn.Close()
|
||||
}
|
||||
|
||||
func Disconnect(ws *websocket.Conn, code int, reason string) {
|
||||
// guacd 无法处理中文字符,所以进行了base64编码。
|
||||
encodeReason := base64.StdEncoding.EncodeToString([]byte(reason))
|
||||
err := NewInstruction("error", encodeReason, strconv.Itoa(code))
|
||||
_ = ws.WriteMessage(websocket.TextMessage, []byte(err.String()))
|
||||
disconnect := NewInstruction("disconnect")
|
||||
_ = ws.WriteMessage(websocket.TextMessage, []byte(disconnect.String()))
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package utils
|
||||
package common
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
3
server/common/maps/maps.go
Normal file
3
server/common/maps/maps.go
Normal file
@ -0,0 +1,3 @@
|
||||
package maps
|
||||
|
||||
type Map map[string]interface{}
|
@ -1,21 +1,7 @@
|
||||
package constant
|
||||
package nt
|
||||
|
||||
import (
|
||||
"next-terminal/server/guacd"
|
||||
)
|
||||
|
||||
const (
|
||||
AppVersion = "v1.2.7"
|
||||
AppName = "Next Terminal"
|
||||
AppBanner = `
|
||||
_______ __ ___________ .__ .__
|
||||
\ \ ____ ___ ____/ |_ \__ ___/__________ _____ |__| ____ _____ | |
|
||||
/ | \_/ __ \\ \/ /\ __\ | |_/ __ \_ __ \/ \| |/ \\__ \ | |
|
||||
/ | \ ___/ > < | | | |\ ___/| | \/ Y Y \ | | \/ __ \| |__
|
||||
\____|__ /\___ >__/\_ \ |__| |____| \___ >__| |__|_| /__|___| (____ /____/
|
||||
\/ \/ \/ \/ \/ \/ \/ %s
|
||||
|
||||
`
|
||||
"next-terminal/server/common/guacamole"
|
||||
)
|
||||
|
||||
const Token = "X-Auth-Token"
|
||||
@ -78,10 +64,16 @@ const (
|
||||
ShareSession = "share-session"
|
||||
|
||||
Anonymous = "anonymous"
|
||||
|
||||
StorageLogActionRm = "rm" // 删除
|
||||
StorageLogActionUpload = "upload" // 上传
|
||||
StorageLogActionDownload = "download" // 下载
|
||||
StorageLogActionMkdir = "mkdir" // 创建文件夹
|
||||
StorageLogActionRename = "rename" // 重命名
|
||||
)
|
||||
|
||||
var SSHParameterNames = []string{guacd.FontName, guacd.FontSize, guacd.ColorScheme, guacd.Backspace, guacd.TerminalType, SshMode, SocksProxyEnable, SocksProxyHost, SocksProxyPort, SocksProxyUsername, SocksProxyPassword}
|
||||
var RDPParameterNames = []string{guacd.Domain, guacd.RemoteApp, guacd.RemoteAppDir, guacd.RemoteAppArgs, guacd.EnableDrive, guacd.DrivePath, guacd.ColorDepth, guacd.ForceLossless, guacd.PreConnectionId, guacd.PreConnectionBlob}
|
||||
var VNCParameterNames = []string{guacd.ColorDepth, guacd.Cursor, guacd.SwapRedBlue, guacd.DestHost, guacd.DestPort}
|
||||
var TelnetParameterNames = []string{guacd.FontName, guacd.FontSize, guacd.ColorScheme, guacd.Backspace, guacd.TerminalType, guacd.UsernameRegex, guacd.PasswordRegex, guacd.LoginSuccessRegex, guacd.LoginFailureRegex}
|
||||
var KubernetesParameterNames = []string{guacd.FontName, guacd.FontSize, guacd.ColorScheme, guacd.Backspace, guacd.TerminalType, guacd.Namespace, guacd.Pod, guacd.Container, guacd.UesSSL, guacd.ClientCert, guacd.ClientKey, guacd.CaCert, guacd.IgnoreCert}
|
||||
var SSHParameterNames = []string{guacamole.FontName, guacamole.FontSize, guacamole.ColorScheme, guacamole.Backspace, guacamole.TerminalType, SshMode, SocksProxyEnable, SocksProxyHost, SocksProxyPort, SocksProxyUsername, SocksProxyPassword}
|
||||
var RDPParameterNames = []string{guacamole.Domain, guacamole.RemoteApp, guacamole.RemoteAppDir, guacamole.RemoteAppArgs, guacamole.EnableDrive, guacamole.DrivePath, guacamole.ColorDepth, guacamole.ForceLossless, guacamole.PreConnectionId, guacamole.PreConnectionBlob}
|
||||
var VNCParameterNames = []string{guacamole.ColorDepth, guacamole.Cursor, guacamole.SwapRedBlue, guacamole.DestHost, guacamole.DestPort}
|
||||
var TelnetParameterNames = []string{guacamole.FontName, guacamole.FontSize, guacamole.ColorScheme, guacamole.Backspace, guacamole.TerminalType, guacamole.UsernameRegex, guacamole.PasswordRegex, guacamole.LoginSuccessRegex, guacamole.LoginFailureRegex}
|
||||
var KubernetesParameterNames = []string{guacamole.FontName, guacamole.FontSize, guacamole.ColorScheme, guacamole.Backspace, guacamole.TerminalType, guacamole.Namespace, guacamole.Pod, guacamole.Container, guacamole.UesSSL, guacamole.ClientCert, guacamole.ClientKey, guacamole.CaCert, guacamole.IgnoreCert}
|
8
server/common/nt/errors.go
Normal file
8
server/common/nt/errors.go
Normal file
@ -0,0 +1,8 @@
|
||||
package nt
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrNameAlreadyUsed = errors.New("name already used")
|
||||
ErrPermissionDenied = errors.New("permission denied")
|
||||
)
|
34
server/common/sets/set.go
Normal file
34
server/common/sets/set.go
Normal file
@ -0,0 +1,34 @@
|
||||
package sets
|
||||
|
||||
func NewStringSet() *Set {
|
||||
return &Set{data: make(map[string]struct{})}
|
||||
}
|
||||
|
||||
type Set struct {
|
||||
data map[string]struct{}
|
||||
}
|
||||
|
||||
func (s *Set) Add(key ...string) {
|
||||
for _, k := range key {
|
||||
s.data[k] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Set) Remove(key ...string) {
|
||||
for _, k := range key {
|
||||
delete(s.data, k)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Set) Contains(key string) bool {
|
||||
_, ok := s.data[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (s *Set) ToArray() []string {
|
||||
var keys []string
|
||||
for key, _ := range s.data {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return keys
|
||||
}
|
1
server/common/slices/slices.go
Normal file
1
server/common/slices/slices.go
Normal file
@ -0,0 +1 @@
|
||||
package slices
|
30
server/common/taskrunner/task_runner.go
Normal file
30
server/common/taskrunner/task_runner.go
Normal file
@ -0,0 +1,30 @@
|
||||
package taskrunner
|
||||
|
||||
import "sync"
|
||||
|
||||
type Runner struct {
|
||||
wg sync.WaitGroup
|
||||
errors []error
|
||||
mux sync.Mutex
|
||||
}
|
||||
|
||||
func (r *Runner) Add(f func() error) {
|
||||
r.wg.Add(1)
|
||||
go func() {
|
||||
defer r.wg.Done()
|
||||
if err := f(); err != nil {
|
||||
r.addError(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (r *Runner) addError(err error) {
|
||||
r.mux.Lock()
|
||||
defer r.mux.Unlock()
|
||||
r.errors = append(r.errors, err)
|
||||
}
|
||||
|
||||
func (r *Runner) Wait() []error {
|
||||
r.wg.Wait()
|
||||
return r.errors
|
||||
}
|
@ -2,6 +2,7 @@ package term
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/pkg/sftp"
|
||||
@ -76,6 +77,9 @@ func newNT(sshClient *ssh.Client, pipe bool, recording string, term string, rows
|
||||
}
|
||||
|
||||
func (ret *NextTerminal) Write(p []byte) (int, error) {
|
||||
if ret.StdinPipe == nil {
|
||||
return 0, errors.New("pipe is not open")
|
||||
}
|
||||
return ret.StdinPipe.Write(p)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package totp
|
||||
package common
|
||||
|
||||
import (
|
||||
otp_t "github.com/pquerna/otp"
|
@ -108,9 +108,7 @@ func SetupConfig() (*Config, error) {
|
||||
if err := viper.BindPFlags(pflag.CommandLine); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_ = viper.ReadInConfig()
|
||||
|
||||
sshdKey, err := homedir.Expand(viper.GetString("sshd.key"))
|
||||
if err != nil {
|
||||
|
@ -1,7 +0,0 @@
|
||||
package constant
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrNameAlreadyUsed = errors.New("name already used")
|
||||
)
|
@ -7,6 +7,7 @@ type Authorization struct {
|
||||
Remember bool
|
||||
Type string // LoginToken: 登录令牌, AccessToken: 授权令牌, ShareSession: 会话分享, AccessSession: 只允许访问特定的会话
|
||||
User *model.User
|
||||
Roles []string
|
||||
}
|
||||
|
||||
type LoginAccount struct {
|
||||
|
52
server/dto/authorised.go
Normal file
52
server/dto/authorised.go
Normal file
@ -0,0 +1,52 @@
|
||||
package dto
|
||||
|
||||
import "next-terminal/server/common"
|
||||
|
||||
type AuthorisedAsset struct {
|
||||
AssetIds []string `json:"assetIds"`
|
||||
CommandFilterId string `json:"commandFilterId"`
|
||||
StrategyId string `json:"strategyId"`
|
||||
UserId string `json:"userId"`
|
||||
UserGroupId string `json:"userGroupId"`
|
||||
}
|
||||
|
||||
type AuthorisedUser struct {
|
||||
UserIds []string `json:"userIds"`
|
||||
CommandFilterId string `json:"commandFilterId"`
|
||||
StrategyId string `json:"strategyId"`
|
||||
AssetId string `json:"assetId"`
|
||||
}
|
||||
|
||||
type AuthorisedUserGroup struct {
|
||||
UserGroupIds []string `json:"UserGroupIds"`
|
||||
CommandFilterId string `json:"commandFilterId"`
|
||||
StrategyId string `json:"strategyId"`
|
||||
AssetId string `json:"assetId"`
|
||||
}
|
||||
|
||||
type AssetPageForAuthorised struct {
|
||||
Id string `json:"id"`
|
||||
AssetId string `json:"assetId"`
|
||||
AssetName string `json:"assetName"`
|
||||
StrategyId string `json:"strategyId"`
|
||||
StrategyName string `json:"strategyName"`
|
||||
Created common.JsonTime `json:"created"`
|
||||
}
|
||||
|
||||
type UserPageForAuthorised struct {
|
||||
Id string `json:"id"`
|
||||
UserId string `json:"userId"`
|
||||
UserName string `json:"userName"`
|
||||
StrategyId string `json:"strategyId"`
|
||||
StrategyName string `json:"strategyName"`
|
||||
Created common.JsonTime `json:"created"`
|
||||
}
|
||||
|
||||
type UserGroupPageForAuthorised struct {
|
||||
Id string `json:"id"`
|
||||
UserGroupId string `json:"userGroupId"`
|
||||
UserGroupName string `json:"userGroupName"`
|
||||
StrategyId string `json:"strategyId"`
|
||||
StrategyName string `json:"strategyName"`
|
||||
Created common.JsonTime `json:"created"`
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
package dto
|
||||
|
||||
type Counter struct {
|
||||
User int64 `json:"user"`
|
||||
Asset int64 `json:"asset"`
|
||||
Credential int64 `json:"credential"`
|
||||
OnlineSession int64 `json:"onlineSession"`
|
||||
TotalUser int64 `json:"totalUser"`
|
||||
OnlineUser int64 `json:"onlineUser"`
|
||||
TotalAsset int64 `json:"totalAsset"`
|
||||
ActiveAsset int64 `json:"activeAsset"`
|
||||
OfflineSession int64 `json:"offlineSession"`
|
||||
FailLoginCount int64 `json:"failLoginCount"`
|
||||
}
|
||||
|
@ -1,7 +1,15 @@
|
||||
package dto
|
||||
|
||||
import "next-terminal/server/common"
|
||||
|
||||
type UserGroup struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Members []string `json:"members"`
|
||||
Members []UserGroupMember `json:"members"`
|
||||
Created common.JsonTime `json:"created"`
|
||||
}
|
||||
|
||||
type UserGroupMember struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
7
server/dto/overview.go
Normal file
7
server/dto/overview.go
Normal file
@ -0,0 +1,7 @@
|
||||
package dto
|
||||
|
||||
type DateCounter struct {
|
||||
Date string `json:"date"`
|
||||
Value uint64 `json:"value"`
|
||||
Type string `json:"type"`
|
||||
}
|
7
server/dto/permission.go
Normal file
7
server/dto/permission.go
Normal file
@ -0,0 +1,7 @@
|
||||
package dto
|
||||
|
||||
type TreeMenu struct {
|
||||
Title string `json:"title"`
|
||||
Key string `json:"key"`
|
||||
Children []TreeMenu `json:"children"`
|
||||
}
|
@ -27,6 +27,5 @@ type Backup struct {
|
||||
Commands []model.Command `json:"commands"`
|
||||
Credentials []model.Credential `json:"credentials"`
|
||||
Assets []map[string]interface{} `json:"assets"`
|
||||
ResourceSharers []model.ResourceSharer `json:"resource_sharers"`
|
||||
Jobs []model.Job `json:"jobs"`
|
||||
}
|
||||
|
15
server/dto/storage_log.go
Normal file
15
server/dto/storage_log.go
Normal file
@ -0,0 +1,15 @@
|
||||
package dto
|
||||
|
||||
import "next-terminal/server/common"
|
||||
|
||||
type StorageLogForPage struct {
|
||||
ID string `json:"id"`
|
||||
AssetId string `json:"assetId"`
|
||||
AssetName string `json:"assetName"`
|
||||
SessionId string `json:"sessionId"`
|
||||
UserId string `json:"userId"`
|
||||
UserName string `json:"userName"`
|
||||
Action string `json:"action"` // 操作类型: 上传、下载、删除、重命名、编辑
|
||||
FileName string `json:"fileName"` // 文件名称
|
||||
Created common.JsonTime `json:"created"` // 操作时间
|
||||
}
|
8
server/env/db.go
vendored
8
server/env/db.go
vendored
@ -48,9 +48,13 @@ func setupDB() *gorm.DB {
|
||||
}
|
||||
|
||||
if err := db.AutoMigrate(&model.User{}, &model.Asset{}, &model.AssetAttribute{}, &model.Session{}, &model.Command{},
|
||||
&model.Credential{}, &model.Property{}, &model.ResourceSharer{}, &model.UserGroup{}, &model.UserGroupMember{},
|
||||
&model.Credential{}, &model.Property{}, &model.UserGroup{}, &model.UserGroupMember{},
|
||||
&model.LoginLog{}, &model.Job{}, &model.JobLog{}, &model.AccessSecurity{}, &model.AccessGateway{},
|
||||
&model.Storage{}, &model.Strategy{}, &model.AccessToken{}); err != nil {
|
||||
&model.Storage{}, &model.Strategy{},
|
||||
&model.AccessToken{}, &model.ShareSession{},
|
||||
&model.Role{}, &model.RoleMenuRef{}, &model.UserRoleRef{},
|
||||
&model.LoginPolicy{}, &model.LoginPolicyUserRef{}, &model.TimePeriod{},
|
||||
&model.StorageLog{}, &model.Authorised{}); err != nil {
|
||||
panic(fmt.Errorf("初始化数据库表结构异常: %v", err.Error()))
|
||||
}
|
||||
return db
|
||||
|
2
server/global/cache/cache.go
vendored
2
server/global/cache/cache.go
vendored
@ -15,8 +15,10 @@ const (
|
||||
|
||||
var TokenManager *cache.Cache
|
||||
var LoginFailedKeyManager *cache.Cache
|
||||
var UserRolesManager *cache.Cache
|
||||
|
||||
func init() {
|
||||
TokenManager = cache.New(5*time.Minute, 10*time.Minute)
|
||||
LoginFailedKeyManager = cache.New(5*time.Minute, 10*time.Minute)
|
||||
UserRolesManager = cache.New(5*time.Minute, 10*time.Minute)
|
||||
}
|
||||
|
@ -4,10 +4,10 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"next-terminal/server/common/term"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"next-terminal/server/term"
|
||||
"next-terminal/server/utils"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
@ -90,7 +90,9 @@ func (g *Gateway) CloseSshTunnel(id string) {
|
||||
}
|
||||
|
||||
if len(g.tunnels) == 0 {
|
||||
if g.SshClient != nil {
|
||||
_ = g.SshClient.Close()
|
||||
}
|
||||
g.Connected = false
|
||||
g.Message = "暂未使用"
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package gateway
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"next-terminal/server/log"
|
||||
"next-terminal/server/model"
|
||||
)
|
||||
|
||||
@ -33,7 +32,6 @@ func (m *manager) Add(model *model.AccessGateway) *Gateway {
|
||||
tunnels: make(map[string]*Tunnel),
|
||||
}
|
||||
m.gateways.Store(g.ID, g)
|
||||
log.Infof("add Gateway: %s", g.ID)
|
||||
return g
|
||||
}
|
||||
|
||||
@ -43,7 +41,6 @@ func (m *manager) Del(id string) {
|
||||
g.Close()
|
||||
}
|
||||
m.gateways.Delete(id)
|
||||
log.Infof("del Gateway: %s", id)
|
||||
}
|
||||
|
||||
var GlobalGatewayManager *manager
|
||||
|
@ -5,8 +5,6 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"next-terminal/server/log"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
@ -22,34 +20,23 @@ type Tunnel struct {
|
||||
}
|
||||
|
||||
func (r *Tunnel) Open(sshClient *ssh.Client) {
|
||||
localAddr := fmt.Sprintf("%s:%d", r.localHost, r.localPort)
|
||||
|
||||
for {
|
||||
log.Debugf("隧道 %v 等待客户端访问 %v", r.id, localAddr)
|
||||
localConn, err := r.listener.Accept()
|
||||
if err != nil {
|
||||
log.Debugf("隧道 %v 接受连接失败 %v, 退出循环", r.id, err.Error())
|
||||
log.Debug("-------------------------------------------------")
|
||||
return
|
||||
}
|
||||
r.localConnections = append(r.localConnections, localConn)
|
||||
|
||||
log.Debugf("隧道 %v 新增本地连接 %v", r.id, localConn.RemoteAddr().String())
|
||||
remoteAddr := fmt.Sprintf("%s:%d", r.remoteHost, r.remotePort)
|
||||
log.Debugf("隧道 %v 连接远程地址 %v ...", r.id, remoteAddr)
|
||||
remoteConn, err := sshClient.Dial("tcp", remoteAddr)
|
||||
if err != nil {
|
||||
log.Debugf("隧道 %v 连接远程地址 %v, 退出循环", r.id, err.Error())
|
||||
log.Debug("-------------------------------------------------")
|
||||
return
|
||||
}
|
||||
r.remoteConnections = append(r.remoteConnections, remoteConn)
|
||||
|
||||
log.Debugf("隧道 %v 连接远程主机成功", r.id)
|
||||
go copyConn(localConn, remoteConn)
|
||||
go copyConn(remoteConn, localConn)
|
||||
log.Debugf("隧道 %v 开始转发数据 [%v]->[%v]", r.id, localAddr, remoteAddr)
|
||||
log.Debug("~~~~~~~~~~~~~~~~~~~~分割线~~~~~~~~~~~~~~~~~~~~~~~~")
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,7 +50,6 @@ func (r *Tunnel) Close() {
|
||||
}
|
||||
r.remoteConnections = nil
|
||||
_ = r.listener.Close()
|
||||
log.Debugf("隧道 %v 监听器关闭", r.id)
|
||||
}
|
||||
|
||||
func copyConn(writer, reader net.Conn) {
|
||||
|
12
server/global/license/license.go
Normal file
12
server/global/license/license.go
Normal file
@ -0,0 +1,12 @@
|
||||
package license
|
||||
|
||||
type License struct {
|
||||
Type string `json:"type"` // 类型:免费版 free,会员版 vip, 旗舰版 ultimate, 企业版 enterprise
|
||||
MachineId string `json:"machineId"` // 唯一机器码:免费版为空
|
||||
Assert int64 `json:"assert"` // 资产数量
|
||||
Concurrent int64 `json:"concurrent"` // 并发数量
|
||||
User int64 `json:"user"` // 用户数量
|
||||
Expired int64 `json:"expired"` // 过期时间
|
||||
}
|
||||
|
||||
var CurrentLicense *License
|
@ -3,8 +3,6 @@ package security
|
||||
import (
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"next-terminal/server/log"
|
||||
)
|
||||
|
||||
type Security struct {
|
||||
@ -54,13 +52,11 @@ func (m *Manager) Values() []*Security {
|
||||
func (m *Manager) Add(s *Security) {
|
||||
m.securities.Store(s.ID, s)
|
||||
m.LoadData()
|
||||
log.Infof("add security: %s", s.ID)
|
||||
}
|
||||
|
||||
func (m *Manager) Del(id string) {
|
||||
m.securities.Delete(id)
|
||||
m.LoadData()
|
||||
log.Infof("del security: %s", id)
|
||||
}
|
||||
|
||||
var GlobalSecurityManager *Manager
|
||||
|
@ -1,14 +1,12 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"next-terminal/server/common/guacamole"
|
||||
"next-terminal/server/common/term"
|
||||
"sync"
|
||||
|
||||
"next-terminal/server/dto"
|
||||
"next-terminal/server/guacd"
|
||||
"next-terminal/server/log"
|
||||
"next-terminal/server/term"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"next-terminal/server/dto"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
@ -16,10 +14,13 @@ type Session struct {
|
||||
Protocol string
|
||||
Mode string
|
||||
WebSocket *websocket.Conn
|
||||
GuacdTunnel *guacd.Tunnel
|
||||
GuacdTunnel *guacamole.Tunnel
|
||||
NextTerminal *term.NextTerminal
|
||||
Observer *Manager
|
||||
mutex sync.Mutex
|
||||
|
||||
Uptime int64
|
||||
Hostname string
|
||||
}
|
||||
|
||||
func (s *Session) WriteMessage(msg dto.Message) error {
|
||||
@ -79,7 +80,6 @@ func (m *Manager) GetById(id string) *Session {
|
||||
|
||||
func (m *Manager) Add(s *Session) {
|
||||
m.sessions.Store(s.ID, s)
|
||||
log.Infof("add session: %s", s.ID)
|
||||
}
|
||||
|
||||
func (m *Manager) Del(id string) {
|
||||
@ -91,7 +91,6 @@ func (m *Manager) Del(id string) {
|
||||
}
|
||||
}
|
||||
m.sessions.Delete(id)
|
||||
log.Infof("del session: %s", id)
|
||||
}
|
||||
|
||||
func (m *Manager) Clear() {
|
||||
|
104
server/global/stat/load.go
Normal file
104
server/global/stat/load.go
Normal file
@ -0,0 +1,104 @@
|
||||
package stat
|
||||
|
||||
type systemLoad struct {
|
||||
LoadStat *LoadStat `json:"loadStat"`
|
||||
Mem *Mem `json:"mem"`
|
||||
MemStat []*entry `json:"memStat"`
|
||||
Cpu *Cpu `json:"cpu"`
|
||||
CpuStat []*entry `json:"cpuStat"`
|
||||
Disk *Disk `json:"disk"`
|
||||
DiskIOStat []*ioEntry `json:"diskIO"`
|
||||
NetIOStat []*ioEntry `json:"netIO"`
|
||||
}
|
||||
|
||||
type Mem struct {
|
||||
Total uint64 `json:"total"`
|
||||
Available uint64 `json:"available"`
|
||||
Used uint64 `json:"used"`
|
||||
UsedPercent float64 `json:"usedPercent"`
|
||||
}
|
||||
|
||||
type Cpu struct {
|
||||
Count int `json:"count"`
|
||||
PhyCount int `json:"phyCount"`
|
||||
UsedPercent float64 `json:"usedPercent"`
|
||||
Info []*CpuInfo `json:"info"`
|
||||
}
|
||||
|
||||
type Disk struct {
|
||||
Total uint64 `json:"total"`
|
||||
Used uint64 `json:"used"`
|
||||
Available uint64 `json:"available"`
|
||||
UsedPercent float64 `json:"usedPercent"`
|
||||
}
|
||||
|
||||
type CpuInfo struct {
|
||||
ModelName string `json:"modelName"`
|
||||
CacheSize int32 `json:"cacheSize"`
|
||||
MHZ float64 `json:"mhz"`
|
||||
}
|
||||
|
||||
type LoadStat struct {
|
||||
Load1 float64 `json:"load1"`
|
||||
Load5 float64 `json:"load5"`
|
||||
Load15 float64 `json:"load15"`
|
||||
Percent float64 `json:"percent"`
|
||||
}
|
||||
|
||||
type entry struct {
|
||||
Time string `json:"time"`
|
||||
Value float64 `json:"value"`
|
||||
}
|
||||
|
||||
func NewStat(time string, value float64) *entry {
|
||||
return &entry{
|
||||
Time: time,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func NewIOStat(time string, read, write uint64) *ioEntry {
|
||||
return &ioEntry{
|
||||
Time: time,
|
||||
Read: read,
|
||||
Write: write,
|
||||
}
|
||||
}
|
||||
|
||||
type ioEntry struct {
|
||||
Time string `json:"time"`
|
||||
Read uint64 `json:"read"`
|
||||
Write uint64 `json:"write"`
|
||||
}
|
||||
|
||||
var SystemLoad *systemLoad
|
||||
|
||||
func init() {
|
||||
SystemLoad = &systemLoad{
|
||||
LoadStat: &LoadStat{
|
||||
Load1: 0,
|
||||
Load5: 0,
|
||||
Load15: 0,
|
||||
Percent: 0,
|
||||
},
|
||||
Mem: &Mem{
|
||||
Total: 0,
|
||||
Available: 0,
|
||||
Used: 0,
|
||||
UsedPercent: 0,
|
||||
},
|
||||
MemStat: make([]*entry, 0),
|
||||
Cpu: &Cpu{
|
||||
Count: 0,
|
||||
UsedPercent: 0,
|
||||
},
|
||||
CpuStat: make([]*entry, 0),
|
||||
Disk: &Disk{
|
||||
Total: 0,
|
||||
Used: 0,
|
||||
UsedPercent: 0,
|
||||
},
|
||||
DiskIOStat: make([]*ioEntry, 0),
|
||||
NetIOStat: make([]*ioEntry, 0),
|
||||
}
|
||||
}
|
@ -1,266 +1,151 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"next-terminal/server/config"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
type Formatter struct{}
|
||||
|
||||
func (s *Formatter) Format(entry *logrus.Entry) ([]byte, error) {
|
||||
timestamp := time.Now().Local().Format("2006-01-02 15:04:05")
|
||||
var file string
|
||||
var l int
|
||||
if entry.HasCaller() {
|
||||
file = filepath.Base(entry.Caller.Function)
|
||||
l = entry.Caller.Line
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("%s %s [%s:%d]%s\n", timestamp, strings.ToUpper(entry.Level.String()), file, l, entry.Message)
|
||||
return []byte(msg), nil
|
||||
}
|
||||
|
||||
var stdOut = NewLogger()
|
||||
|
||||
// Trace logs a message at level Trace on the standard logger.
|
||||
func Trace(args ...interface{}) {
|
||||
stdOut.Trace(args...)
|
||||
}
|
||||
|
||||
// Debug logs a message at level Debug on the standard logger.
|
||||
func Debug(args ...interface{}) {
|
||||
stdOut.Debug(args...)
|
||||
}
|
||||
|
||||
// Print logs a message at level Info on the standard logger.
|
||||
func Print(args ...interface{}) {
|
||||
stdOut.Print(args...)
|
||||
}
|
||||
|
||||
// Info logs a message at level Info on the standard logger.
|
||||
func Info(args ...interface{}) {
|
||||
stdOut.Info(args...)
|
||||
}
|
||||
|
||||
// Warn logs a message at level Warn on the standard logger.
|
||||
func Warn(args ...interface{}) {
|
||||
stdOut.Warn(args...)
|
||||
}
|
||||
|
||||
// Warning logs a message at level Warn on the standard logger.
|
||||
func Warning(args ...interface{}) {
|
||||
stdOut.Warning(args...)
|
||||
}
|
||||
|
||||
// Error logs a message at level Error on the standard logger.
|
||||
func Error(args ...interface{}) {
|
||||
stdOut.Error(args...)
|
||||
}
|
||||
|
||||
// Panic logs a message at level Panic on the standard logger.
|
||||
func Panic(args ...interface{}) {
|
||||
stdOut.Panic(args...)
|
||||
}
|
||||
|
||||
// Fatal logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
|
||||
func Fatal(args ...interface{}) {
|
||||
stdOut.Fatal(args...)
|
||||
}
|
||||
|
||||
// Tracef logs a message at level Trace on the standard logger.
|
||||
func Tracef(format string, args ...interface{}) {
|
||||
stdOut.Tracef(format, args...)
|
||||
}
|
||||
|
||||
// Debugf logs a message at level Debug on the standard logger.
|
||||
func Debugf(format string, args ...interface{}) {
|
||||
stdOut.Debugf(format, args...)
|
||||
}
|
||||
|
||||
// Printf logs a message at level Info on the standard logger.
|
||||
func Printf(format string, args ...interface{}) {
|
||||
stdOut.Printf(format, args...)
|
||||
}
|
||||
|
||||
// Infof logs a message at level Info on the standard logger.
|
||||
func Infof(format string, args ...interface{}) {
|
||||
stdOut.Infof(format, args...)
|
||||
}
|
||||
|
||||
// Warnf logs a message at level Warn on the standard logger.
|
||||
func Warnf(format string, args ...interface{}) {
|
||||
stdOut.Warnf(format, args...)
|
||||
}
|
||||
|
||||
// Warningf logs a message at level Warn on the standard logger.
|
||||
func Warningf(format string, args ...interface{}) {
|
||||
stdOut.Warningf(format, args...)
|
||||
}
|
||||
|
||||
// Errorf logs a message at level Error on the standard logger.
|
||||
func Errorf(format string, args ...interface{}) {
|
||||
stdOut.Errorf(format, args...)
|
||||
}
|
||||
|
||||
// Panicf logs a message at level Panic on the standard logger.
|
||||
func Panicf(format string, args ...interface{}) {
|
||||
stdOut.Panicf(format, args...)
|
||||
}
|
||||
|
||||
// Fatalf logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
|
||||
func Fatalf(format string, args ...interface{}) {
|
||||
stdOut.Fatalf(format, args...)
|
||||
}
|
||||
|
||||
// Traceln logs a message at level Trace on the standard logger.
|
||||
func Traceln(args ...interface{}) {
|
||||
stdOut.Traceln(args...)
|
||||
}
|
||||
|
||||
// Debugln logs a message at level Debug on the standard logger.
|
||||
func Debugln(args ...interface{}) {
|
||||
stdOut.Debugln(args...)
|
||||
}
|
||||
|
||||
// Println logs a message at level Info on the standard logger.
|
||||
func Println(args ...interface{}) {
|
||||
stdOut.Println(args...)
|
||||
}
|
||||
|
||||
// Infoln logs a message at level Info on the standard logger.
|
||||
func Infoln(args ...interface{}) {
|
||||
stdOut.Infoln(args...)
|
||||
}
|
||||
|
||||
// Warnln logs a message at level Warn on the standard logger.
|
||||
func Warnln(args ...interface{}) {
|
||||
stdOut.Warnln(args...)
|
||||
}
|
||||
|
||||
// Warningln logs a message at level Warn on the standard logger.
|
||||
func Warningln(args ...interface{}) {
|
||||
stdOut.Warningln(args...)
|
||||
}
|
||||
|
||||
// Errorln logs a message at level Error on the standard logger.
|
||||
func Errorln(args ...interface{}) {
|
||||
stdOut.Errorln(args...)
|
||||
}
|
||||
|
||||
// Panicln logs a message at level Panic on the standard logger.
|
||||
func Panicln(args ...interface{}) {
|
||||
stdOut.Panicln(args...)
|
||||
}
|
||||
|
||||
// Fatalln logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
|
||||
func Fatalln(args ...interface{}) {
|
||||
stdOut.Fatalln(args...)
|
||||
}
|
||||
|
||||
// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key.
|
||||
func WithError(err error) *logrus.Entry {
|
||||
return stdOut.WithField(logrus.ErrorKey, err)
|
||||
}
|
||||
|
||||
// WithField creates an entry from the standard logger and adds a field to
|
||||
// it. If you want multiple fields, use `WithFields`.
|
||||
//
|
||||
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
||||
// or Panic on the Entry it returns.
|
||||
func WithField(key string, value interface{}) *logrus.Entry {
|
||||
return stdOut.WithField(key, value)
|
||||
}
|
||||
|
||||
// Logrus : implement log
|
||||
type Logrus struct {
|
||||
*logrus.Logger
|
||||
}
|
||||
|
||||
// GetEchoLogger for e.l
|
||||
func NewLogger() Logrus {
|
||||
logFilePath := ""
|
||||
if dir, err := os.Getwd(); err == nil {
|
||||
logFilePath = dir + "/logs/"
|
||||
}
|
||||
if err := os.MkdirAll(logFilePath, 0755); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
logFileName := "next-terminal.log"
|
||||
//日志文件
|
||||
fileName := path.Join(logFilePath, logFileName)
|
||||
if _, err := os.Stat(fileName); err != nil {
|
||||
if _, err := os.Create(fileName); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
//实例化
|
||||
logger := logrus.New()
|
||||
//设置输出
|
||||
logger.SetOutput(io.MultiWriter(&lumberjack.Logger{
|
||||
Filename: fileName,
|
||||
MaxSize: 100, // megabytes
|
||||
MaxBackups: 3,
|
||||
MaxAge: 7, //days
|
||||
Compress: true, // disabled by default
|
||||
}, os.Stdout))
|
||||
logger.SetReportCaller(true)
|
||||
//设置日志级别
|
||||
if config.GlobalCfg.Debug {
|
||||
logger.SetLevel(logrus.DebugLevel)
|
||||
} else {
|
||||
logger.SetLevel(logrus.InfoLevel)
|
||||
}
|
||||
//设置日志格式
|
||||
logger.SetFormatter(new(Formatter))
|
||||
return Logrus{Logger: logger}
|
||||
}
|
||||
|
||||
func logrusMiddlewareHandler(c echo.Context, next echo.HandlerFunc) error {
|
||||
l := NewLogger()
|
||||
req := c.Request()
|
||||
res := c.Response()
|
||||
start := time.Now()
|
||||
if err := next(c); err != nil {
|
||||
c.Error(err)
|
||||
}
|
||||
stop := time.Now()
|
||||
|
||||
l.Debugf("%s %s %s %s %s %3d %s %13v %s %s",
|
||||
c.RealIP(),
|
||||
req.Host,
|
||||
req.Method,
|
||||
req.RequestURI,
|
||||
req.URL.Path,
|
||||
res.Status,
|
||||
strconv.FormatInt(res.Size, 10),
|
||||
stop.Sub(start).String(),
|
||||
req.Referer(),
|
||||
req.UserAgent(),
|
||||
var (
|
||||
_logger *zap.Logger // zap ensure that zap.Logger is safe for concurrent use
|
||||
)
|
||||
|
||||
return nil
|
||||
func init() {
|
||||
cfg := zap.NewProductionConfig()
|
||||
cfg.EncoderConfig.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
|
||||
enc.AppendString(t.Format("2006-01-02 15:04:05.000"))
|
||||
}
|
||||
|
||||
func logger(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
return logrusMiddlewareHandler(c, next)
|
||||
}
|
||||
var cores = []zapcore.Core{
|
||||
zapcore.NewCore(
|
||||
zapcore.NewConsoleEncoder(cfg.EncoderConfig),
|
||||
zapcore.Lock(os.Stdout),
|
||||
zap.LevelEnablerFunc(func(level zapcore.Level) bool {
|
||||
return level <= zapcore.InfoLevel
|
||||
}),
|
||||
),
|
||||
zapcore.NewCore(
|
||||
zapcore.NewJSONEncoder(cfg.EncoderConfig),
|
||||
zapcore.AddSync(&lumberjack.Logger{
|
||||
Filename: "logs/next-terminal.log",
|
||||
MaxSize: 100,
|
||||
MaxAge: 7,
|
||||
MaxBackups: 3,
|
||||
Compress: true,
|
||||
}),
|
||||
zap.LevelEnablerFunc(func(level zapcore.Level) bool {
|
||||
return level <= zapcore.InfoLevel
|
||||
}),
|
||||
),
|
||||
zapcore.NewCore(
|
||||
zapcore.NewConsoleEncoder(cfg.EncoderConfig),
|
||||
zapcore.Lock(os.Stdout),
|
||||
zap.LevelEnablerFunc(func(level zapcore.Level) bool {
|
||||
return level > zapcore.InfoLevel
|
||||
}),
|
||||
),
|
||||
zapcore.NewCore(
|
||||
zapcore.NewJSONEncoder(cfg.EncoderConfig),
|
||||
zapcore.AddSync(&lumberjack.Logger{
|
||||
Filename: "logs/next-terminal-error.log",
|
||||
MaxSize: 100,
|
||||
MaxAge: 7,
|
||||
MaxBackups: 3,
|
||||
Compress: true,
|
||||
}),
|
||||
zap.LevelEnablerFunc(func(level zapcore.Level) bool {
|
||||
return level > zapcore.InfoLevel
|
||||
}),
|
||||
),
|
||||
}
|
||||
|
||||
// Hook is a function to process log.
|
||||
func Hook() echo.MiddlewareFunc {
|
||||
return logger
|
||||
_logger = zap.New(zapcore.NewTee(cores...), zap.AddCaller(), zap.AddStacktrace(zapcore.ErrorLevel))
|
||||
}
|
||||
|
||||
type Field = zap.Field
|
||||
|
||||
// function variables for all field types
|
||||
// in github.com/uber-go/zap/field.go
|
||||
|
||||
var (
|
||||
Skip = zap.Skip
|
||||
Binary = zap.Binary
|
||||
Bool = zap.Bool
|
||||
Boolp = zap.Boolp
|
||||
ByteString = zap.ByteString
|
||||
Complex128 = zap.Complex128
|
||||
Complex128p = zap.Complex128p
|
||||
Complex64 = zap.Complex64
|
||||
Complex64p = zap.Complex64p
|
||||
Float64 = zap.Float64
|
||||
Float64p = zap.Float64p
|
||||
Float32 = zap.Float32
|
||||
Float32p = zap.Float32p
|
||||
Int = zap.Int
|
||||
Intp = zap.Intp
|
||||
Int64 = zap.Int64
|
||||
Int64p = zap.Int64p
|
||||
Int32 = zap.Int32
|
||||
Int32p = zap.Int32p
|
||||
Int16 = zap.Int16
|
||||
Int16p = zap.Int16p
|
||||
Int8 = zap.Int8
|
||||
Int8p = zap.Int8p
|
||||
String = zap.String
|
||||
Stringp = zap.Stringp
|
||||
Uint = zap.Uint
|
||||
Uintp = zap.Uintp
|
||||
Uint64 = zap.Uint64
|
||||
Uint64p = zap.Uint64p
|
||||
Uint32 = zap.Uint32
|
||||
Uint32p = zap.Uint32p
|
||||
Uint16 = zap.Uint16
|
||||
Uint16p = zap.Uint16p
|
||||
Uint8 = zap.Uint8
|
||||
Uint8p = zap.Uint8p
|
||||
Uintptr = zap.Uintptr
|
||||
Uintptrp = zap.Uintptrp
|
||||
Reflect = zap.Reflect
|
||||
Namespace = zap.Namespace
|
||||
Stringer = zap.Stringer
|
||||
Time = zap.Time
|
||||
Timep = zap.Timep
|
||||
Stack = zap.Stack
|
||||
StackSkip = zap.StackSkip
|
||||
Duration = zap.Duration
|
||||
Durationp = zap.Durationp
|
||||
Any = zap.Any
|
||||
NamedError = zap.NamedError
|
||||
)
|
||||
|
||||
func Debug(msg string, fields ...Field) {
|
||||
_logger.Debug(msg, fields...)
|
||||
}
|
||||
|
||||
func Info(msg string, fields ...Field) {
|
||||
_logger.Info(msg, fields...)
|
||||
}
|
||||
|
||||
func Warn(msg string, fields ...Field) {
|
||||
_logger.Warn(msg, fields...)
|
||||
}
|
||||
|
||||
func Error(msg string, fields ...Field) {
|
||||
_logger.Error(msg, fields...)
|
||||
}
|
||||
func DPanic(msg string, fields ...Field) {
|
||||
_logger.DPanic(msg, fields...)
|
||||
}
|
||||
func Panic(msg string, fields ...Field) {
|
||||
_logger.Panic(msg, fields...)
|
||||
}
|
||||
func Fatal(msg string, fields ...Field) {
|
||||
_logger.Fatal(msg, fields...)
|
||||
}
|
||||
|
||||
func Sync() error {
|
||||
return _logger.Sync()
|
||||
}
|
||||
|
17
server/model/StorageLog.go
Normal file
17
server/model/StorageLog.go
Normal file
@ -0,0 +1,17 @@
|
||||
package model
|
||||
|
||||
import "next-terminal/server/common"
|
||||
|
||||
type StorageLog struct {
|
||||
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
|
||||
AssetId string `gorm:"index,type:varchar(36)" json:"assetId"`
|
||||
SessionId string `gorm:"index,type:varchar(36)" json:"sessionId"`
|
||||
UserId string `gorm:"index,type:varchar(36)" json:"userId"`
|
||||
Action string `gorm:"type:varchar(20)" json:"action"` // 操作类型: 上传、下载、删除、重命名、编辑
|
||||
FileName string `gorm:"type:varchar(200)" json:"fileName"` // 文件名称
|
||||
Created common.JsonTime `json:"created"` // 操作时间
|
||||
}
|
||||
|
||||
func (s StorageLog) TableName() string {
|
||||
return "storage_logs"
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
package model
|
||||
|
||||
import "next-terminal/server/utils"
|
||||
import (
|
||||
"next-terminal/server/common"
|
||||
)
|
||||
|
||||
// AccessGateway 接入网关
|
||||
type AccessGateway struct {
|
||||
@ -13,7 +15,7 @@ type AccessGateway struct {
|
||||
Password string `gorm:"type:varchar(500)" json:"password"`
|
||||
PrivateKey string `gorm:"type:text" json:"privateKey"`
|
||||
Passphrase string `gorm:"type:varchar(500)" json:"passphrase"`
|
||||
Created utils.JsonTime `json:"created"`
|
||||
Created common.JsonTime `json:"created"`
|
||||
}
|
||||
|
||||
func (r *AccessGateway) TableName() string {
|
||||
@ -27,7 +29,7 @@ type AccessGatewayForPage struct {
|
||||
Port int `json:"port"`
|
||||
AccountType string `json:"accountType"`
|
||||
Username string `json:"username"`
|
||||
Created utils.JsonTime `json:"created"`
|
||||
Created common.JsonTime `json:"created"`
|
||||
Connected bool `json:"connected"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
@ -1,12 +1,14 @@
|
||||
package model
|
||||
|
||||
import "next-terminal/server/utils"
|
||||
import (
|
||||
"next-terminal/server/common"
|
||||
)
|
||||
|
||||
type AccessToken struct {
|
||||
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
|
||||
UserId string `gorm:"index,type:varchar(200)" json:"userId"`
|
||||
Token string `gorm:"index,type:varchar(128)" json:"token"`
|
||||
Created utils.JsonTime `json:"created"`
|
||||
Created common.JsonTime `json:"created"`
|
||||
}
|
||||
|
||||
func (r *AccessToken) TableName() string {
|
||||
|
@ -1,7 +1,7 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"next-terminal/server/utils"
|
||||
"next-terminal/server/common"
|
||||
)
|
||||
|
||||
type AssetProto string
|
||||
@ -20,7 +20,8 @@ type Asset struct {
|
||||
Passphrase string `gorm:"type:varchar(500)" json:"passphrase"`
|
||||
Description string `json:"description"`
|
||||
Active bool `json:"active"`
|
||||
Created utils.JsonTime `json:"created"`
|
||||
ActiveMessage string `gorm:"type:varchar(200)" json:"activeMessage"`
|
||||
Created common.JsonTime `json:"created"`
|
||||
Tags string `json:"tags"`
|
||||
Owner string `gorm:"index,type:varchar(36)" json:"owner"`
|
||||
Encrypted bool `json:"encrypted"`
|
||||
@ -30,15 +31,16 @@ type Asset struct {
|
||||
type AssetForPage struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
IP string `json:"ip"`
|
||||
Protocol string `json:"protocol"`
|
||||
Port int `json:"port"`
|
||||
Active bool `json:"active"`
|
||||
Created utils.JsonTime `json:"created"`
|
||||
ActiveMessage string `json:"activeMessage"`
|
||||
Created common.JsonTime `json:"created"`
|
||||
Tags string `json:"tags"`
|
||||
Owner string `json:"owner"`
|
||||
OwnerName string `json:"ownerName"`
|
||||
SshMode string `json:"sshMode"`
|
||||
}
|
||||
|
||||
func (r *Asset) TableName() string {
|
||||
|
18
server/model/authorised.go
Normal file
18
server/model/authorised.go
Normal file
@ -0,0 +1,18 @@
|
||||
package model
|
||||
|
||||
import "next-terminal/server/common"
|
||||
|
||||
// Authorised 资产授权
|
||||
type Authorised struct {
|
||||
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
|
||||
AssetId string `gorm:"index,type:varchar(36)" json:"assetId"`
|
||||
CommandFilterId string `gorm:"index,type:varchar(36)" json:"commandFilterId"`
|
||||
StrategyId string `gorm:"index,type:varchar(36)" json:"strategyId"`
|
||||
UserId string `gorm:"index,type:varchar(36)" json:"userId"`
|
||||
UserGroupId string `gorm:"index,type:varchar(36)" json:"userGroupId"`
|
||||
Created common.JsonTime `json:"created"`
|
||||
}
|
||||
|
||||
func (m Authorised) TableName() string {
|
||||
return "authorised"
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"next-terminal/server/utils"
|
||||
"next-terminal/server/common"
|
||||
)
|
||||
|
||||
type Command struct {
|
||||
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
|
||||
Name string `gorm:"type:varchar(500)" json:"name"`
|
||||
Content string `json:"content"`
|
||||
Created utils.JsonTime `json:"created"`
|
||||
Created common.JsonTime `json:"created"`
|
||||
Owner string `gorm:"index,type:varchar(36)" json:"owner"`
|
||||
}
|
||||
|
||||
@ -16,10 +16,9 @@ type CommandForPage struct {
|
||||
ID string `gorm:"primary_key" json:"id"`
|
||||
Name string `json:"name"`
|
||||
Content string `json:"content"`
|
||||
Created utils.JsonTime `json:"created"`
|
||||
Created common.JsonTime `json:"created"`
|
||||
Owner string `json:"owner"`
|
||||
OwnerName string `json:"ownerName"`
|
||||
SharerCount int64 `json:"sharerCount"`
|
||||
}
|
||||
|
||||
func (r *Command) TableName() string {
|
||||
|
@ -1,7 +1,7 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"next-terminal/server/utils"
|
||||
"next-terminal/server/common"
|
||||
)
|
||||
|
||||
type Credential struct {
|
||||
@ -12,7 +12,7 @@ type Credential struct {
|
||||
Password string `gorm:"type:varchar(500)" json:"password"`
|
||||
PrivateKey string `gorm:"type:text" json:"privateKey"`
|
||||
Passphrase string `gorm:"type:varchar(500)" json:"passphrase"`
|
||||
Created utils.JsonTime `json:"created"`
|
||||
Created common.JsonTime `json:"created"`
|
||||
Owner string `gorm:"index,type:varchar(36)" json:"owner"`
|
||||
Encrypted bool `json:"encrypted"`
|
||||
}
|
||||
@ -26,10 +26,9 @@ type CredentialForPage struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Username string `json:"username"`
|
||||
Created utils.JsonTime `json:"created"`
|
||||
Created common.JsonTime `json:"created"`
|
||||
Owner string `json:"owner"`
|
||||
OwnerName string `json:"ownerName"`
|
||||
SharerCount int64 `json:"sharerCount"`
|
||||
}
|
||||
|
||||
type CredentialSimpleVo struct {
|
||||
|
@ -1,7 +1,7 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"next-terminal/server/utils"
|
||||
"next-terminal/server/common"
|
||||
)
|
||||
|
||||
type Job struct {
|
||||
@ -14,8 +14,8 @@ type Job struct {
|
||||
ResourceIds string `json:"resourceIds"`
|
||||
Status string `gorm:"type:varchar(20)" json:"status"`
|
||||
Metadata string `json:"metadata"`
|
||||
Created utils.JsonTime `json:"created"`
|
||||
Updated utils.JsonTime `json:"updated"`
|
||||
Created common.JsonTime `json:"created"`
|
||||
Updated common.JsonTime `json:"updated"`
|
||||
}
|
||||
|
||||
func (r *Job) TableName() string {
|
||||
@ -24,7 +24,7 @@ func (r *Job) TableName() string {
|
||||
|
||||
type JobLog struct {
|
||||
ID string `json:"id"`
|
||||
Timestamp utils.JsonTime `json:"timestamp"`
|
||||
Timestamp common.JsonTime `json:"timestamp"`
|
||||
JobId string `json:"jobId"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"next-terminal/server/utils"
|
||||
"next-terminal/server/common"
|
||||
)
|
||||
|
||||
type LoginLog struct {
|
||||
@ -9,8 +9,8 @@ type LoginLog struct {
|
||||
Username string `gorm:"index,type:varchar(200)" json:"username"`
|
||||
ClientIP string `gorm:"type:varchar(200)" json:"clientIp"`
|
||||
ClientUserAgent string `gorm:"type:varchar(500)" json:"clientUserAgent"`
|
||||
LoginTime utils.JsonTime `json:"loginTime"`
|
||||
LogoutTime utils.JsonTime `json:"logoutTime"`
|
||||
LoginTime common.JsonTime `json:"loginTime"`
|
||||
LogoutTime common.JsonTime `json:"logoutTime"`
|
||||
Remember bool `json:"remember"`
|
||||
State string `gorm:"type:varchar(1)" json:"state"` // 成功 1 失败 0
|
||||
Reason string `gorm:"type:varchar(500)" json:"reason"`
|
||||
|
36
server/model/login_policy.go
Normal file
36
server/model/login_policy.go
Normal file
@ -0,0 +1,36 @@
|
||||
package model
|
||||
|
||||
type LoginPolicy struct {
|
||||
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
|
||||
Name string `gorm:"type:varchar(500)" json:"name"` // 名称
|
||||
IpGroup string `json:"ipGroup"` // IP组 格式为逗号分隔的字符串, 0.0.0.0 匹配所有。例如: 192.168.0.1, 192.168.1.0/24, 192.168.2.0-192.168.2.20
|
||||
Priority int64 `json:"priority"` // 优先级 越小优先级越高
|
||||
Enabled bool `json:"enabled"` // 是否激活
|
||||
Rule string `gorm:"type:varchar(20)" json:"rule"` // 规则 允许或拒绝
|
||||
TimePeriod []TimePeriod `gorm:"-" json:"timePeriod"` // 时间区间
|
||||
}
|
||||
|
||||
func (r *LoginPolicy) TableName() string {
|
||||
return "login_policies"
|
||||
}
|
||||
|
||||
type LoginPolicyUserRef struct {
|
||||
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
|
||||
UserId string `gorm:"index,type:varchar(36)" json:"userId"`
|
||||
LoginPolicyId string `gorm:"index,type:varchar(36)" json:"loginPolicyId"`
|
||||
}
|
||||
|
||||
func (r *LoginPolicyUserRef) TableName() string {
|
||||
return "login_policies_ref"
|
||||
}
|
||||
|
||||
type TimePeriod struct {
|
||||
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
|
||||
LoginPolicyId string `gorm:"index,type:varchar(36)" json:"loginPolicyId"`
|
||||
Key int `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
func (r *TimePeriod) TableName() string {
|
||||
return "time_periods"
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package model
|
||||
|
||||
type ResourceSharer struct {
|
||||
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
|
||||
ResourceId string `gorm:"index,type:varchar(36)" json:"resourceId"`
|
||||
ResourceType string `gorm:"index,type:varchar(36)" json:"resourceType"`
|
||||
StrategyId string `gorm:"index,type:varchar(36)" json:"strategyId"`
|
||||
UserId string `gorm:"index,type:varchar(36)" json:"userId"`
|
||||
UserGroupId string `gorm:"index,type:varchar(36)" json:"userGroupId"`
|
||||
}
|
||||
|
||||
func (r *ResourceSharer) TableName() string {
|
||||
return "resource_sharers"
|
||||
}
|
90
server/model/role.go
Normal file
90
server/model/role.go
Normal file
@ -0,0 +1,90 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/utils"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Role 角色
|
||||
type Role struct {
|
||||
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
|
||||
Name string `gorm:"type:varchar(500)" json:"name"`
|
||||
Type string `gorm:"type:varchar(10)" json:"type"`
|
||||
Deletable bool `json:"deletable"`
|
||||
Modifiable bool `json:"modifiable"`
|
||||
Created common.JsonTime `json:"created"`
|
||||
Menus []RoleMenuRef `gorm:"-" json:"menus"`
|
||||
}
|
||||
|
||||
func (r *Role) TableName() string {
|
||||
return "roles"
|
||||
}
|
||||
|
||||
func NewRole(id, name, _type string, deletable, modifiable bool, menus []RoleMenuRef) *Role {
|
||||
return &Role{
|
||||
ID: id,
|
||||
Name: name,
|
||||
Type: _type,
|
||||
Deletable: deletable,
|
||||
Modifiable: modifiable,
|
||||
Created: common.NowJsonTime(),
|
||||
Menus: menus,
|
||||
}
|
||||
}
|
||||
|
||||
// Menu 菜单
|
||||
type Menu struct {
|
||||
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
|
||||
Name string `gorm:"type:varchar(500)" json:"name"`
|
||||
ParentId string `gorm:"index,type:varchar(36)" json:"parentId"`
|
||||
Permissions []*Permission `gorm:"-"`
|
||||
}
|
||||
|
||||
func NewMenu(id, name, parentId string, permissions ...*Permission) *Menu {
|
||||
return &Menu{
|
||||
ID: id,
|
||||
Name: name,
|
||||
ParentId: parentId,
|
||||
Permissions: permissions,
|
||||
}
|
||||
}
|
||||
|
||||
// Permission 权限
|
||||
type Permission struct {
|
||||
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
|
||||
Name string `gorm:"type:varchar(500)" json:"name"`
|
||||
Method string `gorm:"type:varchar(10)" json:"method"`
|
||||
Path string `gorm:"type:varchar(200)" json:"path"`
|
||||
RequiredParams string `gorm:"type:varchar(200)" json:"params"`
|
||||
}
|
||||
|
||||
func NewPermission(method, path string, requiredParams ...string) *Permission {
|
||||
return &Permission{
|
||||
ID: utils.Sign([]string{method, path}),
|
||||
Method: method,
|
||||
Path: path,
|
||||
RequiredParams: strings.Join(requiredParams, ","),
|
||||
}
|
||||
}
|
||||
|
||||
type RoleMenuRef struct {
|
||||
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
|
||||
RoleId string `gorm:"index,type:varchar(36)" json:"roleId"`
|
||||
MenuId string `gorm:"index,type:varchar(36)" json:"menuId"`
|
||||
Checked bool `json:"checked"`
|
||||
}
|
||||
|
||||
func (r *RoleMenuRef) TableName() string {
|
||||
return "roles_menus_ref"
|
||||
}
|
||||
|
||||
type UserRoleRef struct {
|
||||
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
|
||||
UserId string `gorm:"index,type:varchar(36)" json:"userId"`
|
||||
RoleId string `gorm:"index,type:varchar(36)" json:"roleId"`
|
||||
}
|
||||
|
||||
func (r *UserRoleRef) TableName() string {
|
||||
return "users_roles_ref"
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"next-terminal/server/utils"
|
||||
"next-terminal/server/common"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
@ -23,8 +23,9 @@ type Session struct {
|
||||
Passphrase string `gorm:"type:varchar(500)" json:"passphrase"`
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
ConnectedTime utils.JsonTime `json:"connectedTime"`
|
||||
DisconnectedTime utils.JsonTime `json:"disconnectedTime"`
|
||||
ConnectedTime common.JsonTime `json:"connectedTime"`
|
||||
DisconnectedTime common.JsonTime `json:"disconnectedTime"`
|
||||
|
||||
Mode string `gorm:"type:varchar(10)" json:"mode"`
|
||||
FileSystem string `gorm:"type:varchar(1)" json:"fileSystem"` // 1 = true, 0 = false
|
||||
Upload string `gorm:"type:varchar(1)" json:"upload"`
|
||||
@ -38,6 +39,7 @@ type Session struct {
|
||||
StorageId string `gorm:"type:varchar(36)" json:"storageId"`
|
||||
AccessGatewayId string `gorm:"type:varchar(36)" json:"accessGatewayId"`
|
||||
Reviewed bool `gorm:"type:tinyint(1)" json:"reviewed"`
|
||||
CommandCount int64 `json:"commandCount"`
|
||||
}
|
||||
|
||||
func (r *Session) TableName() string {
|
||||
@ -58,14 +60,15 @@ type SessionForPage struct {
|
||||
Height int `json:"height"`
|
||||
Status string `json:"status"`
|
||||
Recording string `json:"recording"`
|
||||
ConnectedTime utils.JsonTime `json:"connectedTime"`
|
||||
DisconnectedTime utils.JsonTime `json:"disconnectedTime"`
|
||||
ConnectedTime common.JsonTime `json:"connectedTime"`
|
||||
DisconnectedTime common.JsonTime `json:"disconnectedTime"`
|
||||
AssetName string `json:"assetName"`
|
||||
CreatorName string `json:"creatorName"`
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Mode string `json:"mode"`
|
||||
Reviewed bool `json:"reviewed"`
|
||||
CommandCount int64 `json:"commandCount"`
|
||||
}
|
||||
|
||||
type SessionForAccess struct {
|
||||
|
21
server/model/share_session.go
Normal file
21
server/model/share_session.go
Normal file
@ -0,0 +1,21 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"next-terminal/server/common"
|
||||
)
|
||||
|
||||
type ShareSession struct {
|
||||
ID string `gorm:"primary_key,type:varchar(130)" json:"id"`
|
||||
AssetId string `gorm:"index,type:varchar(36)" json:"assetId"`
|
||||
FileSystem string `gorm:"type:varchar(1)" json:"fileSystem"` // 1 = true, 0 = false
|
||||
Upload string `gorm:"type:varchar(1)" json:"upload"`
|
||||
Download string `gorm:"type:varchar(1)" json:"download"`
|
||||
Delete string `gorm:"type:varchar(1)" json:"delete"`
|
||||
Rename string `gorm:"type:varchar(1)" json:"rename"`
|
||||
Edit string `gorm:"type:varchar(1)" json:"edit"`
|
||||
CreateDir string `gorm:"type:varchar(1)" json:"createDir"`
|
||||
Mode string `gorm:"type:varchar(20)" json:"mode"`
|
||||
Creator string `gorm:"type:varchar(36)" json:"creator"`
|
||||
Created common.JsonTime `json:"created"` // 创建时间
|
||||
Expiration common.JsonTime `json:"expiration"` // 过期时间
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
package model
|
||||
|
||||
import "next-terminal/server/utils"
|
||||
import (
|
||||
"next-terminal/server/common"
|
||||
)
|
||||
|
||||
type Storage struct {
|
||||
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
|
||||
@ -9,7 +11,7 @@ type Storage struct {
|
||||
LimitSize int64 `json:"limitSize"` // 大小限制,单位字节
|
||||
IsDefault bool `json:"isDefault"` // 是否为用户默认的
|
||||
Owner string `gorm:"index,type:varchar(36)" json:"owner"`
|
||||
Created utils.JsonTime `json:"created"`
|
||||
Created common.JsonTime `json:"created"`
|
||||
}
|
||||
|
||||
func (r *Storage) TableName() string {
|
||||
@ -25,5 +27,5 @@ type StorageForPage struct {
|
||||
IsDefault bool `json:"isDefault"` // 是否为用户默认的
|
||||
Owner string `gorm:"index" json:"owner"`
|
||||
OwnerName string `json:"ownerName"`
|
||||
Created utils.JsonTime `json:"created"`
|
||||
Created common.JsonTime `json:"created"`
|
||||
}
|
||||
|
@ -1,19 +1,21 @@
|
||||
package model
|
||||
|
||||
import "next-terminal/server/utils"
|
||||
import (
|
||||
"next-terminal/server/common"
|
||||
)
|
||||
|
||||
type Strategy struct {
|
||||
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
|
||||
Name string `gorm:"type:varchar(500)" json:"name"`
|
||||
Upload string `gorm:"type:varchar(1)" json:"upload"` // 1 = true, 0 = false
|
||||
Download string `gorm:"type:varchar(1)" json:"download"`
|
||||
Delete string `gorm:"type:varchar(1)" json:"delete"`
|
||||
Rename string `gorm:"type:varchar(1)" json:"rename"`
|
||||
Edit string `gorm:"type:varchar(1)" json:"edit"`
|
||||
CreateDir string `gorm:"type:varchar(1)" json:"createDir"`
|
||||
Copy string `gorm:"type:varchar(1)" json:"copy"`
|
||||
Paste string `gorm:"type:varchar(1)" json:"paste"`
|
||||
Created utils.JsonTime `json:"created"`
|
||||
Upload *bool `gorm:"type:tinyint;default:false" json:"upload"` // 1 = true, 0 = false
|
||||
Download *bool `gorm:"type:tinyint;default:false" json:"download"`
|
||||
Delete *bool `gorm:"type:tinyint;default:false" json:"delete"`
|
||||
Rename *bool `gorm:"type:tinyint;default:false" json:"rename"`
|
||||
Edit *bool `gorm:"type:tinyint;default:false" json:"edit"`
|
||||
CreateDir *bool `gorm:"type:tinyint;default:false" json:"createDir"`
|
||||
Copy *bool `gorm:"type:tinyint;default:false" json:"copy"`
|
||||
Paste *bool `gorm:"type:tinyint;default:false" json:"paste"`
|
||||
Created common.JsonTime `json:"created"`
|
||||
}
|
||||
|
||||
func (r *Strategy) TableName() string {
|
||||
|
15
server/model/tenant.go
Normal file
15
server/model/tenant.go
Normal file
@ -0,0 +1,15 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"next-terminal/server/common"
|
||||
)
|
||||
|
||||
type Tenant struct {
|
||||
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
|
||||
Name string `gorm:"type:varchar(500)" json:"name"`
|
||||
Created common.JsonTime `json:"created"`
|
||||
}
|
||||
|
||||
func (r *Tenant) TableName() string {
|
||||
return "tenants"
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"next-terminal/server/utils"
|
||||
"next-terminal/server/common"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
@ -10,12 +10,13 @@ type User struct {
|
||||
Password string `gorm:"type:varchar(500)" json:"password"`
|
||||
Nickname string `gorm:"type:varchar(500)" json:"nickname"`
|
||||
TOTPSecret string `json:"-"`
|
||||
Online bool `json:"online"`
|
||||
Online *bool `json:"online"`
|
||||
Status string `gorm:"type:varchar(10)" json:"status"`
|
||||
Created utils.JsonTime `json:"created"`
|
||||
Created common.JsonTime `json:"created"`
|
||||
Type string `gorm:"type:varchar(20)" json:"type"`
|
||||
Mail string `gorm:"type:varchar(500)" json:"mail"`
|
||||
Source string `gorm:"type:varchar(20)" json:"source"`
|
||||
Roles []string `gorm:"-" json:"roles"`
|
||||
}
|
||||
|
||||
type UserForPage struct {
|
||||
@ -26,7 +27,7 @@ type UserForPage struct {
|
||||
Mail string `json:"mail"`
|
||||
Online bool `json:"online"`
|
||||
Status string `json:"status"`
|
||||
Created utils.JsonTime `json:"created"`
|
||||
Created common.JsonTime `json:"created"`
|
||||
Type string `json:"type"`
|
||||
Source string `json:"source"`
|
||||
SharerAssetCount int64 `json:"sharerAssetCount"`
|
||||
|
@ -1,20 +1,20 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"next-terminal/server/utils"
|
||||
"next-terminal/server/common"
|
||||
)
|
||||
|
||||
type UserGroup struct {
|
||||
ID string `gorm:"primary_key,type:varchar(36)" json:"id"`
|
||||
Name string `gorm:"type:varchar(500)" json:"name"`
|
||||
Created utils.JsonTime `json:"created"`
|
||||
Created common.JsonTime `json:"created"`
|
||||
Members []string `gorm:"-" json:"members"`
|
||||
}
|
||||
|
||||
type UserGroupForPage struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Created utils.JsonTime `json:"created"`
|
||||
Created common.JsonTime `json:"created"`
|
||||
AssetCount int64 `json:"assetCount"`
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,8 @@ import (
|
||||
"next-terminal/server/model"
|
||||
)
|
||||
|
||||
var AccessTokenRepository = new(accessTokenRepository)
|
||||
|
||||
type accessTokenRepository struct {
|
||||
baseRepository
|
||||
}
|
||||
|
@ -3,16 +3,19 @@ package repository
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"next-terminal/server/common/nt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"next-terminal/server/config"
|
||||
"next-terminal/server/constant"
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/utils"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
var AssetRepository = new(assetRepository)
|
||||
|
||||
type assetRepository struct {
|
||||
baseRepository
|
||||
}
|
||||
@ -28,7 +31,11 @@ func (r assetRepository) FindByIds(c context.Context, assetIds []string) (o []mo
|
||||
}
|
||||
|
||||
func (r assetRepository) FindByProtocol(c context.Context, protocol string) (o []model.Asset, err error) {
|
||||
err = r.GetDB(c).Where("protocol = ?", protocol).Find(&o).Error
|
||||
db := r.GetDB(c)
|
||||
if protocol != "" {
|
||||
db = db.Where("protocol = ?", protocol)
|
||||
}
|
||||
err = db.Order("name asc").Find(&o).Error
|
||||
return
|
||||
}
|
||||
|
||||
@ -37,65 +44,9 @@ func (r assetRepository) FindByProtocolAndIds(c context.Context, protocol string
|
||||
return
|
||||
}
|
||||
|
||||
func (r assetRepository) FindByProtocolAndUser(c context.Context, protocol string, account model.User) (o []model.Asset, err error) {
|
||||
db := r.GetDB(c).Table("assets").Select("assets.id,assets.name,assets.ip,assets.port,assets.protocol,assets.active,assets.owner,assets.created,assets.tags,assets.description, users.nickname as owner_name").Joins("left join users on assets.owner = users.id").Joins("left join resource_sharers on assets.id = resource_sharers.resource_id").Group("assets.id")
|
||||
|
||||
if constant.TypeUser == account.Type {
|
||||
owner := account.ID
|
||||
db = db.Where("assets.owner = ? or resource_sharers.user_id = ?", owner, owner)
|
||||
|
||||
// 查询用户所在用户组列表
|
||||
userGroupIds, err := UserGroupMemberRepository.FindUserGroupIdsByUserId(c, account.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(userGroupIds) > 0 {
|
||||
db = db.Or("resource_sharers.user_group_id in ?", userGroupIds)
|
||||
}
|
||||
}
|
||||
|
||||
if len(protocol) > 0 {
|
||||
db = db.Where("assets.protocol = ?", protocol)
|
||||
}
|
||||
err = db.Find(&o).Error
|
||||
return
|
||||
}
|
||||
|
||||
func (r assetRepository) Find(c context.Context, pageIndex, pageSize int, name, protocol, tags string, account *model.User, owner, sharer, userGroupId, ip, order, field string) (o []model.AssetForPage, total int64, err error) {
|
||||
db := r.GetDB(c).Table("assets").Select("assets.id,assets.name,assets.ip,assets.port,assets.protocol,assets.active,assets.owner,assets.created,assets.tags,assets.description, users.nickname as owner_name").Joins("left join users on assets.owner = users.id").Joins("left join resource_sharers on assets.id = resource_sharers.resource_id").Group("assets.id")
|
||||
dbCounter := r.GetDB(c).Table("assets").Select("DISTINCT assets.id").Joins("left join resource_sharers on assets.id = resource_sharers.resource_id").Group("assets.id")
|
||||
|
||||
if constant.TypeUser == account.Type {
|
||||
owner := account.ID
|
||||
db = db.Where("assets.owner = ? or resource_sharers.user_id = ?", owner, owner)
|
||||
dbCounter = dbCounter.Where("assets.owner = ? or resource_sharers.user_id = ?", owner, owner)
|
||||
|
||||
// 查询用户所在用户组列表
|
||||
userGroupIds, err := UserGroupMemberRepository.FindUserGroupIdsByUserId(c, account.ID)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if len(userGroupIds) > 0 {
|
||||
db = db.Or("resource_sharers.user_group_id in ?", userGroupIds)
|
||||
dbCounter = dbCounter.Or("resource_sharers.user_group_id in ?", userGroupIds)
|
||||
}
|
||||
} else {
|
||||
if len(owner) > 0 {
|
||||
db = db.Where("assets.owner = ?", owner)
|
||||
dbCounter = dbCounter.Where("assets.owner = ?", owner)
|
||||
}
|
||||
if len(sharer) > 0 {
|
||||
db = db.Where("resource_sharers.user_id = ?", sharer)
|
||||
dbCounter = dbCounter.Where("resource_sharers.user_id = ?", sharer)
|
||||
}
|
||||
|
||||
if len(userGroupId) > 0 {
|
||||
db = db.Where("resource_sharers.user_group_id = ?", userGroupId)
|
||||
dbCounter = dbCounter.Where("resource_sharers.user_group_id = ?", userGroupId)
|
||||
}
|
||||
}
|
||||
func (r assetRepository) Find(c context.Context, pageIndex, pageSize int, name, protocol, tags, ip, active, order, field string) (o []model.AssetForPage, total int64, err error) {
|
||||
db := r.GetDB(c).Table("assets").Select("assets.id,assets.name,assets.ip,assets.port,assets.protocol,assets.active,assets.active_message,assets.owner,assets.created,assets.tags,assets.description, users.nickname as owner_name").Joins("left join users on assets.owner = users.id")
|
||||
dbCounter := r.GetDB(c).Table("assets")
|
||||
|
||||
if len(name) > 0 {
|
||||
db = db.Where("assets.name like ?", "%"+name+"%")
|
||||
@ -125,6 +76,14 @@ func (r assetRepository) Find(c context.Context, pageIndex, pageSize int, name,
|
||||
}
|
||||
}
|
||||
|
||||
if active != "" {
|
||||
_active, err := strconv.ParseBool(active)
|
||||
if err == nil {
|
||||
db = db.Where("assets.active = ?", _active)
|
||||
dbCounter = dbCounter.Where("assets.active = ?", _active)
|
||||
}
|
||||
}
|
||||
|
||||
err = dbCounter.Count(&total).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
@ -146,22 +105,6 @@ func (r assetRepository) Find(c context.Context, pageIndex, pageSize int, name,
|
||||
|
||||
if o == nil {
|
||||
o = make([]model.AssetForPage, 0)
|
||||
} else {
|
||||
for i := 0; i < len(o); i++ {
|
||||
if o[i].Protocol == "ssh" {
|
||||
attributes, err := r.FindAttrById(c, o[i].ID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for j := range attributes {
|
||||
if attributes[j].Name == constant.SshMode {
|
||||
o[i].SshMode = attributes[j].Value
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -180,9 +123,9 @@ func (r assetRepository) UpdateById(c context.Context, o *model.Asset, id string
|
||||
return r.GetDB(c).Updates(o).Error
|
||||
}
|
||||
|
||||
func (r assetRepository) UpdateActiveById(c context.Context, active bool, id string) error {
|
||||
sql := "update assets set active = ? where id = ?"
|
||||
return r.GetDB(c).Exec(sql, active, id).Error
|
||||
func (r assetRepository) UpdateActiveById(c context.Context, active bool, message, id string) error {
|
||||
sql := "update assets set active = ?, active_message = ? where id = ?"
|
||||
return r.GetDB(c).Exec(sql, active, message, id).Error
|
||||
}
|
||||
|
||||
func (r assetRepository) DeleteById(c context.Context, assetId string) (err error) {
|
||||
@ -198,47 +141,16 @@ func (r assetRepository) Count(c context.Context) (total int64, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (r assetRepository) CountByActive(c context.Context, active bool) (total int64, err error) {
|
||||
err = r.GetDB(c).Find(&model.Asset{}).Where("active = ?", active).Count(&total).Error
|
||||
return
|
||||
}
|
||||
|
||||
func (r assetRepository) CountByProtocol(c context.Context, protocol string) (total int64, err error) {
|
||||
err = r.GetDB(c).Find(&model.Asset{}).Where("protocol = ?", protocol).Count(&total).Error
|
||||
return
|
||||
}
|
||||
|
||||
func (r assetRepository) CountByUserId(c context.Context, userId string) (total int64, err error) {
|
||||
db := r.GetDB(c).Joins("left join resource_sharers on assets.id = resource_sharers.resource_id")
|
||||
|
||||
db = db.Where("assets.owner = ? or resource_sharers.user_id = ?", userId, userId)
|
||||
|
||||
// 查询用户所在用户组列表
|
||||
userGroupIds, err := UserGroupMemberRepository.FindUserGroupIdsByUserId(c, userId)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if len(userGroupIds) > 0 {
|
||||
db = db.Or("resource_sharers.user_group_id in ?", userGroupIds)
|
||||
}
|
||||
err = db.Find(&model.Asset{}).Count(&total).Error
|
||||
return
|
||||
}
|
||||
|
||||
func (r assetRepository) CountByUserIdAndProtocol(c context.Context, userId, protocol string) (total int64, err error) {
|
||||
db := r.GetDB(c).Joins("left join resource_sharers on assets.id = resource_sharers.resource_id")
|
||||
|
||||
db = db.Where("( assets.owner = ? or resource_sharers.user_id = ? ) and assets.protocol = ?", userId, userId, protocol)
|
||||
|
||||
// 查询用户所在用户组列表
|
||||
userGroupIds, err := UserGroupMemberRepository.FindUserGroupIdsByUserId(c, userId)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if len(userGroupIds) > 0 {
|
||||
db = db.Or("resource_sharers.user_group_id in ?", userGroupIds)
|
||||
}
|
||||
err = db.Find(&model.Asset{}).Count(&total).Error
|
||||
return
|
||||
}
|
||||
|
||||
func (r assetRepository) FindTags(c context.Context) (o []string, err error) {
|
||||
var assets []model.Asset
|
||||
err = r.GetDB(c).Not("tags = '' or tags = '-' ").Find(&assets).Error
|
||||
@ -265,15 +177,15 @@ func (r assetRepository) UpdateAttributes(c context.Context, assetId, protocol s
|
||||
var parameterNames []string
|
||||
switch protocol {
|
||||
case "ssh":
|
||||
parameterNames = constant.SSHParameterNames
|
||||
parameterNames = nt.SSHParameterNames
|
||||
case "rdp":
|
||||
parameterNames = constant.RDPParameterNames
|
||||
parameterNames = nt.RDPParameterNames
|
||||
case "vnc":
|
||||
parameterNames = constant.VNCParameterNames
|
||||
parameterNames = nt.VNCParameterNames
|
||||
case "telnet":
|
||||
parameterNames = constant.TelnetParameterNames
|
||||
parameterNames = nt.TelnetParameterNames
|
||||
case "kubernetes":
|
||||
parameterNames = constant.KubernetesParameterNames
|
||||
parameterNames = nt.KubernetesParameterNames
|
||||
}
|
||||
|
||||
for i := range parameterNames {
|
||||
@ -322,15 +234,15 @@ func (r assetRepository) FindAssetAttrMapByAssetId(c context.Context, assetId st
|
||||
var parameterNames []string
|
||||
switch asset.Protocol {
|
||||
case "ssh":
|
||||
parameterNames = constant.SSHParameterNames
|
||||
parameterNames = nt.SSHParameterNames
|
||||
case "rdp":
|
||||
parameterNames = constant.RDPParameterNames
|
||||
parameterNames = nt.RDPParameterNames
|
||||
case "vnc":
|
||||
parameterNames = constant.VNCParameterNames
|
||||
parameterNames = nt.VNCParameterNames
|
||||
case "telnet":
|
||||
parameterNames = constant.TelnetParameterNames
|
||||
parameterNames = nt.TelnetParameterNames
|
||||
case "kubernetes":
|
||||
parameterNames = constant.KubernetesParameterNames
|
||||
parameterNames = nt.KubernetesParameterNames
|
||||
}
|
||||
propertiesMap := PropertyRepository.FindAllMap(c)
|
||||
var attributeMap = make(map[string]string)
|
||||
@ -350,3 +262,91 @@ func (r assetRepository) UpdateAttrs(c context.Context, name, value, newValue st
|
||||
sql := "update asset_attributes set value = ? where name = ? and value = ?"
|
||||
return r.GetDB(c).Exec(sql, newValue, name, value).Error
|
||||
}
|
||||
|
||||
func (r assetRepository) ExistById(c context.Context, id string) (bool, error) {
|
||||
m := model.Asset{}
|
||||
var count uint64
|
||||
err := r.GetDB(c).Table(m.TableName()).Select("count(*)").
|
||||
Where("id = ?", id).
|
||||
Find(&count).
|
||||
Error
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
func (r assetRepository) FindMyAssets(c context.Context, pageIndex, pageSize int, name, protocol, tags string, assetIds []string, order, field string) (o []model.AssetForPage, total int64, err error) {
|
||||
db := r.GetDB(c).Table("assets").Select("assets.id,assets.name,assets.protocol,assets.active,assets.active_message,assets.tags,assets.description").
|
||||
Where("id in ?", assetIds)
|
||||
dbCounter := r.GetDB(c).Table("assets").Where("id in ?", assetIds)
|
||||
|
||||
if len(name) > 0 {
|
||||
db = db.Where("assets.name like ?", "%"+name+"%")
|
||||
dbCounter = dbCounter.Where("assets.name like ?", "%"+name+"%")
|
||||
}
|
||||
|
||||
if len(protocol) > 0 {
|
||||
db = db.Where("assets.protocol = ?", protocol)
|
||||
dbCounter = dbCounter.Where("assets.protocol = ?", protocol)
|
||||
}
|
||||
|
||||
if len(tags) > 0 {
|
||||
tagArr := strings.Split(tags, ",")
|
||||
for i := range tagArr {
|
||||
if config.GlobalCfg.DB == "sqlite" {
|
||||
db = db.Where("(',' || assets.tags || ',') LIKE ?", "%,"+tagArr[i]+",%")
|
||||
dbCounter = dbCounter.Where("(',' || assets.tags || ',') LIKE ?", "%,"+tagArr[i]+",%")
|
||||
} else {
|
||||
db = db.Where("find_in_set(?, assets.tags)", tagArr[i])
|
||||
dbCounter = dbCounter.Where("find_in_set(?, assets.tags)", tagArr[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = dbCounter.Count(&total).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if order == "ascend" {
|
||||
order = "asc"
|
||||
} else {
|
||||
order = "desc"
|
||||
}
|
||||
|
||||
if field == "name" {
|
||||
field = "name"
|
||||
} else {
|
||||
field = "created"
|
||||
}
|
||||
|
||||
err = db.Order("assets." + field + " " + order).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&o).Error
|
||||
|
||||
if o == nil {
|
||||
o = make([]model.AssetForPage, 0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r assetRepository) FindMyAssetTags(c context.Context, assetIds []string) (o []string, err error) {
|
||||
|
||||
var assets []model.Asset
|
||||
err = r.GetDB(c).Not("tags = '' or tags = '-' ").Where("id in ?", assetIds).Find(&assets).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
o = make([]string, 0)
|
||||
|
||||
for i := range assets {
|
||||
if len(assets[i].Tags) == 0 {
|
||||
continue
|
||||
}
|
||||
split := strings.Split(assets[i].Tags, ",")
|
||||
|
||||
o = append(o, split...)
|
||||
}
|
||||
|
||||
return utils.Distinct(o), nil
|
||||
}
|
||||
|
167
server/repository/authorised.go
Normal file
167
server/repository/authorised.go
Normal file
@ -0,0 +1,167 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"next-terminal/server/dto"
|
||||
"next-terminal/server/model"
|
||||
)
|
||||
|
||||
var AuthorisedRepository = new(authorisedRepository)
|
||||
|
||||
type authorisedRepository struct {
|
||||
baseRepository
|
||||
}
|
||||
|
||||
func (r authorisedRepository) Create(c context.Context, m *model.Authorised) error {
|
||||
return r.GetDB(c).Create(m).Error
|
||||
}
|
||||
|
||||
func (r authorisedRepository) CreateInBatches(c context.Context, m []model.Authorised) error {
|
||||
return r.GetDB(c).CreateInBatches(m, 100).Error
|
||||
}
|
||||
|
||||
func (r authorisedRepository) DeleteByUserId(c context.Context, userId string) error {
|
||||
return r.GetDB(c).Where("user_id = ?", userId).Delete(model.Authorised{}).Error
|
||||
}
|
||||
|
||||
func (r authorisedRepository) DeleteByUserGroupId(c context.Context, userGroupId string) error {
|
||||
return r.GetDB(c).Where("user_group_id = ?", userGroupId).Delete(model.Authorised{}).Error
|
||||
}
|
||||
|
||||
func (r authorisedRepository) DeleteByAssetId(c context.Context, assetId string) error {
|
||||
return r.GetDB(c).Where("asset_id = ?", assetId).Delete(model.Authorised{}).Error
|
||||
}
|
||||
|
||||
func (r authorisedRepository) FindByUserId(c context.Context, userId string) (items []model.Authorised, err error) {
|
||||
err = r.GetDB(c).Where("user_id = ?", userId).Find(&items).Error
|
||||
return
|
||||
}
|
||||
|
||||
func (r authorisedRepository) FindById(c context.Context, id string) (item model.Authorised, err error) {
|
||||
err = r.GetDB(c).Where("id = ?", id).Find(&item).Error
|
||||
return
|
||||
}
|
||||
|
||||
func (r authorisedRepository) FindByUserGroupId(c context.Context, userGroupId string) (items []model.Authorised, err error) {
|
||||
err = r.GetDB(c).Where("user_group_id = ?", userGroupId).Find(&items).Error
|
||||
return
|
||||
}
|
||||
|
||||
func (r authorisedRepository) FindByUserGroupIdIn(c context.Context, userGroupIds []string) (items []model.Authorised, err error) {
|
||||
err = r.GetDB(c).Where("user_group_id in ?", userGroupIds).Find(&items).Error
|
||||
return
|
||||
}
|
||||
|
||||
func (r authorisedRepository) FindAll(c context.Context, userId, userGroupId, assetId string) (items []model.Authorised, err error) {
|
||||
db := r.GetDB(c)
|
||||
if userId != "" {
|
||||
db = db.Where("user_id = ?", userId)
|
||||
}
|
||||
if userGroupId != "" {
|
||||
db = db.Where("user_group_id = ?", userGroupId)
|
||||
}
|
||||
if assetId != "" {
|
||||
db = db.Where("asset_id = ?", assetId)
|
||||
}
|
||||
err = db.Find(&items).Error
|
||||
return
|
||||
}
|
||||
|
||||
func (r authorisedRepository) FindAssetPage(c context.Context, pageIndex, pageSize int, assetName, userId, userGroupId string) (o []dto.AssetPageForAuthorised, total int64, err error) {
|
||||
db := r.GetDB(c).Table("assets").
|
||||
Select("authorised.id, authorised.created, assets.id as asset_id, assets.name as asset_name, strategies.id as strategy_id, strategies.name as strategy_name ").
|
||||
Joins("left join authorised on authorised.asset_id = assets.id").
|
||||
Joins("left join strategies on strategies.id = authorised.strategy_id").
|
||||
Group("assets.id")
|
||||
dbCounter := r.GetDB(c).Table("assets").Joins("left join authorised on assets.id = authorised.asset_id").Group("assets.id")
|
||||
|
||||
if assetName != "" {
|
||||
db = db.Where("assets.name like ?", "%"+assetName+"%")
|
||||
dbCounter = dbCounter.Where("assets.name like ?", "%"+assetName+"%")
|
||||
}
|
||||
|
||||
if userId != "" {
|
||||
db = db.Where("authorised.user_id = ?", userId)
|
||||
dbCounter = dbCounter.Where("authorised.user_id = ?", userId)
|
||||
}
|
||||
|
||||
if userGroupId != "" {
|
||||
db = db.Where("authorised.user_group_id = ?", userGroupId)
|
||||
dbCounter = dbCounter.Where("authorised.user_group_id = ?", userGroupId)
|
||||
}
|
||||
|
||||
err = dbCounter.Count(&total).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
err = db.Order("authorised.created desc").Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&o).Error
|
||||
if o == nil {
|
||||
o = make([]dto.AssetPageForAuthorised, 0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r authorisedRepository) DeleteById(c context.Context, id string) error {
|
||||
return r.GetDB(c).Where("id = ?", id).Delete(model.Authorised{}).Error
|
||||
}
|
||||
|
||||
func (r authorisedRepository) FindUserPage(c context.Context, pageIndex, pageSize int, userName, assetId string) (o []dto.UserPageForAuthorised, total int64, err error) {
|
||||
db := r.GetDB(c).Table("users").
|
||||
Select("authorised.id, authorised.created, users.id as user_id, users.nickname as user_name, strategies.id as strategy_id, strategies.name as strategy_name ").
|
||||
Joins("left join authorised on authorised.user_id = users.id").
|
||||
Joins("left join strategies on strategies.id = authorised.strategy_id").
|
||||
Group("users.id")
|
||||
dbCounter := r.GetDB(c).Table("assets").Joins("left join authorised on assets.id = authorised.asset_id").Group("assets.id")
|
||||
|
||||
if userName != "" {
|
||||
db = db.Where("users.nickname like ?", "%"+userName+"%")
|
||||
dbCounter = dbCounter.Where("users.nickname like ?", "%"+userName+"%")
|
||||
}
|
||||
|
||||
if assetId != "" {
|
||||
db = db.Where("authorised.asset_id = ?", assetId)
|
||||
dbCounter = dbCounter.Where("authorised.asset_id = ?", assetId)
|
||||
}
|
||||
|
||||
err = dbCounter.Count(&total).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
err = db.Order("authorised.created desc").Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&o).Error
|
||||
if o == nil {
|
||||
o = make([]dto.UserPageForAuthorised, 0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r authorisedRepository) FindUserGroupPage(c context.Context, pageIndex, pageSize int, userName, assetId string) (o []dto.UserGroupPageForAuthorised, total int64, err error) {
|
||||
db := r.GetDB(c).Table("user_groups").
|
||||
Select("authorised.id, authorised.created, user_groups.id as user_group_id, user_groups.name as user_group_name, strategies.id as strategy_id, strategies.name as strategy_name ").
|
||||
Joins("left join authorised on authorised.user_group_id = user_groups.id").
|
||||
Joins("left join strategies on strategies.id = authorised.strategy_id").
|
||||
Group("user_groups.id")
|
||||
dbCounter := r.GetDB(c).Table("assets").Joins("left join authorised on assets.id = authorised.asset_id").Group("assets.id")
|
||||
|
||||
if userName != "" {
|
||||
db = db.Where("user_groups.name like ?", "%"+userName+"%")
|
||||
dbCounter = dbCounter.Where("user_groups.name like ?", "%"+userName+"%")
|
||||
}
|
||||
|
||||
if assetId != "" {
|
||||
db = db.Where("authorised.asset_id = ?", assetId)
|
||||
dbCounter = dbCounter.Where("authorised.asset_id = ?", assetId)
|
||||
}
|
||||
|
||||
err = dbCounter.Count(&total).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
err = db.Order("authorised.created desc").Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&o).Error
|
||||
if o == nil {
|
||||
o = make([]dto.UserGroupPageForAuthorised, 0)
|
||||
}
|
||||
return
|
||||
}
|
@ -2,8 +2,8 @@ package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"next-terminal/server/common/nt"
|
||||
|
||||
"next-terminal/server/constant"
|
||||
"next-terminal/server/env"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@ -13,7 +13,7 @@ type baseRepository struct {
|
||||
}
|
||||
|
||||
func (b *baseRepository) GetDB(c context.Context) *gorm.DB {
|
||||
db, ok := c.Value(constant.DB).(*gorm.DB)
|
||||
db, ok := c.Value(nt.DB).(*gorm.DB)
|
||||
if !ok {
|
||||
return env.GetDB()
|
||||
}
|
||||
|
@ -3,24 +3,57 @@ package repository
|
||||
import (
|
||||
"context"
|
||||
|
||||
"next-terminal/server/constant"
|
||||
"next-terminal/server/model"
|
||||
)
|
||||
|
||||
var CommandRepository = new(commandRepository)
|
||||
|
||||
type commandRepository struct {
|
||||
baseRepository
|
||||
}
|
||||
|
||||
func (r commandRepository) Find(c context.Context, pageIndex, pageSize int, name, content, order, field string, account *model.User) (o []model.CommandForPage, total int64, err error) {
|
||||
db := r.GetDB(c).Table("commands").Select("commands.id,commands.name,commands.content,commands.owner,commands.created, users.nickname as owner_name,COUNT(resource_sharers.user_id) as sharer_count").Joins("left join users on commands.owner = users.id").Joins("left join resource_sharers on commands.id = resource_sharers.resource_id").Group("commands.id")
|
||||
dbCounter := r.GetDB(c).Table("commands").Select("DISTINCT commands.id").Joins("left join resource_sharers on commands.id = resource_sharers.resource_id").Group("commands.id")
|
||||
func (r commandRepository) Find(c context.Context, pageIndex, pageSize int, name, content, order, field string) (o []model.CommandForPage, total int64, err error) {
|
||||
db := r.GetDB(c).Table("commands").Select("commands.id,commands.name,commands.content,commands.owner,commands.created, users.nickname as owner_name").Joins("left join users on commands.owner = users.id").Group("commands.id")
|
||||
dbCounter := r.GetDB(c).Table("commands")
|
||||
|
||||
if constant.TypeUser == account.Type {
|
||||
owner := account.ID
|
||||
db = db.Where("commands.owner = ? or resource_sharers.user_id = ?", owner, owner)
|
||||
dbCounter = dbCounter.Where("commands.owner = ? or resource_sharers.user_id = ?", owner, owner)
|
||||
if len(name) > 0 {
|
||||
db = db.Where("commands.name like ?", "%"+name+"%")
|
||||
dbCounter = dbCounter.Where("commands.name like ?", "%"+name+"%")
|
||||
}
|
||||
|
||||
if len(content) > 0 {
|
||||
db = db.Where("commands.content like ?", "%"+content+"%")
|
||||
dbCounter = dbCounter.Where("commands.content like ?", "%"+content+"%")
|
||||
}
|
||||
|
||||
err = dbCounter.Count(&total).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if order == "ascend" {
|
||||
order = "asc"
|
||||
} else {
|
||||
order = "desc"
|
||||
}
|
||||
|
||||
if field == "name" {
|
||||
field = "name"
|
||||
} else {
|
||||
field = "created"
|
||||
}
|
||||
|
||||
err = db.Order("commands." + field + " " + order).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&o).Error
|
||||
if o == nil {
|
||||
o = make([]model.CommandForPage, 0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r commandRepository) WorkerFind(c context.Context, pageIndex, pageSize int, name, content, order, field, userId string) (o []model.CommandForPage, total int64, err error) {
|
||||
db := r.GetDB(c).Table("commands").Select("commands.id,commands.name,commands.content,commands.owner,commands.created").Where("commands.owner = ?", userId)
|
||||
dbCounter := r.GetDB(c).Table("commands").Where("commands.owner = ?", userId)
|
||||
|
||||
if len(name) > 0 {
|
||||
db = db.Where("commands.name like ?", "%"+name+"%")
|
||||
dbCounter = dbCounter.Where("commands.name like ?", "%"+name+"%")
|
||||
@ -76,21 +109,12 @@ func (r commandRepository) DeleteById(c context.Context, id string) error {
|
||||
return r.GetDB(c).Where("id = ?", id).Delete(&model.Command{}).Error
|
||||
}
|
||||
|
||||
func (r commandRepository) FindByUser(c context.Context, account *model.User) (o []model.CommandForPage, err error) {
|
||||
db := r.GetDB(c).Table("commands").Select("commands.id,commands.name,commands.content,commands.owner,commands.created, users.nickname as owner_name,COUNT(resource_sharers.user_id) as sharer_count").Joins("left join users on commands.owner = users.id").Joins("left join resource_sharers on commands.id = resource_sharers.resource_id").Group("commands.id")
|
||||
|
||||
if constant.TypeUser == account.Type {
|
||||
owner := account.ID
|
||||
db = db.Where("commands.owner = ? or resource_sharers.user_id = ?", owner, owner)
|
||||
}
|
||||
err = db.Order("commands.name asc").Find(&o).Error
|
||||
if o == nil {
|
||||
o = make([]model.CommandForPage, 0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r commandRepository) FindAll(c context.Context) (o []model.Command, err error) {
|
||||
err = r.GetDB(c).Find(&o).Error
|
||||
return
|
||||
}
|
||||
|
||||
func (r commandRepository) FindByUserId(c context.Context, userId string) (o []model.Command, err error) {
|
||||
err = r.GetDB(c).Where("owner = ?", userId).First(&o).Error
|
||||
return
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user