Change db column name. Track missing file with git

This commit is contained in:
leafee98 2023-02-15 12:09:09 +08:00
parent c98e061d2e
commit 80bb2cffc2
8 changed files with 156 additions and 35 deletions

34
cmd/paste-abroad/main.go Normal file
View file

@ -0,0 +1,34 @@
package main
import (
limit "github.com/gin-contrib/size"
"github.com/gin-gonic/gin"
"github.com/leafee98/paste-abroad/internel/router"
"github.com/leafee98/paste-abroad/internel/config"
"github.com/leafee98/paste-abroad/internel/storage/sqlite"
"fmt"
)
func main() {
c, err := config.Load("./config.toml")
if err != nil {
fmt.Printf("Error when loading config: %v", err)
return
}
store, err := sqlite.New(c.DBString, c.IdLength)
if err != nil {
fmt.Println(err)
}
r := gin.Default()
r.Use(limit.RequestSizeLimiter(c.MaxPasteSize))
router.Init(r, store, c.DefaultLifetime)
r.Run(":8081")
}

27
go.mod
View file

@ -3,14 +3,29 @@ module github.com/leafee98/paste-abroad
go 1.19 go 1.19
require ( require (
github.com/fasthttp/router v1.4.14 github.com/BurntSushi/toml v1.2.1
github.com/gin-contrib/size v0.0.0-20230212012657-e14a14094dc4
github.com/gin-gonic/gin v1.8.2
github.com/mattn/go-sqlite3 v1.14.16 github.com/mattn/go-sqlite3 v1.14.16
github.com/valyala/fasthttp v1.44.0
) )
require ( require (
github.com/andybalholm/brotli v1.0.4 // indirect github.com/gin-contrib/sse v0.1.0 // indirect
github.com/klauspost/compress v1.15.9 // indirect github.com/go-playground/locales v0.14.0 // indirect
github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d // indirect github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/go-playground/validator/v10 v10.11.1 // indirect
github.com/goccy/go-json v0.9.11 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/ugorji/go/codec v1.2.7 // indirect
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
golang.org/x/net v0.4.0 // indirect
golang.org/x/sys v0.3.0 // indirect
golang.org/x/text v0.5.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
) )

28
internel/config/config.go Normal file
View file

@ -0,0 +1,28 @@
package config
import (
"github.com/BurntSushi/toml"
"os"
)
type Config struct {
DefaultLifetime int64 `toml:"lifetime"`
IdLength int `toml:"id_length"`
Database string `toml:"database"`
DBString string `toml:"db_string"`
MaxPasteSize int64 `toml:"max_paste_size"`
}
func Load(path string) (*Config, error) {
buffer, err := os.ReadFile(path)
if err != nil {
return nil, err
}
config_content := string(buffer)
var c Config
toml.Decode(config_content, &c)
return &c, nil
}

View file

@ -13,16 +13,15 @@ import (
var store storage.Storage var store storage.Storage
var expireTime int64 var defaultExpireTime int64
func New(s storage.Storage, expireTime int) *gin.Engine { func Init(e *gin.Engine, s storage.Storage, expireTime int64) {
defaultExpireTime = expireTime
store = s store = s
var r = gin.Default() e.POST("/", PostPaste)
r.POST("/", PostPaste) e.GET("/raw/:id", GetPasteRaw)
r.GET("/raw/:id", GetPasteRaw) e.GET("/:id", GetPaste)
r.GET("/:id", GetPaste)
return r
} }
// PostPaste require two parameters from either URL or form-data // PostPaste require two parameters from either URL or form-data
@ -42,10 +41,20 @@ func PostPaste(ctx *gin.Context) {
content_buffer.ReadFrom(c_reader) content_buffer.ReadFrom(c_reader)
content = content_buffer.Bytes() content = content_buffer.Bytes()
} else { } else {
content = []byte(requestValue(ctx, "c", false)) content_str, exists := getRequestValue(ctx, "c", false)
if !exists {
ctx.String(400, "cannot get paste content: missing parameter \"c\"")
return
}
content = []byte(content_str)
} }
case "application/x-www-form-urlencoded": case "application/x-www-form-urlencoded":
content = []byte(requestValue(ctx, "c", false)) content_str, exists := getRequestValue(ctx, "c", false)
if !exists {
ctx.String(400, "cannot get paste content: missing parameter \"c\"")
return
}
content = []byte(content_str)
default: default:
ctx.String(400, "unrecognized content type: %s", ctx.ContentType()) ctx.String(400, "unrecognized content type: %s", ctx.ContentType())
return return
@ -56,13 +65,13 @@ func PostPaste(ctx *gin.Context) {
life_int, err := strconv.ParseInt(life, 10, 64) life_int, err := strconv.ParseInt(life, 10, 64)
if err != nil { if err != nil {
life_int = expireTime life_int = defaultExpireTime
} }
paste := storage.Paste{ paste := storage.Paste{
Content: content, Content: content,
// Content: content_buffer.Bytes(), // Content: content_buffer.Bytes(),
Plain: !isEncrypt(encrypt), Encrypt: isEncrypt(encrypt),
Expire: life_int * 1000 * 3600 * 24 + time.Now().UnixMilli(), Expire: life_int * 1000 * 3600 * 24 + time.Now().UnixMilli(),
} }
@ -73,7 +82,7 @@ func PostPaste(ctx *gin.Context) {
return return
} }
log.Printf("Saved a paste { id: %s, plain: %t, expire: %d}", id, paste.Plain, paste.Expire) log.Printf("Saved a paste { id: %s, encrypt: %t, expire: %d}", id, paste.Encrypt, paste.Expire)
ctx.String(200, id) ctx.String(200, id)
} }
@ -93,10 +102,10 @@ func GetPasteRaw(ctx * gin.Context) {
return return
} }
if content.Plain { if content.Encrypt {
ctx.Data(200, "text/plain", content.Content)
} else {
ctx.Data(200, "application/octet-stream", content.Content) ctx.Data(200, "application/octet-stream", content.Content)
} else {
ctx.Data(200, "text/plain", content.Content)
} }
} }
@ -121,6 +130,25 @@ func requestValue(ctx *gin.Context, key string, urlPrefer bool) string {
return value return value
} }
func getRequestValue(ctx *gin.Context, key string, urlPrefer bool) (string, bool) {
var value string
var exists bool
if urlPrefer {
value, exists = ctx.GetQuery(key)
if !exists {
value, exists = ctx.GetPostForm(key)
}
} else {
value, exists = ctx.GetPostForm(key)
if !exists {
value, exists = ctx.GetQuery(key)
}
}
return value, exists
}
func isEncrypt(s string) bool { func isEncrypt(s string) bool {
ture_values := []string{ "t", "true", "y", "yes", "1" } ture_values := []string{ "t", "true", "y", "yes", "1" }

View file

@ -39,7 +39,7 @@ func initDatabase(db *sql.DB) error {
create table if not exists paste ( create table if not exists paste (
id text unique, id text unique,
content blob, content blob,
plain integer, encrypt integer,
expire integer expire integer
); );
@ -62,22 +62,22 @@ func (s *StorageSqlite) Close() error {
} }
func (s *StorageSqlite) Save(p *storage.Paste) (string, error) { func (s *StorageSqlite) Save(p *storage.Paste) (string, error) {
stmt, err := s.db.Prepare(`insert into paste (id, content, plain, expire) values (?, ?, ?, ?);`) stmt, err := s.db.Prepare(`insert into paste (id, content, encrypt, expire) values (?, ?, ?, ?);`)
if err != nil { if err != nil {
return "", err return "", err
} }
defer stmt.Close() defer stmt.Close()
var id string var id string
var plain = 0 var encrypt = 0
if p.Plain { if p.Encrypt {
plain = 1 encrypt = 1
} }
for true { for true {
id = utils.GenerateId(s.idLength) id = utils.GenerateId(s.idLength)
_, err = stmt.Exec(id, p.Content, plain, p.Expire) _, err = stmt.Exec(id, p.Content, encrypt, p.Expire)
if err == nil { if err == nil {
break break
} else if errors.Is(err, sqlite3.ErrConstraint) { } else if errors.Is(err, sqlite3.ErrConstraint) {
@ -91,7 +91,7 @@ func (s *StorageSqlite) Save(p *storage.Paste) (string, error) {
} }
func (s *StorageSqlite) Get(id string) (*storage.Paste, error) { func (s *StorageSqlite) Get(id string) (*storage.Paste, error) {
stmt, err := s.db.Prepare(`select content, plain, expire from paste where id = ?`) stmt, err := s.db.Prepare(`select content, encrypt, expire from paste where id = ?`)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -108,14 +108,14 @@ func (s *StorageSqlite) Get(id string) (*storage.Paste, error) {
} }
defer rows.Close() defer rows.Close()
var plain = 0 var encrypt = 0
if err = rows.Scan(&p.Content, &plain, &p.Expire); err != nil { if err = rows.Scan(&p.Content, &encrypt, &p.Expire); err != nil {
return nil, err return nil, err
} }
if plain != 0 { if encrypt != 0 {
p.Plain = true p.Encrypt = true
} }
return &p, nil return &p, nil

View file

@ -35,12 +35,12 @@ func TestSqlite(t * testing.T) {
func testGetSave(s *sqlite.StorageSqlite, t * testing.T) { func testGetSave(s *sqlite.StorageSqlite, t * testing.T) {
var a = storage.Paste { var a = storage.Paste {
Content: []byte("abc"), Content: []byte("abc"),
Plain: true, Encrypt: false,
Expire: time.Now().UnixMilli() + 15 * 1000, Expire: time.Now().UnixMilli() + 15 * 1000,
} }
var b = storage.Paste { var b = storage.Paste {
Content: []byte("def"), Content: []byte("def"),
Plain: true, Encrypt: false,
Expire: time.Now().UnixMilli() - 15 * 1000, Expire: time.Now().UnixMilli() - 15 * 1000,
} }

View file

@ -7,7 +7,7 @@ type Storage interface {
} }
type Paste struct { type Paste struct {
Plain bool Encrypt bool
Content []byte Content []byte
Expire int64 Expire int64
} }

16
internel/utils/utils.go Normal file
View file

@ -0,0 +1,16 @@
package utils
import (
"math/rand"
)
var letters = []rune("abcdefghijklmnopqrstuvwxyz")
// GenearteId will create a string contains 8 lowercase alphabets
func GenerateId(length int) string {
buffer := make([]rune, length)
for i := range buffer {
buffer[i] = letters[rand.Intn(len(letters))]
}
return string(buffer)
}