mirror of
https://github.com/lus/pasty.git
synced 2023-08-10 21:13:09 +03:00
refactor package structure & remove v1 API
This commit is contained in:
parent
1a574add49
commit
e93b292daf
@ -5,7 +5,6 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/lus/pasty/internal/config"
|
||||
"github.com/lus/pasty/internal/shared"
|
||||
"github.com/lus/pasty/internal/storage"
|
||||
)
|
||||
|
||||
@ -20,7 +19,7 @@ func main() {
|
||||
config.Load()
|
||||
|
||||
// Create and initialize the first (from) driver
|
||||
from, err := storage.GetDriver(shared.StorageType(os.Args[1]))
|
||||
from, err := storage.GetDriver(storage.Type(os.Args[1]))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -30,7 +29,7 @@ func main() {
|
||||
}
|
||||
|
||||
// Create and initialize the second (to) driver
|
||||
to, err := storage.GetDriver(shared.StorageType(os.Args[2]))
|
||||
to, err := storage.GetDriver(storage.Type(os.Args[2]))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -5,13 +5,12 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/lus/pasty/internal/env"
|
||||
"github.com/lus/pasty/internal/shared"
|
||||
)
|
||||
|
||||
// Config represents the general application configuration structure
|
||||
type Config struct {
|
||||
WebAddress string
|
||||
StorageType shared.StorageType
|
||||
StorageType string
|
||||
HastebinSupport bool
|
||||
IDLength int
|
||||
IDCharacters string
|
||||
@ -80,7 +79,7 @@ func Load() {
|
||||
|
||||
Current = &Config{
|
||||
WebAddress: env.MustString("WEB_ADDRESS", ":8080"),
|
||||
StorageType: shared.StorageType(strings.ToLower(env.MustString("STORAGE_TYPE", "file"))),
|
||||
StorageType: strings.ToLower(env.MustString("STORAGE_TYPE", "file")),
|
||||
HastebinSupport: env.MustBool("HASTEBIN_SUPPORT", false),
|
||||
IDLength: env.MustInt("ID_LENGTH", 6),
|
||||
IDCharacters: env.MustString("ID_CHARACTERS", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"),
|
||||
|
30
internal/paste/paste.go
Normal file
30
internal/paste/paste.go
Normal file
@ -0,0 +1,30 @@
|
||||
package paste
|
||||
|
||||
import (
|
||||
"github.com/alexedwards/argon2id"
|
||||
)
|
||||
|
||||
// Paste represents a paste
|
||||
type Paste struct {
|
||||
ID string `json:"id"`
|
||||
Content string `json:"content"`
|
||||
ModificationToken string `json:"modificationToken,omitempty"`
|
||||
Created int64 `json:"created"`
|
||||
Metadata map[string]interface{} `json:"metadata"`
|
||||
}
|
||||
|
||||
// HashModificationToken hashes the current modification token of a paste
|
||||
func (paste *Paste) HashModificationToken() error {
|
||||
hash, err := argon2id.CreateHash(paste.ModificationToken, argon2id.DefaultParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
paste.ModificationToken = hash
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckModificationToken checks whether or not the given modification token is correct
|
||||
func (paste *Paste) CheckModificationToken(modificationToken string) bool {
|
||||
match, err := argon2id.ComparePasswordAndHash(modificationToken, paste.ModificationToken)
|
||||
return err == nil && match
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/alexedwards/argon2id"
|
||||
)
|
||||
|
||||
// Paste represents a saved paste
|
||||
type Paste struct {
|
||||
ID string `json:"id" bson:"_id"`
|
||||
Content string `json:"content" bson:"content"`
|
||||
DeletionToken string `json:"deletionToken,omitempty" bson:"deletionToken"` // Required for legacy paste storage support
|
||||
ModificationToken string `json:"modificationToken,omitempty" bson:"modificationToken"`
|
||||
Created int64 `json:"created" bson:"created"`
|
||||
Metadata map[string]interface{} `json:"metadata" bson:"metadata"`
|
||||
}
|
||||
|
||||
// HashModificationToken hashes the current modification token of a paste
|
||||
func (paste *Paste) HashModificationToken() error {
|
||||
hash, err := argon2id.CreateHash(paste.ModificationToken, argon2id.DefaultParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
paste.ModificationToken = hash
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckModificationToken checks whether or not the given modification token is correct
|
||||
func (paste *Paste) CheckModificationToken(modificationToken string) bool {
|
||||
// The modification token may be stored in the deletion token field in old pastes
|
||||
usedToken := paste.ModificationToken
|
||||
if usedToken == "" {
|
||||
usedToken = paste.DeletionToken
|
||||
if usedToken != "" {
|
||||
log.Println("WARNING: You seem to have pastes with the old 'deletionToken' field stored in your storage driver. Though this does not cause any issues right now, it may in the future. Consider some kind of migration.")
|
||||
}
|
||||
}
|
||||
|
||||
match, err := argon2id.ComparePasswordAndHash(modificationToken, usedToken)
|
||||
return err == nil && match
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
package shared
|
||||
|
||||
// StorageType represents a type of storage a paste can be stored with
|
||||
type StorageType string
|
||||
|
||||
const (
|
||||
StorageTypeFile = StorageType("file")
|
||||
StorageTypePostgres = StorageType("postgres")
|
||||
StorageTypeMongoDB = StorageType("mongodb")
|
||||
StorageTypeS3 = StorageType("s3")
|
||||
)
|
@ -2,9 +2,10 @@ package storage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/lus/pasty/internal/config"
|
||||
"github.com/lus/pasty/internal/shared"
|
||||
"github.com/lus/pasty/internal/paste"
|
||||
"github.com/lus/pasty/internal/storage/file"
|
||||
"github.com/lus/pasty/internal/storage/mongodb"
|
||||
"github.com/lus/pasty/internal/storage/postgres"
|
||||
@ -19,8 +20,8 @@ type Driver interface {
|
||||
Initialize() error
|
||||
Terminate() error
|
||||
ListIDs() ([]string, error)
|
||||
Get(id string) (*shared.Paste, error)
|
||||
Save(paste *shared.Paste) error
|
||||
Get(id string) (*paste.Paste, error)
|
||||
Save(paste *paste.Paste) error
|
||||
Delete(id string) error
|
||||
Cleanup() (int, error)
|
||||
}
|
||||
@ -43,15 +44,15 @@ func Load() error {
|
||||
}
|
||||
|
||||
// GetDriver returns the driver with the given type if it exists
|
||||
func GetDriver(storageType shared.StorageType) (Driver, error) {
|
||||
switch storageType {
|
||||
case shared.StorageTypeFile:
|
||||
func GetDriver(storageType string) (Driver, error) {
|
||||
switch strings.TrimSpace(strings.ToLower(storageType)) {
|
||||
case "file":
|
||||
return new(file.FileDriver), nil
|
||||
case shared.StorageTypePostgres:
|
||||
case "postgres":
|
||||
return new(postgres.PostgresDriver), nil
|
||||
case shared.StorageTypeMongoDB:
|
||||
case "mongodb":
|
||||
return new(mongodb.MongoDBDriver), nil
|
||||
case shared.StorageTypeS3:
|
||||
case "s3":
|
||||
return new(s3.S3Driver), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid storage type '%s'", storageType)
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/lus/pasty/internal/config"
|
||||
"github.com/lus/pasty/internal/shared"
|
||||
"github.com/lus/pasty/internal/paste"
|
||||
)
|
||||
|
||||
// FileDriver represents the file storage driver
|
||||
@ -35,7 +35,7 @@ func (driver *FileDriver) ListIDs() ([]string, error) {
|
||||
var ids []string
|
||||
|
||||
// Fill the IDs slice
|
||||
err := filepath.Walk(driver.filePath, func(path string, info os.FileInfo, err error) error {
|
||||
err := filepath.Walk(driver.filePath, func(_ string, info os.FileInfo, err error) error {
|
||||
// Check if a walking error occurred
|
||||
if err != nil {
|
||||
return err
|
||||
@ -65,7 +65,7 @@ func (driver *FileDriver) ListIDs() ([]string, error) {
|
||||
}
|
||||
|
||||
// Get loads a paste
|
||||
func (driver *FileDriver) Get(id string) (*shared.Paste, error) {
|
||||
func (driver *FileDriver) Get(id string) (*paste.Paste, error) {
|
||||
// Read the file
|
||||
id = base64.StdEncoding.EncodeToString([]byte(id))
|
||||
data, err := ioutil.ReadFile(filepath.Join(driver.filePath, id+".json"))
|
||||
@ -77,7 +77,7 @@ func (driver *FileDriver) Get(id string) (*shared.Paste, error) {
|
||||
}
|
||||
|
||||
// Unmarshal the file into a paste
|
||||
paste := new(shared.Paste)
|
||||
paste := new(paste.Paste)
|
||||
err = json.Unmarshal(data, &paste)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -86,7 +86,7 @@ func (driver *FileDriver) Get(id string) (*shared.Paste, error) {
|
||||
}
|
||||
|
||||
// Save saves a paste
|
||||
func (driver *FileDriver) Save(paste *shared.Paste) error {
|
||||
func (driver *FileDriver) Save(paste *paste.Paste) error {
|
||||
// Marshal the paste
|
||||
jsonBytes, err := json.Marshal(paste)
|
||||
if err != nil {
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/lus/pasty/internal/config"
|
||||
"github.com/lus/pasty/internal/shared"
|
||||
"github.com/lus/pasty/internal/paste"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
@ -65,7 +65,7 @@ func (driver *MongoDBDriver) ListIDs() ([]string, error) {
|
||||
}
|
||||
|
||||
// Decode all paste documents
|
||||
var pasteSlice []shared.Paste
|
||||
var pasteSlice []paste.Paste
|
||||
err = result.All(ctx, &pasteSlice)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -80,7 +80,7 @@ func (driver *MongoDBDriver) ListIDs() ([]string, error) {
|
||||
}
|
||||
|
||||
// Get loads a paste
|
||||
func (driver *MongoDBDriver) Get(id string) (*shared.Paste, error) {
|
||||
func (driver *MongoDBDriver) Get(id string) (*paste.Paste, error) {
|
||||
// Define the collection to use for this database operation
|
||||
collection := driver.client.Database(driver.database).Collection(driver.collection)
|
||||
|
||||
@ -100,7 +100,7 @@ func (driver *MongoDBDriver) Get(id string) (*shared.Paste, error) {
|
||||
}
|
||||
|
||||
// Return the retrieved paste object
|
||||
paste := new(shared.Paste)
|
||||
paste := new(paste.Paste)
|
||||
err = result.Decode(paste)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -109,7 +109,7 @@ func (driver *MongoDBDriver) Get(id string) (*shared.Paste, error) {
|
||||
}
|
||||
|
||||
// Save saves a paste
|
||||
func (driver *MongoDBDriver) Save(paste *shared.Paste) error {
|
||||
func (driver *MongoDBDriver) Save(paste *paste.Paste) error {
|
||||
// Define the collection to use for this database operation
|
||||
collection := driver.client.Database(driver.database).Collection(driver.collection)
|
||||
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"github.com/johejo/golang-migrate-extra/source/iofs"
|
||||
"github.com/lus/pasty/internal/config"
|
||||
"github.com/lus/pasty/internal/shared"
|
||||
"github.com/lus/pasty/internal/paste"
|
||||
)
|
||||
|
||||
//go:embed migrations/*.sql
|
||||
@ -76,12 +76,12 @@ func (driver *PostgresDriver) ListIDs() ([]string, error) {
|
||||
}
|
||||
|
||||
// Get loads a paste
|
||||
func (driver *PostgresDriver) Get(id string) (*shared.Paste, error) {
|
||||
func (driver *PostgresDriver) Get(id string) (*paste.Paste, error) {
|
||||
query := "SELECT * FROM pastes WHERE id = $1"
|
||||
|
||||
row := driver.pool.QueryRow(context.Background(), query, id)
|
||||
|
||||
paste := new(shared.Paste)
|
||||
paste := new(paste.Paste)
|
||||
if err := row.Scan(&paste.ID, &paste.Content, &paste.ModificationToken, &paste.Created, &paste.Metadata); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, nil
|
||||
@ -92,7 +92,7 @@ func (driver *PostgresDriver) Get(id string) (*shared.Paste, error) {
|
||||
}
|
||||
|
||||
// Save saves a paste
|
||||
func (driver *PostgresDriver) Save(paste *shared.Paste) error {
|
||||
func (driver *PostgresDriver) Save(paste *paste.Paste) error {
|
||||
query := `
|
||||
INSERT INTO pastes (id, content, "modificationToken", created, metadata)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/lus/pasty/internal/config"
|
||||
"github.com/lus/pasty/internal/shared"
|
||||
"github.com/lus/pasty/internal/paste"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
)
|
||||
@ -59,7 +59,7 @@ func (driver *S3Driver) ListIDs() ([]string, error) {
|
||||
}
|
||||
|
||||
// Get loads a paste
|
||||
func (driver *S3Driver) Get(id string) (*shared.Paste, error) {
|
||||
func (driver *S3Driver) Get(id string) (*paste.Paste, error) {
|
||||
// Read the object
|
||||
object, err := driver.client.GetObject(context.Background(), driver.bucket, id+".json", minio.GetObjectOptions{})
|
||||
if err != nil {
|
||||
@ -74,7 +74,7 @@ func (driver *S3Driver) Get(id string) (*shared.Paste, error) {
|
||||
}
|
||||
|
||||
// Unmarshal the object into a paste
|
||||
paste := new(shared.Paste)
|
||||
paste := new(paste.Paste)
|
||||
err = json.Unmarshal(data, &paste)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -83,7 +83,7 @@ func (driver *S3Driver) Get(id string) (*shared.Paste, error) {
|
||||
}
|
||||
|
||||
// Save saves a paste
|
||||
func (driver *S3Driver) Save(paste *shared.Paste) error {
|
||||
func (driver *S3Driver) Save(paste *paste.Paste) error {
|
||||
// Marshal the paste
|
||||
jsonBytes, err := json.Marshal(paste)
|
||||
if err != nil {
|
||||
|
@ -1,72 +0,0 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/lus/pasty/internal/config"
|
||||
"github.com/lus/pasty/internal/shared"
|
||||
"github.com/lus/pasty/internal/storage"
|
||||
"github.com/lus/pasty/internal/utils"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// HastebinSupportHandler handles the legacy hastebin requests
|
||||
func HastebinSupportHandler(ctx *fasthttp.RequestCtx) {
|
||||
// Check content length before reading body into memory
|
||||
if config.Current.LengthCap > 0 &&
|
||||
ctx.Request.Header.ContentLength() > config.Current.LengthCap {
|
||||
ctx.SetStatusCode(fasthttp.StatusBadRequest)
|
||||
ctx.SetBodyString("request body length overflow")
|
||||
return
|
||||
}
|
||||
|
||||
// Define the paste content
|
||||
var content string
|
||||
if string(ctx.Request.Header.ContentType()) == "multipart/form-data" {
|
||||
content = string(ctx.FormValue("data"))
|
||||
} else {
|
||||
content = string(ctx.PostBody())
|
||||
}
|
||||
|
||||
// Acquire the paste ID
|
||||
id, err := storage.AcquireID()
|
||||
if err != nil {
|
||||
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||
ctx.SetBodyString(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Create the paste object
|
||||
paste := &shared.Paste{
|
||||
ID: id,
|
||||
Content: content,
|
||||
Created: time.Now().Unix(),
|
||||
}
|
||||
|
||||
// Set a modification token
|
||||
if config.Current.ModificationTokens {
|
||||
paste.ModificationToken = utils.RandomString(config.Current.ModificationTokenCharacters, config.Current.ModificationTokenLength)
|
||||
|
||||
err = paste.HashModificationToken()
|
||||
if err != nil {
|
||||
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||
ctx.SetBodyString(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Save the paste
|
||||
err = storage.Current.Save(paste)
|
||||
if err != nil {
|
||||
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||
ctx.SetBodyString(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Respond with the paste key
|
||||
jsonData, _ := json.Marshal(map[string]string{
|
||||
"key": paste.ID,
|
||||
})
|
||||
ctx.SetBody(jsonData)
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
package v1
|
||||
|
||||
import "github.com/lus/pasty/internal/shared"
|
||||
|
||||
type legacyPaste struct {
|
||||
ID string `json:"id"`
|
||||
Content string `json:"content"`
|
||||
DeletionToken string `json:"deletionToken,omitempty"`
|
||||
Created int64 `json:"created"`
|
||||
}
|
||||
|
||||
func legacyFromModern(paste *shared.Paste) *legacyPaste {
|
||||
deletionToken := paste.ModificationToken
|
||||
if deletionToken == "" {
|
||||
deletionToken = paste.DeletionToken
|
||||
}
|
||||
|
||||
return &legacyPaste{
|
||||
ID: paste.ID,
|
||||
Content: paste.Content,
|
||||
DeletionToken: deletionToken,
|
||||
Created: paste.Created,
|
||||
}
|
||||
}
|
@ -1,180 +0,0 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/fasthttp/router"
|
||||
"github.com/lus/pasty/internal/config"
|
||||
"github.com/lus/pasty/internal/shared"
|
||||
"github.com/lus/pasty/internal/storage"
|
||||
"github.com/lus/pasty/internal/utils"
|
||||
limitFasthttp "github.com/ulule/limiter/v3/drivers/middleware/fasthttp"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// InitializePastesController initializes the '/v1/pastes/*' controller
|
||||
func InitializePastesController(group *router.Group, rateLimiterMiddleware *limitFasthttp.Middleware) {
|
||||
group.GET("/{id}", rateLimiterMiddleware.Handle(v1GetPaste))
|
||||
group.POST("", rateLimiterMiddleware.Handle(v1PostPaste))
|
||||
group.DELETE("/{id}", rateLimiterMiddleware.Handle(v1DeletePaste))
|
||||
}
|
||||
|
||||
// v1GetPaste handles the 'GET /v1/pastes/{id}' endpoint
|
||||
func v1GetPaste(ctx *fasthttp.RequestCtx) {
|
||||
// Read the ID
|
||||
id := ctx.UserValue("id").(string)
|
||||
|
||||
// Retrieve the paste
|
||||
paste, err := storage.Current.Get(id)
|
||||
if err != nil {
|
||||
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||
ctx.SetBodyString(err.Error())
|
||||
return
|
||||
}
|
||||
if paste == nil {
|
||||
ctx.SetStatusCode(fasthttp.StatusNotFound)
|
||||
ctx.SetBodyString("paste not found")
|
||||
return
|
||||
}
|
||||
legacyPaste := legacyFromModern(paste)
|
||||
legacyPaste.DeletionToken = ""
|
||||
|
||||
// Respond with the paste
|
||||
jsonData, err := json.Marshal(legacyPaste)
|
||||
if err != nil {
|
||||
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||
ctx.SetBodyString(err.Error())
|
||||
return
|
||||
}
|
||||
ctx.SetBody(jsonData)
|
||||
}
|
||||
|
||||
// v1PostPaste handles the 'POST /v1/pastes' endpoint
|
||||
func v1PostPaste(ctx *fasthttp.RequestCtx) {
|
||||
// Check content length before reading body into memory
|
||||
if config.Current.LengthCap > 0 &&
|
||||
ctx.Request.Header.ContentLength() > config.Current.LengthCap {
|
||||
ctx.SetStatusCode(fasthttp.StatusBadRequest)
|
||||
ctx.SetBodyString("request body length overflow")
|
||||
return
|
||||
}
|
||||
|
||||
// Unmarshal the body
|
||||
values := make(map[string]string)
|
||||
err := json.Unmarshal(ctx.PostBody(), &values)
|
||||
if err != nil {
|
||||
ctx.SetStatusCode(fasthttp.StatusBadRequest)
|
||||
ctx.SetBodyString("invalid request body")
|
||||
return
|
||||
}
|
||||
|
||||
// Validate the content of the paste
|
||||
if values["content"] == "" {
|
||||
ctx.SetStatusCode(fasthttp.StatusBadRequest)
|
||||
ctx.SetBodyString("missing 'content' field")
|
||||
return
|
||||
}
|
||||
|
||||
// Acquire the paste ID
|
||||
id, err := storage.AcquireID()
|
||||
if err != nil {
|
||||
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||
ctx.SetBodyString(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Create the paste object
|
||||
paste := &shared.Paste{
|
||||
ID: id,
|
||||
Content: values["content"],
|
||||
Created: time.Now().Unix(),
|
||||
}
|
||||
|
||||
// Set a modification token
|
||||
modificationToken := ""
|
||||
if config.Current.ModificationTokens {
|
||||
modificationToken = utils.RandomString(config.Current.ModificationTokenCharacters, config.Current.ModificationTokenLength)
|
||||
paste.ModificationToken = modificationToken
|
||||
|
||||
err = paste.HashModificationToken()
|
||||
if err != nil {
|
||||
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||
ctx.SetBodyString(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Save the paste
|
||||
err = storage.Current.Save(paste)
|
||||
if err != nil {
|
||||
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||
ctx.SetBodyString(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Respond with the paste
|
||||
pasteCopy := legacyFromModern(paste)
|
||||
pasteCopy.DeletionToken = modificationToken
|
||||
jsonData, err := json.Marshal(pasteCopy)
|
||||
if err != nil {
|
||||
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||
ctx.SetBodyString(err.Error())
|
||||
return
|
||||
}
|
||||
ctx.SetBody(jsonData)
|
||||
}
|
||||
|
||||
// v1DeletePaste handles the 'DELETE /v1/pastes/{id}'
|
||||
func v1DeletePaste(ctx *fasthttp.RequestCtx) {
|
||||
// Read the ID
|
||||
id := ctx.UserValue("id").(string)
|
||||
|
||||
// Unmarshal the body
|
||||
values := make(map[string]string)
|
||||
err := json.Unmarshal(ctx.PostBody(), &values)
|
||||
if err != nil {
|
||||
ctx.SetStatusCode(fasthttp.StatusBadRequest)
|
||||
ctx.SetBodyString("invalid request body")
|
||||
return
|
||||
}
|
||||
|
||||
// Validate the modification token of the paste
|
||||
modificationToken := values["deletionToken"]
|
||||
if modificationToken == "" {
|
||||
ctx.SetStatusCode(fasthttp.StatusBadRequest)
|
||||
ctx.SetBodyString("missing 'deletionToken' field")
|
||||
return
|
||||
}
|
||||
|
||||
// Retrieve the paste
|
||||
paste, err := storage.Current.Get(id)
|
||||
if err != nil {
|
||||
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||
ctx.SetBodyString(err.Error())
|
||||
return
|
||||
}
|
||||
if paste == nil {
|
||||
ctx.SetStatusCode(fasthttp.StatusNotFound)
|
||||
ctx.SetBodyString("paste not found")
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the modification token is correct
|
||||
if (config.Current.ModificationTokenMaster == "" || modificationToken != config.Current.ModificationTokenMaster) && !paste.CheckModificationToken(modificationToken) {
|
||||
ctx.SetStatusCode(fasthttp.StatusForbidden)
|
||||
ctx.SetBodyString("invalid deletion token")
|
||||
return
|
||||
}
|
||||
|
||||
// Delete the paste
|
||||
err = storage.Current.Delete(paste.ID)
|
||||
if err != nil {
|
||||
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||
ctx.SetBodyString(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Respond with 'ok'
|
||||
ctx.SetBodyString("ok")
|
||||
}
|
@ -9,8 +9,7 @@ import (
|
||||
"github.com/lus/pasty/internal/config"
|
||||
"github.com/lus/pasty/internal/static"
|
||||
"github.com/lus/pasty/internal/storage"
|
||||
v1 "github.com/lus/pasty/internal/web/controllers/v1"
|
||||
v2 "github.com/lus/pasty/internal/web/controllers/v2"
|
||||
v2 "github.com/lus/pasty/internal/web/v2"
|
||||
"github.com/ulule/limiter/v3"
|
||||
limitFasthttp "github.com/ulule/limiter/v3/drivers/middleware/fasthttp"
|
||||
"github.com/ulule/limiter/v3/drivers/store/memory"
|
||||
@ -57,18 +56,6 @@ func Serve() error {
|
||||
// Route the API endpoints
|
||||
apiRoute := router.Group("/api")
|
||||
{
|
||||
v1Route := apiRoute.Group("/v1")
|
||||
{
|
||||
v1Route.GET("/info", func(ctx *fasthttp.RequestCtx) {
|
||||
jsonData, _ := json.Marshal(map[string]interface{}{
|
||||
"version": static.Version,
|
||||
"deletionTokens": config.Current.ModificationTokens,
|
||||
})
|
||||
ctx.SetBody(jsonData)
|
||||
})
|
||||
v1.InitializePastesController(v1Route.Group("/pastes"), rateLimiterMiddleware)
|
||||
}
|
||||
|
||||
v2Route := apiRoute.Group("/v2")
|
||||
{
|
||||
pasteLifetime := int64(-1)
|
||||
@ -90,7 +77,7 @@ func Serve() error {
|
||||
|
||||
// Route the hastebin documents route if hastebin support is enabled
|
||||
if config.Current.HastebinSupport {
|
||||
router.POST("/documents", rateLimiterMiddleware.Handle(v1.HastebinSupportHandler))
|
||||
// TODO: Reimplement hastebin support
|
||||
}
|
||||
|
||||
// Serve the web resources
|
@ -7,8 +7,8 @@ import (
|
||||
|
||||
"github.com/fasthttp/router"
|
||||
"github.com/lus/pasty/internal/config"
|
||||
"github.com/lus/pasty/internal/paste"
|
||||
"github.com/lus/pasty/internal/report"
|
||||
"github.com/lus/pasty/internal/shared"
|
||||
"github.com/lus/pasty/internal/storage"
|
||||
"github.com/lus/pasty/internal/utils"
|
||||
limitFasthttp "github.com/ulule/limiter/v3/drivers/middleware/fasthttp"
|
||||
@ -58,7 +58,7 @@ func middlewareInjectPaste(next fasthttp.RequestHandler) fasthttp.RequestHandler
|
||||
// middlewareValidateModificationToken extracts and validates a given modification token for an injected paste
|
||||
func middlewareValidateModificationToken(next fasthttp.RequestHandler) fasthttp.RequestHandler {
|
||||
return func(ctx *fasthttp.RequestCtx) {
|
||||
paste := ctx.UserValue("_paste").(*shared.Paste)
|
||||
paste := ctx.UserValue("_paste").(*paste.Paste)
|
||||
|
||||
authHeaderSplit := strings.SplitN(string(ctx.Request.Header.Peek("Authorization")), " ", 2)
|
||||
if len(authHeaderSplit) < 2 || authHeaderSplit[0] != "Bearer" {
|
||||
@ -85,8 +85,7 @@ func middlewareValidateModificationToken(next fasthttp.RequestHandler) fasthttp.
|
||||
|
||||
// endpointGetPaste handles the 'GET /v2/pastes/{id}' endpoint
|
||||
func endpointGetPaste(ctx *fasthttp.RequestCtx) {
|
||||
paste := ctx.UserValue("_paste").(*shared.Paste)
|
||||
paste.DeletionToken = ""
|
||||
paste := ctx.UserValue("_paste").(*paste.Paste)
|
||||
paste.ModificationToken = ""
|
||||
|
||||
jsonData, err := json.Marshal(paste)
|
||||
@ -135,7 +134,7 @@ func endpointCreatePaste(ctx *fasthttp.RequestCtx) {
|
||||
if payload.Metadata == nil {
|
||||
payload.Metadata = map[string]interface{}{}
|
||||
}
|
||||
paste := &shared.Paste{
|
||||
paste := &paste.Paste{
|
||||
ID: id,
|
||||
Content: payload.Content,
|
||||
Created: time.Now().Unix(),
|
||||
@ -203,7 +202,7 @@ func endpointModifyPaste(ctx *fasthttp.RequestCtx) {
|
||||
}
|
||||
|
||||
// Modify the paste itself
|
||||
paste := ctx.UserValue("_paste").(*shared.Paste)
|
||||
paste := ctx.UserValue("_paste").(*paste.Paste)
|
||||
if payload.Content != nil {
|
||||
paste.Content = *payload.Content
|
||||
}
|
||||
@ -227,7 +226,7 @@ func endpointModifyPaste(ctx *fasthttp.RequestCtx) {
|
||||
|
||||
// endpointDeletePaste handles the 'DELETE /v2/pastes/{id}' endpoint
|
||||
func endpointDeletePaste(ctx *fasthttp.RequestCtx) {
|
||||
paste := ctx.UserValue("_paste").(*shared.Paste)
|
||||
paste := ctx.UserValue("_paste").(*paste.Paste)
|
||||
if err := storage.Current.Delete(paste.ID); err != nil {
|
||||
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||
ctx.SetBodyString(err.Error())
|
||||
@ -254,7 +253,7 @@ func endpointReportPaste(ctx *fasthttp.RequestCtx) {
|
||||
}
|
||||
|
||||
request := &report.ReportRequest{
|
||||
Paste: ctx.UserValue("_paste").(*shared.Paste).ID,
|
||||
Paste: ctx.UserValue("_paste").(*paste.Paste).ID,
|
||||
Reason: payload.Reason,
|
||||
}
|
||||
response, err := report.SendReport(request)
|
Loading…
Reference in New Issue
Block a user