[2024-08-06](UPDATE): 创建用户
This commit is contained in:
parent
57fdfbf8a8
commit
84a5c80057
65
corelib/page.go
Normal file
65
corelib/page.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package corelib
|
||||||
|
|
||||||
|
type PaginationData struct {
|
||||||
|
LeftPages []int `json:"left_pages"` // 当前页左边显示
|
||||||
|
RightPages []int `json:"right_pages"` // 当前页右边
|
||||||
|
CurrentPage int `json:"current_page"` // 当前页
|
||||||
|
NumPages int `json:"num_pages"` // 总页数
|
||||||
|
LeftHasMore bool `json:"left_has_more"` // 左边显示更多
|
||||||
|
RightHasMore bool `json:"right_has_more"` // 右边显示更多
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPaginationData 分页数据
|
||||||
|
// numPages 总页数
|
||||||
|
// currentPage 当前页
|
||||||
|
// aroundCount 当前页左右两边显示多少个分页按钮
|
||||||
|
func GetPaginationData(numPages int, currentPage int, aroundCount int) PaginationData {
|
||||||
|
// 边缘条件判断
|
||||||
|
if currentPage > numPages {
|
||||||
|
currentPage = numPages
|
||||||
|
}
|
||||||
|
if currentPage < 1 {
|
||||||
|
currentPage = 1
|
||||||
|
}
|
||||||
|
// 当前页向左减去around_count后剩下的页数
|
||||||
|
leftAroundPages := currentPage - aroundCount
|
||||||
|
// 当前页向右减去around_count后剩下的页数
|
||||||
|
rightAroundPages := numPages - currentPage - aroundCount + 1
|
||||||
|
// 左边还有更多页
|
||||||
|
leftHasMore := false
|
||||||
|
// 右边还有更多页
|
||||||
|
rightHasMore := false
|
||||||
|
// 当前页左边显示页码
|
||||||
|
var leftPages []int
|
||||||
|
// 当前页右边显示页码
|
||||||
|
var rightPages []int
|
||||||
|
|
||||||
|
if leftAroundPages <= 1 || currentPage-aroundCount-1 == 1 {
|
||||||
|
leftPages = rangeList(1, currentPage-1)
|
||||||
|
} else {
|
||||||
|
leftHasMore = true
|
||||||
|
leftPages = rangeList(leftAroundPages, currentPage-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rightAroundPages <= 1 || currentPage+aroundCount+1 == numPages {
|
||||||
|
rightPages = rangeList(currentPage+1, numPages)
|
||||||
|
} else {
|
||||||
|
rightHasMore = true
|
||||||
|
rightPages = rangeList(currentPage+1, currentPage+aroundCount)
|
||||||
|
}
|
||||||
|
return PaginationData{
|
||||||
|
LeftPages: leftPages, RightPages: rightPages, CurrentPage: currentPage,
|
||||||
|
NumPages: numPages, LeftHasMore: leftHasMore, RightHasMore: rightHasMore}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeftPages RightPages
|
||||||
|
func rangeList(start int, end int) []int {
|
||||||
|
var list []int
|
||||||
|
for i := start; i <= end; i++ {
|
||||||
|
list = append(list, i)
|
||||||
|
}
|
||||||
|
if list == nil {
|
||||||
|
return []int{}
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
17
data/user.go
17
data/user.go
@ -7,6 +7,15 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// SysUserExists 用户是否存在
|
||||||
|
func SysUserExists(userName string) bool {
|
||||||
|
if _, err := SysUserSelectByUsername(userName); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// SysUserSelectByUsername 通过用户名查询用户
|
// SysUserSelectByUsername 通过用户名查询用户
|
||||||
func SysUserSelectByUsername(userName string) (model.SysUser, error) {
|
func SysUserSelectByUsername(userName string) (model.SysUser, error) {
|
||||||
var (
|
var (
|
||||||
@ -17,7 +26,8 @@ func SysUserSelectByUsername(userName string) (model.SysUser, error) {
|
|||||||
corelib.Logger.Infoln("SysUserSelectByUsername 从缓存获取用户信息成功: ", user.Username)
|
corelib.Logger.Infoln("SysUserSelectByUsername 从缓存获取用户信息成功: ", user.Username)
|
||||||
return user, nil
|
return user, nil
|
||||||
} else {
|
} else {
|
||||||
corelib.Logger.Infoln("SysUserSelectByUsername 从缓存获取用户信息失败: ", err)
|
err = nil
|
||||||
|
corelib.Logger.Infoln("SysUserSelectByUsername 从缓存获取用户信息失败: ", user.Username)
|
||||||
}
|
}
|
||||||
if err = corelib.DbMysql.Model(model.SysUser{Username: userName}).First(&user).Error; err != nil {
|
if err = corelib.DbMysql.Model(model.SysUser{Username: userName}).First(&user).Error; err != nil {
|
||||||
corelib.Logger.Infoln("SysUserSelectByUsername 数据库中查询用户信息失败: ", err)
|
corelib.Logger.Infoln("SysUserSelectByUsername 数据库中查询用户信息失败: ", err)
|
||||||
@ -60,3 +70,8 @@ func SysUserGetCacheByUsername(userName string) (model.SysUser, error) {
|
|||||||
}
|
}
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SysUserCreate 新建用户
|
||||||
|
func SysUserCreate(user model.SysUser) error {
|
||||||
|
return corelib.DbMysql.Create(&user).Error
|
||||||
|
}
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CaptchaHandler(w http.ResponseWriter, r *http.Request) {
|
func CaptchaApiHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
var (
|
||||||
res []byte
|
res []byte
|
||||||
err error
|
err error
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
"picgo/model"
|
"picgo/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func DomainHandler(w http.ResponseWriter, r *http.Request) {
|
func DomainPageHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method == "GET" {
|
if r.Method == "GET" {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
@ -27,5 +27,7 @@ func DomainHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
tmpData["IsSuper"] = user.IsSuper
|
tmpData["IsSuper"] = user.IsSuper
|
||||||
w.Header().Add("X-CSRF-Token", csrf.Token(r))
|
w.Header().Add("X-CSRF-Token", csrf.Token(r))
|
||||||
corelib.TemplateHandler(w, tmpData, "layout.html", "view/layout.html", "view/domain.html")
|
corelib.TemplateHandler(w, tmpData, "layout.html", "view/layout.html", "view/domain.html")
|
||||||
|
} else {
|
||||||
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
"picgo/model"
|
"picgo/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func IndexHandler(w http.ResponseWriter, r *http.Request) {
|
func IndexPageHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method == "GET" {
|
if r.Method == "GET" {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
@ -27,5 +27,7 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
tmpData["IsSuper"] = user.IsSuper
|
tmpData["IsSuper"] = user.IsSuper
|
||||||
w.Header().Add("X-CSRF-Token", csrf.Token(r))
|
w.Header().Add("X-CSRF-Token", csrf.Token(r))
|
||||||
corelib.TemplateHandler(w, tmpData, "layout.html", "view/layout.html", "view/index.html")
|
corelib.TemplateHandler(w, tmpData, "layout.html", "view/layout.html", "view/index.html")
|
||||||
|
} else {
|
||||||
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,63 +11,61 @@ import (
|
|||||||
"picgo/model"
|
"picgo/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
func LoginPageHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
switch r.Method {
|
switch r.Method {
|
||||||
case http.MethodGet:
|
case http.MethodGet:
|
||||||
tmpData := map[string]interface{}{
|
tmpData := map[string]interface{}{
|
||||||
csrf.TemplateTag: csrf.TemplateField(r),
|
csrf.TemplateTag: csrf.TemplateField(r),
|
||||||
}
|
}
|
||||||
corelib.TemplateHandler(w, tmpData, "login.html", "view/login.html")
|
corelib.TemplateHandler(w, tmpData, "login.html", "view/login.html")
|
||||||
case http.MethodPost:
|
|
||||||
loginService(w, r)
|
|
||||||
default:
|
default:
|
||||||
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func loginService(w http.ResponseWriter, r *http.Request) {
|
func LoginApiHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
switch r.Method {
|
||||||
res model.LoginRequest
|
case http.MethodPost:
|
||||||
user model.SysUser
|
var (
|
||||||
)
|
err error
|
||||||
|
res model.LoginRequest
|
||||||
|
user model.SysUser
|
||||||
|
)
|
||||||
|
|
||||||
err := json.NewDecoder(r.Body).Decode(&res)
|
if err = json.NewDecoder(r.Body).Decode(&res); err != nil {
|
||||||
if err != nil {
|
corelib.Logger.Error("LoginApiHandler, 参数获取失败")
|
||||||
http.Error(w, "Bad Request", http.StatusBadRequest)
|
corelib.WriteJsonResponse(w, 400, "参数错误", nil)
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
if (res.Username == "") || (res.Password == "") {
|
||||||
|
corelib.Logger.Error("LoginApiHandler, 用户名或者密码为空")
|
||||||
|
corelib.WriteJsonResponse(w, 400, "请输入用户名密码", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cid := getCaptchaId(r)
|
||||||
|
if ok := captcha.Verify(cid, res.Captcha); !ok {
|
||||||
|
corelib.WriteJsonResponse(w, 1040, "验证码错误", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if user, err = data.SysUserSelectByUsername(res.Username); err != nil {
|
||||||
|
corelib.WriteJsonResponse(w, 1041, "用户不存在", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 验证用户名密码
|
||||||
|
if !corelib.ComparePasswords(user.Password, res.Password, user.Salt) {
|
||||||
|
corelib.WriteJsonResponse(w, 1042, "用户名或密码错误", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
session, _ := corelib.SessionStore.Get(r, configs.Settings.Server.SessionName)
|
||||||
|
session.Values["username"] = user.Username
|
||||||
|
if err = session.Save(r, w); err != nil {
|
||||||
|
corelib.Logger.Infoln("session save err:", err)
|
||||||
|
corelib.WriteJsonResponse(w, 1043, "会话保存失败", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
corelib.WriteJsonResponse(w, 200, "登录成功", nil)
|
||||||
|
default:
|
||||||
|
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
||||||
}
|
}
|
||||||
cid := getCaptchaId(r)
|
|
||||||
corelib.Logger.Info("LoginRequest: ", res)
|
|
||||||
corelib.Logger.Info("login cid: ", cid)
|
|
||||||
if ok := captcha.Verify(cid, res.Captcha); !ok {
|
|
||||||
corelib.WriteJsonResponse(w, 1040, "验证码错误", nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if user, err = data.SysUserSelectByUsername(res.Username); err != nil {
|
|
||||||
corelib.WriteJsonResponse(w, 1041, "用户不存在", nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 验证用户名密码
|
|
||||||
if !corelib.ComparePasswords(user.Password, res.Password, user.Salt) {
|
|
||||||
corelib.WriteJsonResponse(w, 1042, "用户名或密码错误", nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
session, _ := corelib.SessionStore.Get(r, configs.Settings.Server.SessionName)
|
|
||||||
session.Values["username"] = user.Username
|
|
||||||
if err = session.Save(r, w); err != nil {
|
|
||||||
corelib.Logger.Infoln("session save err:", err)
|
|
||||||
corelib.WriteJsonResponse(w, 1043, "会话保存失败", nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
corelib.WriteJsonResponse(w, 200, "登录成功", nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//// sysUserSelectDataByUsername 通过用户名查询用户
|
|
||||||
//func sysUserSelectDataByUsername(userName string) (model.SysUser, error) {
|
|
||||||
// var user model.SysUser
|
|
||||||
// if err := corelib.DbMysql.Model(model.SysUser{Username: userName}).First(&user).Error; err != nil {
|
|
||||||
// return user, err
|
|
||||||
// }
|
|
||||||
// return user, nil
|
|
||||||
//}
|
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
"picgo/model"
|
"picgo/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func PictureHandler(w http.ResponseWriter, r *http.Request) {
|
func PicturePageHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method == "GET" {
|
if r.Method == "GET" {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
@ -28,5 +28,7 @@ func PictureHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
w.Header().Add("X-CSRF-Token", csrf.Token(r))
|
w.Header().Add("X-CSRF-Token", csrf.Token(r))
|
||||||
corelib.TemplateHandler(w, tmpData, "layout.html", "view/layout.html", "view/picture.html")
|
corelib.TemplateHandler(w, tmpData, "layout.html", "view/layout.html", "view/picture.html")
|
||||||
|
|
||||||
|
} else {
|
||||||
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
"picgo/corelib"
|
"picgo/corelib"
|
||||||
)
|
)
|
||||||
|
|
||||||
func UploadFileHandler(w http.ResponseWriter, r *http.Request) {
|
func UploadFileApiHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method == "POST" {
|
if r.Method == "POST" {
|
||||||
// 解析表单数据,限制最大内存为3MB 1024*1024*3
|
// 解析表单数据,限制最大内存为3MB 1024*1024*3
|
||||||
err := r.ParseMultipartForm(configs.Settings.Server.UploadMaxSize << 20)
|
err := r.ParseMultipartForm(configs.Settings.Server.UploadMaxSize << 20)
|
||||||
|
105
handler/user.go
105
handler/user.go
@ -1,14 +1,17 @@
|
|||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"github.com/gorilla/csrf"
|
"github.com/gorilla/csrf"
|
||||||
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
"picgo/corelib"
|
"picgo/corelib"
|
||||||
"picgo/data"
|
"picgo/data"
|
||||||
"picgo/model"
|
"picgo/model"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
func UserHandler(w http.ResponseWriter, r *http.Request) {
|
func UserPageHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method == "GET" {
|
if r.Method == "GET" {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
@ -27,6 +30,104 @@ func UserHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
tmpData["IsSuper"] = user.IsSuper
|
tmpData["IsSuper"] = user.IsSuper
|
||||||
w.Header().Add("X-CSRF-Token", csrf.Token(r))
|
w.Header().Add("X-CSRF-Token", csrf.Token(r))
|
||||||
corelib.TemplateHandler(w, tmpData, "layout.html", "view/layout.html", "view/user.html")
|
corelib.TemplateHandler(w, tmpData, "layout.html", "view/layout.html", "view/user.html")
|
||||||
|
} else {
|
||||||
|
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UserCreateApiHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method == http.MethodPost {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
res model.UserCreateRequest
|
||||||
|
user model.SysUser
|
||||||
|
password string
|
||||||
|
salt string
|
||||||
|
)
|
||||||
|
|
||||||
|
if err = json.NewDecoder(r.Body).Decode(&res); err != nil {
|
||||||
|
corelib.Logger.Error("UserCreateApiHandler, 参数获取失败")
|
||||||
|
corelib.WriteJsonResponse(w, 400, "参数错误", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.Username == "") || (res.Password == "") {
|
||||||
|
corelib.Logger.Error("UserCreateApiHandler, 用户名或者密码为空")
|
||||||
|
corelib.WriteJsonResponse(w, 400, "请输入用户名密码", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if isExists := data.SysUserExists(res.Username); !isExists {
|
||||||
|
corelib.Logger.Error("UserCreateApiHandler, 用户已经存在")
|
||||||
|
corelib.WriteJsonResponse(w, 10050, "用户已经存在", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
salt = corelib.GenerateSalt()
|
||||||
|
if password, err = corelib.HashPassword(res.Password, salt); err != nil {
|
||||||
|
corelib.Logger.Error("UserCreateApiHandler, 生成密码失败")
|
||||||
|
corelib.WriteJsonResponse(w, 10051, "生成密码失败", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user.Username = res.Username
|
||||||
|
user.Password = password
|
||||||
|
user.Salt = salt
|
||||||
|
user.IsSuper = 0
|
||||||
|
if err = data.SysUserCreate(user); err != nil {
|
||||||
|
corelib.Logger.Error("UserCreateApiHandler, 数据保存用户数据失败")
|
||||||
|
corelib.WriteJsonResponse(w, 10052, "新建用户失败", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
corelib.WriteJsonResponse(w, 200, "用户创建成功", nil)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
//http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
||||||
|
corelib.WriteJsonResponse(w, 405, "方法错误", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserPageApiHandler 分页查询
|
||||||
|
func UserPageApiHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method == http.MethodPost {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
page int
|
||||||
|
onePageCount int
|
||||||
|
count int
|
||||||
|
)
|
||||||
|
query := r.URL.Query()
|
||||||
|
// 搜索条件
|
||||||
|
search := query.Get("search")
|
||||||
|
// 每页显示多少跳
|
||||||
|
if onePageCount, err = strconv.Atoi(query.Get("count")); err != nil {
|
||||||
|
corelib.Logger.Error("UserPageApiHandler, 获取count参数失败")
|
||||||
|
corelib.WriteJsonResponse(w, 400, "参数错误", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 第几页
|
||||||
|
if page, err = strconv.Atoi(query.Get("page")); err != nil {
|
||||||
|
corelib.Logger.Error("UserPageApiHandler, 获取page参数失败")
|
||||||
|
corelib.WriteJsonResponse(w, 400, "参数错误", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
numPage := math.Ceil(float64(count) / float64(onePageCount))
|
||||||
|
paginationData := corelib.GetPaginationData(int(numPage), page, 2)
|
||||||
|
if search != "" {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
corelib.Logger.Infoln("paginationData: ", paginationData)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
corelib.WriteJsonResponse(w, 405, "方法错误", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func pagination(page, onePageCount int, search string) {
|
||||||
|
|
||||||
|
}
|
||||||
|
22
middleware/csrf.go
Normal file
22
middleware/csrf.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gorilla/csrf"
|
||||||
|
"picgo/configs"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getSecure() (secure bool) {
|
||||||
|
if configs.Settings.Server.Environment == "dev" {
|
||||||
|
secure = false
|
||||||
|
} else {
|
||||||
|
secure = true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CsrfMiddleware 设置CSRF保护
|
||||||
|
var CsrfMiddleware = csrf.Protect(
|
||||||
|
[]byte(configs.Settings.Server.SessionsKey),
|
||||||
|
csrf.Secure(getSecure()), // 在开发环境中禁用HTTPS
|
||||||
|
csrf.RequestHeader("X-CSRF-Token"),
|
||||||
|
)
|
25
middleware/log.go
Normal file
25
middleware/log.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"picgo/corelib"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LoggingMiddleware 是一个中间件函数,用于记录 HTTP 请求日志
|
||||||
|
func LoggingMiddleware(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// 获取请求的详细信息
|
||||||
|
start := time.Now()
|
||||||
|
method := r.Method
|
||||||
|
path := r.URL.Path
|
||||||
|
remoteAddr := r.RemoteAddr
|
||||||
|
referer := r.Referer()
|
||||||
|
|
||||||
|
// 调用下一个处理程序
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
|
||||||
|
// 记录日志
|
||||||
|
corelib.Logger.Infof("[%s] %s %s %s %s", time.Since(start), method, path, remoteAddr, referer)
|
||||||
|
})
|
||||||
|
}
|
6
model/user_request.go
Normal file
6
model/user_request.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type UserCreateRequest struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
@ -22,18 +22,23 @@ func InitRouter() *mux.Router {
|
|||||||
csrf.Secure(secure), // 在开发环境中禁用HTTPS
|
csrf.Secure(secure), // 在开发环境中禁用HTTPS
|
||||||
csrf.RequestHeader("X-CSRF-Token"),
|
csrf.RequestHeader("X-CSRF-Token"),
|
||||||
)
|
)
|
||||||
|
|
||||||
// 创建新的路由器
|
// 创建新的路由器
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
// 不需要鉴权路由
|
// 不需要鉴权路由
|
||||||
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", handler.StaticHandler()))
|
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", handler.StaticHandler())) // 静态资源路由
|
||||||
r.HandleFunc("/login", handler.LoginHandler).Methods(http.MethodGet, http.MethodPost) // 登录
|
r.HandleFunc("/login", handler.LoginPageHandler).Methods(http.MethodGet) // 登录页面
|
||||||
r.HandleFunc("/captcha", handler.CaptchaHandler).Methods(http.MethodGet) // 验证码接口
|
r.HandleFunc("/api/v1/login", handler.LoginApiHandler).Methods(http.MethodPost) // 登录接口
|
||||||
// 需要鉴权路由
|
r.HandleFunc("/captcha", handler.CaptchaApiHandler).Methods(http.MethodGet) // 验证码接口
|
||||||
r.Handle("/", middleware.LoginMiddleware(http.HandlerFunc(handler.IndexHandler))).Methods(http.MethodGet) // 后台首页
|
// 需要鉴权页面路由
|
||||||
r.Handle("/domain", middleware.LoginMiddleware(http.HandlerFunc(handler.DomainHandler))).Methods(http.MethodGet) // 域名管理
|
r.Handle("/", middleware.LoginMiddleware(http.HandlerFunc(handler.IndexPageHandler))).Methods(http.MethodGet) // 后台首页
|
||||||
r.Handle("/user", middleware.LoginMiddleware(http.HandlerFunc(handler.UserHandler))).Methods(http.MethodGet) // 用户管理
|
r.Handle("/domain", middleware.LoginMiddleware(http.HandlerFunc(handler.DomainPageHandler))).Methods(http.MethodGet) // 域名管理
|
||||||
r.Handle("/picture", middleware.LoginMiddleware(http.HandlerFunc(handler.PictureHandler))).Methods(http.MethodGet) // 图片管理
|
r.Handle("/user", middleware.LoginMiddleware(http.HandlerFunc(handler.UserPageHandler))).Methods(http.MethodGet) // 用户管理
|
||||||
r.Handle("/api/v1/upload", middleware.LoginMiddleware(http.HandlerFunc(handler.UploadFileHandler))).Methods(http.MethodPost) // 图片上传接口
|
r.Handle("/picture", middleware.LoginMiddleware(http.HandlerFunc(handler.PicturePageHandler))).Methods(http.MethodGet) // 图片管理
|
||||||
|
// 需要鉴权接口路由
|
||||||
|
r.Handle("/api/v1/upload", middleware.LoginMiddleware(http.HandlerFunc(handler.UploadFileApiHandler))).Methods(http.MethodPost) // 图片上传接口
|
||||||
|
r.Handle("/api/v1/user/create", middleware.LoginMiddleware(http.HandlerFunc(handler.UserCreateApiHandler))).Methods(http.MethodPost) // 新增用户
|
||||||
|
|
||||||
// 应用 CORS CSRF 中间件
|
// 应用 CORS CSRF 中间件
|
||||||
http.Handle("/", middleware.CorsMiddleware(CSRF(r)))
|
http.Handle("/", middleware.CorsMiddleware(CSRF(r)))
|
||||||
|
|
||||||
|
5
static/css/bootstrap-icons.min.css
vendored
5
static/css/bootstrap-icons.min.css
vendored
File diff suppressed because one or more lines are too long
@ -7,8 +7,8 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-display: block;
|
font-display: block;
|
||||||
font-family: "bootstrap-icons";
|
font-family: "bootstrap-icons";
|
||||||
src: url("./fonts/bootstrap-icons.woff2?dd67030699838ea613ee6dbda90effa6") format("woff2"),
|
src: url("fonts/bootstrap-icons.woff2?dd67030699838ea613ee6dbda90effa6") format("woff2"),
|
||||||
url("./fonts/bootstrap-icons.woff?dd67030699838ea613ee6dbda90effa6") format("woff");
|
url("fonts/bootstrap-icons.woff?dd67030699838ea613ee6dbda90effa6") format("woff");
|
||||||
}
|
}
|
||||||
|
|
||||||
.bi::before,
|
.bi::before,
|
5
static/css/lib/bootstrap-icons.min.css
vendored
Normal file
5
static/css/lib/bootstrap-icons.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -56,7 +56,7 @@ Domain.prototype.domainPageInit = function () {
|
|||||||
|
|
||||||
// 表单验证
|
// 表单验证
|
||||||
Domain.prototype.verifyDomain = function (domain) {
|
Domain.prototype.verifyDomain = function (domain) {
|
||||||
let patt = /(https?|ftp|file):\/\/[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]/i
|
let patt = /(https|http):\/\/[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]/i
|
||||||
return patt.test(domain)
|
return patt.test(domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +50,6 @@ Common.prototype.AlertSuccessToast = function (msg) {
|
|||||||
this.AlertToast(msg, 'success')
|
this.AlertToast(msg, 'success')
|
||||||
}
|
}
|
||||||
|
|
||||||
var Alert = new Common()
|
var Alert = new Common()
|
||||||
|
|
||||||
|
|
@ -4,7 +4,7 @@ function Modal(modalTitle, fields, onSubmit) {
|
|||||||
this.onSubmit = onSubmit
|
this.onSubmit = onSubmit
|
||||||
}
|
}
|
||||||
|
|
||||||
Modal.prototype.GenericModalForm = function () {
|
Modal.prototype.RenderModalForm = function () {
|
||||||
let self = this
|
let self = this
|
||||||
// 设置表单标题
|
// 设置表单标题
|
||||||
$('#genericFormModalLabel').text(self.modalTitle)
|
$('#genericFormModalLabel').text(self.modalTitle)
|
@ -33,7 +33,7 @@ Table.prototype.RenderTable = function () {
|
|||||||
if (self.actions.length > 0) {
|
if (self.actions.length > 0) {
|
||||||
let $actionTd = $('<td>');
|
let $actionTd = $('<td>');
|
||||||
self.actions.forEach(function(action) {
|
self.actions.forEach(function(action) {
|
||||||
let $button = $('<button class="btn btn-' + action.class + ' btn-sm">' + action.label + '</button>');
|
let $button = $('<button class="btn btn-' + action.class + ' btn-sm ml-2">' + action.label + '</button>');
|
||||||
$button.on('click', function() {
|
$button.on('click', function() {
|
||||||
action.onClick(row);
|
action.onClick(row);
|
||||||
});
|
});
|
@ -25,7 +25,7 @@ Login.prototype.LoginClick = function () {
|
|||||||
let captcha = $("#login-captcha").val()
|
let captcha = $("#login-captcha").val()
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/login',
|
url: '/api/v1/login',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: JSON.stringify({
|
data: JSON.stringify({
|
||||||
username: username,
|
username: username,
|
||||||
@ -48,7 +48,7 @@ Login.prototype.LoginClick = function () {
|
|||||||
Alert.AlertError("服务器内部错误")
|
Alert.AlertError("服务器内部错误")
|
||||||
console.log('请求失败:', error);
|
console.log('请求失败:', error);
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
function PageHead(fields, actions,elementId) {
|
|
||||||
this.fields = fields
|
|
||||||
this.actions = actions
|
|
||||||
this.element = $(elementId)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PageHead.prototype.RenderPageHead = function () {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
88
static/js/user.js
Normal file
88
static/js/user.js
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
function User() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
User.prototype.CreateUser = function () {
|
||||||
|
// 示例字段配置
|
||||||
|
let fields = [
|
||||||
|
{ label: '用户名:', name: 'username', type: 'text' },
|
||||||
|
{ label: '密码:', name: 'password', type: 'password' },
|
||||||
|
{ label: '确认密码:', name: 'confirm', type: 'password' }
|
||||||
|
]
|
||||||
|
// 初始化表单并显示
|
||||||
|
$('#add-user').on('click', function() {
|
||||||
|
let title = '添加用户'
|
||||||
|
let modal = new Modal(title, fields,function(data) {
|
||||||
|
console.log('表单提交数据:', data)
|
||||||
|
let csrfToken = document.getElementsByName("gorilla.csrf.Token")[0].value
|
||||||
|
console.log('表单提交数据csrfToken:', csrfToken)
|
||||||
|
// 表单
|
||||||
|
if (!data.username) {
|
||||||
|
Alert.AlertError("请输入用户名!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!data.password || !data.confirm) {
|
||||||
|
Alert.AlertError("请输入密码!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (data.password !== data.confirm) {
|
||||||
|
Alert.AlertError("请确认密码!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Alert.AlertSuccessToast("创建用户成功!")
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/v1/user/create',
|
||||||
|
method: 'POST',
|
||||||
|
data: JSON.stringify({
|
||||||
|
username: data.username,
|
||||||
|
password: data.password
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
"X-CSRF-Token": csrfToken
|
||||||
|
},
|
||||||
|
success: function (result) {
|
||||||
|
if (result.status === 200) {
|
||||||
|
Alert.AlertSuccessToast(result.message)
|
||||||
|
location.reload();
|
||||||
|
} else {
|
||||||
|
Alert.AlertError(result.message)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr, status, error) {
|
||||||
|
Alert.AlertError("服务器内部错误!")
|
||||||
|
console.log(" 状态: ", status, ' 请求失败:', error);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
modal.RenderModalForm()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
User.prototype.GetUserData = function () {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
User.prototype.run = function () {
|
||||||
|
this.CreateUser()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构造执行入口
|
||||||
|
$(function () {
|
||||||
|
// feather.replace()
|
||||||
|
// 模板过滤方法
|
||||||
|
// if (window.template) {
|
||||||
|
// template.defaults.imports.domainSubstring = function (dateValue) {
|
||||||
|
// if (dateValue.length > 40 ) {
|
||||||
|
// return dateValue.substring(0, 37) + "..."
|
||||||
|
// } else {
|
||||||
|
// return dateValue
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
let user = new User()
|
||||||
|
user.run()
|
||||||
|
})
|
@ -4,13 +4,14 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<link rel="shortcut icon" href="/static/img/favicon.ico">
|
<link rel="shortcut icon" href="/static/img/favicon.ico">
|
||||||
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
|
<link href="/static/css/lib/bootstrap.min.css" rel="stylesheet">
|
||||||
<link href="/static/css/sweetalert.css" rel="stylesheet">
|
<link href="/static/css/lib/bootstrap-icons.css" rel="stylesheet">
|
||||||
<script src="/static/js/jquery-3.3.1.min.js"></script>
|
<link href="/static/css/lib/sweetalert.css" rel="stylesheet">
|
||||||
<script src="/static/js/popper.min.js"></script>
|
<script src="/static/js/lib/jquery-3.3.1.min.js"></script>
|
||||||
<script src="/static/js/bootstrap.min.js"></script>
|
<script src="/static/js/lib/popper.min.js"></script>
|
||||||
<script src="/static/js/sweetalert.min.js"></script>
|
<script src="/static/js/lib/bootstrap.min.js"></script>
|
||||||
<script src="/static/js/common.js"></script>
|
<script src="/static/js/lib/sweetalert.min.js"></script>
|
||||||
|
<script src="/static/js/lib/common.js"></script>
|
||||||
{{template "style" .}}
|
{{template "style" .}}
|
||||||
<title>{{.Title}}</title>
|
<title>{{.Title}}</title>
|
||||||
</head>
|
</head>
|
||||||
|
@ -6,14 +6,13 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<link rel="shortcut icon" href="/static/img/favicon.ico">
|
<link rel="shortcut icon" href="/static/img/favicon.ico">
|
||||||
<title>后台登录</title>
|
<title>后台登录</title>
|
||||||
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
|
<link href="/static/css/lib/bootstrap.min.css" rel="stylesheet">
|
||||||
<link href="/static/css/bootstrap-icons.css" rel="stylesheet">
|
<link href="/static/css/lib/bootstrap-icons.css" rel="stylesheet">
|
||||||
<link href="/static/css/sweetalert.css" rel="stylesheet">
|
<link href="/static/css/lib/sweetalert.css" rel="stylesheet">
|
||||||
<script src="/static/js/jquery-3.3.1.min.js"></script>
|
<script src="/static/js/lib/jquery-3.3.1.min.js"></script>
|
||||||
<script src="/static/js/bootstrap.min.js"></script>
|
<script src="/static/js/lib/bootstrap.min.js"></script>
|
||||||
<!-- <script src="/static/js/feather-icons.js"></script>-->
|
<script src="/static/js/lib/sweetalert.min.js"></script>
|
||||||
<script src="/static/js/sweetalert.min.js"></script>
|
<script src="/static/js/lib/common.js"></script>
|
||||||
<script src="/static/js/common.js"></script>
|
|
||||||
<style>
|
<style>
|
||||||
.login-container {min-height: 100vh;display: flex;align-items: center;justify-content: center;}
|
.login-container {min-height: 100vh;display: flex;align-items: center;justify-content: center;}
|
||||||
.login-form {width: 100%;max-width: 420px;padding: 15px;margin: auto;border-radius: 12px;}
|
.login-form {width: 100%;max-width: 420px;padding: 15px;margin: auto;border-radius: 12px;}
|
||||||
|
@ -8,14 +8,14 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{define "script"}}
|
{{define "script"}}
|
||||||
<script src="/static/js/modal.js"></script>
|
<script src="/static/js/lib/modal.js"></script>
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
// 示例字段配置
|
// 示例字段配置
|
||||||
let fields = [
|
let fields = [
|
||||||
{ label: '用户名', name: 'username', type: 'text' },
|
{ label: '用户名:', name: 'username', type: 'text' },
|
||||||
{ label: '邮箱', name: 'email', type: 'email' },
|
{ label: '密码:', name: 'password', type: 'password' },
|
||||||
{ label: '密码', name: 'password', type: 'password' }
|
{ label: '确认密码:', name: 'password', type: 'password' }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 初始化表单并显示
|
// 初始化表单并显示
|
||||||
@ -25,7 +25,7 @@
|
|||||||
console.log('表单提交数据:', data)
|
console.log('表单提交数据:', data)
|
||||||
// 在此处处理表单提交,例如通过 AJAX 发送到服务器
|
// 在此处处理表单提交,例如通过 AJAX 发送到服务器
|
||||||
})
|
})
|
||||||
modal.GenericModalForm()
|
modal.RenderModalForm()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -3,45 +3,14 @@
|
|||||||
|
|
||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
<div id="user-inquire">
|
<div id="user-inquire">
|
||||||
<div class="alert alert-primary form-inline" role="alert">
|
<div class="alert alert-primary" role="alert">
|
||||||
<button type="button" id="add-domain" class="btn btn-primary" data-toggle="modal" data-target="#DomainModal"><i class="bi bi-plus-lg"></i> 添加域名</button>
|
<button type="button" class="btn btn-primary" id="add-user"><i class="bi bi-plus-square"></i> 创建用户</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="user-table"></div>
|
<div id="user-table"></div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{define "script"}}
|
{{define "script"}}
|
||||||
<script src="/static/js/table.js"></script>
|
<script src="/static/js/lib/modal.js"></script>
|
||||||
<script>
|
<script src="/static/js/user.js"></script>
|
||||||
$(document).ready(function() {
|
|
||||||
// 示例数据
|
|
||||||
let columns = ['ID', '名称', '年龄'];
|
|
||||||
let data = [
|
|
||||||
[1, '张三', 25],
|
|
||||||
[2, '李四', 30],
|
|
||||||
[3, '王五', 28]
|
|
||||||
];
|
|
||||||
|
|
||||||
// 操作按钮配置
|
|
||||||
let actions = [
|
|
||||||
{
|
|
||||||
label: '编辑',
|
|
||||||
class: 'primary',
|
|
||||||
onClick: function(row) {
|
|
||||||
alert('编辑:' + row[1]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '删除',
|
|
||||||
class: 'danger',
|
|
||||||
onClick: function(row) {
|
|
||||||
alert('删除:' + row[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
let table = new Table(columns, data, actions, "#user-table")
|
|
||||||
table.RenderTable()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
{{end}}
|
{{end}}
|
Loading…
Reference in New Issue
Block a user