mirror of
https://github.com/TeaOSLab/EdgeAdmin.git
synced 2025-11-02 03:40:27 +08:00
初始化
This commit is contained in:
1
build/configs/.gitignore
vendored
Normal file
1
build/configs/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
server.yaml
|
||||
16
build/configs/server.template.yaml
Normal file
16
build/configs/server.template.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
# environment code
|
||||
env: prod
|
||||
|
||||
# http
|
||||
http:
|
||||
"on": true
|
||||
listen: [ "0.0.0.0:8001" ]
|
||||
|
||||
# https
|
||||
https:
|
||||
"on": false
|
||||
listen: [ "0.0.0.0:443"]
|
||||
cert: ""
|
||||
key: ""
|
||||
|
||||
|
||||
1
build/logs/.gitignore
vendored
Normal file
1
build/logs/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.log
|
||||
31
cmd/edge-admin/main.go
Normal file
31
cmd/edge-admin/main.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/apps"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web"
|
||||
"github.com/iwind/TeaGo"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
"github.com/iwind/TeaGo/sessions"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := apps.NewAppCmd().
|
||||
Version(teaconst.Version).
|
||||
Product(teaconst.ProductName).
|
||||
Usage(teaconst.ProcessName + " [-v|start|stop|restart]")
|
||||
|
||||
app.Run(func() {
|
||||
// 启动管理界面
|
||||
server := TeaGo.NewServer(false).
|
||||
AccessLog(false).
|
||||
EndAll().
|
||||
|
||||
Session(sessions.NewFileSessionManager(
|
||||
86400,
|
||||
rands.String(32),
|
||||
))
|
||||
server.Start()
|
||||
})
|
||||
}
|
||||
2
dist/.gitignore
vendored
Normal file
2
dist/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*.zip
|
||||
shield-admin
|
||||
20
docs/TODO.md
Normal file
20
docs/TODO.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# 一期
|
||||
* 服务管理
|
||||
* 节点管理
|
||||
* 边缘节点
|
||||
* 资源管理
|
||||
* 组件管理:缓存策略、WAF等
|
||||
* 设置
|
||||
* 供应商
|
||||
* 用户
|
||||
* 授权管理
|
||||
|
||||
# 二期
|
||||
* 节点管理
|
||||
* 数据库集群
|
||||
* 统计分析节点
|
||||
* API集群
|
||||
* DNS集群
|
||||
* 监控系统
|
||||
|
||||
# TODO
|
||||
5
go.mod
Normal file
5
go.mod
Normal file
@@ -0,0 +1,5 @@
|
||||
module github.com/TeaOSLab/EdgeAdmin
|
||||
|
||||
go 1.14
|
||||
|
||||
require github.com/iwind/TeaGo v0.0.0-20200722010955-47dd648dc761
|
||||
61
go.sum
Normal file
61
go.sum
Normal file
@@ -0,0 +1,61 @@
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o=
|
||||
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/iwind/TeaGo v0.0.0-20200722010955-47dd648dc761 h1:70Iaf6iVF4CiH1P5Au3hqile/2Ea0XIkR4SPpdiaKI0=
|
||||
github.com/iwind/TeaGo v0.0.0-20200722010955-47dd648dc761/go.mod h1:zjM7k+b+Jthhf0T0fKwuF0iy4TWb5SsU1gmKR2l+OmE=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 h1:xoIK0ctDddBMnc74udxJYBqlo9Ylnsp1waqjLsnef20=
|
||||
github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
234
internal/apps/app_cmd.go
Normal file
234
internal/apps/app_cmd.go
Normal file
@@ -0,0 +1,234 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// App命令帮助
|
||||
type AppCmd struct {
|
||||
product string
|
||||
version string
|
||||
usage string
|
||||
options []*CommandHelpOption
|
||||
appendStrings []string
|
||||
|
||||
directives []*Directive
|
||||
}
|
||||
|
||||
func NewAppCmd() *AppCmd {
|
||||
return &AppCmd{}
|
||||
}
|
||||
|
||||
type CommandHelpOption struct {
|
||||
Code string
|
||||
Description string
|
||||
}
|
||||
|
||||
// 产品
|
||||
func (this *AppCmd) Product(product string) *AppCmd {
|
||||
this.product = product
|
||||
return this
|
||||
}
|
||||
|
||||
// 版本
|
||||
func (this *AppCmd) Version(version string) *AppCmd {
|
||||
this.version = version
|
||||
return this
|
||||
}
|
||||
|
||||
// 使用方法
|
||||
func (this *AppCmd) Usage(usage string) *AppCmd {
|
||||
this.usage = usage
|
||||
return this
|
||||
}
|
||||
|
||||
// 选项
|
||||
func (this *AppCmd) Option(code string, description string) *AppCmd {
|
||||
this.options = append(this.options, &CommandHelpOption{
|
||||
Code: code,
|
||||
Description: description,
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
// 附加内容
|
||||
func (this *AppCmd) Append(appendString string) *AppCmd {
|
||||
this.appendStrings = append(this.appendStrings, appendString)
|
||||
return this
|
||||
}
|
||||
|
||||
// 打印
|
||||
func (this *AppCmd) Print() {
|
||||
fmt.Println(this.product + " v" + this.version)
|
||||
|
||||
usage := this.usage
|
||||
fmt.Println("Usage:", "\n "+usage)
|
||||
|
||||
if len(this.options) > 0 {
|
||||
fmt.Println("")
|
||||
fmt.Println("Options:")
|
||||
|
||||
spaces := 20
|
||||
max := 40
|
||||
for _, option := range this.options {
|
||||
l := len(option.Code)
|
||||
if l < max && l > spaces {
|
||||
spaces = l + 4
|
||||
}
|
||||
}
|
||||
|
||||
for _, option := range this.options {
|
||||
if len(option.Code) > max {
|
||||
fmt.Println("")
|
||||
fmt.Println(" " + option.Code)
|
||||
option.Code = ""
|
||||
}
|
||||
|
||||
fmt.Printf(" %-"+strconv.Itoa(spaces)+"s%s\n", option.Code, ": "+option.Description)
|
||||
}
|
||||
}
|
||||
|
||||
if len(this.appendStrings) > 0 {
|
||||
fmt.Println("")
|
||||
for _, s := range this.appendStrings {
|
||||
fmt.Println(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加指令
|
||||
func (this *AppCmd) On(arg string, callback func()) {
|
||||
this.directives = append(this.directives, &Directive{
|
||||
Arg: arg,
|
||||
Callback: callback,
|
||||
})
|
||||
}
|
||||
|
||||
// 运行
|
||||
func (this *AppCmd) Run(main func()) {
|
||||
// 获取参数
|
||||
args := os.Args[1:]
|
||||
if len(args) > 0 {
|
||||
switch args[0] {
|
||||
case "-v", "version", "-version", "--version":
|
||||
this.runVersion()
|
||||
return
|
||||
case "?", "help", "-help", "h", "-h":
|
||||
this.runHelp()
|
||||
return
|
||||
case "start":
|
||||
this.runStart()
|
||||
return
|
||||
case "stop":
|
||||
this.runStop()
|
||||
return
|
||||
case "restart":
|
||||
this.runRestart()
|
||||
return
|
||||
case "status":
|
||||
this.runStatus()
|
||||
return
|
||||
}
|
||||
|
||||
// 查找指令
|
||||
for _, directive := range this.directives {
|
||||
if directive.Arg == args[0] {
|
||||
directive.Callback()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("unknown command '" + args[0] + "'")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 记录PID
|
||||
_ = this.writePid()
|
||||
|
||||
// 日志
|
||||
writer := new(LogWriter)
|
||||
writer.Init()
|
||||
logs.SetWriter(writer)
|
||||
|
||||
// 运行主函数
|
||||
main()
|
||||
}
|
||||
|
||||
// 版本号
|
||||
func (this *AppCmd) runVersion() {
|
||||
fmt.Println(this.product+" v"+this.version, "(build: "+runtime.Version(), runtime.GOOS, runtime.GOARCH+")")
|
||||
}
|
||||
|
||||
// 帮助
|
||||
func (this *AppCmd) runHelp() {
|
||||
this.Print()
|
||||
}
|
||||
|
||||
// 启动
|
||||
func (this *AppCmd) runStart() {
|
||||
proc := this.checkPid()
|
||||
if proc != nil {
|
||||
fmt.Println(this.product+" already started, pid:", proc.Pid)
|
||||
return
|
||||
}
|
||||
|
||||
cmd := exec.Command(os.Args[0])
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
fmt.Println(this.product+" start failed:", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(this.product+" started ok, pid:", cmd.Process.Pid)
|
||||
}
|
||||
|
||||
// 停止
|
||||
func (this *AppCmd) runStop() {
|
||||
proc := this.checkPid()
|
||||
if proc == nil {
|
||||
fmt.Println(this.product + " not started yet")
|
||||
return
|
||||
}
|
||||
|
||||
// 停止进程
|
||||
_ = proc.Kill()
|
||||
|
||||
// 在Windows上经常不能及时释放资源
|
||||
_ = DeletePid(Tea.Root + "/bin/pid")
|
||||
fmt.Println(this.product+" stopped ok, pid:", proc.Pid)
|
||||
}
|
||||
|
||||
// 重启
|
||||
func (this *AppCmd) runRestart() {
|
||||
this.runStop()
|
||||
time.Sleep(1 * time.Second)
|
||||
this.runStart()
|
||||
}
|
||||
|
||||
// 状态
|
||||
func (this *AppCmd) runStatus() {
|
||||
proc := this.checkPid()
|
||||
if proc == nil {
|
||||
fmt.Println(this.product + " not started yet")
|
||||
} else {
|
||||
fmt.Println(this.product + " is running, pid: " + fmt.Sprintf("%d", proc.Pid))
|
||||
}
|
||||
}
|
||||
|
||||
// 检查PID
|
||||
func (this *AppCmd) checkPid() *os.Process {
|
||||
return CheckPid(Tea.Root + "/bin/pid")
|
||||
}
|
||||
|
||||
// 写入PID
|
||||
func (this *AppCmd) writePid() error {
|
||||
return WritePid(Tea.Root + "/bin/pid")
|
||||
}
|
||||
6
internal/apps/directive.go
Normal file
6
internal/apps/directive.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package apps
|
||||
|
||||
type Directive struct {
|
||||
Arg string
|
||||
Callback func()
|
||||
}
|
||||
17
internal/apps/file_others.go
Normal file
17
internal/apps/file_others.go
Normal file
@@ -0,0 +1,17 @@
|
||||
// +build !windows
|
||||
|
||||
package apps
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// lock file
|
||||
func LockFile(fp *os.File) error {
|
||||
return syscall.Flock(int(fp.Fd()), syscall.LOCK_EX|syscall.LOCK_NB)
|
||||
}
|
||||
|
||||
func UnlockFile(fp *os.File) error {
|
||||
return syscall.Flock(int(fp.Fd()), syscall.LOCK_UN)
|
||||
}
|
||||
17
internal/apps/file_windows.go
Normal file
17
internal/apps/file_windows.go
Normal file
@@ -0,0 +1,17 @@
|
||||
// +build windows
|
||||
|
||||
package apps
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
// lock file
|
||||
func LockFile(fp *os.File) error {
|
||||
return errors.New("not implemented on windows")
|
||||
}
|
||||
|
||||
func UnlockFile(fp *os.File) error {
|
||||
return errors.New("not implemented on windows")
|
||||
}
|
||||
51
internal/apps/log_writer.go
Normal file
51
internal/apps/log_writer.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/files"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/utils/time"
|
||||
"log"
|
||||
)
|
||||
|
||||
type LogWriter struct {
|
||||
fileAppender *files.Appender
|
||||
}
|
||||
|
||||
func (this *LogWriter) Init() {
|
||||
// 创建目录
|
||||
dir := files.NewFile(Tea.LogDir())
|
||||
if !dir.Exists() {
|
||||
err := dir.Mkdir()
|
||||
if err != nil {
|
||||
log.Println("[error]" + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
logFile := files.NewFile(Tea.LogFile("run.log"))
|
||||
|
||||
// 打开要写入的日志文件
|
||||
appender, err := logFile.Appender()
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
} else {
|
||||
this.fileAppender = appender
|
||||
}
|
||||
}
|
||||
|
||||
func (this *LogWriter) Write(message string) {
|
||||
log.Println(message)
|
||||
|
||||
if this.fileAppender != nil {
|
||||
_, err := this.fileAppender.AppendString(timeutil.Format("Y/m/d H:i:s ") + message + "\n")
|
||||
if err != nil {
|
||||
log.Println("[error]" + err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (this *LogWriter) Close() {
|
||||
if this.fileAppender != nil {
|
||||
_ = this.fileAppender.Close()
|
||||
}
|
||||
}
|
||||
113
internal/apps/pid.go
Normal file
113
internal/apps/pid.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var pidFileList = []*os.File{}
|
||||
|
||||
// 检查Pid
|
||||
func CheckPid(path string) *os.Process {
|
||||
// windows上打开的文件是不能删除的
|
||||
if runtime.GOOS == "windows" {
|
||||
if os.Remove(path) == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = file.Close()
|
||||
}()
|
||||
|
||||
// 是否能取得Lock
|
||||
err = LockFile(file)
|
||||
if err == nil {
|
||||
_ = UnlockFile(file)
|
||||
return nil
|
||||
}
|
||||
|
||||
pidBytes, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
pid := types.Int(string(pidBytes))
|
||||
|
||||
if pid <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
proc, _ := os.FindProcess(pid)
|
||||
return proc
|
||||
}
|
||||
|
||||
// 写入Pid
|
||||
func WritePid(path string) error {
|
||||
fp, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_RDONLY, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if runtime.GOOS != "windows" {
|
||||
err = LockFile(fp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
pidFileList = append(pidFileList, fp) // hold the file pointers
|
||||
|
||||
_, err = fp.WriteString(fmt.Sprintf("%d", os.Getpid()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 写入Ppid
|
||||
func WritePpid(path string) error {
|
||||
fp, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_RDONLY, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if runtime.GOOS != "windows" {
|
||||
err = LockFile(fp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
pidFileList = append(pidFileList, fp) // hold the file pointers
|
||||
|
||||
_, err = fp.WriteString(fmt.Sprintf("%d", os.Getppid()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 删除Pid
|
||||
func DeletePid(path string) error {
|
||||
_, err := os.Stat(path)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
for _, fp := range pidFileList {
|
||||
_ = UnlockFile(fp)
|
||||
_ = fp.Close()
|
||||
}
|
||||
return os.Remove(path)
|
||||
}
|
||||
14
internal/const/const.go
Normal file
14
internal/const/const.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package teaconst
|
||||
|
||||
const (
|
||||
Version = "0.0.1"
|
||||
|
||||
ProductName = "Edge Admin"
|
||||
ProcessName = "edge-admin"
|
||||
|
||||
Role = "admin"
|
||||
|
||||
EncryptKey = "8f983f4d69b83aaa0d74b21a212f6967"
|
||||
|
||||
ErrServer = "服务器出了点小问题,请稍后重试"
|
||||
)
|
||||
5
internal/web/actions.go
Normal file
5
internal/web/actions.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/index"
|
||||
)
|
||||
18
internal/web/actions/default/index/index.go
Normal file
18
internal/web/actions/default/index/index.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package index
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
)
|
||||
|
||||
type IndexAction actions.Action
|
||||
|
||||
// 首页(登录页)
|
||||
|
||||
var TokenSalt = stringutil.Rand(32)
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
From string
|
||||
}) {
|
||||
this.WriteString("Hello, i am index")
|
||||
}
|
||||
13
internal/web/actions/default/index/init.go
Normal file
13
internal/web/actions/default/index/init.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package index
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.Prefix("/").
|
||||
GetPost("", new(IndexAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
9
web/public/js/axios.min.js
vendored
Executable file
9
web/public/js/axios.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
1
web/public/js/axios.min.map
Executable file
1
web/public/js/axios.min.map
Executable file
File diff suppressed because one or more lines are too long
2
web/public/js/md5.min.js
vendored
Executable file
2
web/public/js/md5.min.js
vendored
Executable file
@@ -0,0 +1,2 @@
|
||||
!function(n){"use strict";function d(n,t){var r=(65535&n)+(65535&t);return(n>>16)+(t>>16)+(r>>16)<<16|65535&r}function f(n,t,r,e,o,u){return d(function(n,t){return n<<t|n>>>32-t}(d(d(t,n),d(e,u)),o),r)}function l(n,t,r,e,o,u,c){return f(t&r|~t&e,n,t,o,u,c)}function g(n,t,r,e,o,u,c){return f(t&e|r&~e,n,t,o,u,c)}function v(n,t,r,e,o,u,c){return f(t^r^e,n,t,o,u,c)}function m(n,t,r,e,o,u,c){return f(r^(t|~e),n,t,o,u,c)}function i(n,t){var r,e,o,u,c;n[t>>5]|=128<<t%32,n[14+(t+64>>>9<<4)]=t;var f=1732584193,i=-271733879,a=-1732584194,h=271733878;for(r=0;r<n.length;r+=16)i=m(i=m(i=m(i=m(i=v(i=v(i=v(i=v(i=g(i=g(i=g(i=g(i=l(i=l(i=l(i=l(o=i,a=l(u=a,h=l(c=h,f=l(e=f,i,a,h,n[r],7,-680876936),i,a,n[r+1],12,-389564586),f,i,n[r+2],17,606105819),h,f,n[r+3],22,-1044525330),a=l(a,h=l(h,f=l(f,i,a,h,n[r+4],7,-176418897),i,a,n[r+5],12,1200080426),f,i,n[r+6],17,-1473231341),h,f,n[r+7],22,-45705983),a=l(a,h=l(h,f=l(f,i,a,h,n[r+8],7,1770035416),i,a,n[r+9],12,-1958414417),f,i,n[r+10],17,-42063),h,f,n[r+11],22,-1990404162),a=l(a,h=l(h,f=l(f,i,a,h,n[r+12],7,1804603682),i,a,n[r+13],12,-40341101),f,i,n[r+14],17,-1502002290),h,f,n[r+15],22,1236535329),a=g(a,h=g(h,f=g(f,i,a,h,n[r+1],5,-165796510),i,a,n[r+6],9,-1069501632),f,i,n[r+11],14,643717713),h,f,n[r],20,-373897302),a=g(a,h=g(h,f=g(f,i,a,h,n[r+5],5,-701558691),i,a,n[r+10],9,38016083),f,i,n[r+15],14,-660478335),h,f,n[r+4],20,-405537848),a=g(a,h=g(h,f=g(f,i,a,h,n[r+9],5,568446438),i,a,n[r+14],9,-1019803690),f,i,n[r+3],14,-187363961),h,f,n[r+8],20,1163531501),a=g(a,h=g(h,f=g(f,i,a,h,n[r+13],5,-1444681467),i,a,n[r+2],9,-51403784),f,i,n[r+7],14,1735328473),h,f,n[r+12],20,-1926607734),a=v(a,h=v(h,f=v(f,i,a,h,n[r+5],4,-378558),i,a,n[r+8],11,-2022574463),f,i,n[r+11],16,1839030562),h,f,n[r+14],23,-35309556),a=v(a,h=v(h,f=v(f,i,a,h,n[r+1],4,-1530992060),i,a,n[r+4],11,1272893353),f,i,n[r+7],16,-155497632),h,f,n[r+10],23,-1094730640),a=v(a,h=v(h,f=v(f,i,a,h,n[r+13],4,681279174),i,a,n[r],11,-358537222),f,i,n[r+3],16,-722521979),h,f,n[r+6],23,76029189),a=v(a,h=v(h,f=v(f,i,a,h,n[r+9],4,-640364487),i,a,n[r+12],11,-421815835),f,i,n[r+15],16,530742520),h,f,n[r+2],23,-995338651),a=m(a,h=m(h,f=m(f,i,a,h,n[r],6,-198630844),i,a,n[r+7],10,1126891415),f,i,n[r+14],15,-1416354905),h,f,n[r+5],21,-57434055),a=m(a,h=m(h,f=m(f,i,a,h,n[r+12],6,1700485571),i,a,n[r+3],10,-1894986606),f,i,n[r+10],15,-1051523),h,f,n[r+1],21,-2054922799),a=m(a,h=m(h,f=m(f,i,a,h,n[r+8],6,1873313359),i,a,n[r+15],10,-30611744),f,i,n[r+6],15,-1560198380),h,f,n[r+13],21,1309151649),a=m(a,h=m(h,f=m(f,i,a,h,n[r+4],6,-145523070),i,a,n[r+11],10,-1120210379),f,i,n[r+2],15,718787259),h,f,n[r+9],21,-343485551),f=d(f,e),i=d(i,o),a=d(a,u),h=d(h,c);return[f,i,a,h]}function a(n){var t,r="",e=32*n.length;for(t=0;t<e;t+=8)r+=String.fromCharCode(n[t>>5]>>>t%32&255);return r}function h(n){var t,r=[];for(r[(n.length>>2)-1]=void 0,t=0;t<r.length;t+=1)r[t]=0;var e=8*n.length;for(t=0;t<e;t+=8)r[t>>5]|=(255&n.charCodeAt(t/8))<<t%32;return r}function e(n){var t,r,e="0123456789abcdef",o="";for(r=0;r<n.length;r+=1)t=n.charCodeAt(r),o+=e.charAt(t>>>4&15)+e.charAt(15&t);return o}function r(n){return unescape(encodeURIComponent(n))}function o(n){return function(n){return a(i(h(n),8*n.length))}(r(n))}function u(n,t){return function(n,t){var r,e,o=h(n),u=[],c=[];for(u[15]=c[15]=void 0,16<o.length&&(o=i(o,8*n.length)),r=0;r<16;r+=1)u[r]=909522486^o[r],c[r]=1549556828^o[r];return e=i(u.concat(h(t)),512+8*t.length),a(i(c.concat(e),640))}(r(n),r(t))}function t(n,t,r){return t?r?u(t,n):function(n,t){return e(u(n,t))}(t,n):r?o(n):function(n){return e(o(n))}(n)}"function"==typeof define&&define.amd?define(function(){return t}):"object"==typeof module&&module.exports?module.exports=t:n.md5=t}(this);
|
||||
//# sourceMappingURL=md5.min.js.map
|
||||
1
web/public/js/md5.min.js.map
Executable file
1
web/public/js/md5.min.js.map
Executable file
File diff suppressed because one or more lines are too long
1
web/public/js/moment.min.js
vendored
Normal file
1
web/public/js/moment.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
239
web/public/js/pikaday.css
Executable file
239
web/public/js/pikaday.css
Executable file
@@ -0,0 +1,239 @@
|
||||
@charset "UTF-8";
|
||||
|
||||
/*!
|
||||
* Pikaday
|
||||
* Copyright © 2014 David Bushell | BSD & MIT license | https://dbushell.com/
|
||||
*/
|
||||
|
||||
.pika-single {
|
||||
z-index: 9999;
|
||||
display: block;
|
||||
position: relative;
|
||||
color: #333;
|
||||
background: #fff;
|
||||
border: 1px solid #ccc;
|
||||
border-bottom-color: #bbb;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
/*
|
||||
clear child float (pika-lendar), using the famous micro clearfix hack
|
||||
http://nicolasgallagher.com/micro-clearfix-hack/
|
||||
*/
|
||||
.pika-single:before,
|
||||
.pika-single:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
.pika-single:after { clear: both }
|
||||
.pika-single { *zoom: 1 }
|
||||
|
||||
.pika-single.is-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pika-single.is-bound {
|
||||
position: absolute;
|
||||
box-shadow: 0 5px 15px -5px rgba(0,0,0,.5);
|
||||
}
|
||||
|
||||
.pika-lendar {
|
||||
float: left;
|
||||
width: 240px;
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
.pika-title {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pika-label {
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
position: relative;
|
||||
z-index: 9999;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
padding: 5px 3px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
font-weight: bold;
|
||||
background-color: #fff;
|
||||
}
|
||||
.pika-title select {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
z-index: 9998;
|
||||
margin: 0;
|
||||
left: 0;
|
||||
top: 5px;
|
||||
filter: alpha(opacity=0);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.pika-prev,
|
||||
.pika-next {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
outline: none;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
width: 20px;
|
||||
height: 30px;
|
||||
/* hide text using text-indent trick, using width value (it's enough) */
|
||||
text-indent: 20px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
background-color: transparent;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 75% 75%;
|
||||
opacity: .5;
|
||||
*position: absolute;
|
||||
*top: 0;
|
||||
}
|
||||
|
||||
.pika-prev:hover,
|
||||
.pika-next:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.pika-prev,
|
||||
.is-rtl .pika-next {
|
||||
float: left;
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg==');
|
||||
*left: 0;
|
||||
}
|
||||
|
||||
.pika-next,
|
||||
.is-rtl .pika-prev {
|
||||
float: right;
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII=');
|
||||
*right: 0;
|
||||
}
|
||||
|
||||
.pika-prev.is-disabled,
|
||||
.pika-next.is-disabled {
|
||||
cursor: default;
|
||||
opacity: .2;
|
||||
}
|
||||
|
||||
.pika-select {
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
}
|
||||
|
||||
.pika-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.pika-table th,
|
||||
.pika-table td {
|
||||
width: 14.285714285714286%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.pika-table th {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
line-height: 25px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pika-button {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
outline: none;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: right;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.pika-week {
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.is-today .pika-button {
|
||||
color: #33aaff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.is-selected .pika-button,
|
||||
.has-event .pika-button {
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
background: #33aaff;
|
||||
box-shadow: inset 0 1px 3px #178fe5;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.has-event .pika-button {
|
||||
background: #005da9;
|
||||
box-shadow: inset 0 1px 3px #0076c9;
|
||||
}
|
||||
|
||||
.is-disabled .pika-button,
|
||||
.is-inrange .pika-button {
|
||||
background: #D5E9F7;
|
||||
}
|
||||
|
||||
.is-startrange .pika-button {
|
||||
color: #fff;
|
||||
background: #6CB31D;
|
||||
box-shadow: none;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.is-endrange .pika-button {
|
||||
color: #fff;
|
||||
background: #33aaff;
|
||||
box-shadow: none;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.is-disabled .pika-button {
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
color: #999;
|
||||
opacity: .3;
|
||||
}
|
||||
|
||||
.is-outside-current-month .pika-button {
|
||||
color: #999;
|
||||
opacity: .3;
|
||||
}
|
||||
|
||||
.is-selection-disabled {
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.pika-button:hover,
|
||||
.pika-row.pick-whole-week:hover .pika-button {
|
||||
color: #fff;
|
||||
background: #ff8000;
|
||||
box-shadow: none;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
/* styling for abbr */
|
||||
.pika-table abbr {
|
||||
border-bottom: none;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
1257
web/public/js/pikaday.js
Executable file
1257
web/public/js/pikaday.js
Executable file
File diff suppressed because it is too large
Load Diff
56
web/public/js/pikaday.theme.css
Executable file
56
web/public/js/pikaday.theme.css
Executable file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* This theme is an example to show how you can create your own.
|
||||
*/
|
||||
.pika-single.dark-theme {
|
||||
color: #fff;
|
||||
background: #333;
|
||||
border: 1px solid #666;
|
||||
border-bottom-color: #999;
|
||||
}
|
||||
|
||||
.dark-theme .pika-label {
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
.dark-theme .pika-prev,
|
||||
.dark-theme .is-rtl .pika-next {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAQAAACGG/bgAAAAQ0lEQVR4Ae3KIQ4AIBTD0N0/IeHGI3UIRA3ut/Zl+ltXc5++htVAmIAwAWECwgSEKbgthEoIlRAqIVRCqINQB9nDgQd7ktwFo6UpWQAAAABJRU5ErkJggg==');
|
||||
}
|
||||
|
||||
.dark-theme .pika-next,
|
||||
.dark-theme .is-rtl .pika-prev {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAQAAACGG/bgAAAAP0lEQVQ4y+3TMQoAMAgEwfwfAvvjTZ1uGzuvHhBPPGczEG+FRqqRaqQaqUaqkX6QBmmjacvQ6qEVTjsh+xizebvlaWptGXZAAAAAAElFTkSuQmCC');
|
||||
}
|
||||
|
||||
.dark-theme .pika-table th {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.dark-theme .pika-button {
|
||||
color: #fff;
|
||||
background: #222;
|
||||
}
|
||||
|
||||
.dark-theme .pika-week {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.dark-theme .is-today .pika-button {
|
||||
color: #33aaff;
|
||||
}
|
||||
|
||||
.dark-theme .is-selected .pika-button {
|
||||
color: #fff;
|
||||
background: #33aaff;
|
||||
box-shadow: inset 0 1px 3px #178fe5;
|
||||
}
|
||||
|
||||
.dark-theme .is-disabled .pika-button {
|
||||
color: #999;
|
||||
opacity: .3;
|
||||
}
|
||||
|
||||
.dark-theme .pika-button:hover {
|
||||
color: #fff !important;
|
||||
background: #ff8000 !important;
|
||||
}
|
||||
6
web/public/js/pikaday.triangle.css
Executable file
6
web/public/js/pikaday.triangle.css
Executable file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* This theme is an example to show how you can add triangles to the dropdowns.
|
||||
*/
|
||||
.triangle-theme .pika-label:after {
|
||||
content:" \25be";
|
||||
}
|
||||
11
web/public/js/semantic.min.js
vendored
Executable file
11
web/public/js/semantic.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
2
web/public/js/sortable.min.js
vendored
Normal file
2
web/public/js/sortable.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
212
web/public/js/utils.js
Normal file
212
web/public/js/utils.js
Normal file
@@ -0,0 +1,212 @@
|
||||
window.teaweb = {
|
||||
set: function (key, value) {
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
},
|
||||
get: function (key) {
|
||||
var item = localStorage.getItem(key);
|
||||
if (item == null || item.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return JSON.parse(item);
|
||||
},
|
||||
getString: function (key) {
|
||||
var value = this.get(key);
|
||||
if (typeof (value) == "string") {
|
||||
return value;
|
||||
}
|
||||
return "";
|
||||
},
|
||||
getBool: function (key) {
|
||||
return Boolean(this.get(key));
|
||||
},
|
||||
remove: function (key) {
|
||||
localStorage.removeItem(key)
|
||||
},
|
||||
match: function (source, keyword) {
|
||||
if (source == null) {
|
||||
return false;
|
||||
}
|
||||
if (keyword == null) {
|
||||
return true;
|
||||
}
|
||||
source = source.trim();
|
||||
keyword = keyword.trim();
|
||||
if (keyword.length == 0) {
|
||||
return true;
|
||||
}
|
||||
if (source.length == 0) {
|
||||
return false;
|
||||
}
|
||||
var pieces = keyword.split(/\s+/);
|
||||
for (var i = 0; i < pieces.length; i++) {
|
||||
var pattern = pieces[i];
|
||||
pattern = pattern.replace(/(\+|\*|\?|[|]|{|}|\||\\|\(|\)|\.)/g, "\\$1");
|
||||
var reg = new RegExp(pattern, "i");
|
||||
if (!reg.test(source)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
datepicker: function (element, callback) {
|
||||
if (typeof (element) == "string") {
|
||||
element = document.getElementById(element);
|
||||
}
|
||||
var year = new Date().getFullYear();
|
||||
var picker = new Pikaday({
|
||||
field: element,
|
||||
firstDay: 1,
|
||||
minDate: new Date(year - 1, 0, 1),
|
||||
maxDate: new Date(year + 10, 11, 31),
|
||||
yearRange: [year - 1, year + 10],
|
||||
format: "YYYY-MM-DD",
|
||||
i18n: {
|
||||
previousMonth: '上月',
|
||||
nextMonth: '下月',
|
||||
months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
|
||||
weekdays: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
|
||||
weekdaysShort: ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
|
||||
},
|
||||
theme: 'triangle-theme',
|
||||
onSelect: function () {
|
||||
if (typeof (callback) == "function") {
|
||||
callback.call(Tea.Vue, picker.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
formatBytes: function (bytes) {
|
||||
bytes = Math.ceil(bytes);
|
||||
if (bytes < 1024) {
|
||||
return bytes + " bytes";
|
||||
}
|
||||
if (bytes < 1024 * 1024) {
|
||||
return (Math.ceil(bytes * 100 / 1024) / 100) + " k";
|
||||
}
|
||||
return (Math.ceil(bytes * 100 / 1024 / 1024) / 100) + " m";
|
||||
},
|
||||
|
||||
popup: function (url, options) {
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
var width = "40em";
|
||||
var height = "20em";
|
||||
window.POPUP_CALLBACK = function () {
|
||||
Swal.close();
|
||||
};
|
||||
|
||||
if (options["width"] != null) {
|
||||
width = options["width"];
|
||||
}
|
||||
if (options["height"] != null) {
|
||||
height = options["height"];
|
||||
}
|
||||
if (typeof (options["callback"]) == "function") {
|
||||
window.POPUP_CALLBACK = function () {
|
||||
Swal.close();
|
||||
options["callback"].apply(window, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
Swal.fire({
|
||||
html: '<iframe src="' + url + '#popup-' + width + '" style="border:0; width: 100%; height:' + height + '"></iframe>',
|
||||
width: width,
|
||||
padding: "0.5em",
|
||||
showConfirmButton: false,
|
||||
showCloseButton: true,
|
||||
focusConfirm: false
|
||||
});
|
||||
},
|
||||
popupFinish: function () {
|
||||
if (window.POPUP_CALLBACK != null) {
|
||||
window.POPUP_CALLBACK.apply(window, arguments);
|
||||
}
|
||||
},
|
||||
isPopup: function () {
|
||||
var hash = window.location.hash;
|
||||
return hash != null && hash.startsWith("#popup");
|
||||
},
|
||||
Swal: function () {
|
||||
return this.isPopup() ? window.parent.Swal : window.Swal;
|
||||
},
|
||||
success: function (message, callback) {
|
||||
var width = "20em";
|
||||
if (message.length > 30) {
|
||||
width = "30em";
|
||||
}
|
||||
Swal.fire({
|
||||
text: message,
|
||||
confirmButtonText: "确定",
|
||||
buttonsStyling: false,
|
||||
icon: "success",
|
||||
customClass: {
|
||||
closeButton: "ui button",
|
||||
cancelButton: "ui button",
|
||||
confirmButton: "ui button primary"
|
||||
},
|
||||
width: width,
|
||||
onAfterClose: function () {
|
||||
if (typeof (callback) == "function") {
|
||||
setTimeout(function () {
|
||||
callback();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
warn: function (message, callback) {
|
||||
var width = "20em";
|
||||
if (message.length > 30) {
|
||||
width = "30em";
|
||||
}
|
||||
Swal.fire({
|
||||
text: message,
|
||||
confirmButtonText: "确定",
|
||||
buttonsStyling: false,
|
||||
customClass: {
|
||||
closeButton: "ui button",
|
||||
cancelButton: "ui button",
|
||||
confirmButton: "ui button primary"
|
||||
},
|
||||
icon: "warning",
|
||||
width: width,
|
||||
onAfterClose: function () {
|
||||
if (typeof (callback) == "function") {
|
||||
setTimeout(function () {
|
||||
callback();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
confirm: function (message, callback) {
|
||||
var width = "20em";
|
||||
if (message.length > 30) {
|
||||
width = "30em";
|
||||
}
|
||||
Swal.fire({
|
||||
text: message,
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
showCancelButton: true,
|
||||
showCloseButton: false,
|
||||
buttonsStyling: false,
|
||||
customClass: {
|
||||
closeButton: "ui button",
|
||||
cancelButton: "ui button",
|
||||
confirmButton: "ui button primary"
|
||||
},
|
||||
icon: "warning",
|
||||
width: width,
|
||||
preConfirm: function () {
|
||||
if (typeof (callback) == "function") {
|
||||
callback.call(Tea.Vue);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
10978
web/public/js/vue.js
Normal file
10978
web/public/js/vue.js
Normal file
File diff suppressed because it is too large
Load Diff
6
web/public/js/vue.min.js
vendored
Normal file
6
web/public/js/vue.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1155
web/public/js/vue.tea.js
Normal file
1155
web/public/js/vue.tea.js
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user