Commit 3390cab7 authored by chenhan wang's avatar chenhan wang
Browse files

feat:docker

parents
# lot-backend
FROM golang:1.19.13-alpine3.18 AS go-builder
ENV \
GO111MODULE=on \
CGO_ENABLED=0
WORKDIR /home/pastebin
COPY ./ backend/
WORKDIR /home/pastebin/backend
# Build your Go code
RUN go build -o ./pastebin .
# Final image for running only the built executable
FROM alpine:latest
# Copy Go backend executable
COPY --from=go-builder /home/pastebin/backend/pastebin /home/pastebin/backend/
COPY --from=go-builder /home/pastebin/backend/config.yaml /home/pastebin/backend/
# 设置工作目录,这里需要根据实际情况修改
WORKDIR /home/pastebin/backend
# 你可以添加其他一些需要的命令或设置,根据实际情况进行调整
# 例如,你可能需要设置启动命令
CMD ["/home/pastebin/backend/pastebin"]
package controller
import (
"backend/model"
"bytes"
"encoding/json"
"io"
"math/rand"
"mime/multipart"
"net/http"
"strconv"
"time"
"github.com/labstack/echo/v4"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
)
// =======authenticate==========
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
/*
* generate new uuid
*/
func IdGen(n int) string {
b := make([]rune, n)
rand.Seed(time.Now().UnixNano())
//for i:=0;i<n;i++{
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}
/*
func IdGen(n int ) string{
id:=newId(n)
var check bool
// 数据库里查找有无sid与id相同
while (!check){
}
}
*/
/*
* 新建一个sid和url的关联,并返回一个bool表示成功与否
* 若无sessionId,生成一个八位sid并返回
*/
func newAuthenticate(sid string, url string, passwd string, time time.Time, isFirst bool) (rsid string, stat uint) {
if sid == "" {
rsid = IdGen(8)
} else {
rsid = sid
}
if isFirst {
model.CreatelinkFirstTime(rsid, url, time)
stat = 1
} else {
stat = model.Createlink(rsid, passwd, url, time)
}
return rsid, stat
}
/*
* 判断用户有无权限访问
* sid 用cookie 储存传输,如果新生成sid,则保存至cookie
*/
func Autheticate(cookie *http.Cookie, url string, passwd string, time time.Time) uint {
sid := cookie.Value
if model.Find(sid, url) {
return 1 // 鉴权通过
} else {
var stat uint
sid, stat = newAuthenticate(sid, url, passwd, time, false)
cookie.Value = sid
return stat
}
}
// 设置cookie name sid, value link
func SetCookie(c echo.Context, cookie *http.Cookie, sid string, maxAge int, time_ time.Time) error {
cookie.Name = "User" // 标识为user
//cookie.Value = string(uuid) // 通过uuid和数据库,确定user是谁
cookie.Value = sid
cookie.Path = "/"
// cookie有效期为3600秒
if maxAge == 0 {
if time_.IsZero() {
cookie.MaxAge = 3600
} else {
cookie.MaxAge = int(time.Until(time_).Seconds())
if cookie.MaxAge <= 0 {
cookie.MaxAge = 3600
}
}
} else {
cookie.MaxAge = maxAge
}
// 设置cookie
c.SetCookie(cookie)
return nil
}
func FileRead(file *multipart.FileHeader) (string, int, error) {
// 打开用户上传的文件
src, err := file.Open()
if err != nil {
logrus.Println(err)
return "", 0, err
}
defer src.Close()
//读取文件内容
var data []byte
size := 0
buf := make([]byte, 1024)
for {
n, err := src.Read(buf)
if err != nil && err != io.EOF {
logrus.Panic(err)
return "", 0, err
}
//说明读取结束
if n == 0 {
break
}
//读取到最终的缓冲区中
data = append(data, buf[:n]...)
size = size + n
if size > Settings.MaxSize+1024*2 {
break // 超过大小
}
}
str := string(data)
return str, size, nil
}
/*
* 判断文件大小是否超过阈值(threshold,单位B)
*/
func overflow(content string, threshold int) bool {
len := len(content)
// 大小比较
if len > threshold {
return true
} else {
return false
}
}
// / ========== DB related==================
func DBupdate(c echo.Context, info *Upload) (string, string) {
url := "http://pastebin/" + IdGen(8)
cookie, _ := c.Cookie("User")
var sid string
if cookie == nil {
sid = ""
} else {
sid = cookie.Value
}
sid, _ = newAuthenticate(sid, url, url, info.Expiration, true)
if info.MaxView == 0 {
//num,_=strconv.ParseUint(GetSetting("maxDefaultAccess"), 10, 64)
info.MaxView = uint(Settings.MaxDefaultView) // 设置最大默认可访问次数
}
model.Savetext(info.Content, info.MaxView, info.Passwd, info.Expiration, url, info.Type, info.Name, info.HighLight)
return sid, url
}
/*
* 没有设定过期时间,oriTime 为 0
* 此函数将默认过期时间设为当前时间后半小时
*/
func timeAssign(oriTime time.Time) time.Time {
if oriTime.IsZero() {
return time.Now().Add(30 * time.Minute)
} else {
return oriTime
}
}
// ===========sumdry=======================
// 从config读取数据
// 从配置文件里面读取设置
func InitSettings() {
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath("./")
err := viper.ReadInConfig()
if err != nil {
logrus.Panic(err)
}
// connect to database
logInfo := viper.GetStringMapString("setting")
Settings.Url = logInfo["url"]
if Settings.Url == "" {
logrus.Fatal("can not get domain name from config")
}
Settings.MaxDefaultView, err = strconv.Atoi(logInfo["maxdefaultview"])
if err != nil {
logrus.Fatal(err)
}
Settings.MaxSize, err = strconv.Atoi(logInfo["maxsize"])
if err != nil {
logrus.Fatal(err)
}
}
/*
* 通过文件扩展名获取ContentType
*/
func GetFileContentType(fileType string) string {
var StrRet string = ""
switch fileType {
case ".txt":
StrRet = "text/plain"
case ".csv":
StrRet = "text/csv"
case ".tex":
StrRet = "application/x-tex"
case ".md":
StrRet = "text/x-markdown"
default:
StrRet = "text/plain"
}
return StrRet
}
// 格式化后缀,仿止出错
func TypeComplement(typ string) string {
// 类型判断
if typ == "" {
typ = ".txt"
} else if typ[0] != '.' {
typ = "." + typ
}
return typ
}
func myMarshal(data interface{}) (string, error) {
bf := bytes.NewBuffer([]byte{})
jsonEncoder := json.NewEncoder(bf)
jsonEncoder.SetEscapeHTML(false)
if err := jsonEncoder.Encode(data); err != nil {
return "", err
}
return bf.String(), nil
}
package controller
import (
"backend/app/response"
"backend/model"
"net/http"
"time"
"github.com/labstack/echo/v4"
"github.com/sirupsen/logrus"
)
func Ping(c echo.Context) error {
// just test
return response.SendResponse(c, http.StatusOK, "", "pong!")
}
// 申请一个包含uid的cookie
func AskUid(c echo.Context) error {
nsid := IdGen(8)
info := new(Upload)
cookie := new(http.Cookie)
err := SetCookie(c, cookie, nsid, info.MaxAge, info.Expiration)
if err != nil {
return err
} else {
return c.HTML(http.StatusOK, "success")
}
}
// 成功则返回上传成功,否则报错
// sessionId不直接绑定,通过cookie传
func TextUp(c echo.Context) error {
info := new(Upload)
if err := c.Bind(info); err != nil {
return err
}
info.Expiration = timeAssign(info.Expiration) // 默认时间
if overflow(info.Content, 8*1024*1024) {
return response.SendResponse(c, http.StatusForbidden, "error:文件上传失败: 文件大小超过8MB.", "")
}
// 更新数据库
_, url := DBupdate(c, info) // 不分配uid
//return response.SendResponse2(c, http.StatusOK, "文件上传成功", info.TextName, GetTextContentType(info.TextType), url)
return response.SendResponse(c, http.StatusOK, "文件上传成功", url)
}
func FileUp(c echo.Context) error {
info := new(Upload)
if err := c.Bind(info); err != nil {
return err
}
info.Expiration = timeAssign(info.Expiration) // 默认时间
file, err := c.FormFile("file")
if err != nil {
logrus.Println(err)
return err
}
info.Name = file.Filename
data := new(response.Msg2)
info.Content, data.Size, _ = FileRead(file)
info.MaxView = uint(Settings.MaxDefaultView)
if data.Size > 8*1024*1024 {
return response.SendResponse(c, http.StatusForbidden, "error:文件上传失败: 文件大小超过8MB.", "")
}
// 更新数据库
_, data.Url = DBupdate(c, info) // 不分配uid
data.Name = info.Name
//dataj, err := myMarshal(data)
if err != nil {
logrus.Println(err)
return err
}
//return response.SendResponse2(c, http.StatusOK, "文件上传成功", info.TextName, GetTextContentType(info.TextType), url)
return response.SendResponse2(c, http.StatusOK, "文件上传成功", *data)
}
/*
* 输入:前端提供的文件链接,
* 返回:一个可供URL访问的链接(string)
* cookie.Value 传sessionId
*/
func Down(c echo.Context) error {
info := new(Download)
if err := c.Bind(info); err != nil {
logrus.Println(err)
return err
}
//info.Time = timeAssign(info.Time) // 默认时间为当前半小时后
cookie, _ := c.Cookie("User")
cookieMsg := ""
if cookie == nil {
// cookieMsg = "没有cookie,已分配.\n"
cookie = new(http.Cookie)
SetCookie(c, cookie, IdGen(8), 1800, time.Time{})
}
//c.Request().URL.
// 鉴权
stat := Autheticate(cookie, info.Url, info.Passwd, time.Now().Add(1800)) // 包含创建链接Createlink
// response
switch stat {
case 0:
return response.SendResponse(c, http.StatusForbidden, cookieMsg+"error:密码错误", info.Url) //403
case 1: // 鉴权通过
Data := new(response.Msg)
Data.Content = model.Find1(info.Url, "content") // 文件内容
Data.Type = GetFileContentType(model.Find1(info.Url, "TextType")) // 文件类型
Data.Name = model.Find1(info.Url, "TextName")
Data.Url = info.Url
//dataj, _ := myMarshal(Data) // 文件名
return response.SendResponse1(c, http.StatusOK, cookieMsg+"success", *Data) // 返回数据
case 2:
return response.SendResponse(c, http.StatusGone, cookieMsg+"error:内容过期", "") //410
case 3:
return response.SendResponse(c, http.StatusUnauthorized, cookieMsg+"error:内容过期", "") //401
case 4:
return response.SendResponse(c, http.StatusInternalServerError, cookieMsg+"error:Internal Server Error", "") //500
}
return nil
}
package controller
import (
"time"
)
type Upload struct {
Content string `json:"Content" form:"Content" query:"Content"`
Passwd string `json:"Passwd" form:"Passwd" query:"Passwd"`
Name string `json:"Name" form:"Name" query:"Name"`
Type string `json:"Type" form:"Type" query:"Type"`
HighLight bool `json:"HighLight" form:"HighLight" query:"HighLight"`
Expiration time.Time `json:"Expiration" form:"Expiration" query:"Expiration"`
MaxAge int `json:"MaxAge" form:"MaxAge" query:"MaxAge"` // 用户指定的时间期限
MaxView uint `json:"MaxView" form:"MaxView" query:"MaxView"` // 文件最大可访问次数
//Expiry time.Time `json:"expiry"` // 有效期
//Content string `json:"content"`
}
type Download struct {
Passwd string `json:"Passwd" form:"Passwd" query:"Passwd"`
Url string `json:"Url" form:"Url" query:"Url"`
}
// 从config文件获取
type Setting struct {
Url string // 配置的域名
MaxDefaultView int // 单文件默认最大可访问次数
MaxSize int // 文件最大可上传大小(单位B)
}
var Settings Setting
package app
import (
"backend/app/controller"
"backend/utils"
//"net/http"
"github.com/labstack/echo/v4"
"github.com/sirupsen/logrus"
)
var e *echo.Echo
func InitWebFramework() {
e = echo.New()
e.HideBanner = true
addRoutes()
e.Validator = &utils.CustomValidator{}
logrus.Info("echo framework initialized")
}
func StartServer() {
e.Logger.Fatal(e.Start(controller.Settings.Url)) // 启动服务,注意默认端口80不能省略
//e.Logger.Fatal(e.Start("0.0.0.0:8080")) // 监听所有端口
//e.Logger.Fatal(e.Start("127.0.0.1:80")) // 启动服务,注意默认端口80不能省略
//e.Logger.Fatal(e.Start("http://xlab.zju.edu.cn/test/pastebin/group-1:80")) // 启动服务,注意默认端口80不能省略,需要域名解析,config
}
/*
* 初始化logger设置
*/
func InitLogger() {
//自定义日志格式
logrus.SetFormatter(&logrus.TextFormatter{
ForceQuote: true, //键值对加引号
TimestampFormat: "2006-01-02 15:04:05", //时间格式
FullTimestamp: true,
})
logrus.SetReportCaller(true)
}
package middleware
import (
"fmt"
"github.com/labstack/echo/v4"
"github.com/sirupsen/logrus"
)
func Auth(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
mylogger(c)
uid := getUid(c)
c.Set("uid", uid)
return next(c)
}
}
/*
向文件或 stdout 输出详细的日志,记录用户的 User-Agent、IP 地址、访问时间、访问路径等信息
User-Agent,访问路径,暂且无法实现
*/
func mylogger(c echo.Context) {
//c.GET("User-Agent")
userAgent := c.Get("User-Agent")
ip := echo.ExtractIPDirect()(c.Request())
msg := "User-Agent:" + fmt.Sprint(userAgent) + ",ip:" + ip + ",path:" + c.Path()
logrus.Println(msg)
}
// 检测 uid 是否有效
func isUidValid(uid string) bool {
if uid == "" {
return false
}
return true
}
// 从报文中获得uid,给新用户分配uid
func getUid(c echo.Context) interface{} {
cookie, err := c.Cookie("User")
if err != nil || !isUidValid(cookie.Value) {
logrus.Println("uid invalid,分配新uid")
return 114514
} else {
return cookie.Value
}
}
package response
import (
"net/http"
"github.com/labstack/echo/v4"
)
type Msg struct {
Name string
Type string
Content string
Url string
}
type Msg2 struct {
Url string
Name string
Size int
}
type Response struct {
Code int `json:"Code"`
Msg string `json:"Msg"`
Data interface{} `json:"Data"`
}
type Response1 struct {
Code int `json:"Code"`
Msg string `json:"Msg"`
Data Msg `json:"Data"`
}
type Response2 struct {
Code int `json:"Code"`
Msg string `json:"Msg"`
Data Msg2 `json:"Data"`
}
func SendResponse(c echo.Context, code int, msg string, data ...interface{}) error {
return c.JSON(http.StatusOK, Response{
Code: code,
Msg: msg,
Data: data,
})
}
func SendResponse1(c echo.Context, code int, msg string, data Msg) error {
return c.JSON(http.StatusOK, Response1{
Code: code,
Msg: msg,
Data: data,
})
}
func SendResponse2(c echo.Context, code int, msg string, data Msg2) error {
return c.JSON(http.StatusOK, Response2{
Code: code,
Msg: msg,
Data: data,
})
}
// 返回err
func SendResponse4(c echo.Context, err error) error {
return c.JSON(http.StatusBadRequest, err.Error())
}
package app
import (
"backend/app/controller"
"backend/app/middleware"
//"backend/model"
)
func addRoutes() {
api := e.Group("api")
api.Use(middleware.Auth)
api.GET("/ping", controller.Ping) // 测试用
// 前端向后端申请一个包含sessionId的cookie,暂不不支持自定义cookie有效时间,默认半小时
api.GET("/ask/sid", controller.AskUid)
/* 前端用于上传文件的接口,
* 传输文件内容,content:"xxx", 以文本形式传输
* 使用时带上文件类型,如fileType:"application/x-tex",不设置则下载时无法告知前端文件类型,默认为"text/plain"
* 使用时最好带cookie,没有就用申请api申请。
* 其余参数可以不设置,有效期默认半小时后, 最大可访问次数默认30(config文件)
*/
api.POST("/file/upload", controller.FileUp) // 接收文件
/*
* 前端用于下载文件的接口
* 需要cookie,url,passwd
* 使用时带cookie。如果cookie有权限,则直接返回有文件内容的response。没有则返回一个需要密码的状态码
* 前端跳转密码输入界面获取密码后带上密码再次使用此接口,密码正确则给接口权限,返回内容
* 若文件没设密码则不带密码也可访问
* 状态码: 403:密码错误; 410:内容过期; 200:访问成功; 401:请进行身份验证(输密码)
*/
api.POST("/file/download", controller.Down)
api.POST("/text/upload", controller.TextUp)
api.POST("/text/download", controller.Down)
}
func ApiAssign() {
}
mysql:
host: host.docker.internal
user: rootp
password: pastebingroup1
database: pastebin
setting:
url: 0.0.0.0:30031 # 服务器部署时需修改
maxDefaultView: 30 # 单文件默认最大可访问次数
maxSize: 8388608 # 文件最大可上传大小(单位B),8*1024*1024=8388608
\ No newline at end of file
module backend
go 1.19
require (
github.com/go-playground/validator/v10 v10.11.1
github.com/labstack/echo/v4 v4.9.1
github.com/sirupsen/logrus v1.9.0
github.com/spf13/viper v1.14.0
gorm.io/driver/mysql v1.4.4
gorm.io/gorm v1.24.2
)
require (
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/labstack/gommon v0.4.0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
github.com/spf13/afero v1.9.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.4.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.1 // indirect
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b // indirect
golang.org/x/sys v0.0.0-20220908164124-27713097b956 // indirect
golang.org/x/text v0.4.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
This diff is collapsed.
// @title Golang Service Template
// @version 0.1
// @description Golang back-end service template, get started with back-end projects quickly
// @BasePath /api
package main
import (
"backend/app"
"backend/app/controller"
"backend/model"
)
func main() {
app.InitLogger() // 初始化logger设置
controller.InitSettings() // 从配置文件中获取设置
model.Init()
app.InitWebFramework()
app.StartServer()
}
package model
import (
//"fmt"
"time"
// "math/rand"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var DB *gorm.DB
func Init() {
ConnectDatabase()
var err error
// insert auto-table
err = DB.AutoMigrate(&Content{})
if err != nil {
logrus.Fatal(err)
}
err = DB.AutoMigrate(&Users{})
if err != nil {
logrus.Fatal(err)
}
err = DB.AutoMigrate(&Rel{})
if err != nil {
logrus.Fatal(err)
}
}
func ConnectDatabase() {
// config
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath("./")
err := viper.ReadInConfig()
if err != nil {
logrus.Panic(err)
}
// connect to database
logInfo := viper.GetStringMapString("mysql")
sqlInfo := logInfo["user"] + ":" + logInfo["password"] +
"@(" + logInfo["host"] + ")/" + logInfo["database"] + "?charset=utf8mb4&parseTime=True&loc=Local"
DB, err = gorm.Open(mysql.Open(sqlInfo), &gorm.Config{})
if err != nil {
logrus.Panic(err)
}
}
// Backcheck content through link
// 通过链接反查内容
func Findlink(_url string) string {
var p Content
err := DB.First(&p, "Url1 = ?", _url)
if err != nil {
logrus.Error(err)
}
return p.S
}
// save text information
// 保存文本和文件信息
func Savetext(_S string, _Time uint, _Passwd string, _Time1 time.Time, _url string, _Filetype string, _Filename string,_Hlight bool) {
var id1 int64
DB.Model(&Content{}).Count(&id1)
id1++
p := Content{
ID: id1,
S: _S,
Time: _Time,
Passwd: _Passwd,
Date: time.Now(),
Time1: _Time1,
Time2: 0,
Url1: _url,
Filetype: _Filetype,
Filename: _Filename,
Hlight: _Hlight,
}
DB.Create(&p)
}
// Check whether the number of visits exceeds the threshold and the time limit
// 检查是否超过总访问次数和截止时间
func Checkt(p Content) bool {
t := time.Now()
p.Time2++
//fmt.Println(p.Time2)
if p.Time2 > p.Time || t.After(p.Time1) {
DB.Delete(&p)
return true
}
DB.Model(&Content{}).Where("Url1 = ?", p.Url1).Update("Time2", p.Time2)
return false
}
// 这个函数那边有可能需要
// 检查sid_url是否超时
//同时也可以查询sid,_url是否关联
func Find(sid string,_url string) bool {
var s Rel
err := DB.Where(&Rel{Sid: sid,Url: _url}).First(&s).Error
if err != nil {
return false
}
t := time.Now()
if t.After(s.Time) {
DB.Delete(&s)
return false
} else {
return true
}
}
// 通过url查询文件类型
func Find1(_url string, key string) string {
var p Content
DB.First(&p, "Url1 = ?", _url)
switch key {
case "content":
return p.S
case "fileType":
return p.Filetype
case "fileName":
return p.Filename
}
return ""
}
// 通过url查询文件是否高亮
func Find2(_url string) bool {
var p Content
DB.First(&p, "Url1 = ?", _url)
return p.Hlight
}
/*//*随机生成字符串
func randStr(n int) string {
rand.Seed(time.Now().Unix())
b := make([]rune, n)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}*/
// 新建用户
func Createuser(_User Users) {
//_User.SessionId=randStr(8)
DB.Create(&_User)
}
//新建链接
/* 返回值:
3 表示sid_url已经过期,需要重新分配
2 表示内容过期
1 表示密码正确
0 表示密码不正确
*/
func Createlink(sid string, _passwd string, _url string, _Time1 time.Time) uint {
var p Content
DB.First(&p,"Url1 = ?", _url)
if p.Passwd == _passwd {
if Checkt(p) {
//fmt.Println(sid,_passwd,_url,_Time1)
return 2
}
var p1 Rel
err := DB.Where(&Rel{Sid: sid,Url: _url}).First(&p1).Error
if err != nil {
rel1 := Rel{
Sid: sid,
Url: _url,
Time: _Time1,
}
DB.Create(&rel1)
} else {
p1.Time = _Time1
DB.Save(&p1)
}
return 1
}
return 0
}
// 第一次上传
func CreatelinkFirstTime(sid string, _url string, _Time1 time.Time) {
sid1 := Rel{
Sid: sid,
Url: _url,
Time: _Time1,
}
DB.Create(&sid1)
}
/*
func Test() {
fmt.Println(Find("ab","12345"))
}*/
package model
import (
"time"
"gorm.io/gorm"
)
type Content struct {
ID int64 `gorm:"primarykey"`
S string
Time uint
Passwd string
Date time.Time
Size uint
Time1 time.Time
Time2 uint
Url1 string
Filetype string
Filename string
Hlight bool
}
type Rel struct {
gorm.Model
Sid string
Url string
Time time.Time
}
type Users struct {
SessionId string
Username string
Passwd string
Name string
Size uint
Route string
}
create database pastebin;
create user 'rootp'@'localhost' identified by 'pastebingroup1';
grant all privileges on pastebin.* to 'rootp'@'localhost';
use pastebin;
-- MySQL dump 10.13 Distrib 8.0.31, for Win64 (x86_64)
--
-- Host: localhost Database: pastebin
-- ------------------------------------------------------
-- Server version 8.0.31
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!50503 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `contents`
--
DROP TABLE IF EXISTS `contents`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `contents` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`s` longtext,
`time` bigint unsigned DEFAULT NULL,
`passwd` longtext,
`date` datetime(3) DEFAULT NULL,
`size` bigint unsigned DEFAULT NULL,
`time1` datetime(3) DEFAULT NULL,
`time2` bigint unsigned DEFAULT NULL,
`url1` longtext,
`filetype` longtext,
`filename` longtext,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `contents`
--
LOCK TABLES `contents` WRITE;
/*!40000 ALTER TABLE `contents` DISABLE KEYS */;
/*!40000 ALTER TABLE `contents` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `sid_urls`
--
DROP TABLE IF EXISTS `sid_urls`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `sid_urls` (
`sid_id` bigint unsigned NOT NULL,
`url_id` bigint unsigned NOT NULL,
PRIMARY KEY (`sid_id`,`url_id`),
KEY `fk_sid_urls_url` (`url_id`),
CONSTRAINT `fk_sid_urls_sid` FOREIGN KEY (`sid_id`) REFERENCES `sids` (`id`),
CONSTRAINT `fk_sid_urls_url` FOREIGN KEY (`url_id`) REFERENCES `urls` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `sid_urls`
--
LOCK TABLES `sid_urls` WRITE;
/*!40000 ALTER TABLE `sid_urls` DISABLE KEYS */;
/*!40000 ALTER TABLE `sid_urls` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `sids`
--
DROP TABLE IF EXISTS `sids`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `sids` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`created_at` datetime(3) DEFAULT NULL,
`updated_at` datetime(3) DEFAULT NULL,
`deleted_at` datetime(3) DEFAULT NULL,
`time` datetime(3) DEFAULT NULL,
`s` longtext,
PRIMARY KEY (`id`),
KEY `idx_sids_deleted_at` (`deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `sids`
--
LOCK TABLES `sids` WRITE;
/*!40000 ALTER TABLE `sids` DISABLE KEYS */;
/*!40000 ALTER TABLE `sids` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `urls`
--
DROP TABLE IF EXISTS `urls`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `urls` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`created_at` datetime(3) DEFAULT NULL,
`updated_at` datetime(3) DEFAULT NULL,
`deleted_at` datetime(3) DEFAULT NULL,
`url1` longtext,
PRIMARY KEY (`id`),
KEY `idx_urls_deleted_at` (`deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `urls`
--
LOCK TABLES `urls` WRITE;
/*!40000 ALTER TABLE `urls` DISABLE KEYS */;
/*!40000 ALTER TABLE `urls` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `users`
--
DROP TABLE IF EXISTS `users`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `users` (
`session_id` longtext,
`username` longtext,
`passwd` longtext,
`name` longtext,
`size` bigint unsigned DEFAULT NULL,
`route` longtext
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `users`
--
LOCK TABLES `users` WRITE;
/*!40000 ALTER TABLE `users` DISABLE KEYS */;
/*!40000 ALTER TABLE `users` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2023-01-17 22:07:33
create database mydb;
create user 'root'@'localhost' identified by 'passwd';
grant all privileges on pastebin.* to 'root'@'localhost';
use mydb;
文件结构:
app:
controller:
init:route对应的handle函数
foo: handle函数需要调用的函数
middleware:
login:登录中间件,添加uid,增加日志
response:
response:自定义reseponse,都采用json返回信息
init: 网络初始化,域名在config.yaml获取。
日志自定义格式
routes: 路由分配
model:
init: 数据库相关函数,包含着有关和数据库交互的各种操作,比如连接数据库,通过数据库反查内容,保存文本和文件信息等操作的函数
model: 存储着各表的结构,一共有五个结构体
config.yaml: 修改设置
go.mod
go.sum
main.go
readme
api:
见route.go
\ No newline at end of file
package utils
import (
"sync"
"github.com/go-playground/validator/v10"
)
type CustomValidator struct {
once sync.Once
validate *validator.Validate
}
func (c *CustomValidator) Validate(i interface{}) error {
c.lazyInit()
return c.validate.Struct(i)
}
func (c *CustomValidator) lazyInit() {
c.once.Do(func() {
c.validate = validator.New()
})
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment