init
This commit is contained in:
commit
0a55c9742c
1
.env.example
Normal file
1
.env.example
Normal file
@ -0,0 +1 @@
|
||||
db=user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
.env
|
||||
temp
|
||||
static/resourse
|
||||
public/upload/*
|
8
.vscode/settings.json
vendored
Normal file
8
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"iostream": "cpp",
|
||||
"ostream": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"xstring": "cpp"
|
||||
}
|
||||
}
|
7
MEADME.md
Normal file
7
MEADME.md
Normal file
@ -0,0 +1,7 @@
|
||||
# yitao
|
||||
|
||||
这是一个对 NexusPHP 的 go 实现
|
||||
|
||||
目标是在兼容 NexusPHP 数据库的基础上实现一套 Restful Api
|
||||
|
||||
文档:0.0.0.0:5001/swagger
|
31
config/config.go
Normal file
31
config/config.go
Normal file
@ -0,0 +1,31 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/caarlos0/env/v10"
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
DB string `env:"DB"`
|
||||
RDB string `env:"RDB"`
|
||||
JwtSecret string `env:"JWT_SECRET"`
|
||||
GeoipDB string `env:"GEOIP_DB"`
|
||||
}
|
||||
|
||||
var conf = Config{}
|
||||
|
||||
func LoadConfig() Config {
|
||||
godotenv.Load()
|
||||
if err := env.Parse(&conf); err != nil {
|
||||
log.Fatalf("%+v\n", err)
|
||||
}
|
||||
fmt.Printf("%+v\n", conf)
|
||||
return conf
|
||||
}
|
||||
|
||||
func GetConfig() Config {
|
||||
return conf
|
||||
}
|
157
controller/captcha_controller.go
Normal file
157
controller/captcha_controller.go
Normal file
@ -0,0 +1,157 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"yitao/util"
|
||||
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
)
|
||||
|
||||
// TEMP : Auth
|
||||
|
||||
type CaptchaController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
func (m *CaptchaController) BeforeActivation(b mvc.BeforeActivation) {
|
||||
{
|
||||
// 普通图片验证码
|
||||
b.Handle("GET", "/img", "GetIMG")
|
||||
b.Handle("GET", "/img/gen", "ImgGen")
|
||||
b.Handle("GET", "/img/{key:string}", "GetImgByKey")
|
||||
b.Handle("GET", "/img/validate/{key:string}", "ImgValidate")
|
||||
}
|
||||
{
|
||||
// 高级点击验证码
|
||||
b.Handle("GET", "/click/gen", "ClickGen")
|
||||
b.Handle("POST", "/click/{key:string}/submit", "ClickSubmit")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 获取验证码信息
|
||||
// @Summary Captcha Img Generate
|
||||
// @Description 获取一张图片验证码的标识符
|
||||
// @Tags captcha api
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} map[string]interface{} "{"key": "ASDFGHJK"}"
|
||||
// @Failure 400 {object} map[string]interface{} "{"msg": "错误信息","code":0}"
|
||||
// @Router /api/captcha/img/gen [get]
|
||||
func (con *CaptchaController) ImgGen() map[string]interface{} {
|
||||
hs := con.Ctx.URLParam("h")
|
||||
var h int = 120
|
||||
if hs != "" {
|
||||
h, _ = strconv.Atoi(hs)
|
||||
}
|
||||
ws := con.Ctx.URLParam("w")
|
||||
var w int = 40
|
||||
if ws != "" {
|
||||
w, _ = strconv.Atoi(ws)
|
||||
}
|
||||
code, img := con.Service.Captcha.GetIMGWithHW(4, true, h, w)
|
||||
|
||||
key := util.RandString(8)
|
||||
// 保存到文件
|
||||
util.SaveToFile("./temp/captcha/img/"+key+".png", img)
|
||||
// 保存到数据库
|
||||
con.Service.Captcha.SaveToDB(key, code)
|
||||
|
||||
return map[string]interface{}{
|
||||
// 打开这个注释就能在 api 里看到答案了
|
||||
//"code": code,
|
||||
"key": key,
|
||||
}
|
||||
}
|
||||
|
||||
// 验证码本体
|
||||
func (con *CaptchaController) GetIMG() {
|
||||
_, img := con.Service.Captcha.GetIMG(4, true)
|
||||
con.Ctx.Write(img)
|
||||
}
|
||||
|
||||
// 通过 key 获取验证码图片
|
||||
// @Summary Captcha Img Get
|
||||
// @Description 通过 key 获取验证码图片
|
||||
// @Tags captcha api
|
||||
// @Accept json
|
||||
// @Produce png
|
||||
// @Param key path string true "验证码标识符"
|
||||
// @Success 200 {object} []byte "IMG"
|
||||
// @Failure 400 {object} map[string]interface{} "{"msg": "错误信息","code":0}"
|
||||
// @Router /api/captcha/img/{key} [get]
|
||||
func (con *CaptchaController) GetImgByKey(key string) {
|
||||
b, err := util.ReadFromFile("./temp/captcha/img/" + key + ".png")
|
||||
if err != nil {
|
||||
con.Ctx.Write([]byte("error"))
|
||||
} else {
|
||||
con.Ctx.ResponseWriter().Header().Add("content-type", "image/png")
|
||||
con.Ctx.Write(b)
|
||||
}
|
||||
|
||||
// 读取完直接删了,都是一次性的
|
||||
util.DeleteFile("./temp/captcha/img/" + key + ".png")
|
||||
}
|
||||
|
||||
// 验证码有效性
|
||||
// @Summary Captcha Img Validate
|
||||
// @Description 图片验证码校验(一般情况下后端校验)
|
||||
// @Tags captcha api
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param key path string true "验证码标识符"
|
||||
// @Param code query string true "验证码"
|
||||
// @Success 200 {object} map[string]interface{} "{"msg": "正确信息","code":0}"
|
||||
// @Failure 400 {object} map[string]interface{} "{"msg": "错误信息","code":0}"
|
||||
// @Router /api/captcha/img/validate/{key} [get]
|
||||
func (con *CaptchaController) ImgValidate(key string) mvc.Result {
|
||||
input := con.Ctx.URLParam("code")
|
||||
e := con.Service.Captcha.Validate(key, input)
|
||||
con.Service.Captcha.Delete(key)
|
||||
if e.Error() {
|
||||
return e.Response()
|
||||
} else {
|
||||
// 好像ecode不出错就返回正确似乎
|
||||
return e.Response()
|
||||
}
|
||||
}
|
||||
|
||||
// @Summary Captcha Click Generate
|
||||
// @Description 生成按次序点击验证码
|
||||
// @Tags captcha api
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} map[string]interface{} "{"key": "验证码标识符","master":"主图(用户点击图)base64","subimg":"子图(文字次序图)base64"}"
|
||||
// @Failure 400 {object} map[string]interface{} "{"msg": "错误信息","code":0}"
|
||||
// @Router /api/captcha/click/gen [get]
|
||||
func (con *CaptchaController) ClickGen() mvc.Result {
|
||||
key, img1, img2 := con.Service.Captcha.GetClick()
|
||||
return mvc.Response{
|
||||
Object: map[string]interface{}{
|
||||
"key": key,
|
||||
"master": img1,
|
||||
"subimg": img2,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// @Summary Captcha Click Validate
|
||||
// @Description 校验按次序点击验证码
|
||||
// @Tags captcha api
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param key path string true "验证码标识符"
|
||||
// @Param key formData string true "15,851;458,18;58,15;x,y; #用户点击的坐标集合"
|
||||
// @Success 200 {object} map[string]interface{} "{"flag": true|false}"
|
||||
// @Failure 400 {object} map[string]interface{} "{"msg": "错误信息","code":0}"
|
||||
// @Router /api/captcha/click/{key}/submit [post]
|
||||
func (con *CaptchaController) ClickSubmit(key string) mvc.Result {
|
||||
input := con.Ctx.PostValue("data")
|
||||
|
||||
f := con.Service.Captcha.ClickValidate(key, input)
|
||||
return mvc.Response{
|
||||
Object: map[string]interface{}{
|
||||
"flag": f,
|
||||
},
|
||||
}
|
||||
}
|
40
controller/controller.go
Normal file
40
controller/controller.go
Normal file
@ -0,0 +1,40 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"yitao/service"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/core/router"
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
)
|
||||
|
||||
type BaseController struct {
|
||||
Ctx iris.Context
|
||||
Service *service.Service
|
||||
}
|
||||
|
||||
var serv *service.Service
|
||||
var apiParty router.Party
|
||||
|
||||
type ControllerInterface interface {
|
||||
BeforeActivation(mvc.BeforeActivation)
|
||||
}
|
||||
|
||||
func LoadRouter(app *iris.Application, service *service.Service) {
|
||||
serv = service
|
||||
apiParty = app.Party("/api")
|
||||
|
||||
registerController("/user", new(UserController))
|
||||
registerController("/captcha", new(CaptchaController))
|
||||
registerController("/item", new(ItemController))
|
||||
registerController("/file", new(FileController))
|
||||
|
||||
}
|
||||
|
||||
func registerController(root string, controller ControllerInterface) {
|
||||
router := mvc.New(apiParty.Party(root))
|
||||
router.Register(
|
||||
serv,
|
||||
)
|
||||
router.Handle(controller)
|
||||
}
|
58
controller/file_controller.go
Normal file
58
controller/file_controller.go
Normal file
@ -0,0 +1,58 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"yitao/ecode"
|
||||
"yitao/model"
|
||||
"yitao/util"
|
||||
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
)
|
||||
|
||||
type FileController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
func (c *FileController) BeforeActivation(b mvc.BeforeActivation) {
|
||||
b.Handle("PUT", "/upload/item-img", "UploadItemImg")
|
||||
}
|
||||
|
||||
// @Summary 上传商品图片
|
||||
// @Description 上传商品图片
|
||||
// @Tags file api
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param file formData file true "图片文件"
|
||||
// @Success 200 {object} map[string]interface{} "{"id": 1,"key":"图片唯一标识"}"
|
||||
// @Failure 400 {object} map[string]interface{} "{"msg": "错误信息","code":0}"
|
||||
// @Router /api/file/upload/item-img [put]
|
||||
func (c *FileController) UploadItemImg() mvc.Result {
|
||||
e := ecode.OK()
|
||||
// 获取原文件
|
||||
_, file_header, err := c.Ctx.FormFile("file")
|
||||
if err != nil {
|
||||
return ecode.Set(255, err.Error()).Response()
|
||||
}
|
||||
file, e := c.Service.File.UploadItemImg(file_header)
|
||||
if e.Error() {
|
||||
return e.Response()
|
||||
}
|
||||
// 保存文件到 ./public/upload/item/img 目录
|
||||
_, err = c.Ctx.SaveFormFile(file_header, file.SystemPath)
|
||||
if err != nil {
|
||||
return ecode.Set(255, err.Error()).Response()
|
||||
}
|
||||
if !util.FileIsImage(file.SystemPath) {
|
||||
// 文件不是图片文件
|
||||
util.DeleteFile(file.SystemPath)
|
||||
c.Service.File.SetFileStatus(file.ID, model.FileStatusDelete)
|
||||
return ecode.File(ecode.FILE_TYPE_ERR).Response()
|
||||
}
|
||||
|
||||
return mvc.Response{
|
||||
Object: map[string]interface{}{
|
||||
"id": file.ID,
|
||||
"key": file.Key,
|
||||
"code": 0,
|
||||
},
|
||||
}
|
||||
}
|
66
controller/item_controller.go
Normal file
66
controller/item_controller.go
Normal file
@ -0,0 +1,66 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"yitao/ecode"
|
||||
"yitao/middleware"
|
||||
"yitao/validate"
|
||||
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
)
|
||||
|
||||
type ItemController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
func (c *ItemController) BeforeActivation(b mvc.BeforeActivation) {
|
||||
b.Handle("GET", "/{id:uint}", "Get")
|
||||
//b.Handle("GET", "/list/{:type}", "GetList")
|
||||
|
||||
// 管理员接口
|
||||
{
|
||||
b.Handle("POST", "/oper/create", "Create", middleware.JwtMiddleware.Serve)
|
||||
//b.Handle("POST", "/admin/update", "Update", middleware.JwtMiddleware.Serve, middleware.AdminMiddleware)
|
||||
//b.Handle("POST", "/admin/delete", "Delete", middleware.JwtMiddleware.Serve, middleware.AdminMiddleware)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// @Summary get item api
|
||||
// @Description 获取相关ID的商品信息
|
||||
// @Tags item api
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path uint true "商品ID"
|
||||
// @Success 200 {object} map[string]interface{} "{"jwt": "jwt.jwt.jwt"}"
|
||||
// @Failure 400 {object} map[string]interface{} "{"msg": "错误信息","code":0}"
|
||||
// @Router /api/item/get/{id} [post]
|
||||
func (c *ItemController) Get(id uint) mvc.Result {
|
||||
return nil
|
||||
}
|
||||
|
||||
// @Summary 创建商品
|
||||
// @Description 创建商品接口
|
||||
// @Tags item api
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param createItemParam body validate.CreateItemParam true "商品信息"
|
||||
// @Success 200 {object} model.ItemModel "{"id": 1}"
|
||||
// @Failure 400 {object} map[string]interface{} "{"msg": "错误信息","code":0}"
|
||||
// @Router /api/item/oper/create [post]
|
||||
func (c *ItemController) Create() mvc.Result {
|
||||
e := ecode.OK()
|
||||
create_item_param := new(validate.CreateItemParam)
|
||||
e = validate.ReadJSON(c.Ctx, create_item_param)
|
||||
if e.Error() {
|
||||
return e.Response()
|
||||
}
|
||||
|
||||
item, e := c.Service.Item.CreateItem(create_item_param.Itemname, create_item_param.Desc, create_item_param.Types, create_item_param.Img, create_item_param.Price)
|
||||
if e.Error() {
|
||||
return e.Response()
|
||||
}
|
||||
|
||||
return mvc.Response{
|
||||
Object: item,
|
||||
}
|
||||
}
|
109
controller/user_controller.go
Normal file
109
controller/user_controller.go
Normal file
@ -0,0 +1,109 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"yitao/config"
|
||||
"yitao/ecode"
|
||||
"yitao/util"
|
||||
"yitao/validate"
|
||||
|
||||
"github.com/iris-contrib/middleware/jwt"
|
||||
"github.com/kataras/iris/v12/mvc"
|
||||
)
|
||||
|
||||
type UserController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
func (con *UserController) BeforeActivation(b mvc.BeforeActivation) {
|
||||
// Login Api
|
||||
{
|
||||
b.Handle("POST", "/auth/login", "AuthLogin")
|
||||
b.Handle("POST", "/auth/signup", "AuthSignup")
|
||||
}
|
||||
}
|
||||
|
||||
// @Summary login api
|
||||
// @Description the 用户登录接口
|
||||
// @Tags auth api
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param loginParam body validate.LoginParam true "登录参数"
|
||||
// @Success 200 {object} map[string]interface{} "{"jwt": "jwt.jwt.jwt"}"
|
||||
// @Failure 400 {object} map[string]interface{} "{"msg": "错误信息","code":0}"
|
||||
// @Router /api/user/auth/login [post]
|
||||
func (con *UserController) AuthLogin() mvc.Result {
|
||||
login_param := new(validate.LoginParam)
|
||||
e := validate.ReadJSON(con.Ctx, login_param)
|
||||
if e.Error() {
|
||||
return e.Response()
|
||||
}
|
||||
|
||||
e = con.Service.Captcha.Validate(login_param.CaptchaKey, login_param.CaptchaData)
|
||||
if e.Error() {
|
||||
return e.Response()
|
||||
}
|
||||
|
||||
user, e := con.Service.User.AuthLogin(login_param.Username, login_param.Password)
|
||||
if e.Error() {
|
||||
return e.Response()
|
||||
}
|
||||
|
||||
// 写入登录日志
|
||||
ip := con.Ctx.Request().RemoteAddr
|
||||
// 一个测试ip
|
||||
//ip = "109.123.229.220" // 日本 东京
|
||||
// 获取 ip 地址
|
||||
city, country, _ := util.GetCityAndCountry(ip)
|
||||
con.Service.LoginLog.Save(user.ID, ip, "Web", country, city)
|
||||
|
||||
// 原版 NP 在这里会触发一个对登录地点的检测,但我看数据库它就没成功获取到过国家于城市
|
||||
// 所以就先不做异地检测了
|
||||
// @TODO 如果有人愿意的话在这做个异地检测
|
||||
// 好吧现在好了,但我懒得写了,就这样吧
|
||||
|
||||
// 开始生成jwt
|
||||
conf := config.GetConfig()
|
||||
token := jwt.NewTokenWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
"uid": user.ID,
|
||||
"username": user.Username,
|
||||
"time": util.GetTime(),
|
||||
"is_admin": user.IsAdmin,
|
||||
})
|
||||
// 使用设置的秘钥,签名生成jwt字符串
|
||||
tokenString, _ := token.SignedString([]byte(conf.JwtSecret))
|
||||
|
||||
return mvc.Response{
|
||||
Object: map[string]interface{}{
|
||||
"jwt": tokenString,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// @Summary 注册API
|
||||
// @Description 用户注册接口
|
||||
// @Tags auth api
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param signupParam body validate.SignupParam true "注册参数"
|
||||
// @Success 200 {object} map[string]interface{} "{"msg": "注册成功","code":1}"
|
||||
// @Failure 400 {object} map[string]interface{} "{"msg": "错误信息","code":0}"
|
||||
// @Router /api/user/auth/signup [post]
|
||||
func (con *UserController) AuthSignup() mvc.Result {
|
||||
var e *ecode.Ecode
|
||||
signup_param := new(validate.SignupParam)
|
||||
e = validate.ReadJSON(con.Ctx, signup_param)
|
||||
if e.Error() {
|
||||
return e.Response()
|
||||
}
|
||||
|
||||
e = con.Service.Captcha.Validate(signup_param.CaptchaKey, signup_param.CaptchaData)
|
||||
if e.Error() {
|
||||
return e.Response()
|
||||
}
|
||||
|
||||
_, e = con.Service.User.AuthSignup(signup_param.Username, signup_param.Password)
|
||||
if e.Error() {
|
||||
return e.Response()
|
||||
}
|
||||
return ecode.OK().Response()
|
||||
}
|
590
docs/docs.go
Normal file
590
docs/docs.go
Normal file
@ -0,0 +1,590 @@
|
||||
// Package docs Code generated by swaggo/swag. DO NOT EDIT
|
||||
package docs
|
||||
|
||||
import "github.com/swaggo/swag"
|
||||
|
||||
const docTemplate = `{
|
||||
"schemes": {{ marshal .Schemes }},
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "{{escape .Description}}",
|
||||
"title": "{{.Title}}",
|
||||
"termsOfService": "http://swagger.io/terms/",
|
||||
"contact": {
|
||||
"name": "API Support",
|
||||
"url": "http://www.swagger.io/support",
|
||||
"email": "support@swagger.io"
|
||||
},
|
||||
"license": {
|
||||
"name": "Apache 2.0",
|
||||
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
|
||||
},
|
||||
"version": "{{.Version}}"
|
||||
},
|
||||
"host": "{{.Host}}",
|
||||
"basePath": "{{.BasePath}}",
|
||||
"paths": {
|
||||
"/api/captcha/click/gen": {
|
||||
"get": {
|
||||
"description": "生成按次序点击验证码",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"captcha api"
|
||||
],
|
||||
"summary": "Captcha Click Generate",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{\"key\": \"验证码标识符\",\"master\":\"主图(用户点击图)base64\",\"subimg\":\"子图(文字次序图)base64\"}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "{\"msg\": \"错误信息\",\"code\":0}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/captcha/click/{key}/submit": {
|
||||
"post": {
|
||||
"description": "校验按次序点击验证码",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"captcha api"
|
||||
],
|
||||
"summary": "Captcha Click Validate",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "验证码标识符",
|
||||
"name": "key",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "15,851;458,18;58,15;x,y; #用户点击的坐标集合",
|
||||
"name": "key",
|
||||
"in": "formData",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{\"flag\": true|false}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "{\"msg\": \"错误信息\",\"code\":0}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/captcha/img/gen": {
|
||||
"get": {
|
||||
"description": "获取一张图片验证码的标识符",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"captcha api"
|
||||
],
|
||||
"summary": "Captcha Img Generate",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{\"key\": \"ASDFGHJK\"}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "{\"msg\": \"错误信息\",\"code\":0}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/captcha/img/validate/{key}": {
|
||||
"get": {
|
||||
"description": "图片验证码校验(一般情况下后端校验)",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"captcha api"
|
||||
],
|
||||
"summary": "Captcha Img Validate",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "验证码标识符",
|
||||
"name": "key",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "验证码",
|
||||
"name": "code",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{\"msg\": \"正确信息\",\"code\":0}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "{\"msg\": \"错误信息\",\"code\":0}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/captcha/img/{key}": {
|
||||
"get": {
|
||||
"description": "通过 key 获取验证码图片",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"image/png"
|
||||
],
|
||||
"tags": [
|
||||
"captcha api"
|
||||
],
|
||||
"summary": "Captcha Img Get",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "验证码标识符",
|
||||
"name": "key",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "IMG",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "{\"msg\": \"错误信息\",\"code\":0}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/file/upload/item-img": {
|
||||
"put": {
|
||||
"description": "上传商品图片",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"file api"
|
||||
],
|
||||
"summary": "上传商品图片",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "file",
|
||||
"description": "图片文件",
|
||||
"name": "file",
|
||||
"in": "formData",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{\"id\": 1,\"key\":\"图片唯一标识\"}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "{\"msg\": \"错误信息\",\"code\":0}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/item/get/{id}": {
|
||||
"post": {
|
||||
"description": "获取相关ID的商品信息",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"item api"
|
||||
],
|
||||
"summary": "get item api",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "商品ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{\"jwt\": \"jwt.jwt.jwt\"}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "{\"msg\": \"错误信息\",\"code\":0}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/item/oper/create": {
|
||||
"post": {
|
||||
"description": "创建商品接口",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"item api"
|
||||
],
|
||||
"summary": "创建商品",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "商品信息",
|
||||
"name": "createItemParam",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/validate.CreateItemParam"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{\"id\": 1}",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/model.ItemModel"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "{\"msg\": \"错误信息\",\"code\":0}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/user/auth/login": {
|
||||
"post": {
|
||||
"description": "the 用户登录接口",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"auth api"
|
||||
],
|
||||
"summary": "login api",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "登录参数",
|
||||
"name": "loginParam",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/validate.LoginParam"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{\"jwt\": \"jwt.jwt.jwt\"}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "{\"msg\": \"错误信息\",\"code\":0}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/user/auth/signup": {
|
||||
"post": {
|
||||
"description": "用户注册接口",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"auth api"
|
||||
],
|
||||
"summary": "注册API",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "注册参数",
|
||||
"name": "signupParam",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/validate.SignupParam"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{\"msg\": \"注册成功\",\"code\":1}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "{\"msg\": \"错误信息\",\"code\":0}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"model.ItemModel": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"desc": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"img": {
|
||||
"type": "string"
|
||||
},
|
||||
"itemname": {
|
||||
"type": "string"
|
||||
},
|
||||
"price": {
|
||||
"type": "integer"
|
||||
},
|
||||
"states": {
|
||||
"type": "string"
|
||||
},
|
||||
"typeString": {
|
||||
"type": "string"
|
||||
},
|
||||
"types": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"validate.CreateItemParam": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"desc",
|
||||
"img",
|
||||
"itemname",
|
||||
"price",
|
||||
"types"
|
||||
],
|
||||
"properties": {
|
||||
"desc": {
|
||||
"type": "string",
|
||||
"example": "这是一个女大自用的iPhone18"
|
||||
},
|
||||
"img": {
|
||||
"description": "@description 图片ID,请用 /file/upload/item-img 接口上传图片后获取的图片唯一标识",
|
||||
"type": "string",
|
||||
"example": "123456789012"
|
||||
},
|
||||
"itemname": {
|
||||
"type": "string",
|
||||
"maxLength": 12,
|
||||
"example": "女大自用iPhone18"
|
||||
},
|
||||
"price": {
|
||||
"description": "@description 商品价格,单位为分",
|
||||
"type": "integer",
|
||||
"example": 1000
|
||||
},
|
||||
"types": {
|
||||
"description": "@description 商品类型,可以有多个类型,请用数组传过来,类型id请使用 /item/types 接口获取",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
},
|
||||
"example": [
|
||||
1,
|
||||
2
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"validate.LoginParam": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"captcha_data",
|
||||
"captcha_key",
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"properties": {
|
||||
"captcha_data": {
|
||||
"type": "string",
|
||||
"example": "DEV1"
|
||||
},
|
||||
"captcha_key": {
|
||||
"type": "string",
|
||||
"example": "devonlyy"
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"minLength": 6,
|
||||
"example": "123456"
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"maxLength": 12,
|
||||
"example": "wzjian"
|
||||
}
|
||||
}
|
||||
},
|
||||
"validate.SignupParam": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"captcha_data",
|
||||
"captcha_key",
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"properties": {
|
||||
"captcha_data": {
|
||||
"type": "string",
|
||||
"example": "DEV1"
|
||||
},
|
||||
"captcha_key": {
|
||||
"description": "Email string ` + "`" + `json:\"email\" validate:\"required,email\" example:\"wzjian@njtech.edu.cn\"` + "`" + `",
|
||||
"type": "string",
|
||||
"example": "devonlyy"
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"minLength": 6,
|
||||
"example": "123456"
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"maxLength": 12,
|
||||
"example": "wzjian"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// SwaggerInfo holds exported Swagger Info so clients can modify it
|
||||
var SwaggerInfo = &swag.Spec{
|
||||
Version: "1.0",
|
||||
Host: "localhost:5001",
|
||||
BasePath: "/api",
|
||||
Schemes: []string{},
|
||||
Title: "Yitao Backend API",
|
||||
Description: "Yitao Backend API.",
|
||||
InfoInstanceName: "swagger",
|
||||
SwaggerTemplate: docTemplate,
|
||||
LeftDelim: "{{",
|
||||
RightDelim: "}}",
|
||||
}
|
||||
|
||||
func init() {
|
||||
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
|
||||
}
|
566
docs/swagger.json
Normal file
566
docs/swagger.json
Normal file
@ -0,0 +1,566 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "Yitao Backend API.",
|
||||
"title": "Yitao Backend API",
|
||||
"termsOfService": "http://swagger.io/terms/",
|
||||
"contact": {
|
||||
"name": "API Support",
|
||||
"url": "http://www.swagger.io/support",
|
||||
"email": "support@swagger.io"
|
||||
},
|
||||
"license": {
|
||||
"name": "Apache 2.0",
|
||||
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
|
||||
},
|
||||
"version": "1.0"
|
||||
},
|
||||
"host": "localhost:5001",
|
||||
"basePath": "/api",
|
||||
"paths": {
|
||||
"/api/captcha/click/gen": {
|
||||
"get": {
|
||||
"description": "生成按次序点击验证码",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"captcha api"
|
||||
],
|
||||
"summary": "Captcha Click Generate",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{\"key\": \"验证码标识符\",\"master\":\"主图(用户点击图)base64\",\"subimg\":\"子图(文字次序图)base64\"}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "{\"msg\": \"错误信息\",\"code\":0}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/captcha/click/{key}/submit": {
|
||||
"post": {
|
||||
"description": "校验按次序点击验证码",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"captcha api"
|
||||
],
|
||||
"summary": "Captcha Click Validate",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "验证码标识符",
|
||||
"name": "key",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "15,851;458,18;58,15;x,y; #用户点击的坐标集合",
|
||||
"name": "key",
|
||||
"in": "formData",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{\"flag\": true|false}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "{\"msg\": \"错误信息\",\"code\":0}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/captcha/img/gen": {
|
||||
"get": {
|
||||
"description": "获取一张图片验证码的标识符",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"captcha api"
|
||||
],
|
||||
"summary": "Captcha Img Generate",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{\"key\": \"ASDFGHJK\"}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "{\"msg\": \"错误信息\",\"code\":0}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/captcha/img/validate/{key}": {
|
||||
"get": {
|
||||
"description": "图片验证码校验(一般情况下后端校验)",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"captcha api"
|
||||
],
|
||||
"summary": "Captcha Img Validate",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "验证码标识符",
|
||||
"name": "key",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "验证码",
|
||||
"name": "code",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{\"msg\": \"正确信息\",\"code\":0}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "{\"msg\": \"错误信息\",\"code\":0}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/captcha/img/{key}": {
|
||||
"get": {
|
||||
"description": "通过 key 获取验证码图片",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"image/png"
|
||||
],
|
||||
"tags": [
|
||||
"captcha api"
|
||||
],
|
||||
"summary": "Captcha Img Get",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "验证码标识符",
|
||||
"name": "key",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "IMG",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "{\"msg\": \"错误信息\",\"code\":0}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/file/upload/item-img": {
|
||||
"put": {
|
||||
"description": "上传商品图片",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"file api"
|
||||
],
|
||||
"summary": "上传商品图片",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "file",
|
||||
"description": "图片文件",
|
||||
"name": "file",
|
||||
"in": "formData",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{\"id\": 1,\"key\":\"图片唯一标识\"}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "{\"msg\": \"错误信息\",\"code\":0}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/item/get/{id}": {
|
||||
"post": {
|
||||
"description": "获取相关ID的商品信息",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"item api"
|
||||
],
|
||||
"summary": "get item api",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "商品ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{\"jwt\": \"jwt.jwt.jwt\"}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "{\"msg\": \"错误信息\",\"code\":0}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/item/oper/create": {
|
||||
"post": {
|
||||
"description": "创建商品接口",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"item api"
|
||||
],
|
||||
"summary": "创建商品",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "商品信息",
|
||||
"name": "createItemParam",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/validate.CreateItemParam"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{\"id\": 1}",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/model.ItemModel"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "{\"msg\": \"错误信息\",\"code\":0}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/user/auth/login": {
|
||||
"post": {
|
||||
"description": "the 用户登录接口",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"auth api"
|
||||
],
|
||||
"summary": "login api",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "登录参数",
|
||||
"name": "loginParam",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/validate.LoginParam"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{\"jwt\": \"jwt.jwt.jwt\"}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "{\"msg\": \"错误信息\",\"code\":0}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/user/auth/signup": {
|
||||
"post": {
|
||||
"description": "用户注册接口",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"auth api"
|
||||
],
|
||||
"summary": "注册API",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "注册参数",
|
||||
"name": "signupParam",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/validate.SignupParam"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{\"msg\": \"注册成功\",\"code\":1}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "{\"msg\": \"错误信息\",\"code\":0}",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"model.ItemModel": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"desc": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"img": {
|
||||
"type": "string"
|
||||
},
|
||||
"itemname": {
|
||||
"type": "string"
|
||||
},
|
||||
"price": {
|
||||
"type": "integer"
|
||||
},
|
||||
"states": {
|
||||
"type": "string"
|
||||
},
|
||||
"typeString": {
|
||||
"type": "string"
|
||||
},
|
||||
"types": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"validate.CreateItemParam": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"desc",
|
||||
"img",
|
||||
"itemname",
|
||||
"price",
|
||||
"types"
|
||||
],
|
||||
"properties": {
|
||||
"desc": {
|
||||
"type": "string",
|
||||
"example": "这是一个女大自用的iPhone18"
|
||||
},
|
||||
"img": {
|
||||
"description": "@description 图片ID,请用 /file/upload/item-img 接口上传图片后获取的图片唯一标识",
|
||||
"type": "string",
|
||||
"example": "123456789012"
|
||||
},
|
||||
"itemname": {
|
||||
"type": "string",
|
||||
"maxLength": 12,
|
||||
"example": "女大自用iPhone18"
|
||||
},
|
||||
"price": {
|
||||
"description": "@description 商品价格,单位为分",
|
||||
"type": "integer",
|
||||
"example": 1000
|
||||
},
|
||||
"types": {
|
||||
"description": "@description 商品类型,可以有多个类型,请用数组传过来,类型id请使用 /item/types 接口获取",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
},
|
||||
"example": [
|
||||
1,
|
||||
2
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"validate.LoginParam": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"captcha_data",
|
||||
"captcha_key",
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"properties": {
|
||||
"captcha_data": {
|
||||
"type": "string",
|
||||
"example": "DEV1"
|
||||
},
|
||||
"captcha_key": {
|
||||
"type": "string",
|
||||
"example": "devonlyy"
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"minLength": 6,
|
||||
"example": "123456"
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"maxLength": 12,
|
||||
"example": "wzjian"
|
||||
}
|
||||
}
|
||||
},
|
||||
"validate.SignupParam": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"captcha_data",
|
||||
"captcha_key",
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"properties": {
|
||||
"captcha_data": {
|
||||
"type": "string",
|
||||
"example": "DEV1"
|
||||
},
|
||||
"captcha_key": {
|
||||
"description": "Email string `json:\"email\" validate:\"required,email\" example:\"wzjian@njtech.edu.cn\"`",
|
||||
"type": "string",
|
||||
"example": "devonlyy"
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"minLength": 6,
|
||||
"example": "123456"
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"maxLength": 12,
|
||||
"example": "wzjian"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
390
docs/swagger.yaml
Normal file
390
docs/swagger.yaml
Normal file
@ -0,0 +1,390 @@
|
||||
basePath: /api
|
||||
definitions:
|
||||
model.ItemModel:
|
||||
properties:
|
||||
createdAt:
|
||||
type: string
|
||||
desc:
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
img:
|
||||
type: string
|
||||
itemname:
|
||||
type: string
|
||||
price:
|
||||
type: integer
|
||||
states:
|
||||
type: string
|
||||
typeString:
|
||||
type: string
|
||||
types:
|
||||
items:
|
||||
type: integer
|
||||
type: array
|
||||
updatedAt:
|
||||
type: string
|
||||
type: object
|
||||
validate.CreateItemParam:
|
||||
properties:
|
||||
desc:
|
||||
example: 这是一个女大自用的iPhone18
|
||||
type: string
|
||||
img:
|
||||
description: '@description 图片ID,请用 /file/upload/item-img 接口上传图片后获取的图片唯一标识'
|
||||
example: "123456789012"
|
||||
type: string
|
||||
itemname:
|
||||
example: 女大自用iPhone18
|
||||
maxLength: 12
|
||||
type: string
|
||||
price:
|
||||
description: '@description 商品价格,单位为分'
|
||||
example: 1000
|
||||
type: integer
|
||||
types:
|
||||
description: '@description 商品类型,可以有多个类型,请用数组传过来,类型id请使用 /item/types 接口获取'
|
||||
example:
|
||||
- 1
|
||||
- 2
|
||||
items:
|
||||
type: integer
|
||||
type: array
|
||||
required:
|
||||
- desc
|
||||
- img
|
||||
- itemname
|
||||
- price
|
||||
- types
|
||||
type: object
|
||||
validate.LoginParam:
|
||||
properties:
|
||||
captcha_data:
|
||||
example: DEV1
|
||||
type: string
|
||||
captcha_key:
|
||||
example: devonlyy
|
||||
type: string
|
||||
password:
|
||||
example: "123456"
|
||||
minLength: 6
|
||||
type: string
|
||||
username:
|
||||
example: wzjian
|
||||
maxLength: 12
|
||||
type: string
|
||||
required:
|
||||
- captcha_data
|
||||
- captcha_key
|
||||
- password
|
||||
- username
|
||||
type: object
|
||||
validate.SignupParam:
|
||||
properties:
|
||||
captcha_data:
|
||||
example: DEV1
|
||||
type: string
|
||||
captcha_key:
|
||||
description: Email string `json:"email" validate:"required,email" example:"wzjian@njtech.edu.cn"`
|
||||
example: devonlyy
|
||||
type: string
|
||||
password:
|
||||
example: "123456"
|
||||
minLength: 6
|
||||
type: string
|
||||
username:
|
||||
example: wzjian
|
||||
maxLength: 12
|
||||
type: string
|
||||
required:
|
||||
- captcha_data
|
||||
- captcha_key
|
||||
- password
|
||||
- username
|
||||
type: object
|
||||
host: localhost:5001
|
||||
info:
|
||||
contact:
|
||||
email: support@swagger.io
|
||||
name: API Support
|
||||
url: http://www.swagger.io/support
|
||||
description: Yitao Backend API.
|
||||
license:
|
||||
name: Apache 2.0
|
||||
url: http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
termsOfService: http://swagger.io/terms/
|
||||
title: Yitao Backend API
|
||||
version: "1.0"
|
||||
paths:
|
||||
/api/captcha/click/{key}/submit:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 校验按次序点击验证码
|
||||
parameters:
|
||||
- description: 验证码标识符
|
||||
in: path
|
||||
name: key
|
||||
required: true
|
||||
type: string
|
||||
- description: '15,851;458,18;58,15;x,y; #用户点击的坐标集合'
|
||||
in: formData
|
||||
name: key
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: '{"flag": true|false}'
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"400":
|
||||
description: '{"msg": "错误信息","code":0}'
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
summary: Captcha Click Validate
|
||||
tags:
|
||||
- captcha api
|
||||
/api/captcha/click/gen:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 生成按次序点击验证码
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: '{"key": "验证码标识符","master":"主图(用户点击图)base64","subimg":"子图(文字次序图)base64"}'
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"400":
|
||||
description: '{"msg": "错误信息","code":0}'
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
summary: Captcha Click Generate
|
||||
tags:
|
||||
- captcha api
|
||||
/api/captcha/img/{key}:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 通过 key 获取验证码图片
|
||||
parameters:
|
||||
- description: 验证码标识符
|
||||
in: path
|
||||
name: key
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- image/png
|
||||
responses:
|
||||
"200":
|
||||
description: IMG
|
||||
schema:
|
||||
items:
|
||||
type: integer
|
||||
type: array
|
||||
"400":
|
||||
description: '{"msg": "错误信息","code":0}'
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
summary: Captcha Img Get
|
||||
tags:
|
||||
- captcha api
|
||||
/api/captcha/img/gen:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 获取一张图片验证码的标识符
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: '{"key": "ASDFGHJK"}'
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"400":
|
||||
description: '{"msg": "错误信息","code":0}'
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
summary: Captcha Img Generate
|
||||
tags:
|
||||
- captcha api
|
||||
/api/captcha/img/validate/{key}:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 图片验证码校验(一般情况下后端校验)
|
||||
parameters:
|
||||
- description: 验证码标识符
|
||||
in: path
|
||||
name: key
|
||||
required: true
|
||||
type: string
|
||||
- description: 验证码
|
||||
in: query
|
||||
name: code
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: '{"msg": "正确信息","code":0}'
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"400":
|
||||
description: '{"msg": "错误信息","code":0}'
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
summary: Captcha Img Validate
|
||||
tags:
|
||||
- captcha api
|
||||
/api/file/upload/item-img:
|
||||
put:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 上传商品图片
|
||||
parameters:
|
||||
- description: 图片文件
|
||||
in: formData
|
||||
name: file
|
||||
required: true
|
||||
type: file
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: '{"id": 1,"key":"图片唯一标识"}'
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"400":
|
||||
description: '{"msg": "错误信息","code":0}'
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
summary: 上传商品图片
|
||||
tags:
|
||||
- file api
|
||||
/api/item/get/{id}:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 获取相关ID的商品信息
|
||||
parameters:
|
||||
- description: 商品ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: '{"jwt": "jwt.jwt.jwt"}'
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"400":
|
||||
description: '{"msg": "错误信息","code":0}'
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
summary: get item api
|
||||
tags:
|
||||
- item api
|
||||
/api/item/oper/create:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 创建商品接口
|
||||
parameters:
|
||||
- description: 商品信息
|
||||
in: body
|
||||
name: createItemParam
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/validate.CreateItemParam'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: '{"id": 1}'
|
||||
schema:
|
||||
$ref: '#/definitions/model.ItemModel'
|
||||
"400":
|
||||
description: '{"msg": "错误信息","code":0}'
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
summary: 创建商品
|
||||
tags:
|
||||
- item api
|
||||
/api/user/auth/login:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: the 用户登录接口
|
||||
parameters:
|
||||
- description: 登录参数
|
||||
in: body
|
||||
name: loginParam
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/validate.LoginParam'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: '{"jwt": "jwt.jwt.jwt"}'
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"400":
|
||||
description: '{"msg": "错误信息","code":0}'
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
summary: login api
|
||||
tags:
|
||||
- auth api
|
||||
/api/user/auth/signup:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 用户注册接口
|
||||
parameters:
|
||||
- description: 注册参数
|
||||
in: body
|
||||
name: signupParam
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/validate.SignupParam'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: '{"msg": "注册成功","code":1}'
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"400":
|
||||
description: '{"msg": "错误信息","code":0}'
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
summary: 注册API
|
||||
tags:
|
||||
- auth api
|
||||
swagger: "2.0"
|
30
ecode/auth_ecode.go
Normal file
30
ecode/auth_ecode.go
Normal file
@ -0,0 +1,30 @@
|
||||
package ecode
|
||||
|
||||
const (
|
||||
AUTH_OK = iota
|
||||
USER_NOT_EXIST
|
||||
PASSWORD_ERROR
|
||||
USERNAME_EXIST
|
||||
EMAIL_EXIST
|
||||
|
||||
OAUTH_ERROR_KEY
|
||||
OAUTH_ERROR_TOKEN
|
||||
)
|
||||
|
||||
var AUTH_MSG = map[int]string{
|
||||
AUTH_OK: "成功!",
|
||||
USER_NOT_EXIST: "用户不存在丫",
|
||||
PASSWORD_ERROR: "密码打错了啊",
|
||||
USERNAME_EXIST: "用户名有点火啊",
|
||||
EMAIL_EXIST: "邮箱已经被注册",
|
||||
|
||||
OAUTH_ERROR_KEY: "错误的 OAuth Key",
|
||||
OAUTH_ERROR_TOKEN: "错误的 OAuth Token",
|
||||
}
|
||||
|
||||
func Auth(id int) *Ecode {
|
||||
code := new(Ecode)
|
||||
code.Code = id
|
||||
code.Msg = AUTH_MSG[id]
|
||||
return code
|
||||
}
|
20
ecode/captcha_ecode.go
Normal file
20
ecode/captcha_ecode.go
Normal file
@ -0,0 +1,20 @@
|
||||
package ecode
|
||||
|
||||
const (
|
||||
CAPTCHA_OK = iota
|
||||
CAPTCHA_CODE_ERR
|
||||
CAPTCHA_TIME_ERR
|
||||
)
|
||||
|
||||
var CAPTCHA_MSG = map[int]string{
|
||||
CAPTCHA_OK: "验证成功",
|
||||
CAPTCHA_CODE_ERR: "验证码错误",
|
||||
CAPTCHA_TIME_ERR: "验证码超时,请刷新重试",
|
||||
}
|
||||
|
||||
func Captcha(id int) *Ecode {
|
||||
code := new(Ecode)
|
||||
code.Code = id
|
||||
code.Msg = CAPTCHA_MSG[id]
|
||||
return code
|
||||
}
|
18
ecode/common_ecode.go
Normal file
18
ecode/common_ecode.go
Normal file
@ -0,0 +1,18 @@
|
||||
package ecode
|
||||
|
||||
const (
|
||||
COMMON_OK = iota
|
||||
PARAM_ERROR
|
||||
)
|
||||
|
||||
var COMMON_MEG = map[int]string{
|
||||
COMMON_OK: "OK",
|
||||
PARAM_ERROR: "参数错误",
|
||||
}
|
||||
|
||||
func Common(id int) *Ecode {
|
||||
code := new(Ecode)
|
||||
code.Code = id
|
||||
code.Msg = COMMON_MEG[id]
|
||||
return code
|
||||
}
|
36
ecode/ecode.go
Normal file
36
ecode/ecode.go
Normal file
@ -0,0 +1,36 @@
|
||||
package ecode
|
||||
|
||||
import "github.com/kataras/iris/v12/mvc"
|
||||
|
||||
type Ecode struct {
|
||||
Code int
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (e *Ecode) Response() mvc.Result {
|
||||
return mvc.Response{
|
||||
Object: map[string]interface{}{
|
||||
"code": e.Code,
|
||||
"msg": e.Msg,
|
||||
},
|
||||
Code: 400,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Ecode) Error() bool {
|
||||
return e.Code != 0
|
||||
}
|
||||
|
||||
func Set(code int, msg string) *Ecode {
|
||||
e := new(Ecode)
|
||||
e.Code = code
|
||||
e.Msg = msg
|
||||
return e
|
||||
}
|
||||
|
||||
func OK() *Ecode {
|
||||
e := new(Ecode)
|
||||
e.Code = 0
|
||||
e.Msg = ""
|
||||
return e
|
||||
}
|
20
ecode/file_ecode.go
Normal file
20
ecode/file_ecode.go
Normal file
@ -0,0 +1,20 @@
|
||||
package ecode
|
||||
|
||||
const (
|
||||
FILE_OK = iota
|
||||
FILE_TYPE_ERR
|
||||
FILE_NOT_FOUND
|
||||
)
|
||||
|
||||
var FILE_MSG = map[int]string{
|
||||
FILE_OK: "文件操作成功",
|
||||
FILE_TYPE_ERR: "文件类型错误!",
|
||||
FILE_NOT_FOUND: "文件不存在",
|
||||
}
|
||||
|
||||
func File(id int) *Ecode {
|
||||
code := new(Ecode)
|
||||
code.Code = id
|
||||
code.Msg = FILE_MSG[id]
|
||||
return code
|
||||
}
|
102
go.mod
Normal file
102
go.mod
Normal file
@ -0,0 +1,102 @@
|
||||
module yitao
|
||||
|
||||
go 1.23
|
||||
|
||||
toolchain go1.23.2
|
||||
|
||||
require (
|
||||
github.com/caarlos0/env/v10 v10.0.0
|
||||
github.com/iris-contrib/swagger v0.0.0-20230820002204-56b041d3471a
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/swaggo/swag v1.16.4
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/spec v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.22.1 // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/llgcode/draw2d v0.0.0-20240627062922-0ed1ff131195 // indirect
|
||||
github.com/oschwald/maxminddb-golang v1.13.0 // indirect
|
||||
github.com/redis/go-redis/v9 v9.7.0 // indirect
|
||||
golang.org/x/image v0.18.0 // indirect
|
||||
golang.org/x/tools v0.26.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/BurntSushi/toml v1.4.0 // indirect
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect
|
||||
github.com/CloudyKit/jet/v6 v6.2.0 // indirect
|
||||
github.com/Joker/jade v1.1.3 // indirect
|
||||
github.com/Shopify/goreferrer v0.0.0-20240724165105-aceaa0259138 // indirect
|
||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/fatih/structs v1.1.0 // indirect
|
||||
github.com/flosch/pongo2/v4 v4.0.2 // indirect
|
||||
github.com/go-playground/validator v9.31.0+incompatible
|
||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/gobwas/ws v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/gomarkdown/markdown v0.0.0-20240930133441-72d49d9543d8 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/css v1.0.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/iris-contrib/middleware/jwt v0.0.0-20240926134003-a252b7a49da9
|
||||
github.com/iris-contrib/schema v0.0.6 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/kataras/blocks v0.0.8 // indirect
|
||||
github.com/kataras/golog v0.1.12 // indirect
|
||||
github.com/kataras/iris/v12 v12.2.11
|
||||
github.com/kataras/neffos v0.0.24-0.20240408172741-99c879ba0ede // indirect
|
||||
github.com/kataras/pio v0.0.14-0.20240707171706-2005199e2703 // indirect
|
||||
github.com/kataras/sitemap v0.0.6 // indirect
|
||||
github.com/kataras/tunnel v0.0.4 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/mailgun/raymond/v2 v2.0.48 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mediocregopher/radix/v3 v3.8.1 // indirect
|
||||
github.com/microcosm-cc/bluemonday v1.0.27 // indirect
|
||||
github.com/nats-io/nats.go v1.34.1 // indirect
|
||||
github.com/nats-io/nkeys v0.4.7 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
github.com/oschwald/geoip2-golang v1.11.0
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/schollz/closestmatch v2.1.0+incompatible // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/tdewolff/minify/v2 v2.21.1 // indirect
|
||||
github.com/tdewolff/parse/v2 v2.7.19 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/vcqr/captcha v0.0.0-20190226134436-33e818ba057b
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/wenlng/go-captcha v1.2.5
|
||||
github.com/yosssi/ace v0.0.5 // indirect
|
||||
golang.org/x/crypto v0.28.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/sys v0.26.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
golang.org/x/time v0.7.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/protobuf v1.35.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gorm.io/driver/mysql v1.5.7
|
||||
gorm.io/gorm v1.25.12
|
||||
)
|
289
go.sum
Normal file
289
go.sum
Normal file
@ -0,0 +1,289 @@
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
|
||||
github.com/CloudyKit/jet/v6 v6.2.0 h1:EpcZ6SR9n28BUGtNJSvlBqf90IpjeFr36Tizxhn/oME=
|
||||
github.com/CloudyKit/jet/v6 v6.2.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4=
|
||||
github.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=
|
||||
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
|
||||
github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=
|
||||
github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/Shopify/goreferrer v0.0.0-20240724165105-aceaa0259138 h1:gjbp60h8IZQbN/TpDaYJedWbbD1h1aDPEwWnYWaDaUY=
|
||||
github.com/Shopify/goreferrer v0.0.0-20240724165105-aceaa0259138/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/caarlos0/env/v10 v10.0.0 h1:yIHUBZGsyqCnpTkbjk8asUlx6RFhhEs+h7TOBdgdzXA=
|
||||
github.com/caarlos0/env/v10 v10.0.0/go.mod h1:ZfulV76NvVPw3tm591U4SwL3Xx9ldzBP9aGxzeN7G18=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
|
||||
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=
|
||||
github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
|
||||
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator v9.31.0+incompatible h1:UA72EPEogEnq76ehGdEDp4Mit+3FDh548oRqwVgNsHA=
|
||||
github.com/go-playground/validator v9.31.0+incompatible/go.mod h1:yrEkQXlcI+PugkyDjY2bRrL/UBU4f3rvrgkN3V8JEig=
|
||||
github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
|
||||
github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.3.2 h1:zlnbNHxumkRvfPWgfXu8RBwyNR1x8wh9cf5PTOCqs9Q=
|
||||
github.com/gobwas/ws v1.3.2/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/gomarkdown/markdown v0.0.0-20240930133441-72d49d9543d8 h1:4txT5G2kqVAKMjzidIabL/8KqjIK71yj30YOeuxLn10=
|
||||
github.com/gomarkdown/markdown v0.0.0-20240930133441-72d49d9543d8/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
|
||||
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
|
||||
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
|
||||
github.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=
|
||||
github.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=
|
||||
github.com/iris-contrib/middleware/jwt v0.0.0-20240926134003-a252b7a49da9 h1:JZER97FGNR0fAe6RYs1YWBQ21gxOS67ycb/OAR46uw4=
|
||||
github.com/iris-contrib/middleware/jwt v0.0.0-20240926134003-a252b7a49da9/go.mod h1:5hNczCIIfMiMwMRlYwc5tulSXskbR61hMBWYtJNHzgc=
|
||||
github.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=
|
||||
github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=
|
||||
github.com/iris-contrib/swagger v0.0.0-20230820002204-56b041d3471a h1:EC+WWO8I4TU9jkmTO/pZciMTNTz/LHQHfM4vSypPDGQ=
|
||||
github.com/iris-contrib/swagger v0.0.0-20230820002204-56b041d3471a/go.mod h1:z3K0yN8DqDkwlfqSzR8HHqiZCb57W/8QaPOMBmJiMLU=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=
|
||||
github.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=
|
||||
github.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=
|
||||
github.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=
|
||||
github.com/kataras/iris/v12 v12.2.11 h1:sGgo43rMPfzDft8rjVhPs6L3qDJy3TbBrMD/zGL1pzk=
|
||||
github.com/kataras/iris/v12 v12.2.11/go.mod h1:uMAeX8OqG9vqdhyrIPv8Lajo/wXTtAF43wchP9WHt2w=
|
||||
github.com/kataras/neffos v0.0.24-0.20240408172741-99c879ba0ede h1:ZnSJQ+ri9x46Yz15wHqSb93Q03yY12XMVLUFDJJ0+/g=
|
||||
github.com/kataras/neffos v0.0.24-0.20240408172741-99c879ba0ede/go.mod h1:i0dtcTbpnw1lqIbojYtGtZlu6gDWPxJ4Xl2eJ6oQ1bE=
|
||||
github.com/kataras/pio v0.0.14-0.20240707171706-2005199e2703 h1:RzWeszUyNUlyKH+3Nz1tfAj5FWn5UZBG5QP9LIhJZzI=
|
||||
github.com/kataras/pio v0.0.14-0.20240707171706-2005199e2703/go.mod h1:WNpzgFvXTQ11zsIKHxpQhaoVIxQGTSowpnElAbVOeN8=
|
||||
github.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=
|
||||
github.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=
|
||||
github.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=
|
||||
github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/llgcode/draw2d v0.0.0-20240627062922-0ed1ff131195 h1:Vdz2cBh5Fw2MYHWi3ED2PraDQaWEUhNCr1XFHrP4N5A=
|
||||
github.com/llgcode/draw2d v0.0.0-20240627062922-0ed1ff131195/go.mod h1:1Vk0LDW6jG5cGc2D9RQUxHaE0vYhTvIwSo9mOL6K4/U=
|
||||
github.com/llgcode/ps v0.0.0-20210114104736-f4b0c5d1e02e h1:ZAvbj5hI/G/EbAYAcj4yCXUNiFKefEhH0qfImDDD0/8=
|
||||
github.com/llgcode/ps v0.0.0-20210114104736-f4b0c5d1e02e/go.mod h1:1l8ky+Ew27CMX29uG+a2hNOKpeNYEQjjtiALiBlFQbY=
|
||||
github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=
|
||||
github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mediocregopher/radix/v3 v3.8.1 h1:rOkHflVuulFKlwsLY01/M2cM2tWCjDoETcMqKbAWu1M=
|
||||
github.com/mediocregopher/radix/v3 v3.8.1/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
|
||||
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
||||
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||
github.com/nats-io/nats.go v1.34.1 h1:syWey5xaNHZgicYBemv0nohUPPmaLteiBEUT6Q5+F/4=
|
||||
github.com/nats-io/nats.go v1.34.1/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
|
||||
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
|
||||
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/oschwald/geoip2-golang v1.11.0 h1:hNENhCn1Uyzhf9PTmquXENiWS6AlxAEnBII6r8krA3w=
|
||||
github.com/oschwald/geoip2-golang v1.11.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo=
|
||||
github.com/oschwald/maxminddb-golang v1.13.0 h1:R8xBorY71s84yO06NgTmQvqvTvlS/bnYZrrWX1MElnU=
|
||||
github.com/oschwald/maxminddb-golang v1.13.0/go.mod h1:BU0z8BfFVhi1LQaonTwwGQlsHUEu9pWNdMfmq4ztm0o=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
|
||||
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
|
||||
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
|
||||
github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=
|
||||
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
|
||||
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
||||
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A=
|
||||
github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg=
|
||||
github.com/tdewolff/minify/v2 v2.21.1 h1:AAf5iltw6+KlUvjRNPAPrANIXl3XEJNBBzuZom5iCAM=
|
||||
github.com/tdewolff/minify/v2 v2.21.1/go.mod h1:PoqFH8ugcuTUvKqVM9vOqXw4msxvuhL/DTmV5ZXhSCI=
|
||||
github.com/tdewolff/parse/v2 v2.7.19 h1:7Ljh26yj+gdLFEq/7q9LT4SYyKtwQX4ocNrj45UCePg=
|
||||
github.com/tdewolff/parse/v2 v2.7.19/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
|
||||
github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03u/dMQK9g+Iw9ewps4mCl1nB8Sscbo=
|
||||
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/vcqr/captcha v0.0.0-20190226134436-33e818ba057b h1:daTYPysHyKk9cxIeXH2bab3jaDMNCj2nKB/2ElYtALY=
|
||||
github.com/vcqr/captcha v0.0.0-20190226134436-33e818ba057b/go.mod h1:CrLLRaUNkf6egyJWGeSvl+c4mlJ5U1cGQNGikoILyrA=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||
github.com/wenlng/go-captcha v1.2.5 h1:zA0/fovEl9oAhSg+KwHBwmq99GeeAXknWx6wYKjhjTg=
|
||||
github.com/wenlng/go-captcha v1.2.5/go.mod h1:QgPgpEURSa37gF3GtojNoNRwbMwuatSBx5NXrzASOb0=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=
|
||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
|
||||
github.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=
|
||||
github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=
|
||||
github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
|
||||
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
|
||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
||||
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
|
||||
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
|
||||
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||
moul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=
|
||||
moul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=
|
65
main.go
Normal file
65
main.go
Normal file
@ -0,0 +1,65 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"yitao/config"
|
||||
"yitao/controller"
|
||||
_ "yitao/docs"
|
||||
"yitao/model"
|
||||
"yitao/service"
|
||||
"yitao/util"
|
||||
"yitao/validate"
|
||||
|
||||
"github.com/iris-contrib/swagger"
|
||||
"github.com/iris-contrib/swagger/swaggerFiles"
|
||||
"github.com/kataras/iris/v12"
|
||||
)
|
||||
|
||||
// @title Yitao Backend API
|
||||
// @version 1.0
|
||||
// @description Yitao Backend API.
|
||||
// @termsOfService http://swagger.io/terms/
|
||||
|
||||
// @contact.name API Support
|
||||
// @contact.url http://www.swagger.io/support
|
||||
// @contact.email support@swagger.io
|
||||
|
||||
// @license.name Apache 2.0
|
||||
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
// @host localhost:5001
|
||||
// @BasePath /api
|
||||
func main() {
|
||||
app := iris.New()
|
||||
|
||||
swaggerUI := swagger.Handler(swaggerFiles.Handler,
|
||||
swagger.URL("/swagger/swagger.json"),
|
||||
swagger.DeepLinking(true),
|
||||
swagger.Prefix("/swagger"),
|
||||
)
|
||||
|
||||
// Register on http://localhost:5001/swagger
|
||||
app.Get("/swagger", swaggerUI)
|
||||
// And the wildcard one for index.html, *.js, *.css and e.t.c.
|
||||
app.Get("/swagger/{any:path}", swaggerUI)
|
||||
|
||||
conf := config.LoadConfig()
|
||||
|
||||
// init util
|
||||
util.Init(&conf)
|
||||
|
||||
// connect to Database - NexusPHP`s MySQL
|
||||
model.ConnectToDb(conf.DB)
|
||||
model.ConnectToRedis(conf.RDB)
|
||||
|
||||
// init service
|
||||
serv := service.InitService()
|
||||
|
||||
validate.InitValidator()
|
||||
|
||||
// load router
|
||||
controller.LoadRouter(app, serv)
|
||||
|
||||
app.Run(
|
||||
iris.Addr("0.0.0.0:5001"),
|
||||
)
|
||||
}
|
25
middleware/admin.go
Normal file
25
middleware/admin.go
Normal file
@ -0,0 +1,25 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/iris-contrib/middleware/jwt"
|
||||
"github.com/kataras/iris/v12"
|
||||
)
|
||||
|
||||
var AdminMiddleware = iris.Handler(func(ctx iris.Context) {
|
||||
|
||||
if token, ok := ctx.Values().Get("jwt").(*jwt.Token); ok {
|
||||
// Use the token if needed
|
||||
is_admin := token.Claims.(jwt.MapClaims)["is_admin"].(bool)
|
||||
if !is_admin {
|
||||
ctx.StatusCode(iris.StatusForbidden)
|
||||
ctx.JSON(iris.Map{"message": "forbidden"})
|
||||
}
|
||||
} else {
|
||||
ctx.StatusCode(iris.StatusUnauthorized)
|
||||
ctx.JSON(iris.Map{"message": "unauthorized"})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Next()
|
||||
|
||||
})
|
29
middleware/jwt.go
Normal file
29
middleware/jwt.go
Normal file
@ -0,0 +1,29 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"yitao/config"
|
||||
|
||||
"github.com/iris-contrib/middleware/jwt"
|
||||
)
|
||||
|
||||
var JwtMiddleware *jwt.Middleware
|
||||
|
||||
func init() {
|
||||
JwtMiddleware = jwt.New(jwt.Config{
|
||||
// Extractor属性可以选择从什么地方获取jwt进行验证,默认从http请求的header中的Authorization字段提取,也可指定为请求参数中的某个字段
|
||||
|
||||
// 从请求参数token中提取
|
||||
// Extractor: jwt.FromParameter("token"),
|
||||
|
||||
// 从请求头的Authorization字段中提取,这个是默认值
|
||||
Extractor: jwt.FromAuthHeader,
|
||||
|
||||
// 设置一个函数返回秘钥,关键在于return []byte("这里设置秘钥")
|
||||
ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(config.GetConfig().JwtSecret), nil
|
||||
},
|
||||
|
||||
// 设置一个加密方法
|
||||
SigningMethod: jwt.SigningMethodHS256,
|
||||
})
|
||||
}
|
16
model/captcha_model.go
Normal file
16
model/captcha_model.go
Normal file
@ -0,0 +1,16 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
type CaptchaModel struct {
|
||||
ID uint `gorm:"primarykey"`
|
||||
CreatedAt time.Time `gorm:"column:added;autoCreateTime"`
|
||||
Code string
|
||||
Key string `gorm:"column:captkey;autoCreateTime"`
|
||||
State int
|
||||
Data string
|
||||
}
|
||||
|
||||
func (m *CaptchaModel) TableName() string {
|
||||
return "captcha"
|
||||
}
|
23
model/file_model.go
Normal file
23
model/file_model.go
Normal file
@ -0,0 +1,23 @@
|
||||
package model
|
||||
|
||||
const (
|
||||
FileTypeItemImg = "item-img"
|
||||
|
||||
FileStatusNormal = "normal"
|
||||
FileStatusDelete = "delete"
|
||||
)
|
||||
|
||||
type FileModel struct {
|
||||
BaseModel
|
||||
Name string
|
||||
Type string
|
||||
Size int64
|
||||
Url string
|
||||
SystemPath string
|
||||
Key string
|
||||
Status string
|
||||
}
|
||||
|
||||
func (f *FileModel) TableName() string {
|
||||
return "files"
|
||||
}
|
37
model/item_model.go
Normal file
37
model/item_model.go
Normal file
@ -0,0 +1,37 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const (
|
||||
ItemStateOnSale = "onsale"
|
||||
)
|
||||
|
||||
// type 是外部输入的 []int 类型,需要转换成 string 类型存储到数据库
|
||||
type ItemModel struct {
|
||||
BaseModel
|
||||
Itemname string
|
||||
Types []int `gorm:"-"`
|
||||
TypeString string
|
||||
Price int
|
||||
Desc string
|
||||
Img string
|
||||
States string
|
||||
}
|
||||
|
||||
func (m *ItemModel) TableName() string {
|
||||
return "items"
|
||||
}
|
||||
func (i *ItemModel) BeforeCreate(tx *gorm.DB) (err error) {
|
||||
types_string, _ := json.Marshal(i.Types)
|
||||
i.TypeString = string(types_string)
|
||||
return
|
||||
}
|
||||
|
||||
func (i *ItemModel) AfterFind(tx *gorm.DB) (err error) {
|
||||
err = json.Unmarshal([]byte(i.TypeString), &i.Types)
|
||||
return
|
||||
}
|
18
model/login_log_model.go
Normal file
18
model/login_log_model.go
Normal file
@ -0,0 +1,18 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
type LoginLogModel struct {
|
||||
ID uint `gorm:"primarykey"`
|
||||
Country string
|
||||
City string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
Uid uint
|
||||
Ip string
|
||||
Client string
|
||||
}
|
||||
|
||||
func (m *LoginLogModel) TableName() string {
|
||||
return "login_logs"
|
||||
}
|
30
model/model.go
Normal file
30
model/model.go
Normal file
@ -0,0 +1,30 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var DB *gorm.DB
|
||||
|
||||
type BaseModel struct {
|
||||
ID uint `gorm:"primarykey"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
func ConnectToDb(dsn string) *gorm.DB {
|
||||
var err error
|
||||
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = DB.AutoMigrate(&CaptchaModel{}, &UserModel{}, &ItemModel{}, &LoginLogModel{}, &FileModel{})
|
||||
return DB
|
||||
}
|
||||
|
||||
type ModelInterface interface {
|
||||
TableName() string
|
||||
}
|
32
model/redis.go
Normal file
32
model/redis.go
Normal file
@ -0,0 +1,32 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
var RDB *redis.Client
|
||||
|
||||
// 连接到 redis
|
||||
func ConnectToRedis(dsn string) *redis.Client {
|
||||
opt, err := redis.ParseURL(dsn)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
rdb := redis.NewClient(opt)
|
||||
RDB = rdb
|
||||
return rdb
|
||||
}
|
||||
|
||||
func RedisRemember(key string, timeout int, funct func() interface{}) string {
|
||||
ctx := context.Background()
|
||||
if RDB.Exists(ctx, key).Val() == 0 {
|
||||
data := funct()
|
||||
RDB.Set(ctx, key, data, time.Duration(timeout)*time.Second)
|
||||
return RDB.Get(ctx, key).Val()
|
||||
}
|
||||
return RDB.Get(ctx, key).Val()
|
||||
}
|
22
model/user_model.go
Normal file
22
model/user_model.go
Normal file
@ -0,0 +1,22 @@
|
||||
package model
|
||||
|
||||
type UserStatus string
|
||||
|
||||
const (
|
||||
UserStatusPending UserStatus = "pending"
|
||||
UserStatusComfirmed UserStatus = "confirmed"
|
||||
)
|
||||
|
||||
type UserModel struct {
|
||||
BaseModel
|
||||
ID uint `gorm:"primarykey"`
|
||||
Username string
|
||||
Passhash string
|
||||
Secret string
|
||||
Status UserStatus `gorm:"type:enum('pending', 'confirmed')"`
|
||||
IsAdmin bool `gorm:"default:false"`
|
||||
}
|
||||
|
||||
func (m *UserModel) TableName() string {
|
||||
return "users"
|
||||
}
|
108
service/captcha_service.go
Normal file
108
service/captcha_service.go
Normal file
@ -0,0 +1,108 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"image/png"
|
||||
"strconv"
|
||||
"strings"
|
||||
"yitao/ecode"
|
||||
"yitao/model"
|
||||
"yitao/util"
|
||||
|
||||
imgCaptcha "github.com/vcqr/captcha"
|
||||
clickCaptcha "github.com/wenlng/go-captcha/captcha"
|
||||
)
|
||||
|
||||
type CaptchaService struct{}
|
||||
|
||||
func (s *CaptchaService) GetIMG(length int, pure bool) (string, []byte) {
|
||||
return s.GetIMGWithHW(length, pure, 120, 40)
|
||||
}
|
||||
|
||||
func (s *CaptchaService) GetIMGWithHW(length int, pure bool, h int, w int) (string, []byte) {
|
||||
cp := imgCaptcha.NewCaptcha(h, w, length)
|
||||
if !pure {
|
||||
cp.SetMode(1)
|
||||
}
|
||||
cp.SetFontPath("static/font/")
|
||||
cp.SetFontName("common")
|
||||
code, img := cp.OutPut()
|
||||
var buf bytes.Buffer
|
||||
png.Encode(&buf, img)
|
||||
return code, buf.Bytes()
|
||||
}
|
||||
|
||||
func (s *CaptchaService) SaveToDB(key, code string) {
|
||||
captcha := new(model.CaptchaModel)
|
||||
captcha.Code = code
|
||||
captcha.Key = key
|
||||
model.DB.Create(captcha)
|
||||
}
|
||||
|
||||
func (s *CaptchaService) Validate(key, code string) *ecode.Ecode {
|
||||
if key == "" || code == "" {
|
||||
return ecode.Captcha(ecode.CAPTCHA_CODE_ERR)
|
||||
}
|
||||
if key == "devonlyy" && code == "DEV1" {
|
||||
return ecode.Captcha(ecode.CAPTCHA_OK)
|
||||
}
|
||||
captcha := new(model.CaptchaModel)
|
||||
model.DB.Where("captkey=?", key).First(captcha)
|
||||
// 验证码一分钟有效
|
||||
if util.GetTime() > captcha.CreatedAt.Unix()+60*1 {
|
||||
return ecode.Captcha(ecode.CAPTCHA_TIME_ERR)
|
||||
}
|
||||
if captcha.Code != code {
|
||||
return ecode.Captcha(ecode.CAPTCHA_CODE_ERR)
|
||||
}
|
||||
return ecode.Captcha(ecode.CAPTCHA_OK)
|
||||
}
|
||||
|
||||
func (s *CaptchaService) Delete(key string) {
|
||||
util.DeleteFile("/temp/captcha/img/" + key + ".png")
|
||||
captcha := new(model.CaptchaModel)
|
||||
model.DB.Where("captkey=?", key).Delete(captcha)
|
||||
}
|
||||
|
||||
func (s *CaptchaService) GetClick() (string, string, string) {
|
||||
capt := clickCaptcha.GetCaptcha()
|
||||
//capt.SetRangChars([]string{"四", "零", "二", "六", "一", "三", "五", "七", "八", "九"})
|
||||
dots, b64, bt64, key, _ := capt.Generate()
|
||||
cap := new(model.CaptchaModel)
|
||||
j, _ := json.Marshal(dots)
|
||||
cap.Code = string(j)
|
||||
//cap.Data = b64
|
||||
cap.Key = key
|
||||
model.DB.Create(cap)
|
||||
return key, b64, bt64
|
||||
}
|
||||
|
||||
func (s *CaptchaService) ClickValidate(key string, input string) bool {
|
||||
input_dots := strings.Split(input, ";")
|
||||
cap := new(model.CaptchaModel)
|
||||
model.DB.Where("captkey=?", key).First(cap)
|
||||
//var right_dots *map[int]clickCaptcha.CharDot
|
||||
var right_json map[int]clickCaptcha.CharDot
|
||||
right_json = make(map[int]clickCaptcha.CharDot)
|
||||
json.Unmarshal([]byte(cap.Code), &right_json)
|
||||
|
||||
if len(input_dots)-1 != len(right_json) {
|
||||
return false
|
||||
}
|
||||
|
||||
//i := 0
|
||||
for k, v := range input_dots {
|
||||
if v == "" {
|
||||
break
|
||||
}
|
||||
input_dot := strings.Split(v, ",")
|
||||
input_x, _ := strconv.Atoi(input_dot[0])
|
||||
input_y, _ := strconv.Atoi(input_dot[1])
|
||||
d := (right_json[k].Dx-input_x)*(right_json[k].Dx-input_x) + (right_json[k].Dy-input_y)*(right_json[k].Dy-input_y)
|
||||
if d > 1600 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
49
service/common_service.go
Normal file
49
service/common_service.go
Normal file
@ -0,0 +1,49 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"yitao/model"
|
||||
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type CommonService struct{}
|
||||
|
||||
type TypeSetting map[string]string
|
||||
type TypeSettings map[string]TypeSetting
|
||||
|
||||
func (c *CommonService) GetSetting(key string) TypeSetting {
|
||||
settings_json := model.RedisRemember("nexus_settings_in_nexus", 600, func() interface{} {
|
||||
j, _ := json.Marshal(c.GetSettingsFromDB())
|
||||
return j
|
||||
})
|
||||
m := make(TypeSettings)
|
||||
json.Unmarshal([]byte(settings_json), &m)
|
||||
return m[key]
|
||||
}
|
||||
|
||||
func (c *CommonService) GetSettingsFromDB() TypeSettings {
|
||||
// settings 表中的两个字段:name 和 value,把他们读取出来再组装成一个 map
|
||||
var results []struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
model.DB.Table("settings").Select("name", "value").Find(&results)
|
||||
// 从 results 中取出 name 和 value 组装成一个 map
|
||||
// 其中 name 的格式是 xxx.yyy ,那么就是一个嵌套的 map
|
||||
settings := make(TypeSettings)
|
||||
for _, result := range results {
|
||||
keys := strings.Split(result.Name, ".")
|
||||
if len(keys) == 1 {
|
||||
settings[keys[0]] = TypeSetting{keys[0]: result.Value}
|
||||
} else {
|
||||
if _, ok := settings[keys[0]]; !ok {
|
||||
settings[keys[0]] = TypeSetting{}
|
||||
}
|
||||
settings[keys[0]][keys[1]] = result.Value
|
||||
}
|
||||
}
|
||||
fmt.Printf("settings: %v\n", settings)
|
||||
return settings
|
||||
}
|
7
service/email_service.go
Normal file
7
service/email_service.go
Normal file
@ -0,0 +1,7 @@
|
||||
package service
|
||||
|
||||
type EmailService struct{}
|
||||
|
||||
func (e *EmailService) ValidateSignupEmail(email string) bool {
|
||||
return true
|
||||
}
|
37
service/file_service.go
Normal file
37
service/file_service.go
Normal file
@ -0,0 +1,37 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"mime/multipart"
|
||||
"yitao/ecode"
|
||||
"yitao/model"
|
||||
"yitao/util"
|
||||
)
|
||||
|
||||
type FileService struct{}
|
||||
|
||||
// UploadItemImg 上传商品图片
|
||||
func (s *FileService) UploadItemImg(header *multipart.FileHeader) (*model.FileModel, *ecode.Ecode) {
|
||||
file := new(model.FileModel)
|
||||
file.Type = model.FileTypeItemImg
|
||||
file.Name = header.Filename
|
||||
file.Key = util.RandString(12)
|
||||
file.Size = header.Size
|
||||
file.Status = model.FileStatusNormal
|
||||
file.Url = "/upload/item/img/" + file.Key + ".jpg"
|
||||
file.SystemPath = "./public" + file.Url
|
||||
model.DB.Create(file)
|
||||
|
||||
return file, ecode.OK()
|
||||
}
|
||||
|
||||
// 设置文件状态
|
||||
func (s *FileService) SetFileStatus(id uint, status string) *ecode.Ecode {
|
||||
file := new(model.FileModel)
|
||||
model.DB.First(file, id)
|
||||
if file.ID == 0 {
|
||||
return ecode.File(ecode.FILE_NOT_FOUND)
|
||||
}
|
||||
file.Status = status
|
||||
model.DB.Save(file)
|
||||
return ecode.OK()
|
||||
}
|
21
service/item_service.go
Normal file
21
service/item_service.go
Normal file
@ -0,0 +1,21 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"yitao/ecode"
|
||||
"yitao/model"
|
||||
)
|
||||
|
||||
type ItemService struct{}
|
||||
|
||||
func (s *ItemService) CreateItem(itemname string, desc string, types []int, img string, price int) (item *model.ItemModel, e *ecode.Ecode) {
|
||||
item = new(model.ItemModel)
|
||||
e = ecode.OK()
|
||||
item.Itemname = itemname
|
||||
item.Desc = desc
|
||||
item.Types = types
|
||||
item.Img = img
|
||||
item.Price = price
|
||||
item.States = model.ItemStateOnSale
|
||||
model.DB.Create(item)
|
||||
return
|
||||
}
|
15
service/login_log_service.go
Normal file
15
service/login_log_service.go
Normal file
@ -0,0 +1,15 @@
|
||||
package service
|
||||
|
||||
import "yitao/model"
|
||||
|
||||
type LoginLogService struct{}
|
||||
|
||||
func (s *LoginLogService) Save(uid uint, ip string, client string, country string, city string) {
|
||||
login_log := new(model.LoginLogModel)
|
||||
login_log.Uid = uid
|
||||
login_log.Ip = ip
|
||||
login_log.Client = client
|
||||
login_log.Country = country
|
||||
login_log.City = city
|
||||
model.DB.Create(login_log)
|
||||
}
|
23
service/service.go
Normal file
23
service/service.go
Normal file
@ -0,0 +1,23 @@
|
||||
package service
|
||||
|
||||
type Service struct {
|
||||
User *UserService
|
||||
Captcha *CaptchaService
|
||||
LoginLog *LoginLogService
|
||||
Common *CommonService
|
||||
Item *ItemService
|
||||
File *FileService
|
||||
}
|
||||
|
||||
func InitService() *Service {
|
||||
s := new(Service)
|
||||
|
||||
s.User = new(UserService)
|
||||
s.Captcha = new(CaptchaService)
|
||||
s.LoginLog = new(LoginLogService)
|
||||
s.Common = new(CommonService)
|
||||
s.Item = new(ItemService)
|
||||
s.File = new(FileService)
|
||||
|
||||
return s
|
||||
}
|
52
service/user_service.go
Normal file
52
service/user_service.go
Normal file
@ -0,0 +1,52 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"yitao/ecode"
|
||||
"yitao/model"
|
||||
"yitao/util"
|
||||
)
|
||||
|
||||
type UserService struct{}
|
||||
|
||||
func (u *UserService) AuthLogin(username string, password string) (user *model.UserModel, e *ecode.Ecode) {
|
||||
e = ecode.Common(ecode.AUTH_OK)
|
||||
// 验证用户名密码,返回 jwt
|
||||
user = new(model.UserModel)
|
||||
model.DB.Where("username=?", username).First(user)
|
||||
if util.MD5(user.Secret+password+user.Secret) != user.Passhash {
|
||||
// 密码错误
|
||||
return user, ecode.Auth(ecode.PASSWORD_ERROR)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (u *UserService) AuthSignup(username string, password string) (user *model.UserModel, e *ecode.Ecode) {
|
||||
e = ecode.Common(ecode.AUTH_OK)
|
||||
// 验证用户名密码,返回 jwt
|
||||
user = new(model.UserModel)
|
||||
var c int64
|
||||
model.DB.Where("username=?", username).Count(&c)
|
||||
if c > 0 {
|
||||
// 用户名已存在
|
||||
return user, ecode.Auth(ecode.USERNAME_EXIST)
|
||||
}
|
||||
user.Username = username
|
||||
user.Secret = util.RandString(16)
|
||||
user.Passhash = util.MD5(user.Secret + password + user.Secret)
|
||||
user.Status = model.UserStatusComfirmed
|
||||
model.DB.Create(user)
|
||||
return
|
||||
}
|
||||
|
||||
func (u *UserService) RegisterationCheck(invite bool, max_user_check bool, ip_check bool, edu bool) (e *ecode.Ecode) {
|
||||
common_service := new(CommonService)
|
||||
setting := common_service.GetSetting("main")
|
||||
if invite {
|
||||
// 邀请注册,查看是否开启了可被邀请注册
|
||||
invitesystem := setting["invitesystem"] == "true"
|
||||
fmt.Printf("invitesystem: %v\n", invitesystem)
|
||||
}
|
||||
return ecode.OK()
|
||||
}
|
BIN
static/font/common.ttf
Normal file
BIN
static/font/common.ttf
Normal file
Binary file not shown.
70
util/file.go
Normal file
70
util/file.go
Normal file
@ -0,0 +1,70 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
func InitMkDir() {
|
||||
os.MkdirAll("./public/upload/item/img/", os.ModePerm)
|
||||
}
|
||||
|
||||
func SaveToFile(filename string, data []byte) error {
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = file.Write(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func ReadFromFile(filename string) ([]byte, error) {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
return io.ReadAll(file)
|
||||
}
|
||||
|
||||
func DeleteFile(filename string) error {
|
||||
return os.Remove(filename)
|
||||
}
|
||||
|
||||
// 判断文件是否为图片
|
||||
func FileIsImage(filename string) bool {
|
||||
// 使用文件头检测文件类型
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 读取文件前 512 个字节
|
||||
head := make([]byte, 512)
|
||||
_, err = file.Read(head)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// 判断文件类型
|
||||
if head[0] == 0xFF && head[1] == 0xD8 {
|
||||
return true
|
||||
}
|
||||
if head[0] == 0x89 && head[1] == 0x50 && head[2] == 0x4E && head[3] == 0x47 {
|
||||
return true
|
||||
}
|
||||
if head[0] == 0x47 && head[1] == 0x49 && head[2] == 0x46 && head[3] == 0x38 {
|
||||
return true
|
||||
}
|
||||
if head[0] == 0x42 && head[1] == 0x4D {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
38
util/geoip.go
Normal file
38
util/geoip.go
Normal file
@ -0,0 +1,38 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/oschwald/geoip2-golang"
|
||||
)
|
||||
|
||||
var GeoIpDB *geoip2.Reader
|
||||
|
||||
func InitGeoIP(path string) (*geoip2.Reader, error) {
|
||||
db, err := geoip2.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
GeoIpDB = db
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func GetCityAndCountry_zh(ip string) (string, string, error) {
|
||||
record, err := GeoIpDB.City(net.ParseIP(ip))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return record.City.Names["zh-CN"], record.Country.Names["zh-CN"], nil
|
||||
}
|
||||
|
||||
func GetCityAndCountry_en(ip string) (string, string, error) {
|
||||
record, err := GeoIpDB.City(net.ParseIP(ip))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return record.City.Names["en"], record.Country.Names["en"], nil
|
||||
}
|
||||
|
||||
func GetCityAndCountry(ip string) (string, string, error) {
|
||||
return GetCityAndCountry_en(ip)
|
||||
}
|
32
util/obj.go
Normal file
32
util/obj.go
Normal file
@ -0,0 +1,32 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// 删除一个 struct 中为 nil 或 "" 的元素
|
||||
func DeleteNilItems(obj interface{}) interface{} {
|
||||
sVal := reflect.ValueOf(obj)
|
||||
sType := reflect.TypeOf(obj)
|
||||
var returnMap map[string]interface{}
|
||||
returnMap = make(map[string]interface{})
|
||||
if sType.Kind() == reflect.Ptr {
|
||||
//用Elem()获得实际的value
|
||||
sVal = sVal.Elem()
|
||||
sType = sType.Elem()
|
||||
}
|
||||
num := sVal.NumField()
|
||||
for i := 0; i < num; i++ {
|
||||
f := sType.Field(i)
|
||||
val := sVal.Field(i).Interface()
|
||||
switch val.(type) {
|
||||
case string:
|
||||
if val.(string) != "" {
|
||||
returnMap[f.Name] = val
|
||||
}
|
||||
default:
|
||||
returnMap[f.Name] = val
|
||||
}
|
||||
}
|
||||
return returnMap
|
||||
}
|
33
util/str.go
Normal file
33
util/str.go
Normal file
@ -0,0 +1,33 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
func MD5(str string) string {
|
||||
data := []byte(str)
|
||||
has := md5.Sum(data)
|
||||
md5str := fmt.Sprintf("%x", has)
|
||||
return md5str
|
||||
}
|
||||
|
||||
const charset = "abcdefghijklmnopqrstuvwxyz" +
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
|
||||
var seededRand *rand.Rand = rand.New(
|
||||
rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
func RandStringWithCharset(length int, charset string) string {
|
||||
b := make([]byte, length)
|
||||
for i := range b {
|
||||
b[i] = charset[seededRand.Intn(len(charset))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func RandString(length int) string {
|
||||
return RandStringWithCharset(length, charset)
|
||||
}
|
7
util/time.go
Normal file
7
util/time.go
Normal file
@ -0,0 +1,7 @@
|
||||
package util
|
||||
|
||||
import "time"
|
||||
|
||||
func GetTime() int64 {
|
||||
return time.Now().Unix()
|
||||
}
|
3
util/type.go
Normal file
3
util/type.go
Normal file
@ -0,0 +1,3 @@
|
||||
package util
|
||||
|
||||
type MAP map[string]interface{}
|
10
util/url.go
Normal file
10
util/url.go
Normal file
@ -0,0 +1,10 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func CheckURL(u string) bool {
|
||||
_, err := url.ParseRequestURI(u)
|
||||
return err == nil
|
||||
}
|
8
util/util.go
Normal file
8
util/util.go
Normal file
@ -0,0 +1,8 @@
|
||||
package util
|
||||
|
||||
import "yitao/config"
|
||||
|
||||
func Init(conf *config.Config) {
|
||||
InitGeoIP(conf.GeoipDB)
|
||||
InitMkDir()
|
||||
}
|
12
validate/item_validate.go
Normal file
12
validate/item_validate.go
Normal file
@ -0,0 +1,12 @@
|
||||
package validate
|
||||
|
||||
type CreateItemParam struct {
|
||||
Itemname string `json:"itemname" validate:"required,max=12" example:"女大自用iPhone18"`
|
||||
// @description 商品价格,单位为分
|
||||
Price int `json:"price" validate:"required" example:"1000"`
|
||||
//@description 图片ID,请用 /file/upload/item-img 接口上传图片后获取的图片唯一标识
|
||||
Img string `json:"img" validate:"required,len=12" example:"123456789012" description:""`
|
||||
Desc string `json:"desc" validate:"required" example:"这是一个女大自用的iPhone18"`
|
||||
// @description 商品类型,可以有多个类型,请用数组传过来,类型id请使用 /item/types 接口获取
|
||||
Types []int `json:"types" validate:"required" example:"1,2"`
|
||||
}
|
16
validate/user_validate.go
Normal file
16
validate/user_validate.go
Normal file
@ -0,0 +1,16 @@
|
||||
package validate
|
||||
|
||||
type LoginParam struct {
|
||||
Username string `json:"username" validate:"required,max=12" example:"wzjian"`
|
||||
Password string `json:"password" validate:"required,min=6" example:"123456"`
|
||||
CaptchaKey string `json:"captcha_key" validate:"required,len=8" example:"devonlyy"`
|
||||
CaptchaData string `json:"captcha_data" validate:"required,len=4" example:"DEV1"`
|
||||
}
|
||||
|
||||
type SignupParam struct {
|
||||
Username string `json:"username" validate:"required,max=12" example:"wzjian"`
|
||||
Password string `json:"password" validate:"required,min=6" example:"123456"`
|
||||
//Email string `json:"email" validate:"required,email" example:"wzjian@njtech.edu.cn"`
|
||||
CaptchaKey string `json:"captcha_key" validate:"required,len=8" example:"devonlyy"`
|
||||
CaptchaData string `json:"captcha_data" validate:"required,len=4" example:"DEV1"`
|
||||
}
|
52
validate/validate.go
Normal file
52
validate/validate.go
Normal file
@ -0,0 +1,52 @@
|
||||
package validate
|
||||
|
||||
import (
|
||||
"yitao/ecode"
|
||||
|
||||
"github.com/go-playground/locales/zh"
|
||||
ut "github.com/go-playground/universal-translator"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/kataras/iris/v12"
|
||||
)
|
||||
|
||||
var validate *validator.Validate
|
||||
var trans ut.Translator
|
||||
|
||||
func InitValidator() {
|
||||
validate = validator.New()
|
||||
eng := zh.New()
|
||||
uni := ut.New(eng, eng)
|
||||
trans, _ = uni.GetTranslator("zh")
|
||||
|
||||
addTranslation("min", "{0} 长度必须至少为 {1} 个字符")
|
||||
addTranslation("max", "{0} 长度不能超过 {1} 个字符")
|
||||
addTranslation("len", "{0} 长度应该为 {1} 个字符")
|
||||
}
|
||||
|
||||
func addTranslation(tag string, tr string) {
|
||||
validate.RegisterTranslation(tag, trans, func(ut ut.Translator) error {
|
||||
return ut.Add(tag, tr, true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T(tag, fe.Field(), fe.Param())
|
||||
return t
|
||||
})
|
||||
}
|
||||
|
||||
func GetValidator() *validator.Validate {
|
||||
return validate
|
||||
}
|
||||
|
||||
func ReadJSON(ctx iris.Context, result interface{}) *ecode.Ecode {
|
||||
ctx.ReadBody(result)
|
||||
err := GetValidator().Struct(result)
|
||||
if err != nil {
|
||||
validateErr := err.(validator.ValidationErrors)
|
||||
firstValue := ""
|
||||
for _, v := range validateErr.Translate(trans) {
|
||||
firstValue = v
|
||||
break // 退出循环,确保只获取第一个元素
|
||||
}
|
||||
return ecode.Set(255, firstValue)
|
||||
}
|
||||
return ecode.OK()
|
||||
}
|
Loading…
Reference in New Issue
Block a user