Change db column name. Track missing file with git
This commit is contained in:
parent
c98e061d2e
commit
80bb2cffc2
34
cmd/paste-abroad/main.go
Normal file
34
cmd/paste-abroad/main.go
Normal 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
27
go.mod
|
@ -3,14 +3,29 @@ module github.com/leafee98/paste-abroad
|
|||
go 1.19
|
||||
|
||||
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/valyala/fasthttp v1.44.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/klauspost/compress v1.15.9 // indirect
|
||||
github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.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
28
internel/config/config.go
Normal 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
|
||||
}
|
|
@ -13,16 +13,15 @@ import (
|
|||
|
||||
|
||||
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
|
||||
|
||||
var r = gin.Default()
|
||||
r.POST("/", PostPaste)
|
||||
r.GET("/raw/:id", GetPasteRaw)
|
||||
r.GET("/:id", GetPaste)
|
||||
return r
|
||||
e.POST("/", PostPaste)
|
||||
e.GET("/raw/:id", GetPasteRaw)
|
||||
e.GET("/:id", GetPaste)
|
||||
}
|
||||
|
||||
// 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 = content_buffer.Bytes()
|
||||
} 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":
|
||||
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:
|
||||
ctx.String(400, "unrecognized content type: %s", ctx.ContentType())
|
||||
return
|
||||
|
@ -56,13 +65,13 @@ func PostPaste(ctx *gin.Context) {
|
|||
|
||||
life_int, err := strconv.ParseInt(life, 10, 64)
|
||||
if err != nil {
|
||||
life_int = expireTime
|
||||
life_int = defaultExpireTime
|
||||
}
|
||||
|
||||
paste := storage.Paste{
|
||||
Content: content,
|
||||
// Content: content_buffer.Bytes(),
|
||||
Plain: !isEncrypt(encrypt),
|
||||
Encrypt: isEncrypt(encrypt),
|
||||
Expire: life_int * 1000 * 3600 * 24 + time.Now().UnixMilli(),
|
||||
}
|
||||
|
||||
|
@ -73,7 +82,7 @@ func PostPaste(ctx *gin.Context) {
|
|||
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)
|
||||
}
|
||||
|
@ -93,10 +102,10 @@ func GetPasteRaw(ctx * gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if content.Plain {
|
||||
ctx.Data(200, "text/plain", content.Content)
|
||||
} else {
|
||||
if content.Encrypt {
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
ture_values := []string{ "t", "true", "y", "yes", "1" }
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ func initDatabase(db *sql.DB) error {
|
|||
create table if not exists paste (
|
||||
id text unique,
|
||||
content blob,
|
||||
plain integer,
|
||||
encrypt integer,
|
||||
expire integer
|
||||
);
|
||||
|
||||
|
@ -62,22 +62,22 @@ func (s *StorageSqlite) Close() 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 {
|
||||
return "", err
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
var id string
|
||||
var plain = 0
|
||||
var encrypt = 0
|
||||
|
||||
if p.Plain {
|
||||
plain = 1
|
||||
if p.Encrypt {
|
||||
encrypt = 1
|
||||
}
|
||||
|
||||
for true {
|
||||
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 {
|
||||
break
|
||||
} 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) {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -108,14 +108,14 @@ func (s *StorageSqlite) Get(id string) (*storage.Paste, error) {
|
|||
}
|
||||
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
|
||||
}
|
||||
|
||||
if plain != 0 {
|
||||
p.Plain = true
|
||||
if encrypt != 0 {
|
||||
p.Encrypt = true
|
||||
}
|
||||
|
||||
return &p, nil
|
||||
|
|
|
@ -35,12 +35,12 @@ func TestSqlite(t * testing.T) {
|
|||
func testGetSave(s *sqlite.StorageSqlite, t * testing.T) {
|
||||
var a = storage.Paste {
|
||||
Content: []byte("abc"),
|
||||
Plain: true,
|
||||
Encrypt: false,
|
||||
Expire: time.Now().UnixMilli() + 15 * 1000,
|
||||
}
|
||||
var b = storage.Paste {
|
||||
Content: []byte("def"),
|
||||
Plain: true,
|
||||
Encrypt: false,
|
||||
Expire: time.Now().UnixMilli() - 15 * 1000,
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ type Storage interface {
|
|||
}
|
||||
|
||||
type Paste struct {
|
||||
Plain bool
|
||||
Encrypt bool
|
||||
Content []byte
|
||||
Expire int64
|
||||
}
|
||||
|
|
16
internel/utils/utils.go
Normal file
16
internel/utils/utils.go
Normal 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)
|
||||
}
|
Loading…
Reference in a new issue