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
|
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
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 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" }
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
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