mirror of https://github.com/bjdgyc/anylink.git
更新程序为单文件
This commit is contained in:
parent
a616e42432
commit
0ef18ee2f9
|
@ -19,7 +19,7 @@ COPY --from=builder_node /web/ui /anylink/server/ui
|
|||
#TODO 本地打包时使用镜像
|
||||
#RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories
|
||||
RUN apk add --no-cache git
|
||||
RUN cd /anylink/server;go build -o anylink -ldflags "-X main.COMMIT_ID=$(git rev-parse HEAD)" \
|
||||
RUN cd /anylink/server;go build -o anylink -ldflags "-X main.CommitId=$(git rev-parse HEAD)" \
|
||||
&& /anylink/server/anylink tool -v
|
||||
|
||||
# anylink
|
||||
|
|
|
@ -35,7 +35,7 @@ AnyLink 服务端仅在CentOS 7、Ubuntu 18.04测试通过,如需要安装在
|
|||
>
|
||||
> https://github.com/bjdgyc/anylink/releases
|
||||
|
||||
> 升级 go version = 1.15
|
||||
> 升级 go version = 1.16
|
||||
>
|
||||
> 需要提前安装好 golang 和 nodejs
|
||||
|
||||
|
@ -47,11 +47,11 @@ sh build.sh
|
|||
|
||||
# 注意使用root权限运行
|
||||
cd anylink-deploy
|
||||
sudo ./anylink --conf="conf/server.toml"
|
||||
sudo ./anylink
|
||||
|
||||
# 默认管理后台访问地址
|
||||
# http://host:8800
|
||||
# 默认账号密码
|
||||
# 默认账号 密码
|
||||
# admin 123456
|
||||
|
||||
```
|
||||
|
|
20
build.sh
20
build.sh
|
@ -12,11 +12,6 @@ function RETVAL() {
|
|||
#当前目录
|
||||
cpath=$(pwd)
|
||||
|
||||
echo "编译二进制文件"
|
||||
cd $cpath/server
|
||||
go build -v -o anylink -ldflags "-X main.COMMIT_ID=$(git rev-parse HEAD)"
|
||||
RETVAL $?
|
||||
|
||||
echo "编译前端项目"
|
||||
cd $cpath/web
|
||||
#国内可替换源加快速度
|
||||
|
@ -26,21 +21,26 @@ npm run build --registry=https://registry.npm.taobao.org
|
|||
#npm run build
|
||||
RETVAL $?
|
||||
|
||||
echo "编译二进制文件"
|
||||
cd $cpath/server
|
||||
rm -rf ui
|
||||
cp -rf $cpath/web/ui .
|
||||
go build -v -o anylink -ldflags "-X main.CommitId=$(git rev-parse HEAD)"
|
||||
RETVAL $?
|
||||
|
||||
cd $cpath
|
||||
|
||||
echo "整理部署文件"
|
||||
deploy="anylink-deploy"
|
||||
rm -rf $deploy
|
||||
rm -rf $deploy ${deploy}.tar.gz
|
||||
mkdir $deploy
|
||||
mkdir $deploy/log
|
||||
|
||||
cp -r server/anylink $deploy
|
||||
cp -r server/conf $deploy
|
||||
cp -r server/files $deploy
|
||||
cp -r server/bridge-init.sh $deploy
|
||||
|
||||
cp -r systemd $deploy
|
||||
cp -r web/ui $deploy
|
||||
|
||||
tar zcvf ${deploy}.tar.gz $deploy
|
||||
|
||||
#注意使用root权限运行
|
||||
#cd anylink-deploy
|
||||
|
|
|
@ -16,7 +16,7 @@ case $var1 in
|
|||
*)
|
||||
sysctl -w net.ipv4.ip_forward=1
|
||||
iptables -t nat -A POSTROUTING -s "${IPV4_CIDR}" -o eth0+ -j MASQUERADE
|
||||
# iptables -nL -t nat
|
||||
iptables -nL -t nat
|
||||
|
||||
/app/anylink "$@"
|
||||
;;
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
> 添加QQ群: 567510628
|
||||
|
||||
### 远程桌面连接
|
||||
> 本软件不支持远程桌面连接,请注意。
|
||||
> 本软件不支持远程桌面里面连接anyconnect,请注意。
|
||||
|
||||
### 私有证书问题
|
||||
> anylink 默认不支持私有证书
|
||||
>
|
||||
> 仅测试的话,可以通过 https://github.com/square/certstrap 生成私有的证书, 然后把CA证书放在客户端机器上即可以连接。
|
||||
> 其他使用私有证书的问题,请自行解决
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
package admin
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"net/http"
|
||||
"net/http/pprof"
|
||||
|
||||
|
@ -9,7 +10,9 @@ import (
|
|||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// 开启服务
|
||||
var UiData embed.FS
|
||||
|
||||
// StartAdmin 开启服务
|
||||
func StartAdmin() {
|
||||
|
||||
r := mux.NewRouter()
|
||||
|
@ -17,7 +20,8 @@ func StartAdmin() {
|
|||
|
||||
r.Handle("/", http.RedirectHandler("/ui/", http.StatusFound)).Name("index")
|
||||
r.PathPrefix("/ui/").Handler(
|
||||
http.StripPrefix("/ui/", http.FileServer(http.Dir(base.Cfg.UiPath))),
|
||||
// http.StripPrefix("/ui/", http.FileServer(http.Dir(base.Cfg.UiPath))),
|
||||
http.FileServer(http.FS(UiData)),
|
||||
).Name("static")
|
||||
r.HandleFunc("/base/login", Login).Name("login")
|
||||
|
||||
|
|
|
@ -39,7 +39,6 @@ type ServerConfig struct {
|
|||
DbFile string `json:"db_file"`
|
||||
CertFile string `json:"cert_file"`
|
||||
CertKey string `json:"cert_key"`
|
||||
UiPath string `json:"ui_path"`
|
||||
FilesPath string `json:"files_path"`
|
||||
LogPath string `json:"log_path"`
|
||||
LogLevel string `json:"log_level"`
|
||||
|
@ -70,20 +69,20 @@ type ServerConfig struct {
|
|||
|
||||
func initServerCfg() {
|
||||
|
||||
sf, _ := filepath.Abs(cfgFile)
|
||||
base := filepath.Dir(sf)
|
||||
// TODO 取消绝对地址转换
|
||||
// sf, _ := filepath.Abs(cfgFile)
|
||||
// base := filepath.Dir(sf)
|
||||
|
||||
// 转换成绝对路径
|
||||
Cfg.DbFile = getAbsPath(base, Cfg.DbFile)
|
||||
Cfg.CertFile = getAbsPath(base, Cfg.CertFile)
|
||||
Cfg.CertKey = getAbsPath(base, Cfg.CertKey)
|
||||
Cfg.UiPath = getAbsPath(base, Cfg.UiPath)
|
||||
Cfg.FilesPath = getAbsPath(base, Cfg.FilesPath)
|
||||
Cfg.LogPath = getAbsPath(base, Cfg.LogPath)
|
||||
// Cfg.DbFile = getAbsPath(base, Cfg.DbFile)
|
||||
// Cfg.CertFile = getAbsPath(base, Cfg.CertFile)
|
||||
// Cfg.CertKey = getAbsPath(base, Cfg.CertKey)
|
||||
// Cfg.UiPath = getAbsPath(base, Cfg.UiPath)
|
||||
// Cfg.FilesPath = getAbsPath(base, Cfg.FilesPath)
|
||||
// Cfg.LogPath = getAbsPath(base, Cfg.LogPath)
|
||||
|
||||
if len(Cfg.JwtSecret) < 20 {
|
||||
fmt.Println("请设置 jwt_secret 长度20位以上")
|
||||
os.Exit(0)
|
||||
if Cfg.JwtSecret == defaultJwt {
|
||||
fmt.Fprintln(os.Stderr, "=== 使用默认的jwt_secret有安全风险,请设置新的jwt_secret ===")
|
||||
}
|
||||
|
||||
fmt.Printf("ServerCfg: %+v \n", Cfg)
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
package base
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bjdgyc/anylink/pkg/utils"
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -64,13 +61,12 @@ func init() {
|
|||
viper.SetConfigFile(cfgFile)
|
||||
viper.AutomaticEnv()
|
||||
|
||||
_, err := os.Stat(cfgFile)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
// 文件不存在,不做处理
|
||||
if cfgFile == "" {
|
||||
// 没有配置文件,不做处理
|
||||
return
|
||||
}
|
||||
|
||||
err = viper.ReadInConfig()
|
||||
err := viper.ReadInConfig()
|
||||
if err != nil {
|
||||
fmt.Println("Using config file:", err)
|
||||
}
|
||||
|
@ -79,7 +75,7 @@ func init() {
|
|||
viper.SetEnvPrefix("link")
|
||||
|
||||
// 基础配置
|
||||
rootCmd.Flags().StringVarP(&cfgFile, "conf", "c", "./conf/server.toml", "config file")
|
||||
rootCmd.Flags().StringVarP(&cfgFile, "conf", "c", "", "config file")
|
||||
|
||||
for _, v := range configs {
|
||||
if v.Typ == cfgStr {
|
||||
|
@ -118,7 +114,6 @@ func initToolCmd() *cobra.Command {
|
|||
fmt.Printf("%s v%s build on %s [%s, %s] commit_id(%s) \n",
|
||||
APP_NAME, APP_VER, runtime.Version(), runtime.GOOS, runtime.GOARCH, CommitId)
|
||||
case secret:
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
s, _ := utils.RandSecret(40, 60)
|
||||
s = strings.Trim(s, "=")
|
||||
fmt.Printf("Secret:%s\n", s)
|
||||
|
|
|
@ -4,6 +4,8 @@ const (
|
|||
cfgStr = iota
|
||||
cfgInt
|
||||
cfgBool
|
||||
|
||||
defaultJwt = "abcdef.0123456789.abcdef"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
|
@ -24,7 +26,6 @@ var configs = []config{
|
|||
{Typ: cfgStr, Name: "db_file", Usage: "数据库地址", ValStr: "./data.db"},
|
||||
{Typ: cfgStr, Name: "cert_file", Usage: "证书文件", ValStr: "./vpn_cert.pem"},
|
||||
{Typ: cfgStr, Name: "cert_key", Usage: "证书密钥", ValStr: "./vpn_cert.key"},
|
||||
{Typ: cfgStr, Name: "ui_path", Usage: "ui文件路径", ValStr: "./ui"},
|
||||
{Typ: cfgStr, Name: "files_path", Usage: "外部下载文件路径", ValStr: "./files"},
|
||||
{Typ: cfgStr, Name: "log_path", Usage: "日志文件路径", ValStr: ""},
|
||||
{Typ: cfgStr, Name: "log_level", Usage: "日志等级", ValStr: "info"},
|
||||
|
@ -32,7 +33,7 @@ var configs = []config{
|
|||
{Typ: cfgStr, Name: "issuer", Usage: "系统名称", ValStr: "XX公司VPN"},
|
||||
{Typ: cfgStr, Name: "admin_user", Usage: "管理用户名", ValStr: "admin"},
|
||||
{Typ: cfgStr, Name: "admin_pass", Usage: "管理用户密码", ValStr: "$2a$10$UQ7C.EoPifDeJh6d8.31TeSPQU7hM/NOM2nixmBucJpAuXDQNqNke"},
|
||||
{Typ: cfgStr, Name: "jwt_secret", Usage: "JWT密钥", ValStr: "iLmspvOiz*%ovfcs*wersdf#heR8pNU4XxBm&mW$aPCjSRMbYH#&"},
|
||||
{Typ: cfgStr, Name: "jwt_secret", Usage: "JWT密钥", ValStr: defaultJwt},
|
||||
{Typ: cfgStr, Name: "link_mode", Usage: "虚拟网络类型", ValStr: "tun"},
|
||||
{Typ: cfgStr, Name: "ipv4_cidr", Usage: "ip地址网段", ValStr: "192.168.10.0/24"},
|
||||
{Typ: cfgStr, Name: "ipv4_gateway", Usage: "ipv4_gateway", ValStr: "192.168.10.1"},
|
||||
|
|
|
@ -6,10 +6,9 @@
|
|||
#数据文件
|
||||
db_file = "./data.db"
|
||||
#证书文件
|
||||
cert_file = "./test_vpn_cert.pem"
|
||||
cert_key = "./test_vpn_key.pem"
|
||||
ui_path = "../ui"
|
||||
files_path = "../files"
|
||||
cert_file = "./vpn_cert.pem"
|
||||
cert_key = "./vpn_cert.key"
|
||||
files_path = "./files"
|
||||
#日志目录,为空写入标准输出
|
||||
#log_path = "../log"
|
||||
log_path = ""
|
||||
|
@ -22,7 +21,7 @@ issuer = "XX公司VPN"
|
|||
admin_user = "admin"
|
||||
#pass 123456
|
||||
admin_pass = "$2a$10$UQ7C.EoPifDeJh6d8.31TeSPQU7hM/NOM2nixmBucJpAuXDQNqNke"
|
||||
jwt_secret = "iLmspvOiz*%ovfcs*wersdf#heR8pNU4XxBm&mW$aPCjSRMbYH#&"
|
||||
jwt_secret = "abcdef.0123456789.abcdef"
|
||||
|
||||
|
||||
#服务监听地址
|
|
@ -1,19 +0,0 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDGDCCAgACCQCecQDpy/8hRTANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJD
|
||||
TjELMAkGA1UECAwCQkoxCzAJBgNVBAcMAkJKMQswCQYDVQQKDAJCRDELMAkGA1UE
|
||||
CwwCQkQxCzAJBgNVBAMMAkNTMB4XDTIxMDMyNjA5MTkwNloXDTMxMDMyNDA5MTkw
|
||||
NlowTjELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAlNIMQswCQYDVQQHDAJTSDELMAkG
|
||||
A1UECgwCVE0xCzAJBgNVBAsMAlRNMQswCQYDVQQDDAJDUzCCASIwDQYJKoZIhvcN
|
||||
AQEBBQADggEPADCCAQoCggEBAJtDxHduS8gjI0P6txHS+cODxKjyjNiCBa7tFgSc
|
||||
d9hRrzCvK4Q4M5StKJoSczmHl0C3HVoq92Gv1vENxq4irYdCrwLeOZGyt7urUlbs
|
||||
PkvEoVXxfAkPpue+JewG/CvGArJeP7UGsP5IrD0Dt5X1DP677K6qf5igzyaJqYJu
|
||||
RDJ5wR84BoDvY66Zc578N9tK9XusdJ63gQ5jGcG4Dneu1UX3g8lQkJ6P0xLXTh7W
|
||||
u5Sjx8axbDcFxbDLxNGL1yPgAjhIRgMfaWLwuQQg4WKFsdMljv1Flz8/h91z2xo+
|
||||
+E/B4YF0UFWTcWQ2TQ8w8noDqnnXVVQyOvuI3aajodml/f0CAwEAATANBgkqhkiG
|
||||
9w0BAQsFAAOCAQEAd89n0eWXgO1lqMciWmS9xY8Sj/U840bPo/4Kclsm1vFNvIXu
|
||||
I50PeaNiU2E5+CMk8AwXaJ5gDO7vsRxvLLRAUWZeuxSror2a0RkViEFW+UKcBuuB
|
||||
Izl9giXUhB/P85+We1ma5jizqj7OpzgMkzkcTZL2M6Gw6IWY4jopvLQjiCooSiYF
|
||||
wtLZjuFKfpLrPw5RgpWI4L8Hftbkmh6Q8nqcoQvgwm7rLrD5VqiTu7Rk1SXTFuXn
|
||||
uuazXasWIWRVGFuFcYP1rwyOfp9HhCFKngi0w8IRnbOcaPdXydtbKMcKt5z9zQX5
|
||||
BqrZ3ZfPp5HeklG7L8eQrnp4ines6YDshPnaRQ==
|
||||
-----END CERTIFICATE-----
|
|
@ -1,27 +0,0 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAm0PEd25LyCMjQ/q3EdL5w4PEqPKM2IIFru0WBJx32FGvMK8r
|
||||
hDgzlK0omhJzOYeXQLcdWir3Ya/W8Q3GriKth0KvAt45kbK3u6tSVuw+S8ShVfF8
|
||||
CQ+m574l7Ab8K8YCsl4/tQaw/kisPQO3lfUM/rvsrqp/mKDPJompgm5EMnnBHzgG
|
||||
gO9jrplznvw320r1e6x0nreBDmMZwbgOd67VRfeDyVCQno/TEtdOHta7lKPHxrFs
|
||||
NwXFsMvE0YvXI+ACOEhGAx9pYvC5BCDhYoWx0yWO/UWXPz+H3XPbGj74T8HhgXRQ
|
||||
VZNxZDZNDzDyegOqeddVVDI6+4jdpqOh2aX9/QIDAQABAoIBADWT2fz4g5AJiAbS
|
||||
QlAVRHjSRI+kOzQPEhT93SY0NCribRjYqaSTnEEGy8b27OoCPxBm3+sYfosoGXzP
|
||||
Kys17jmJqkjMFIORb1OEWAKEvS56KM42aX3a99ZqSD29X1Ffn9ibK1K1f2gP/deE
|
||||
K9rEV/qjMJZJYYRyoWkEAglvMXtU/NMRoTuFYtrJPr9sFEfpBFq97WpWiyMdLKTG
|
||||
MmlN+T1CXFQj/+mpv+DDSXcwLPBxAttDYE2GeqlhntId0I6cgaEGMO42D6fnqrKi
|
||||
PDilA/D6zos4o/bpRGvVBdXHqOXvX2stNHK+PvEX46GRd+OZhLh0KEcrWAx8cXs9
|
||||
ZhugTyECgYEAyffRPd98acL0OhXJR9mZTgDdotl7iYq+RTZbmEvAFst3mL3LA6Ba
|
||||
BTrwRLh9x8lzxoTQHHFaJL63kIrN6QAR9e3+pR0e8IX3vYCVGIlRCYB5CrE/O3Pi
|
||||
B9R17tCI5dFrFXYiST38sjwrWG9+geKarbUH5AZrZEO5uw0q7+4F3TkCgYEAxM1h
|
||||
Xo+xRt8RXoWZ6Cl66HhZKIvDcxkBtoNh54YLzrVpv0D+RvAWNDzRVXbbIUUpBGPN
|
||||
pHrwU8G0qWr4Q/Zx+vnckqotGMTNCB7vcmB/qwF9grNW9E0rCyIYLXtJcEiclJIF
|
||||
Oe406YXl7mSG1I6QjAADz8PNb4++Ct1+hVS56uUCgYAx9g/Y0nQgZY2s4L7N+1Il
|
||||
LammI06gE6ZF0NCPuA1oliSbsDeMShp6uL2/AjR7O6ZcMXaZ0qCN/m/CXdPaE55d
|
||||
y+X2SmHg9gL26dv4Gd/mDdXjgz01I9GCRlh2Hzf+QfPPd027+I2OObwvQEV3M+s3
|
||||
lVTCX6QpRWeokfVRLPxeYQKBgDIYPVK+rNdnbJps05JfDKQkDj3d5bBkiyUUKFWw
|
||||
r0y8rOA8AP25m01MtdRVXs4HNruhU/UsPgRz6DK/wdY64ySJeXXzz2rgnXgVt8mb
|
||||
eqPiyzn7wISLKAu7cAATw8vLD+BZku7+DYXryW13NULhzzVzw4SdSKu/IRbO7qet
|
||||
u21pAoGAd2mBJ+PWKnUkARS8gQ3Y3cagA/qGGr094P9relglRDBv/Pm7kTUt6K8B
|
||||
NnpqWydcVtcrXmNzGRx4ftm18SzmTJEohF14nF9424q4aiWoNZyG8adxaI0Yqv3G
|
||||
LnH8n2fzC+pf31LijBRM8DRnepah64mLF+OM/SxgVg1nP9jVUG4=
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -2,27 +2,52 @@ package handler
|
|||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/bjdgyc/anylink/base"
|
||||
"github.com/bjdgyc/anylink/pkg/proxyproto"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/pion/dtls/v2/pkg/crypto/selfsign"
|
||||
)
|
||||
|
||||
func startTls() {
|
||||
addr := base.Cfg.ServerAddr
|
||||
certFile := base.Cfg.CertFile
|
||||
keyFile := base.Cfg.CertKey
|
||||
|
||||
var (
|
||||
err error
|
||||
|
||||
addr = base.Cfg.ServerAddr
|
||||
certFile = base.Cfg.CertFile
|
||||
keyFile = base.Cfg.CertKey
|
||||
certs = make([]tls.Certificate, 1)
|
||||
ln net.Listener
|
||||
)
|
||||
|
||||
// 判断证书文件
|
||||
_, err = os.Stat(certFile)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
// 自动生成证书
|
||||
certs[0], err = selfsign.GenerateSelfSignedWithDNS("vpn.anylink")
|
||||
} else {
|
||||
// 使用自定义证书
|
||||
certs[0], err = tls.LoadX509KeyPair(certFile, keyFile)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 设置tls信息
|
||||
tlsConfig := &tls.Config{
|
||||
NextProtos: []string{"http/1.1"},
|
||||
MinVersion: tls.VersionTLS12,
|
||||
InsecureSkipVerify: true,
|
||||
Certificates: certs,
|
||||
}
|
||||
srv := &http.Server{
|
||||
Addr: addr,
|
||||
|
@ -31,9 +56,7 @@ func startTls() {
|
|||
ErrorLog: base.GetBaseLog(),
|
||||
}
|
||||
|
||||
var ln net.Listener
|
||||
|
||||
ln, err := net.Listen("tcp", addr)
|
||||
ln, err = net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -44,7 +67,7 @@ func startTls() {
|
|||
}
|
||||
|
||||
base.Info("listen server", addr)
|
||||
err = srv.ServeTLS(ln, certFile, keyFile)
|
||||
err = srv.ServeTLS(ln, "", "")
|
||||
if err != nil {
|
||||
base.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -1,21 +1,30 @@
|
|||
// AnyLink 是一个企业级远程办公vpn软件,可以支持多人同时在线使用。
|
||||
|
||||
// +build !windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/bjdgyc/anylink/admin"
|
||||
|
||||
"github.com/bjdgyc/anylink/base"
|
||||
"github.com/bjdgyc/anylink/handler"
|
||||
)
|
||||
|
||||
//go:embed ui
|
||||
var uiData embed.FS
|
||||
|
||||
// 程序版本
|
||||
var COMMIT_ID string
|
||||
var CommitId string
|
||||
|
||||
func main() {
|
||||
base.CommitId = COMMIT_ID
|
||||
base.CommitId = CommitId
|
||||
admin.UiData = uiData
|
||||
|
||||
base.Start()
|
||||
handler.Start()
|
||||
|
|
Loading…
Reference in New Issue