1
0
mirror of https://github.com/lus/pasty.git synced 2023-08-10 21:13:09 +03:00

Switch from snowflake to random string ID system

This commit is contained in:
Lukas SP
2020-08-24 20:22:53 +02:00
parent a1cd915759
commit 5585a26ab0
13 changed files with 97 additions and 65 deletions

View File

@@ -2,13 +2,10 @@ package pastes
import (
"github.com/Lukaesebrot/pasty/internal/env"
"math/rand"
"github.com/Lukaesebrot/pasty/internal/utils"
"strconv"
)
// deletionTokenContents represents the characters a deletion token may contain
const deletionTokenContents = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#+-.,"
// generateDeletionToken generates a new deletion token
func generateDeletionToken() (string, error) {
// Read the deletion token length
@@ -19,9 +16,5 @@ func generateDeletionToken() (string, error) {
}
// Generate the deletion token
bytes := make([]byte, length)
for i := range bytes {
bytes[i] = deletionTokenContents[rand.Int63()%int64(len(deletionTokenContents))]
}
return string(bytes), nil
return utils.RandomString(length), nil
}

View File

@@ -2,26 +2,18 @@ package pastes
import (
"github.com/alexedwards/argon2id"
"github.com/bwmarrin/snowflake"
)
func init() {
snowflakeNode, _ = snowflake.NewNode(1)
}
// snowflakeNode holds the current snowflake node
var snowflakeNode *snowflake.Node
// Paste represents a saved paste
type Paste struct {
ID snowflake.ID `json:"id" bson:"_id"`
Content string `json:"content" bson:"content"`
SuggestedSyntaxType string `json:"suggestedSyntaxType" bson:"suggestedSyntaxType"`
DeletionToken string `json:"deletionToken" bson:"deletionToken"`
ID string `json:"id" bson:"_id"`
Content string `json:"content" bson:"content"`
SuggestedSyntaxType string `json:"suggestedSyntaxType" bson:"suggestedSyntaxType"`
DeletionToken string `json:"deletionToken" bson:"deletionToken"`
}
// Create creates a new paste object using the given content
func Create(content string) (*Paste, error) {
func Create(id, content string) (*Paste, error) {
// TODO: Generate the suggested syntax type
suggestedSyntaxType := ""
@@ -33,7 +25,7 @@ func Create(content string) (*Paste, error) {
// Return the paste object
return &Paste{
ID: snowflakeNode.Generate(),
ID: id,
Content: content,
SuggestedSyntaxType: suggestedSyntaxType,
DeletionToken: deletionToken,

View File

@@ -4,7 +4,6 @@ import (
"fmt"
"github.com/Lukaesebrot/pasty/internal/env"
"github.com/Lukaesebrot/pasty/internal/pastes"
"github.com/bwmarrin/snowflake"
"strings"
)
@@ -15,9 +14,9 @@ var Current Driver
type Driver interface {
Initialize() error
Terminate() error
Get(id snowflake.ID) (*pastes.Paste, error)
Get(id string) (*pastes.Paste, error)
Save(paste *pastes.Paste) error
Delete(id snowflake.ID) error
Delete(id string) error
}
// Load loads the current storage driver

View File

@@ -4,7 +4,6 @@ import (
"encoding/json"
"github.com/Lukaesebrot/pasty/internal/env"
"github.com/Lukaesebrot/pasty/internal/pastes"
"github.com/bwmarrin/snowflake"
"io/ioutil"
"os"
"path/filepath"
@@ -27,9 +26,9 @@ func (driver *FileDriver) Terminate() error {
}
// Get loads a paste
func (driver *FileDriver) Get(id snowflake.ID) (*pastes.Paste, error) {
func (driver *FileDriver) Get(id string) (*pastes.Paste, error) {
// Read the file
data, err := ioutil.ReadFile(filepath.Join(driver.filePath, id.String()+".json"))
data, err := ioutil.ReadFile(filepath.Join(driver.filePath, id+".json"))
if err != nil {
if os.IsNotExist(err) {
return nil, nil
@@ -55,7 +54,7 @@ func (driver *FileDriver) Save(paste *pastes.Paste) error {
}
// Create the file to save the paste to
file, err := os.Create(filepath.Join(driver.filePath, paste.ID.String()+".json"))
file, err := os.Create(filepath.Join(driver.filePath, paste.ID+".json"))
if err != nil {
return err
}
@@ -67,6 +66,6 @@ func (driver *FileDriver) Save(paste *pastes.Paste) error {
}
// Delete deletes a paste
func (driver *FileDriver) Delete(id snowflake.ID) error {
return os.Remove(filepath.Join(driver.filePath, id.String()+".json"))
func (driver *FileDriver) Delete(id string) error {
return os.Remove(filepath.Join(driver.filePath, id+".json"))
}

View File

@@ -0,0 +1,29 @@
package storage
import (
"github.com/Lukaesebrot/pasty/internal/env"
"github.com/Lukaesebrot/pasty/internal/utils"
"strconv"
)
// AcquireID generates a new unique ID
func AcquireID() (string, error) {
// Read the ID length
rawLength := env.Get("ID_LENGTH", "6")
length, err := strconv.Atoi(rawLength)
if err != nil {
return "", err
}
// Generate the unique ID
for {
id := utils.RandomString(length)
paste, err := Current.Get(id)
if err != nil {
return "", err
}
if paste == nil {
return id, nil
}
}
}

View File

@@ -4,7 +4,6 @@ import (
"context"
"github.com/Lukaesebrot/pasty/internal/env"
"github.com/Lukaesebrot/pasty/internal/pastes"
"github.com/bwmarrin/snowflake"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
@@ -50,7 +49,7 @@ func (driver *MongoDBDriver) Terminate() error {
}
// Get loads a paste
func (driver *MongoDBDriver) Get(id snowflake.ID) (*pastes.Paste, error) {
func (driver *MongoDBDriver) Get(id string) (*pastes.Paste, error) {
// Define the collection to use for this database operation
collection := driver.client.Database(driver.database).Collection(driver.collection)
@@ -59,7 +58,7 @@ func (driver *MongoDBDriver) Get(id snowflake.ID) (*pastes.Paste, error) {
defer cancel()
// Try to retrieve the corresponding paste document
filter := bson.M{"_id": id.String()}
filter := bson.M{"_id": id}
result := collection.FindOne(ctx, filter)
err := result.Err()
if err != nil {
@@ -90,7 +89,7 @@ func (driver *MongoDBDriver) Save(paste *pastes.Paste) error {
}
// Delete deletes a paste
func (driver *MongoDBDriver) Delete(id snowflake.ID) error {
func (driver *MongoDBDriver) Delete(id string) error {
// Define the collection to use for this database operation
collection := driver.client.Database(driver.database).Collection(driver.collection)
@@ -99,7 +98,7 @@ func (driver *MongoDBDriver) Delete(id snowflake.ID) error {
defer cancel()
// Delete the document
filter := bson.M{"_id": id.String()}
filter := bson.M{"_id": id}
_, err := collection.DeleteOne(ctx, filter)
return err
}

View File

@@ -6,7 +6,6 @@ import (
"encoding/json"
"github.com/Lukaesebrot/pasty/internal/env"
"github.com/Lukaesebrot/pasty/internal/pastes"
"github.com/bwmarrin/snowflake"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"io/ioutil"
@@ -39,9 +38,9 @@ func (driver *S3Driver) Terminate() error {
}
// Get loads a paste
func (driver *S3Driver) Get(id snowflake.ID) (*pastes.Paste, error) {
func (driver *S3Driver) Get(id string) (*pastes.Paste, error) {
// Read the object
object, err := driver.client.GetObject(context.Background(), driver.bucket, id.String()+".json", minio.GetObjectOptions{})
object, err := driver.client.GetObject(context.Background(), driver.bucket, id+".json", minio.GetObjectOptions{})
if err != nil {
return nil, err
}
@@ -69,13 +68,13 @@ func (driver *S3Driver) Save(paste *pastes.Paste) error {
// Put the object
reader := bytes.NewReader(jsonBytes)
_, err = driver.client.PutObject(context.Background(), driver.bucket, paste.ID.String()+".json", reader, reader.Size(), minio.PutObjectOptions{
_, err = driver.client.PutObject(context.Background(), driver.bucket, paste.ID+".json", reader, reader.Size(), minio.PutObjectOptions{
ContentType: "application/json",
})
return err
}
// Delete deletes a paste
func (driver *S3Driver) Delete(id snowflake.ID) error {
return driver.client.RemoveObject(context.Background(), driver.bucket, id.String()+".json", minio.RemoveObjectOptions{})
func (driver *S3Driver) Delete(id string) error {
return driver.client.RemoveObject(context.Background(), driver.bucket, id+".json", minio.RemoveObjectOptions{})
}

View File

@@ -0,0 +1,15 @@
package utils
import "math/rand"
// stringContents holds the chars a random string can contain
const stringContents = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
// RandomString returns a random string with the given length
func RandomString(length int) string {
bytes := make([]byte, length)
for i := range bytes {
bytes[i] = stringContents[rand.Int63()%int64(len(stringContents))]
}
return string(bytes)
}

View File

@@ -24,8 +24,16 @@ func HastebinSupportHandler(ctx *fasthttp.RequestCtx) {
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, err := pastes.Create(content)
paste, err := pastes.Create(id, content)
if err != nil {
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
ctx.SetBodyString(err.Error())
@@ -50,7 +58,7 @@ func HastebinSupportHandler(ctx *fasthttp.RequestCtx) {
// Respond with the paste key
jsonData, _ := json.Marshal(map[string]string{
"key": paste.ID.String(),
"key": paste.ID,
})
ctx.SetBody(jsonData)
}

View File

@@ -4,7 +4,6 @@ import (
"encoding/json"
"github.com/Lukaesebrot/pasty/internal/pastes"
"github.com/Lukaesebrot/pasty/internal/storage"
"github.com/bwmarrin/snowflake"
"github.com/fasthttp/router"
limitFasthttp "github.com/ulule/limiter/v3/drivers/middleware/fasthttp"
"github.com/valyala/fasthttp"
@@ -19,13 +18,8 @@ func InitializePastesController(group *router.Group, rateLimiterMiddleware *limi
// v1GetPaste handles the 'GET /v1/pastes/{id}' endpoint
func v1GetPaste(ctx *fasthttp.RequestCtx) {
// Parse the ID
id, err := snowflake.ParseString(ctx.UserValue("id").(string))
if err != nil {
ctx.SetStatusCode(fasthttp.StatusBadRequest)
ctx.SetBodyString("invalid ID format")
return
}
// Read the ID
id := ctx.UserValue("id").(string)
// Retrieve the paste
paste, err := storage.Current.Get(id)
@@ -68,8 +62,16 @@ func v1PostPaste(ctx *fasthttp.RequestCtx) {
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, err := pastes.Create(values["content"])
paste, err := pastes.Create(id, values["content"])
if err != nil {
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
ctx.SetBodyString(err.Error())
@@ -105,17 +107,12 @@ func v1PostPaste(ctx *fasthttp.RequestCtx) {
// v1DeletePaste handles the 'DELETE /v1/pastes/{id}'
func v1DeletePaste(ctx *fasthttp.RequestCtx) {
// Parse the ID
id, err := snowflake.ParseString(ctx.UserValue("id").(string))
if err != nil {
ctx.SetStatusCode(fasthttp.StatusBadRequest)
ctx.SetBodyString("invalid ID format")
return
}
// Read the ID
id := ctx.UserValue("id").(string)
// Unmarshal the body
values := make(map[string]string)
err = json.Unmarshal(ctx.PostBody(), &values)
err := json.Unmarshal(ctx.PostBody(), &values)
if err != nil {
ctx.SetStatusCode(fasthttp.StatusBadRequest)
ctx.SetBodyString("invalid request body")