default
This commit is contained in:
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
1
.idea/.name
generated
Normal file
1
.idea/.name
generated
Normal file
@@ -0,0 +1 @@
|
||||
main.go
|
12
.idea/dataSources.xml
generated
Normal file
12
.idea/dataSources.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="snctf.db" uuid="1f1ae82f-c0e0-4fcb-a9c5-2156507a34a7">
|
||||
<driver-ref>sqlite.xerial</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/snctf.db</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/snctf.iml" filepath="$PROJECT_DIR$/.idea/snctf.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
9
.idea/snctf.iml
generated
Normal file
9
.idea/snctf.iml
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="Go" enabled="true" />
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
38
api/admin/auth.go
Normal file
38
api/admin/auth.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
."main.go/type"
|
||||
"main.go/api"
|
||||
|
||||
)
|
||||
|
||||
|
||||
|
||||
// AuthRequired 用于管理员权限控制的中间件
|
||||
func AuthRequired()gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
session, err := api.Store.Get(c.Request, "SNCTFSESSID")
|
||||
if err != nil {
|
||||
c.JSON(200, gin.H{"code": 400, "msg": "Get SNCTFSESSID error"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
user, ok := session.Values["user"].(User)
|
||||
if !ok {
|
||||
c.JSON(200, gin.H{"code": 400, "msg": "No session"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if user.Role != 1 {
|
||||
c.JSON(200, gin.H{"code": 400, "msg": "Unauthorized access!"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
|
||||
}
|
79
api/admin/challenge.go
Normal file
79
api/admin/challenge.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"main.go/api"
|
||||
db "main.go/database"
|
||||
. "main.go/type"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CrDb 专门用作给数据替换。
|
||||
type CrDb struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Score int `json:"score" binding:"required"`
|
||||
Flag string `json:"flag"` // 暂时一个题只能一个flag
|
||||
Description string `json:"description"`
|
||||
Attachment string `json:"attachment"`
|
||||
Category string `json:"category" binding:"required"`
|
||||
Tags string `json:"tags"`
|
||||
Hints string `json:"hints"`
|
||||
Visible int `json:"visible"`
|
||||
}
|
||||
|
||||
|
||||
//NewChallenge 新建一个题目
|
||||
func NewChallenge(c *gin.Context){
|
||||
var request ChallengeRequest
|
||||
if err := c.ShouldBindJSON(&request); err != nil {
|
||||
fmt.Println(err)
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Request format wrong!"})
|
||||
return
|
||||
}
|
||||
challenge := &Challenge{
|
||||
Name: request.Name,
|
||||
Score: request.Score,
|
||||
Flag: request.Flag,
|
||||
Description: request.Description,
|
||||
Attachment: request.Attachment,
|
||||
Category: request.Category,
|
||||
Tags: request.Tags,
|
||||
Hints: request.Hints,
|
||||
Visible: request.Visible,
|
||||
}
|
||||
if err := addChallenge(challenge); err != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Add challenge failure!"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{"code": 200, "msg": "Add challenge success!"})
|
||||
}
|
||||
|
||||
//addChallenge 添加题目内容
|
||||
func addChallenge(c *Challenge) error {
|
||||
api.Link()
|
||||
DB := db.DBsnctf
|
||||
// 使用逗号分隔字符串
|
||||
attachmentString := strings.Join(c.Attachment, ",")
|
||||
hintString := strings.Join(c.Hints, ",")
|
||||
crdb := &CrDb{
|
||||
Name: c.Name,
|
||||
Score: c.Score,
|
||||
Flag: c.Flag,
|
||||
Description: c.Description,
|
||||
Attachment: attachmentString,
|
||||
Category: c.Category,
|
||||
Tags: c.Tags,
|
||||
Hints: hintString,
|
||||
Visible: c.Visible,
|
||||
}
|
||||
//插入数据
|
||||
err := DB.Table("challenge").Create(crdb).Error
|
||||
//command := "INSERT INTO challenge (name,score,flag,description,attachment,category,tags,hints,visible) VALUES (?,?,?,?,?,?,?,?,?);"
|
||||
//err := DB.Debug().Raw(command,c.Name, c.Score, c.Flag, c.Description, attachmentString, c.Category, c.Tags, hintString, c.Visible).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
58
api/category.go
Normal file
58
api/category.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
db "main.go/database"
|
||||
|
||||
)
|
||||
|
||||
|
||||
// GetCategories 获取所有题目分类。
|
||||
func GetCategories(c *gin.Context) {
|
||||
var categories []string
|
||||
|
||||
if err := getAllCategories(&categories); err != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Get categories failure!"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{"code": 200, "data": categories})
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
//getAllCategories 操作数据库所有题目分类
|
||||
func getAllCategories(categories *[]string) error {
|
||||
Link()
|
||||
DB := db.DBsnctf
|
||||
rows,err:= DB.Raw("select category from category").Rows()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var category string
|
||||
if err := rows.Scan(&category); err != nil {
|
||||
return err
|
||||
}
|
||||
*categories = append(*categories, category)
|
||||
}
|
||||
return rows.Err()
|
||||
|
||||
}
|
||||
|
||||
// CheckCategory 检查类别是否正确
|
||||
func CheckCategory(c string) bool {
|
||||
var categories []string
|
||||
if err := getAllCategories(&categories); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, category := range categories {
|
||||
if category == c {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
84
api/challenge.go
Normal file
84
api/challenge.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
db "main.go/database"
|
||||
. "main.go/type"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//GetAllChallenges 获取全部题目
|
||||
|
||||
func GetAllChallenges(c * gin.Context){
|
||||
var challenges []ChallengeResponse
|
||||
|
||||
if err := getAllChallenges(c, &challenges); err != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Get all challenges failure!"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{"code": 200, "data": challenges})
|
||||
|
||||
}
|
||||
|
||||
//GetChallengesByCategory 获取某个分类下的题目
|
||||
|
||||
func GetChallengesByCategory(c *gin.Context){
|
||||
category := c.Param("category")
|
||||
if matched := CheckCategory(category); !matched {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Wrong category!"})
|
||||
return
|
||||
}
|
||||
var challenges []ChallengeResponse
|
||||
if err := getAllChallenges(c, &challenges); err != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Get all challenges failure!"})
|
||||
return
|
||||
}
|
||||
c.JSON(200, gin.H{"code": 200, "data": challenges})
|
||||
|
||||
}
|
||||
|
||||
|
||||
// getAllChallenges 操作数据库获取所有题目。
|
||||
func getAllChallenges(c *gin.Context, challenges *[]ChallengeResponse) error {
|
||||
var attachmentString, hints string
|
||||
Link()
|
||||
DB := db.DBsnctf
|
||||
rows,err := DB.Raw("SELECT id, name, score, description, attachment, category, tags, hints FROM challenge WHERE visible=1;").Rows()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next(){
|
||||
var challenge ChallengeResponse
|
||||
err = rows.Scan(&challenge.ID, &challenge.Name, &challenge.Score, &challenge.Description, &attachmentString, &challenge.Category, &challenge.Tags, &hints)
|
||||
fmt.Println(err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 解析为切片
|
||||
challenge.Attachment = strings.Split(attachmentString, ",")
|
||||
challenge.Hints = strings.Split(hints, ",")
|
||||
|
||||
solverCount, err := getSolverCount(challenge.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
challenge.SolverCount = solverCount
|
||||
session, err := Store.Get(c.Request, "SNCTFSESSID")
|
||||
if err != nil {
|
||||
c.JSON(200, gin.H{"code": 400, "msg": "Get SNCTFSESSID error"})
|
||||
return err
|
||||
}
|
||||
user, ok := session.Values["user"].(User)
|
||||
if !ok {
|
||||
c.JSON(200, gin.H{"code": 400, "msg": "No session"})
|
||||
return errors.New("no session")
|
||||
}
|
||||
challenge.IsSolved = hasAlreadySolved(user.ID, challenge.ID)
|
||||
*challenges = append(*challenges, challenge)
|
||||
}
|
||||
return rows.Err()
|
||||
}
|
39
api/notice.go
Normal file
39
api/notice.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
. "main.go/type"
|
||||
db "main.go/database"
|
||||
)
|
||||
|
||||
// GetAllNotices 获取所有的公告
|
||||
func GetAllNotices(c *gin.Context) {
|
||||
var notices []Notice
|
||||
if err := getAllNotices(¬ices); err != nil {
|
||||
c.JSON(200, gin.H{"code": 400, "msg": "Get all notices failure!"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{"code": 200, "data": notices})
|
||||
}
|
||||
|
||||
|
||||
func getAllNotices(notices *[]Notice) error {
|
||||
Link()
|
||||
DB := db.DBsnctf
|
||||
command := "SELECT id, title, content, created_at FROM notice ORDER BY created_at ASC;"
|
||||
rows, err := DB.Raw(command).Rows()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var b Notice
|
||||
err = rows.Scan(&b.ID, &b.Title, &b.Content, &b.CreatedAt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*notices = append(*notices, b)
|
||||
}
|
||||
return rows.Err()
|
||||
}
|
78
api/score.go
Normal file
78
api/score.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
db "main.go/database"
|
||||
"main.go/tools"
|
||||
. "main.go/type"
|
||||
)
|
||||
|
||||
//GetAllScore 获取所有的积分,按照积分降序排列
|
||||
func GetAllScore(c *gin.Context){
|
||||
var s ScoreResponse
|
||||
var scores []ScoreResponse
|
||||
Link()
|
||||
DB :=db.DBsnctf
|
||||
//rows,err := DB.Raw("SELECT s.id, s.username, s.score FROM score AS s, user AS u WHERE u.hidden = 0 AND s.username = u.username ORDER BY s.score DESC;").Scan(&s).Rows()
|
||||
rows,err := DB.Debug().Raw("SELECT s.id, s.username, s.score FROM score AS s, user AS u WHERE u.hidden = 0 AND s.username = u.username ORDER BY s.score DESC;").Rows()
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Get all score error!"})
|
||||
return
|
||||
}
|
||||
// 循环读取数据
|
||||
for rows.Next() {
|
||||
rows.Scan(&s.ID, &s.Username, &s.Score)
|
||||
scores = append(scores, s)
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{"code": 200, "data": scores})
|
||||
}
|
||||
|
||||
//GetScoreByUserId 获取用户分数
|
||||
func GetScoreByUserId(c *gin.Context) {
|
||||
var score int
|
||||
Link()
|
||||
DB := db.DBsnctf
|
||||
//获取用户id
|
||||
id := c.Params.ByName("id")
|
||||
if id == "" {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Need id!"})
|
||||
return
|
||||
}
|
||||
//检查id是否合法
|
||||
if !tools.CheckID(id) {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "ID format error!"})
|
||||
return
|
||||
}
|
||||
//查询用户信息
|
||||
err := DB.Raw("SELECT s.score FROM score AS s, user AS u WHERE u.id = ? AND u.hidden = 0 AND u.username = s.username LIMIT 1;", id).Scan(&score).Error
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Get info error!"})
|
||||
return
|
||||
}
|
||||
c.JSON(200, gin.H{"code": 200, "data": score})
|
||||
}
|
||||
|
||||
|
||||
//GetSelfScoreAndRank 获取当前登录用户的分数和排名
|
||||
func GetSelfScoreAndRank(c *gin.Context){
|
||||
var scoreAndRank ScoreRankResponse
|
||||
DB := db.DBsnctf
|
||||
session, err := Store.Get(c.Request, "SNCTFSESSID")
|
||||
if err != nil {
|
||||
c.JSON(200, gin.H{"code": 400, "msg": "Get SNCTFSESSID error"})
|
||||
return
|
||||
}
|
||||
user, ok := session.Values["user"].(User)
|
||||
if !ok {
|
||||
c.JSON(200, gin.H{"code": 400, "msg": "No session"})
|
||||
return
|
||||
}
|
||||
err = DB.Raw("SELECT score, (SELECT count(DISTINCT score) FROM score WHERE score>=s.score) AS rank FROM score AS s,user AS u WHERE u.id = ? AND u.username = s.username ORDER BY score DESC LIMIT 1;",user.ID).Scan(&scoreAndRank).Error
|
||||
if err != nil {
|
||||
c.JSON(200, gin.H{"code": 400, "msg": "Get info error!"})
|
||||
return
|
||||
}
|
||||
c.JSON(200, gin.H{"code": 200, "data": scoreAndRank})
|
||||
|
||||
}
|
27
api/session.go
Normal file
27
api/session.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"github.com/gorilla/securecookie"
|
||||
"github.com/gorilla/sessions"
|
||||
. "main.go/type"
|
||||
)
|
||||
|
||||
|
||||
// sessions 存储于文件系统
|
||||
var Store *sessions.FilesystemStore
|
||||
|
||||
func init() {
|
||||
Store = sessions.NewFilesystemStore("./sessions", securecookie.GenerateRandomKey(32))
|
||||
|
||||
Store.Options = &sessions.Options{
|
||||
Domain: "",
|
||||
Path: "/",
|
||||
MaxAge: 24 * 60 * 60, // 1 day
|
||||
// SameSite: http.SameSiteNoneMode,
|
||||
Secure: false,
|
||||
HttpOnly: false,
|
||||
}
|
||||
|
||||
gob.Register(User{})
|
||||
}
|
153
api/solve.go
Normal file
153
api/solve.go
Normal file
@@ -0,0 +1,153 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
db "main.go/database"
|
||||
."main.go/type"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
//GetAllSolves 获取所有解题记录
|
||||
func GetAllSolves(c *gin.Context){
|
||||
var solves []SolveResponse
|
||||
|
||||
if err := getAllSolves(&solves); err != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Get all solves failure!"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{"code": 200, "data": solves})
|
||||
}
|
||||
//GetSolvesByCid 获取某个用户的所有解题记录
|
||||
func GetSolvesByCid(c *gin.Context){
|
||||
var solves []SolveResponse
|
||||
|
||||
cid, err := strconv.ParseInt(c.Param("cid"), 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Get solves failure!"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := getSolvesByCid(&solves, int(cid)); err != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Get solves failure!"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{"code": 200, "data": solves})
|
||||
}
|
||||
|
||||
// GetSolvesByUid 根据用户id获取正确的flag提交记录。
|
||||
func GetSolvesByUid(c *gin.Context) {
|
||||
uid, err := strconv.ParseInt(c.Param("uid"), 10, 64)
|
||||
if err != nil {
|
||||
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Wrong uid!"})
|
||||
return
|
||||
}
|
||||
|
||||
if uid == 1 {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Not allowed!"})
|
||||
return
|
||||
}
|
||||
|
||||
var solves []SolveResponse
|
||||
if err := getSolvesByUid(&solves, int(uid)); err != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Get specified solves failure!"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{"code": 200, "data": solves})
|
||||
}
|
||||
|
||||
// GetSelfSolves 获取当前用户的所有解题记录
|
||||
func GetSelfSolves(c *gin.Context){
|
||||
var solves []SolveResponse
|
||||
|
||||
session, err := Store.Get(c.Request, "SNCTFSESSID")
|
||||
if err != nil {
|
||||
c.JSON(200, gin.H{"code": 400, "msg": "Get SNCTFSESSID error"})
|
||||
return
|
||||
}
|
||||
user, ok := session.Values["user"].(User)
|
||||
if !ok {
|
||||
c.JSON(200, gin.H{"code": 400, "msg": "No session"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := getSolvesByUid(&solves, user.ID); err != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Get self solves failure!"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{"code": 200, "data": solves})
|
||||
}
|
||||
|
||||
// getSolverCount 操作数据库获取指定id题目的解出人数。
|
||||
func getSolverCount(id int) (count int, err error) {
|
||||
Link()
|
||||
DB := db.DBsnctf
|
||||
//SELECT COUNT(*) FROM solve WHERE cid = ?;
|
||||
err = DB.Table("solve").Select("COUNT(*)").Where("cid = ?", id).Scan(&count).Error
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
// getAllSolves 操作数据库获取所有正确的提交记录,按提交时间从早到晚排序。
|
||||
func getAllSolves(solves *[]SolveResponse) error {
|
||||
DB :=db.DBsnctf
|
||||
rows,err := DB.Raw("SELECT s.id, s.uid, s.cid, u.username, c.name, s.submitted_at, c.score FROM solve AS s, user AS u, challenge AS c WHERE u.hidden=0 AND s.uid=u.id AND s.cid=c.id ORDER BY s.submitted_at ASC;").Rows()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var s SolveResponse
|
||||
err = rows.Scan(&s.ID, &s.Uid, &s.Cid, &s.Username, &s.ChallengeName, &s.SubmittedAt, &s.Score)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*solves = append(*solves, s)
|
||||
}
|
||||
return rows.Err()
|
||||
}
|
||||
|
||||
// getSolvesByUid 操作数据库根据用户id获取正确的flag提交记录,按提交时间从早到晚排序。
|
||||
func getSolvesByUid(solves *[]SolveResponse, uid int) error {
|
||||
DB :=db.DBsnctf
|
||||
rows,err := DB.Debug().Raw("SELECT s.id, s.uid, s.cid, u.username, c.name, s.submitted_at, c.score FROM solve AS s, user AS u, challenge AS c WHERE u.hidden=0 AND s.uid=? AND u.id=s.uid AND c.id=s.cid ORDER BY s.submitted_at ASC;",uid).Rows()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var s SolveResponse
|
||||
err = rows.Scan(&s.ID, &s.Uid, &s.Cid, &s.Username, &s.ChallengeName, &s.SubmittedAt, &s.Score)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*solves = append(*solves, s)
|
||||
}
|
||||
return rows.Err()
|
||||
}
|
||||
// getSolvesByCid 操作数据库根据题目id获取正确的提交记录,按提交时间从早到晚排序。
|
||||
func getSolvesByCid(solves *[]SolveResponse, cid int) error {
|
||||
DB :=db.DBsnctf
|
||||
rows,err := DB.Raw("SELECT s.id, s.uid, s.cid, u.username, c.name, s.submitted_at, c.score FROM solve AS s, user AS u, challenge AS c WHERE u.hidden=0 AND s.cid=? AND u.id=s.uid AND c.id=s.cid ORDER BY s.submitted_at ASC;",cid).Rows()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var s SolveResponse
|
||||
err = rows.Scan(&s.ID, &s.Uid, &s.Cid, &s.Username, &s.ChallengeName, &s.SubmittedAt, &s.Score)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*solves = append(*solves, s)
|
||||
}
|
||||
return rows.Err()
|
||||
}
|
211
api/submissions.go
Normal file
211
api/submissions.go
Normal file
@@ -0,0 +1,211 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
db "main.go/database"
|
||||
"main.go/tools"
|
||||
. "main.go/type"
|
||||
"math"
|
||||
)
|
||||
|
||||
// hasAlreadySolved 检查某道题是否已经被某用户解出。
|
||||
func hasAlreadySolved(uid int, cid int) (exists bool) {
|
||||
Link()
|
||||
DB := db.DBsnctf
|
||||
err := DB.Raw("SELECT EXISTS(SELECT 1 FROM solve WHERE uid=? AND cid=?)",uid,cid).Scan(&exists).Error
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return exists
|
||||
}
|
||||
// isChallengeExisted 检查数据库中是否存在某个题目。
|
||||
func isChallengeExisted(id int) (exists bool) {
|
||||
Link()
|
||||
DB := db.DBsnctf
|
||||
command := "SELECT EXISTS(SELECT 1 FROM challenge WHERE id = ?);"
|
||||
err := DB.Raw(command,id).Scan(&exists).Error
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return exists
|
||||
}
|
||||
|
||||
//addSubmission 添加一个提交记录。
|
||||
func addSubmission(s *Submission) error {
|
||||
Link()
|
||||
DB := db.DBsnctf
|
||||
err := DB.Table("submission").Create(&s).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// getFlag 根据题目id获取该题的flag
|
||||
func getFlag(id int) (flag string, err error) {
|
||||
Link()
|
||||
DB := db.DBsnctf
|
||||
err = DB.Table("challenge").Select("flag").Where("id = ?",id).Find(&flag).Error
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return flag, nil
|
||||
}
|
||||
|
||||
// addSolve 操作数据库加入一条正确的flag提交记录。
|
||||
func addSolve(s *Solve) error {
|
||||
DB := db.DBsnctf
|
||||
err := DB.Table("solve").Create(&s).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// addUserScore 操作数据库为指定用户增加某题的分数。
|
||||
func addUserScore(username string, cid int) error {
|
||||
var newScore int
|
||||
DB := db.DBsnctf
|
||||
err := DB.Table("challenge").Select("score").Where("id = ?", cid).Find(&newScore).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
command := "UPDATE score SET score=score+? WHERE username=?"
|
||||
err = DB.Exec(command, newScore, username).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// updateUserScores 操作数据库更新解出用户的分数。
|
||||
func updateUserScores(reducedScore, cid int) error {
|
||||
|
||||
DB:=db.DBsnctf
|
||||
command := "UPDATE score SET score=score-? WHERE EXISTS(SELECT 1 FROM user,solve WHERE user.id=solve.uid AND score.username=user.username AND solve.cid=?);"
|
||||
err := DB.Exec(command, reducedScore, cid).Error
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// editChallengeScore 操作数据库修改指定题目增的动态分数。
|
||||
func editChallengeScore(cid int) (reducedScore int, err error) {
|
||||
DB := db.DBsnctf
|
||||
var currentScore int
|
||||
err = DB.Table("challenge").Select("score").Where("id = ?", cid).Find(¤tScore).Error
|
||||
command := "SELECT score FROM challenge WHERE id=? LIMIT 1;"
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
solverCount, err := getSolverCount(cid)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// According to https://github.com/o-o-overflow/scoring-playground
|
||||
newScore := int(100 + (1000-100)/(1.0+float64(solverCount)*0.04*math.Log(float64(solverCount))))
|
||||
reducedScore = currentScore - newScore
|
||||
|
||||
command = "UPDATE challenge SET score=? WHERE id=?;"
|
||||
err = DB.Exec(command, newScore, cid).Error
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return reducedScore, nil
|
||||
}
|
||||
|
||||
// SubmitFlag 提交flag
|
||||
func SubmitFlag(c *gin.Context) {
|
||||
var request SubmissionRequest
|
||||
if err := c.ShouldBindJSON(&request); err != nil {
|
||||
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Request format wrong!"})
|
||||
return
|
||||
}
|
||||
|
||||
session,err := Store.Get(c.Request, "SNCTFSESSID")
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "GET SNCTFSESSID error!"})
|
||||
return
|
||||
}
|
||||
user,ok := session.Values["user"].(User)
|
||||
if !ok {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "GET user error!"})
|
||||
return
|
||||
}
|
||||
// 检查题目是否存在
|
||||
if !isChallengeExisted(request.Cid) {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Challenge not existed!"})
|
||||
return
|
||||
}
|
||||
// Submission记录
|
||||
solvedTime := tools.Timestamp()
|
||||
submission := &Submission{
|
||||
UserID: user.ID,
|
||||
ChallengeID: request.Cid,
|
||||
Flag: request.Flag,
|
||||
IP: c.ClientIP(),
|
||||
Time: solvedTime,
|
||||
}
|
||||
err = addSubmission(submission)
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Record submission failure!"})
|
||||
return
|
||||
}
|
||||
// 是否已经解出该题
|
||||
if hasAlreadySolved(user.ID, request.Cid) {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Already solved!"})
|
||||
return
|
||||
}
|
||||
// 获取flag
|
||||
flag, err := getFlag(request.Cid)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Get flag failure!"})
|
||||
return
|
||||
}
|
||||
// 检查flag是否正确
|
||||
if flag != request.Flag {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Wrong flag!"})
|
||||
return
|
||||
}else {
|
||||
// Solve记录
|
||||
solve := &Solve{
|
||||
UserID: user.ID,
|
||||
ChallengeID: request.Cid,
|
||||
Time: solvedTime,
|
||||
}
|
||||
err = addSolve(solve)
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Record solve failure!"})
|
||||
return
|
||||
}
|
||||
//加分
|
||||
err = addUserScore(user.Username, request.Cid)
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Add score failure!"})
|
||||
return
|
||||
}
|
||||
// 题目动态分数
|
||||
|
||||
reducedScore, err := editChallengeScore(request.Cid)
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Edit challenge score failure!"})
|
||||
return
|
||||
}
|
||||
//更新所有用户分数
|
||||
err = updateUserScores(reducedScore, request.Cid)
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Update user scores failure!"})
|
||||
return
|
||||
}
|
||||
c.JSON(200, gin.H{"code": 200, "msg": "Correct flag!"})
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
}
|
296
api/user.go
Normal file
296
api/user.go
Normal file
@@ -0,0 +1,296 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
_ "gorm.io/driver/sqlite"
|
||||
db "main.go/database"
|
||||
"main.go/tools"
|
||||
. "main.go/type"
|
||||
"regexp"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
//连接数据库
|
||||
func Link() {
|
||||
err := db.Inimysql()
|
||||
if err != nil{
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
func Login(c * gin.Context) {
|
||||
var request LoginRequest
|
||||
var user User
|
||||
|
||||
Link()
|
||||
DB := db.DBsnctf
|
||||
DB.AutoMigrate(&User{})
|
||||
//用ShouldBindJSON解析绑定传入的Json数据。
|
||||
if err := c.ShouldBindJSON(&request); err != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Bind json error!"})
|
||||
return
|
||||
}
|
||||
//code 0用户名不存在
|
||||
//code 1用户名或密码错误
|
||||
|
||||
err := DB.Take(&user,"username = ?",request.Username).Error
|
||||
if err != nil {
|
||||
c.JSON(200,gin.H{
|
||||
"Error":true,
|
||||
"Msg":"登陆失败",
|
||||
"Code": 0,
|
||||
})
|
||||
return
|
||||
}
|
||||
//判断md5值与数据库内容是否相同
|
||||
if user.Password != tools.MD5(request.Password){
|
||||
c.JSON(200,gin.H{
|
||||
"Error":true,
|
||||
"Msg":"用户名或密码错误",
|
||||
"Code": 1,
|
||||
})
|
||||
return
|
||||
}
|
||||
//身份认证结束
|
||||
//session
|
||||
// 设置session
|
||||
session, _ := Store.Get(c.Request, "SNCTFSESSID")
|
||||
user.Password = ""
|
||||
// 根据remember值设置session有效期
|
||||
if request.Remember {
|
||||
session.Options.MaxAge = 7 * 24 * 60 * 60 // 7 days
|
||||
} else {
|
||||
session.Options.MaxAge = 24 * 60 * 60 // 1 day
|
||||
}
|
||||
//保存session
|
||||
session.Values["user"] = user
|
||||
|
||||
err = session.Save(c.Request, c.Writer)
|
||||
fmt.Println(err)
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Save SNCTFSESSID error"})
|
||||
return
|
||||
}
|
||||
//登录成功
|
||||
c.JSON(200, gin.H{"code": 200, "username": user.Username, "role": user.Role, "msg": "Login success!"})
|
||||
|
||||
}
|
||||
|
||||
|
||||
func Register(c *gin.Context) {
|
||||
var request RegisterRequest
|
||||
var user User
|
||||
var score ScoreResponse
|
||||
|
||||
Link()
|
||||
DB := db.DBsnctf
|
||||
err := DB.AutoMigrate(&User{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//用ShouldBindJSON解析绑定传入的Json数据。
|
||||
if err := c.ShouldBindJSON(&request); err != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Bind json error!"})
|
||||
return
|
||||
}
|
||||
//限制传入用户名为中文、数字、大小写字母下划线和横杠,1到10位
|
||||
if !checkUsername(request.Username) {
|
||||
c.JSON(200, gin.H{
|
||||
"Error": true,
|
||||
"Msg": "用户名不符合规范",
|
||||
"Code": 2,
|
||||
})
|
||||
return
|
||||
}
|
||||
//限制密码长度6到20位
|
||||
if !checkPassword(request.Password) {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Password format error!"})
|
||||
return
|
||||
}
|
||||
//限制传入邮箱符合格式
|
||||
if !checkEmail(request.Email) {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Email format error!"})
|
||||
return
|
||||
}
|
||||
//判断用户名是否已被使用
|
||||
if isNameExisted(user, request.Username) {
|
||||
c.JSON(200, gin.H{"code": 1000, "msg": "Username has already been used!"})
|
||||
return
|
||||
}
|
||||
//判断邮箱是否已被使用
|
||||
if isEmailExisted(user, request.Email) {
|
||||
c.JSON(200, gin.H{"code": 1001, "msg": "Email has already been used!"})
|
||||
return
|
||||
}
|
||||
user.Token = tools.Token()
|
||||
user.Username = request.Username
|
||||
user.Password = tools.MD5(request.Password)
|
||||
user.Email = request.Email
|
||||
user.Created = tools.Timestamp()
|
||||
score.Score = 0
|
||||
score.Username =request.Username
|
||||
//创建数据
|
||||
err1 := DB.Table("user").Create(&user).Error
|
||||
err2 := DB.Table("score").Create(&score).Error
|
||||
if err1 != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Register error!"})
|
||||
return
|
||||
}
|
||||
if err2 != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Register error!"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"success": "注册成功",
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Logout 实现用户注销登陆
|
||||
func Logout(c *gin.Context) {
|
||||
var user User
|
||||
|
||||
session, err := Store.Get(c.Request, "SNCTFSESSID")
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Get CTFGOSESSID error"})
|
||||
return
|
||||
}
|
||||
user, ok := session.Values["user"].(User)
|
||||
if !ok {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "No session"})
|
||||
return
|
||||
}
|
||||
|
||||
session.Options.MaxAge = -1
|
||||
err = session.Save(c.Request, c.Writer)
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Save CTFGOSESSID error"})
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Sprintf("[%s] logout success!", user.Username)
|
||||
c.JSON(200, gin.H{"code": 200, "msg": "Logout success!"})
|
||||
}
|
||||
//GetInfoByUserId 获取用户信息
|
||||
func GetInfoByUserId(c *gin.Context) {
|
||||
var info PublicInfoResponse
|
||||
Link()
|
||||
DB := db.DBsnctf
|
||||
//获取用户id
|
||||
id := c.Params.ByName("id")
|
||||
if id == "" {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Need id!"})
|
||||
return
|
||||
}
|
||||
//检查id是否合法
|
||||
|
||||
|
||||
if !tools.CheckID(id) {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "ID format error!"})
|
||||
return
|
||||
}
|
||||
|
||||
err := DB.Debug().Raw("SELECT username,affiliation,country,team_id FROM user WHERE id = ? LIMIT 1", id).Scan(&info).Error
|
||||
//err := DB.Where("id = ?", id).First(user).Error
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Get info error!"})
|
||||
return
|
||||
}
|
||||
c.JSON(200, gin.H{"code": 200, "data": info})
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// checkUsername 验证用户名是否符合中文数字字母下划线横杠,长度1到10位,返回true或false
|
||||
func checkUsername(username string) bool {
|
||||
if !(utf8.RuneCountInString(username) > 0) || !(utf8.RuneCountInString(username) < 11) {
|
||||
return false
|
||||
}
|
||||
pattern := `^[-\w\p{Han}]+$`
|
||||
reg := regexp.MustCompile(pattern)
|
||||
return reg.MatchString(username)
|
||||
}
|
||||
|
||||
// checkEmail 验证是否符合邮箱格式,返回true或false
|
||||
func checkEmail(email string) bool {
|
||||
pattern := `^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$`
|
||||
reg := regexp.MustCompile(pattern)
|
||||
return reg.MatchString(email)
|
||||
}
|
||||
|
||||
|
||||
// checkPassword 验证密码是否符合长度6到20位,返回true或false
|
||||
func checkPassword(password string) bool {
|
||||
if !(utf8.RuneCountInString(password) > 5) || !(utf8.RuneCountInString(password) < 21) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
// isNameExisted 判断用户名是否已经被占用,被占用返回true,未被占用则返回false
|
||||
func isNameExisted(user User, username string) bool {
|
||||
Link()
|
||||
DB := db.DBsnctf
|
||||
err := DB.First(&user,"Username = ?",username).Error
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
||||
}
|
||||
// isNameExisted 判断邮箱是否已经被占用,被占用返回true,未被占用则返回false
|
||||
func isEmailExisted(user User, email string) bool {
|
||||
Link()
|
||||
DB := db.DBsnctf
|
||||
|
||||
err := DB.First(&user,"Email = ?",email).Error
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Session 获取当前用户session信息
|
||||
func Session(c *gin.Context) {
|
||||
var user User
|
||||
|
||||
// 默认在此之前已经通过了中间件的session权限验证
|
||||
session, _ := Store.Get(c.Request, "SNCTFSESSID")
|
||||
user = session.Values["user"].(User)
|
||||
|
||||
c.JSON(200, gin.H{"code": 200, "data": user})
|
||||
}
|
||||
|
||||
|
||||
|
||||
//下面是身份认证用 AUTH部分
|
||||
// AuthRequired 用于普通用户权限控制的中间件
|
||||
func AuthRequired()gin.HandlerFunc{
|
||||
return func(c *gin.Context) {
|
||||
session, err := Store.Get(c.Request, "SNCTFSESSID")
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Get SNCTFSESSID error"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
user, ok := session.Values["user"].(User)
|
||||
if !ok {
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "No session"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if user.Role != 0&&user.Role!=1{
|
||||
c.JSON(400, gin.H{"code": 400, "msg": "Permission denied"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
30
database/db.go
Normal file
30
database/db.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/schema"
|
||||
)
|
||||
|
||||
//定义全局数据库
|
||||
var (
|
||||
DBsnctf *gorm.DB
|
||||
)
|
||||
//定义全局数据库连接
|
||||
func Inimysql()(err error){
|
||||
DBsnctf, err = gorm.Open(sqlite.Open("snctf.db"), &gorm.Config{
|
||||
NamingStrategy: schema.NamingStrategy{
|
||||
SingularTable: true, // 使用单数表名
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
panic("failed to connect database")
|
||||
}
|
||||
return
|
||||
}
|
||||
func DBLink() {
|
||||
err := Inimysql()
|
||||
if err != nil{
|
||||
panic(err)
|
||||
}
|
||||
}
|
31
go.mod
Normal file
31
go.mod
Normal file
@@ -0,0 +1,31 @@
|
||||
module main.go
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/gin-gonic/gin v1.7.7
|
||||
github.com/gorilla/securecookie v1.1.1
|
||||
github.com/gorilla/sessions v1.2.1
|
||||
gorm.io/driver/sqlite v1.3.2
|
||||
gorm.io/gorm v1.23.5
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.13.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.17.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.4.1 // indirect
|
||||
github.com/golang/protobuf v1.3.3 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/json-iterator/go v1.1.9 // indirect
|
||||
github.com/leodido/go-urn v1.2.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.12 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect
|
||||
github.com/ugorji/go/codec v1.1.7 // indirect
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.8 // indirect
|
||||
)
|
73
go.sum
Normal file
73
go.sum
Normal file
@@ -0,0 +1,73 @@
|
||||
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/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
|
||||
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
|
||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
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.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0=
|
||||
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
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-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/sqlite v1.3.2 h1:nWTy4cE52K6nnMhv23wLmur9Y3qWbZvOBz+V4PrGAxg=
|
||||
gorm.io/driver/sqlite v1.3.2/go.mod h1:B+8GyC9K7VgzJAcrcXMRPdnMcck+8FgJynEehEPM16U=
|
||||
gorm.io/gorm v1.23.4/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
gorm.io/gorm v1.23.5 h1:TnlF26wScKSvknUC/Rn8t0NLLM22fypYBlvj1+aH6dM=
|
||||
gorm.io/gorm v1.23.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
0
identifier.sqlite
Normal file
0
identifier.sqlite
Normal file
7
main.go
Normal file
7
main.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package main
|
||||
|
||||
import "main.go/routers"
|
||||
|
||||
func main() {
|
||||
routers.Initrouter()
|
||||
}
|
72
routers/router.go
Normal file
72
routers/router.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package routers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"main.go/api"
|
||||
"main.go/api/admin"
|
||||
)
|
||||
|
||||
|
||||
func Initrouter(){
|
||||
router := gin.Default()
|
||||
api_v1 := router.Group("/api/v1")
|
||||
|
||||
//公共接口(无需登录)
|
||||
public := api_v1.Group("")
|
||||
{
|
||||
//用户登录
|
||||
public.POST("/login",api.Login)
|
||||
//用户注册
|
||||
public.POST("/register",api.Register)
|
||||
//用户登出
|
||||
public.GET("/logout",api.Logout)
|
||||
//获取指定id用户可公开信息
|
||||
public.GET("/user/:id",api.GetInfoByUserId)
|
||||
//获取指定id用户分数
|
||||
public.GET("/score/:id",api.GetScoreByUserId)
|
||||
//获取所有用户分数,降序排列
|
||||
public.GET("/score",api.GetAllScore)
|
||||
//获取全部公告
|
||||
public.GET("/notice",api.GetAllNotices)
|
||||
|
||||
}
|
||||
// 普通用户api,需要用户登陆且Role=0才能访问
|
||||
personal := api_v1.Group("/user")
|
||||
personal.Use(api.AuthRequired())
|
||||
{
|
||||
// 获取当前用户信息
|
||||
personal.GET("/session", api.Session)
|
||||
// 获取题目分类
|
||||
personal.GET("/category", api.GetCategories)
|
||||
// 获取所有题目信息
|
||||
personal.GET("/challenges/all", api.GetAllChallenges)
|
||||
// 获取指定分类题目信息
|
||||
personal.GET("/challenges/:category", api.GetChallengesByCategory)
|
||||
|
||||
// 提交flag
|
||||
personal.POST("/submitflag", api.SubmitFlag)
|
||||
// 获取所有正确的flag提交记录
|
||||
personal.GET("/solves/all", api.GetAllSolves)
|
||||
// 获取指定用户正确的flag提交记录
|
||||
personal.GET("/solves/uid/:uid", api.GetSolvesByUid)
|
||||
// 获取指定题目正确的flag提交记录
|
||||
personal.GET("/solves/cid/:cid", api.GetSolvesByCid)
|
||||
// 获取当前用户正确flag提交记录(即解题记录)按时间从早到晚排序
|
||||
personal.GET("/solves/self", api.GetSelfSolves)
|
||||
// 获取当前用户分数、排名
|
||||
personal.GET("/score/self", api.GetSelfScoreAndRank)
|
||||
// 校内排行api,暂时留空
|
||||
}
|
||||
// 管理员api,需要用户登陆且Role=1才能访问
|
||||
manager := api_v1.Group("/admin")
|
||||
manager.Use(admin.AuthRequired())
|
||||
{
|
||||
// 创建新题目
|
||||
manager.POST("/challenge", admin.NewChallenge)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
router.Run(":9000")
|
||||
}
|
@@ -0,0 +1 @@
|
||||
MTY1MTY1MDA1NHxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXyZp7R_7HdiX8UyxFTMz3PPRiS83yhpcaLmGMFD-L145A==
|
@@ -0,0 +1 @@
|
||||
MTY1MTU4Mjk5OHxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUVBU0JoTnpnM056VXhOek0yTnpVMk9XWTBabVV6WlRnd01tRXdOMkk0Tm1KaU13RURNVEl6QWdzeE1qTTBRSEZ4TG1OdmJRZjh4TjlxaEFBPXzX2Hfpvc6Jpgio2o1bCyD8p5MlWNkPlboWDL4O92SG8w==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY1MDA0OHxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXx0YsVP0Bsx_UC-SMnT2H5sXa3Rv-emyNOprM7KTZO22g==
|
@@ -0,0 +1 @@
|
||||
MTY1Mjk3NTUwM3xEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUXYtRVB3RUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdFQ0FBPT18R_nBzwfvM0VB9HgjG3j_tOYrJh9OwzMtHi7AxxPqDG4=
|
@@ -0,0 +1 @@
|
||||
MTY1MTY3NDQ4NnxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUXYtRVB3RUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdFQ0FBPT18aaWkxhTEgthoUKJ1GTlpyzmrevXI2QOM7XmE1f4Tws4=
|
@@ -0,0 +1 @@
|
||||
MTY1MTY3MzM5OXxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUXYtRVB3RUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdFQ0FBPT18g3L0zUnti_MnEg_YIhPvXDONG7L4o30Yq1IHE4hKPzI=
|
@@ -0,0 +1 @@
|
||||
MTY1MTY2NTYyNnxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXyEWhEqOaCFOXqnMIdP2SHN9WFhZxAjVudLfyUTS4zkfg==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY1MDgyMXxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXwtNDxPyD59Y9CBHfAh54_pGb0xED5BrnS12WfwMbrHxQ==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY2NTQ2M3xEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXyBdgjcvRGhK6JnTrLlQETdtrVHCfqXHN9UWI5yMVsMTg==
|
@@ -0,0 +1 @@
|
||||
MTY1MTU4MjEyNXxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUVBU0JoTnpnM056VXhOek0yTnpVMk9XWTBabVV6WlRnd01tRXdOMkk0Tm1KaU13RURNVEl6QWdzeE1qTTBRSEZ4TG1OdmJRZjh4TjlxaEFBPXz6W0gwBf2X0RyprFJTaSBL1JLYgGYkXgcIh29Db_gJpw==
|
@@ -0,0 +1 @@
|
||||
MTY1Mjk3Mzc2OHxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUXYtRVB3RUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdFQ0FBPT18ONHNYhjemKmcTQ56VURz0wNePYzECS31waCtlRhCu78=
|
@@ -0,0 +1 @@
|
||||
MTY1MTY1MDk3M3xEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXyYBQByVjLbJZ2x4QafNqgpp-n6kCesEB6BNfoXvq-1ig==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY1MDcxMHxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXzPyoA6p6szfVSUU-zkLkOCK4zfP6gZq5orisMvUYEDuA==
|
@@ -0,0 +1 @@
|
||||
MTY1MTU3NDg2NnxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUVBU0JoTnpnM056VXhOek0yTnpVMk9XWTBabVV6WlRnd01tRXdOMkk0Tm1KaU13RURNVEl6QWdzeE1qTTBRSEZ4TG1OdmJRZjh4TjlxaEFBPXy1hayYz7EXP5XMGKWMk_wssR8J1BlbjDrxaakLbTIh1Q==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY2NTYyMXxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXx7IIV8mOmTS0o0qjO2cO8ESW-joxUKV1YJS3-WHMUsFw==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY1MDYzOXxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXwcSl56VFVQxQEhpD-LhkTqwgcmHgnPsmyT5kMXen_-1A==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY3NDA3NnxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUXYtRVB3RUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdFQ0FBPT18EL_WCkeFjFbdJfyQGOSGVdIiX90CG6tvNZGdzl-mz2Y=
|
@@ -0,0 +1 @@
|
||||
MTY1MTU3NDk0NnxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUVBU0JoTnpnM056VXhOek0yTnpVMk9XWTBabVV6WlRnd01tRXdOMkk0Tm1KaU13RURNVEl6QWdzeE1qTTBRSEZ4TG1OdmJRZjh4TjlxaEFBPXyOhGtdUodGwIpp0X9-g40AHGjmu3wvk-AU00RFNBkUuA==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY3NDQ4OXxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUXYtRVB3RUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdFQ0FBPT18eF3jTQqiTGwMrs-jtADV7OUSEV_wRzjg4EPnsC3tODw=
|
@@ -0,0 +1 @@
|
||||
MTY1MTY1MTEwMXxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXz5N5DorLr_UFgR8-PWyhwNcuD4pxzodlbWGkz7ANTDeA==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY1MDc0NnxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXxXvX5_tBpv8gObNjrYH-KN7qp4tSGZ9725DdUo6qCe7g==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY1MTE0OHxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXwcVGMp_v68wUnLRiSrd_xE_xurHal0es8Gxhq2FwOz4A==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY3MzkwNHxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUXYtRVB3RUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdFQ0FBPT18rYkgKtXvBGRfxZ9HaRsSscZ2QAXjFu9Y9b1pwoeu-B0=
|
@@ -0,0 +1 @@
|
||||
MTY1MTY1MDE0NXxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXxt8__tXjZbcAc0ZhuT_1Ae991kPMs2j823Shb1IgUMhw==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY1MDc0N3xEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXzQxs1oG1_iJaION26ztr96l3eyW0Bm6hvMT6X9wucpzw==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY3MzE2M3xEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUXYtRVB3RUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdFQ0FBPT18XfrVpy5QOn89OAXXX_p5Om024hdVvn9DzpOVBUOKfTc=
|
@@ -0,0 +1 @@
|
||||
MTY1MTY1MDYzOXxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXwcSl56VFVQxQEhpD-LhkTqwgcmHgnPsmyT5kMXen_-1A==
|
@@ -0,0 +1 @@
|
||||
MTY1MTU4MjE3OHxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUVBU0JoTnpnM056VXhOek0yTnpVMk9XWTBabVV6WlRnd01tRXdOMkk0Tm1KaU13RURNVEl6QWdzeE1qTTBRSEZ4TG1OdmJRZjh4TjlxaEFBPXyzrYkoEMyAeckMlz8wT3gRi8kpbicHZdXjmaYCSYEbTQ==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY3NDA3N3xEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUXYtRVB3RUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdFQ0FBPT188eytn4LgdEMhXv4tFo0JNWC070N3GUuIsRNEYTyvx5c=
|
@@ -0,0 +1 @@
|
||||
MTY1MTY1MDcxMHxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXzPyoA6p6szfVSUU-zkLkOCK4zfP6gZq5orisMvUYEDuA==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY1MDE1MnxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXzM8T4GoEMSqIe8zK2A6wn67v1nguMDHjUUo-yjKjNfcQ==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY1MDM2MXxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXx7XIYKo0HoZNY_DZVwi9ellyBp7o7V2gvM9Vb0pUli3Q==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY1MDk3MnxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXwR9evf4tXU9hUpH4hw_D6l7w4n0j-1Vf16locrZWzLFQ==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY3MzE2MnxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUXYtRVB3RUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdFQ0FBPT18br434P_axalM2X4F6b6cEEkrCpk8OnhOsjp85410v0c=
|
@@ -0,0 +1 @@
|
||||
MTY1MTY2NTQ2MnxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXyfWEkkjK48q8lwLJEJSc7cQXbRGdDabcUHO9FVZID_5w==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY0OTk3OHxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXz0n4VX35X4e92mOLcdNVwHgoxgzHHGs7fSQ33yrgyMbA==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY1MDM1M3xEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXzkpY_PPKBue5Qf7ZOP3tDQATS2rujJjRsNsVHTEL94lQ==
|
@@ -0,0 +1 @@
|
||||
MTY1Mjk3MTY3NHxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUXYtRVB3RUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdFQ0FBPT1808avPSF-CPY2M9KO1Gx-opThsHsrJxKGaUPR7BZp9WQ=
|
@@ -0,0 +1 @@
|
||||
MTY1MTY1MDcxMHxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXzPyoA6p6szfVSUU-zkLkOCK4zfP6gZq5orisMvUYEDuA==
|
@@ -0,0 +1 @@
|
||||
MTY1MTU3NzQwOXxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUVBU0JoTnpnM056VXhOek0yTnpVMk9XWTBabVV6WlRnd01tRXdOMkk0Tm1KaU13RURNVEl6QWdzeE1qTTBRSEZ4TG1OdmJRZjh4TjlxaEFBPXwP-FCJdqMaFJIxqG_AYodOb2iE8gguAw2E3W5WVu4v8g==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY3NDE5MnxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUXYtRVB3RUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdFQ0FBPT18gUD_A631PFOBncH-QOxKbkmt0xtW6Fgug3qcEUfijcA=
|
@@ -0,0 +1 @@
|
||||
MTY1MTY1MTEwMnxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXxgHRdZoPZBE0kDMmtyHrzAaMfiKhlGdCF47u58n45cSw==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY3NDE5M3xEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUXYtRVB3RUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdFQ0FBPT18Cyx59YJB_tTb2EBULWXlfVECSpk1iqkG-odREjbKGsc=
|
@@ -0,0 +1 @@
|
||||
MTY1MTY1MDcxMHxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXzPyoA6p6szfVSUU-zkLkOCK4zfP6gZq5orisMvUYEDuA==
|
@@ -0,0 +1 @@
|
||||
MTY1MTU4MjQzNXxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUVBU0JoTnpnM056VXhOek0yTnpVMk9XWTBabVV6WlRnd01tRXdOMkk0Tm1KaU13RURNVEl6QWdzeE1qTTBRSEZ4TG1OdmJRZjh4TjlxaEFBPXw8cY50NHs9S6zCZtQkXsjSnr7Wv1O2LJ1X2cLAjybXIA==
|
@@ -0,0 +1 @@
|
||||
MTY1MTU4MTg5MHxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUVBU0JoTnpnM056VXhOek0yTnpVMk9XWTBabVV6WlRnd01tRXdOMkk0Tm1KaU13RURNVEl6QWdzeE1qTTBRSEZ4TG1OdmJRZjh4TjlxaEFBPXzKFszom5pRA96lq3Lb2fT6iDLQL1sguZ3xph75DDNpCQ==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY1MTE0OHxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXwcVGMp_v68wUnLRiSrd_xE_xurHal0es8Gxhq2FwOz4A==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY1MDgxOXxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXyD0OzJ2XvmQTfT3cln5USKo4wbM01lrwy_1tkmotJG-w==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY3NDE5M3xEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUXYtRVB3RUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdFQ0FBPT18Cyx59YJB_tTb2EBULWXlfVECSpk1iqkG-odREjbKGsc=
|
@@ -0,0 +1 @@
|
||||
MTY1MTY3MzQwNHxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUXYtRVB3RUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdFQ0FBPT18UB3CM0FYHNdONL4rdGwb5t1aFPoJvD0sQsEEM4YJxHo=
|
@@ -0,0 +1 @@
|
||||
MTY1MTY1MDcxMHxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUVAtRVBRRUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdBPXzPyoA6p6szfVSUU-zkLkOCK4zfP6gZq5orisMvUYEDuA==
|
@@ -0,0 +1 @@
|
||||
MTY1MTY3MzkwM3xEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUXYtRVB3RUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdFQ0FBPT184E0y_hLsOmZ_gsWkLUJ_O2Xo6AGbJfaUboiHww2JZoM=
|
@@ -0,0 +1 @@
|
||||
MTY1MTY3MzMwM3xEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUXYtRVB3RUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdFQ0FBPT18XAWDOQNDSejAMWM96plPufODrSs0R38IJWaPlh7NpZc=
|
@@ -0,0 +1 @@
|
||||
MTY1Mjk3MjEwMnxEdi1CQkFFQ180SUFBUkFCRUFBQV84cl9nZ0FCQm5OMGNtbHVad3dHQUFSMWMyVnlFVzFoYVc0dVoyOHZkSGx3WlM1VmMyVnlfNE1EQVFFRVZYTmxjZ0hfaEFBQkRRRUNTVVFCQkFBQkJWUnZhMlZ1QVF3QUFRaFZjMlZ5Ym1GdFpRRU1BQUVJVUdGemMzZHZjbVFCREFBQkJVVnRZV2xzQVF3QUFRdEJabVpwYkdsaGRHbHZiZ0VNQUFFSFEyOTFiblJ5ZVFFTUFBRUhWMlZpYzJsMFpRRU1BQUVHU0dsa1pHVnVBUVFBQVFaQ1lXNXVaV1FCQkFBQkJsUmxZVzFKUkFFRUFBRUhRM0psWVhSbFpBRUVBQUVFVW05c1pRRUVBQUFBUXYtRVB3RUNBU0JqTjJVeU1EazBaRGxtTkRVd1ltVmhNak5tTURWa01HTmpZemszWldWaVpRRUVNVEl6TkFJS01USXpRSEZ4TG1OdmJRZjh4TjlxUmdFQ0FBPT18EA6m0QoNTA0TT7IkJ-AfPqtOHKKazFKMpf5FQI2Xr6A=
|
55
tools/aes.go
Normal file
55
tools/aes.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
)
|
||||
|
||||
var Aeskey = []byte("snowywar12345678") //aeskey
|
||||
|
||||
//@brief:填充明文
|
||||
func PKCS5Padding(plaintext []byte, blockSize int) []byte{
|
||||
padding := blockSize-len(plaintext)%blockSize
|
||||
padtext := bytes.Repeat([]byte{byte(padding)},padding)
|
||||
return append(plaintext,padtext...)
|
||||
}
|
||||
|
||||
//@brief:去除填充数据
|
||||
func PKCS5UnPadding(origData []byte) []byte{
|
||||
length := len(origData)
|
||||
unpadding := int(origData[length-1])
|
||||
return origData[:(length - unpadding)]
|
||||
}
|
||||
|
||||
//@brief:AES加密
|
||||
func AesEncrypt(origData, key []byte) ([]byte, error){
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//AES分组长度为128位,所以blockSize=16,单位字节
|
||||
blockSize := block.BlockSize()
|
||||
origData = PKCS5Padding(origData,blockSize)
|
||||
blockMode := cipher.NewCBCEncrypter(block,key[:blockSize]) //初始向量的长度必须等于块block的长度16字节
|
||||
crypted := make([]byte, len(origData))
|
||||
blockMode.CryptBlocks(crypted,origData)
|
||||
return crypted, nil
|
||||
}
|
||||
|
||||
//@brief:AES解密
|
||||
func AesDecrypt(crypted, key []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//AES分组长度为128位,所以blockSize=16,单位字节
|
||||
blockSize := block.BlockSize()
|
||||
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) //初始向量的长度必须等于块block的长度16字节
|
||||
origData := make([]byte, len(crypted))
|
||||
blockMode.CryptBlocks(origData, crypted)
|
||||
origData = PKCS5UnPadding(origData)
|
||||
return origData, nil
|
||||
}
|
14
tools/checkid.go
Normal file
14
tools/checkid.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package tools
|
||||
|
||||
import "regexp"
|
||||
|
||||
// CheckID 验证id是否为非负正整数。
|
||||
func CheckID(id string) bool {
|
||||
if id == "1" {
|
||||
return false
|
||||
}
|
||||
pattern := `^[1-9]\d*$`
|
||||
reg := regexp.MustCompile(pattern)
|
||||
return reg.MatchString(id)
|
||||
}
|
||||
|
13
tools/md5.go
Normal file
13
tools/md5.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
func MD5(v string)string{
|
||||
d := []byte(v)
|
||||
m := md5.New()
|
||||
m.Write(d)
|
||||
return hex.EncodeToString(m.Sum(nil))
|
||||
}
|
35
tools/token.go
Normal file
35
tools/token.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Random 生成随机数。
|
||||
func Random() []byte {
|
||||
b := make([]byte, 32)
|
||||
//ReadFull从rand.Reader精确地读取len(b)字节数据填充进b
|
||||
//rand.Reader是一个全局、共享的密码用强随机数生成器
|
||||
if _, err := io.ReadFull(rand.Reader, b); err != nil {
|
||||
fmt.Printf("random number generation error: %v", err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Token 生成随机token。
|
||||
func Token() string {
|
||||
b := Random()[:16]
|
||||
return fmt.Sprintf("%x", b)
|
||||
}
|
||||
|
||||
// Timestamp 用于获取当前10位数时间戳。
|
||||
func Timestamp() int {
|
||||
// time_zone := time.FixedZone("UTC", 0)
|
||||
// t := time.Now().In(time_zone).Unix()
|
||||
t := time.Now().Unix()
|
||||
return int(t)
|
||||
}
|
||||
|
||||
|
20
type/adminTypes.go
Normal file
20
type/adminTypes.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package types
|
||||
|
||||
// ChallengeRequest 定义新增/修改题目的一个请求
|
||||
type ChallengeRequest struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Score int `json:"score" binding:"required"`
|
||||
Flag string `json:"flag"` // 暂时一个题只能一个flag
|
||||
Description string `json:"description"`
|
||||
Attachment []string `json:"attachment"`
|
||||
Category string `json:"category" binding:"required"`
|
||||
Tags string `json:"tags"`
|
||||
Hints []string `json:"hints"`
|
||||
Visible int `json:"visible"`
|
||||
}
|
||||
|
||||
// NoticeRequest 定义新增公告的一个请求
|
||||
type NoticeRequest struct {
|
||||
Title string `form:"title" json:"title" binding:"required"`
|
||||
Content string `form:"content" json:"content" binding:"required"`
|
||||
}
|
58
type/commentypes.go
Normal file
58
type/commentypes.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package types
|
||||
|
||||
//user定义用户结构体。
|
||||
type User struct {
|
||||
ID int `json:"id"` //用户id,唯一,自增
|
||||
Token string `json:"token"` //用户token,唯一
|
||||
Username string `json:"username"` //用户名,唯一
|
||||
Password string `json:"password"` //用户密码md5值,md5(原密码)
|
||||
Email string `json:"email"` //邮箱,唯一
|
||||
Affiliation string `json:"affiliation"` //组织、战队或机构等,非必需,默认为0
|
||||
Country string `json:"country"` //国家,非必需,默认为0
|
||||
Website string `json:"website"` //个人链接,默认为0
|
||||
Hidden int `json:"hidden"` //1:隐藏,0:显示,默认为0
|
||||
Banned int `json:"banned"` //1:禁止,0:正常,默认为1,邮箱激活后为0
|
||||
TeamID int `json:"team_id"` //队伍id,在团队模式下必须,个人模式非必需,默认为0
|
||||
Created int `json:"created"` //用户注册时间,10位数时间戳
|
||||
Role int `json:"role"` //0:普通用户,默认为0,1:普通管理员,2:所有者(最高权限)
|
||||
}
|
||||
|
||||
// Notice 定义一个公告
|
||||
type Notice struct {
|
||||
ID int `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
CreatedAt int `json:"created_at"`
|
||||
}
|
||||
|
||||
// Challenge 定义一个题目
|
||||
type Challenge struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Score int `json:"score"`
|
||||
Flag string `json:"flag"`
|
||||
Description string `json:"description"`
|
||||
Attachment []string `json:"attachment"`
|
||||
Category string `json:"category"`
|
||||
Tags string `json:"tags"`
|
||||
Hints []string `json:"hints"`
|
||||
Visible int `json:"visible"` // 0表示隐藏,1表示可见
|
||||
}
|
||||
|
||||
// Submission 表示一次flag提交记录
|
||||
type Submission struct {
|
||||
ID int `json:"id"`
|
||||
UserID int `json:"uid" gorm:"column:uid"`
|
||||
ChallengeID int `json:"cid" gorm:"column:cid"`
|
||||
Flag string `json:"flag"`
|
||||
IP string `json:"ip"`
|
||||
Time int `json:"submitted_at" gorm:"column:submitted_at"`
|
||||
}
|
||||
|
||||
// Solve 表示一次正确的flag提交记录
|
||||
type Solve struct {
|
||||
ID int `json:"id" `
|
||||
UserID int `json:"uid" gorm:"column:uid"`
|
||||
ChallengeID int `json:"cid" gorm:"column:cid"`
|
||||
Time int `json:"solved_at" gorm:"column:submitted_at"`
|
||||
}
|
124
type/userTypes.go
Normal file
124
type/userTypes.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package types
|
||||
|
||||
// LoginRequest 定义接收登录数据的结构体。
|
||||
type LoginRequest struct {
|
||||
// binding:"required"修饰的字段,若接收为空值,则报错,是必须字段
|
||||
Username string `json:"username" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
Remember bool `json:"remember"`
|
||||
}
|
||||
|
||||
// RegisterRequest 定义接收注册数据的结构体。
|
||||
type RegisterRequest struct {
|
||||
Username string `json:"username" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
Email string `json:"email" binding:"required"`
|
||||
CaptchaID string `json:"captchaid" binding:"required"`
|
||||
Solution string `json:"solution" binding:"required"`
|
||||
}
|
||||
|
||||
// InfoRequest 定义接收用户修改信息的结构体。
|
||||
type InfoRequest struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Email string `json:"email"`
|
||||
Affiliation string `json:"affiliation"`
|
||||
Country string `json:"country"`
|
||||
Website string `json:"website"`
|
||||
}
|
||||
|
||||
// InstallRequest 定义接收installRequest数据的结构体。
|
||||
type InstallRequest struct {
|
||||
Username string `json:"username" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
Email string `json:"email" binding:"required"`
|
||||
}
|
||||
|
||||
// SubmissionRequest 定义接收提交flag数据的结构体。
|
||||
type SubmissionRequest struct {
|
||||
Cid int `json:"cid" binding:"required"`
|
||||
Flag string `json:"flag" binding:"required"`
|
||||
}
|
||||
|
||||
// StudentInfo 定义校内学生信息结构体。
|
||||
type StudentInfo struct {
|
||||
Username string `json:"username"`
|
||||
StudentID string `json:"student_id"`
|
||||
QQ string `json:"qq"`
|
||||
}
|
||||
|
||||
// SubmitStudentInfoRequest 定义接收提交校内学生用户信息的结构体。
|
||||
type SubmitStudentInfoRequest struct {
|
||||
Student1 StudentInfo `json:"student1" binding:"required"`
|
||||
Student2 StudentInfo `json:"student2"`
|
||||
Student3 StudentInfo `json:"student3"`
|
||||
Student4 StudentInfo `json:"student4"`
|
||||
}
|
||||
|
||||
// OthersInfo 定义校外用户信息结构体。
|
||||
type OthersInfo struct {
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
QQ string `json:"qq"`
|
||||
}
|
||||
|
||||
// SubmitOthersInfoRequest 定义接收提交校外学生用户信息的结构体。
|
||||
type SubmitOthersInfoRequest struct {
|
||||
Others1 OthersInfo `json:"others1" binding:"required"`
|
||||
Others2 OthersInfo `json:"others2"`
|
||||
Others3 OthersInfo `json:"others3"`
|
||||
Others4 OthersInfo `json:"others4"`
|
||||
}
|
||||
|
||||
// ScoreResponse 定义返回得分情况结构体。
|
||||
type ScoreResponse struct {
|
||||
ID int `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
// SolveResponse 定义返回解题情况结构体。
|
||||
type SolveResponse struct {
|
||||
ID int `json:"id"`
|
||||
Uid int `json:"uid"`
|
||||
Cid int `json:"cid"`
|
||||
Username string `json:"username"`
|
||||
ChallengeName string `json:"challenge_name"`
|
||||
SubmittedAt int `json:"submitted_at"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
// PublicInfoResponse 定义返回用户公开信息结构体。
|
||||
type PublicInfoResponse struct {
|
||||
Username string `json:"username"`
|
||||
Affiliation string `json:"affiliation"`
|
||||
Country string `json:"country"`
|
||||
TeamID int `json:"team_id"`
|
||||
}
|
||||
|
||||
// ChallengeResponse 定义获取题目的一个响应。
|
||||
type ChallengeResponse struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Score int `json:"score"`
|
||||
Description string `json:"description"` //题目描述
|
||||
Attachment []string `json:"attachment"` //题目附件
|
||||
Category string `json:"category"` //分类
|
||||
Tags string `json:"tags"` //标签
|
||||
Hints []string `json:"hints"` //提示
|
||||
SolverCount int `json:"solver_count"` //解决人数
|
||||
IsSolved bool `json:"is_solved"` // true:已解决,false:未解决
|
||||
}
|
||||
|
||||
// ScoreRankResponse 定义获取当前用户分数和排名的一个响应。
|
||||
type ScoreRankResponse struct {
|
||||
Score int `json:"score"`
|
||||
Rank int `json:"rank"`
|
||||
}
|
||||
|
||||
// StudentsOrOthersInfoResponse 定义获取校内用户或校外用户信息的响应。
|
||||
type StudentsOrOthersInfoResponse struct {
|
||||
Username string `json:"username"`
|
||||
IDOrEmail string `json:"id_email"`
|
||||
QQ string `json:"qq"`
|
||||
}
|
Reference in New Issue
Block a user