mirror of
https://github.com/nezhahq/nezha.git
synced 2025-02-08 12:38:13 -05:00
持久化Token
This commit is contained in:
parent
af146872fe
commit
70f0e92343
@ -26,18 +26,17 @@ func (cp *commonPage) serve() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cp *commonPage) home(c *gin.Context) {
|
func (cp *commonPage) home(c *gin.Context) {
|
||||||
var admin *model.User
|
|
||||||
isLogin, ok := c.Get(model.CtxKeyIsUserLogin)
|
|
||||||
if ok && isLogin.(bool) {
|
|
||||||
admin = dao.Admin
|
|
||||||
}
|
|
||||||
dao.ServerLock.RLock()
|
dao.ServerLock.RLock()
|
||||||
defer dao.ServerLock.RUnlock()
|
defer dao.ServerLock.RUnlock()
|
||||||
c.HTML(http.StatusOK, "page/home", mygin.CommonEnvironment(c, gin.H{
|
data := gin.H{
|
||||||
"Admin": admin,
|
|
||||||
"Domain": dao.Conf.Site.Domain,
|
"Domain": dao.Conf.Site.Domain,
|
||||||
"Servers": dao.ServerList,
|
"Servers": dao.ServerList,
|
||||||
}))
|
}
|
||||||
|
u, ok := c.Get(model.CtxKeyAuthorizedUser)
|
||||||
|
if ok {
|
||||||
|
data["Admin"] = u
|
||||||
|
}
|
||||||
|
c.HTML(http.StatusOK, "page/home", mygin.CommonEnvironment(c, data))
|
||||||
}
|
}
|
||||||
|
|
||||||
var upgrader = websocket.Upgrader{}
|
var upgrader = websocket.Upgrader{}
|
||||||
|
@ -28,14 +28,16 @@ func (ma *memberAPI) serve() {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
mr.POST("/logout", ma.logout)
|
mr.POST("/logout", ma.logout)
|
||||||
mr.POST("/server", ma.addServer)
|
mr.POST("/server", ma.addOrEditServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
type serverForm struct {
|
type serverForm struct {
|
||||||
|
ID uint64
|
||||||
Name string `binding:"required"`
|
Name string `binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ma *memberAPI) addServer(c *gin.Context) {
|
func (ma *memberAPI) addOrEditServer(c *gin.Context) {
|
||||||
|
admin := c.MustGet(model.CtxKeyAuthorizedUser).(*model.User)
|
||||||
var sf serverForm
|
var sf serverForm
|
||||||
var s model.Server
|
var s model.Server
|
||||||
err := c.ShouldBindJSON(&sf)
|
err := c.ShouldBindJSON(&sf)
|
||||||
@ -43,9 +45,13 @@ func (ma *memberAPI) addServer(c *gin.Context) {
|
|||||||
dao.ServerLock.Lock()
|
dao.ServerLock.Lock()
|
||||||
defer dao.ServerLock.Unlock()
|
defer dao.ServerLock.Unlock()
|
||||||
s.Name = sf.Name
|
s.Name = sf.Name
|
||||||
s.Secret = com.MD5(fmt.Sprintf("%s%s%d", time.Now(), sf.Name, dao.Admin.ID))
|
}
|
||||||
|
if sf.ID == 0 {
|
||||||
|
s.Secret = com.MD5(fmt.Sprintf("%s%s%d", time.Now(), sf.Name, admin.ID))
|
||||||
s.Secret = s.Secret[:10]
|
s.Secret = s.Secret[:10]
|
||||||
err = dao.DB.Create(&s).Error
|
err = dao.DB.Create(&s).Error
|
||||||
|
} else {
|
||||||
|
err = dao.DB.Save(&s).Error
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusOK, model.Response{
|
c.JSON(http.StatusOK, model.Response{
|
||||||
@ -65,6 +71,7 @@ type logoutForm struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ma *memberAPI) logout(c *gin.Context) {
|
func (ma *memberAPI) logout(c *gin.Context) {
|
||||||
|
admin := c.MustGet(model.CtxKeyAuthorizedUser).(*model.User)
|
||||||
var lf logoutForm
|
var lf logoutForm
|
||||||
if err := c.ShouldBindJSON(&lf); err != nil {
|
if err := c.ShouldBindJSON(&lf); err != nil {
|
||||||
c.JSON(http.StatusOK, model.Response{
|
c.JSON(http.StatusOK, model.Response{
|
||||||
@ -73,15 +80,17 @@ func (ma *memberAPI) logout(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if lf.ID != dao.Admin.ID {
|
if lf.ID != admin.ID {
|
||||||
c.JSON(http.StatusOK, model.Response{
|
c.JSON(http.StatusOK, model.Response{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
Message: fmt.Sprintf("请求错误:%s", "用户ID不匹配"),
|
Message: fmt.Sprintf("请求错误:%s", "用户ID不匹配"),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
dao.Admin.Token = ""
|
dao.DB.Model(admin).UpdateColumns(model.User{
|
||||||
dao.Admin.TokenExpired = time.Now()
|
Token: "",
|
||||||
|
TokenExpired: time.Now(),
|
||||||
|
})
|
||||||
c.JSON(http.StatusOK, model.Response{
|
c.JSON(http.StatusOK, model.Response{
|
||||||
Code: http.StatusOK,
|
Code: http.StatusOK,
|
||||||
})
|
})
|
||||||
|
@ -75,9 +75,9 @@ func (oa *oauth2controller) callback(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
user := model.NewUserFromGitHub(gu)
|
user := model.NewUserFromGitHub(gu)
|
||||||
dao.Admin = &user
|
user.IssueNewToken()
|
||||||
dao.Admin.IssueNewToken()
|
dao.DB.Save(&user)
|
||||||
c.SetCookie(dao.Conf.Site.CookieName, dao.Admin.Token, 60*60*24*14, "", "", false, false)
|
c.SetCookie(dao.Conf.Site.CookieName, user.Token, 60*60*24*14, "", "", false, false)
|
||||||
c.Status(http.StatusOK)
|
c.Status(http.StatusOK)
|
||||||
c.Writer.WriteString("<script>window.location.href='/'</script>")
|
c.Writer.WriteString("<script>window.location.href='/'</script>")
|
||||||
}
|
}
|
||||||
|
@ -17,13 +17,11 @@ import (
|
|||||||
func init() {
|
func init() {
|
||||||
var err error
|
var err error
|
||||||
dao.ServerList = make(map[string]*model.Server)
|
dao.ServerList = make(map[string]*model.Server)
|
||||||
dao.Conf, err = model.ReadInConfig("data/config.yaml")
|
dao.Conf = &model.Config{}
|
||||||
|
err = dao.Conf.Read("data/config.yaml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
dao.Admin = &model.User{
|
|
||||||
Login: dao.Conf.GitHub.Admin,
|
|
||||||
}
|
|
||||||
dao.DB, err = gorm.Open("sqlite3", "data/sqlite.db")
|
dao.DB, err = gorm.Open("sqlite3", "data/sqlite.db")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -36,7 +34,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func initDB() {
|
func initDB() {
|
||||||
dao.DB.AutoMigrate(model.Server{})
|
dao.DB.AutoMigrate(model.Server{}, model.User{})
|
||||||
// load cache
|
// load cache
|
||||||
var servers []model.Server
|
var servers []model.Server
|
||||||
dao.DB.Find(&servers)
|
dao.DB.Find(&servers)
|
||||||
|
2
go.mod
2
go.mod
@ -22,7 +22,5 @@ require (
|
|||||||
github.com/spf13/viper v1.6.1
|
github.com/spf13/viper v1.6.1
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
||||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 // indirect
|
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 // indirect
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 // indirect
|
|
||||||
google.golang.org/grpc v1.25.1
|
google.golang.org/grpc v1.25.1
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc // indirect
|
|
||||||
)
|
)
|
||||||
|
@ -2,8 +2,8 @@ package model
|
|||||||
|
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
// CtxKeyIsUserLogin ..
|
// CtxKeyAuthorizedUser ..
|
||||||
const CtxKeyIsUserLogin = "ckiul"
|
const CtxKeyAuthorizedUser = "ckau"
|
||||||
|
|
||||||
// CtxKeyOauth2State ..
|
// CtxKeyOauth2State ..
|
||||||
const CtxKeyOauth2State = "cko2s"
|
const CtxKeyOauth2State = "cko2s"
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
@ -18,26 +20,29 @@ type Config struct {
|
|||||||
ClientID string
|
ClientID string
|
||||||
ClientSecret string
|
ClientSecret string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v *viper.Viper
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadInConfig ..
|
// ReadInConfig ..
|
||||||
func ReadInConfig(path string) (*Config, error) {
|
func (c *Config) Read(path string) error {
|
||||||
viper.SetConfigFile(path)
|
c.v = viper.New()
|
||||||
err := viper.ReadInConfig()
|
c.v.SetConfigFile(path)
|
||||||
|
err := c.v.ReadInConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
|
||||||
var c Config
|
|
||||||
|
|
||||||
err = viper.Unmarshal(&c)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
viper.OnConfigChange(func(in fsnotify.Event) {
|
err = c.v.Unmarshal(c)
|
||||||
viper.Unmarshal(&c)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.v.OnConfigChange(func(in fsnotify.Event) {
|
||||||
|
fmt.Println("配置文件更新,重载配置")
|
||||||
|
c.v.Unmarshal(c)
|
||||||
})
|
})
|
||||||
|
|
||||||
go viper.WatchConfig()
|
go c.v.WatchConfig()
|
||||||
return &c, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -48,5 +48,5 @@ func NewUserFromGitHub(gu *github.User) User {
|
|||||||
// IssueNewToken ...
|
// IssueNewToken ...
|
||||||
func (u *User) IssueNewToken() {
|
func (u *User) IssueNewToken() {
|
||||||
u.Token = com.MD5(fmt.Sprintf("%d%d%s", time.Now().UnixNano(), u.ID, u.Login))
|
u.Token = com.MD5(fmt.Sprintf("%d%d%s", time.Now().UnixNano(), u.ID, u.Login))
|
||||||
u.TokenExpired = time.Now().AddDate(0, 0, 14)
|
u.TokenExpired = time.Now().AddDate(0, 2, 0)
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package mygin
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@ -24,6 +25,7 @@ type AuthorizeOption struct {
|
|||||||
func Authorize(opt AuthorizeOption) func(*gin.Context) {
|
func Authorize(opt AuthorizeOption) func(*gin.Context) {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
token, err := c.Cookie(dao.Conf.Site.CookieName)
|
token, err := c.Cookie(dao.Conf.Site.CookieName)
|
||||||
|
token = strings.TrimSpace(token)
|
||||||
var code uint64 = http.StatusForbidden
|
var code uint64 = http.StatusForbidden
|
||||||
if opt.Guest {
|
if opt.Guest {
|
||||||
code = http.StatusBadRequest
|
code = http.StatusBadRequest
|
||||||
@ -35,12 +37,18 @@ func Authorize(opt AuthorizeOption) func(*gin.Context) {
|
|||||||
Link: opt.Redirect,
|
Link: opt.Redirect,
|
||||||
Btn: opt.Btn,
|
Btn: opt.Btn,
|
||||||
}
|
}
|
||||||
var isLogin bool
|
if token != "" {
|
||||||
if err == nil {
|
|
||||||
isLogin = token == dao.Admin.Token && dao.Admin.Token != "" &&
|
}
|
||||||
dao.Admin.TokenExpired.After(time.Now())
|
var isLogin bool
|
||||||
|
var u model.User
|
||||||
|
err = dao.DB.Where("token = ?", token).First(&u).Error
|
||||||
|
if err == nil {
|
||||||
|
isLogin = u.TokenExpired.After(time.Now())
|
||||||
|
}
|
||||||
|
if isLogin {
|
||||||
|
c.Set(model.CtxKeyAuthorizedUser, &u)
|
||||||
}
|
}
|
||||||
c.Set(model.CtxKeyIsUserLogin, isLogin)
|
|
||||||
// 已登录且只能游客访问
|
// 已登录且只能游客访问
|
||||||
if isLogin && opt.Guest {
|
if isLogin && opt.Guest {
|
||||||
ShowErrorPage(c, commonErr, opt.IsPage)
|
ShowErrorPage(c, commonErr, opt.IsPage)
|
||||||
|
@ -20,9 +20,9 @@ func CommonEnvironment(c *gin.Context, data map[string]interface{}) gin.H {
|
|||||||
} else {
|
} else {
|
||||||
data["Title"] = fmt.Sprintf("%s - %s", t, dao.Conf.Site.Brand)
|
data["Title"] = fmt.Sprintf("%s - %s", t, dao.Conf.Site.Brand)
|
||||||
}
|
}
|
||||||
isLogin, ok := c.Get(model.CtxKeyIsUserLogin)
|
u, ok := c.Get(model.CtxKeyAuthorizedUser)
|
||||||
if ok && isLogin.(bool) {
|
if ok {
|
||||||
data["Admin"] = dao.Admin
|
data["Admin"] = u
|
||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
@ -153,8 +153,10 @@
|
|||||||
}
|
}
|
||||||
// 刷新进度条
|
// 刷新进度条
|
||||||
bars.forEach((b, i) => {
|
bars.forEach((b, i) => {
|
||||||
b.progress('set total', i == 0 ? 100 : b[0].dataset.total);
|
if (b[0] && b[0].dataset) {
|
||||||
b.progress('update progress', b[0].dataset.value);
|
b.progress('set total', i == 0 ? 100 : b[0].dataset.total);
|
||||||
|
b.progress('update progress', b[0].dataset.value);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
{{template "common/menu" .}}
|
{{template "common/menu" .}}
|
||||||
<div class="nb-container">
|
<div class="nb-container">
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<button class="ui right labeled positive icon button" onclick="addServer()"><i class="add icon"></i> 添加服务器
|
<button class="ui right labeled positive icon button" onclick="addOrEditServer()"><i class="add icon"></i> 添加服务器
|
||||||
</button>
|
</button>
|
||||||
<table class="ui very basic table">
|
<table class="ui very basic table">
|
||||||
<thead>
|
<thead>
|
||||||
@ -11,6 +11,7 @@
|
|||||||
<th>ID</th>
|
<th>ID</th>
|
||||||
<th>备注</th>
|
<th>备注</th>
|
||||||
<th>密钥</th>
|
<th>密钥</th>
|
||||||
|
<th>管理</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -19,6 +20,16 @@
|
|||||||
<td>{{$server.ID}}</td>
|
<td>{{$server.ID}}</td>
|
||||||
<td>{{$server.Name}}</td>
|
<td>{{$server.Name}}</td>
|
||||||
<td>{{$server.Secret}}</td>
|
<td>{{$server.Secret}}</td>
|
||||||
|
<td>
|
||||||
|
<div class="ui mini icon buttons">
|
||||||
|
<button class="ui button" onclick="addOrEditServer({{$server}})">
|
||||||
|
<i class="edit icon"></i>
|
||||||
|
</button>
|
||||||
|
<button class="ui button">
|
||||||
|
<i class="delete icon"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -19,9 +19,6 @@ var Cache *cache.Cache
|
|||||||
// DB ..
|
// DB ..
|
||||||
var DB *gorm.DB
|
var DB *gorm.DB
|
||||||
|
|
||||||
// Admin ..
|
|
||||||
var Admin *model.User
|
|
||||||
|
|
||||||
// ServerList ..
|
// ServerList ..
|
||||||
var ServerList map[string]*model.Server
|
var ServerList map[string]*model.Server
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user