Make linter happy and update dependencies.
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is failing
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	continuous-integration/drone/push Build is failing
				
			This commit is contained in:
		@@ -15,6 +15,8 @@ linters:
 | 
			
		||||
    # This linter MIGHT BE good, but who decided that I want keepFor in
 | 
			
		||||
    # JSON instead of keep_for for KeepFor field?
 | 
			
		||||
    - tagliatelle
 | 
			
		||||
    # Deprecated.
 | 
			
		||||
    - exhaustivestruct
 | 
			
		||||
linters-settings:
 | 
			
		||||
  lll:
 | 
			
		||||
    line-length: 420
 | 
			
		||||
 
 | 
			
		||||
@@ -28,13 +28,13 @@ import (
 | 
			
		||||
	"go.dev.pztrn.name/fastpastebin/internal/context"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var c *context.Context
 | 
			
		||||
var ctx *context.Context
 | 
			
		||||
 | 
			
		||||
// New initializes pastes package and adds necessary HTTP and API
 | 
			
		||||
// endpoints.
 | 
			
		||||
func New(cc *context.Context) {
 | 
			
		||||
	c = cc
 | 
			
		||||
	ctx = cc
 | 
			
		||||
 | 
			
		||||
	c.Echo.GET("/database_not_available", dbNotAvailableGet)
 | 
			
		||||
	c.Echo.GET("/database_not_available/raw", dbNotAvailableRawGet)
 | 
			
		||||
	ctx.Echo.GET("/database_not_available", dbNotAvailableGet)
 | 
			
		||||
	ctx.Echo.GET("/database_not_available/raw", dbNotAvailableRawGet)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -28,12 +28,12 @@ import (
 | 
			
		||||
	"go.dev.pztrn.name/fastpastebin/internal/context"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var c *context.Context
 | 
			
		||||
var ctx *context.Context
 | 
			
		||||
 | 
			
		||||
// New initializes pastes package and adds necessary HTTP and API
 | 
			
		||||
// endpoints.
 | 
			
		||||
func New(cc *context.Context) {
 | 
			
		||||
	c = cc
 | 
			
		||||
	ctx = cc
 | 
			
		||||
 | 
			
		||||
	c.Echo.GET("/", indexGet)
 | 
			
		||||
	ctx.Echo.GET("/", indexGet)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -37,8 +37,8 @@ import (
 | 
			
		||||
// Index of this site.
 | 
			
		||||
func indexGet(ectx echo.Context) error {
 | 
			
		||||
	// We should check if database connection available.
 | 
			
		||||
	dbConn := c.Database.GetDatabaseConnection()
 | 
			
		||||
	if c.Config.Database.Type != flatfiles.FlatFileDialect && dbConn == nil {
 | 
			
		||||
	dbConn := ctx.Database.GetDatabaseConnection()
 | 
			
		||||
	if ctx.Config.Database.Type != flatfiles.FlatFileDialect && dbConn == nil {
 | 
			
		||||
		// nolint:wrapcheck
 | 
			
		||||
		return ectx.Redirect(http.StatusFound, "/database_not_available")
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -32,30 +32,30 @@ import (
 | 
			
		||||
 | 
			
		||||
var regexInts = regexp.MustCompile("[0-9]+")
 | 
			
		||||
 | 
			
		||||
var c *context.Context
 | 
			
		||||
var ctx *context.Context
 | 
			
		||||
 | 
			
		||||
// New initializes pastes package and adds necessary HTTP and API
 | 
			
		||||
// endpoints.
 | 
			
		||||
func New(cc *context.Context) {
 | 
			
		||||
	c = cc
 | 
			
		||||
	ctx = cc
 | 
			
		||||
 | 
			
		||||
	////////////////////////////////////////////////////////////
 | 
			
		||||
	// HTTP endpoints.
 | 
			
		||||
	////////////////////////////////////////////////////////////
 | 
			
		||||
	// New paste.
 | 
			
		||||
	c.Echo.POST("/paste/", pastePOSTWebInterface)
 | 
			
		||||
	ctx.Echo.POST("/paste/", pastePOSTWebInterface)
 | 
			
		||||
	// Show public paste.
 | 
			
		||||
	c.Echo.GET("/paste/:id", pasteGETWebInterface)
 | 
			
		||||
	ctx.Echo.GET("/paste/:id", pasteGETWebInterface)
 | 
			
		||||
	// Show RAW representation of public paste.
 | 
			
		||||
	c.Echo.GET("/paste/:id/raw", pasteRawGETWebInterface)
 | 
			
		||||
	ctx.Echo.GET("/paste/:id/raw", pasteRawGETWebInterface)
 | 
			
		||||
	// Show private paste.
 | 
			
		||||
	c.Echo.GET("/paste/:id/:timestamp", pasteGETWebInterface)
 | 
			
		||||
	ctx.Echo.GET("/paste/:id/:timestamp", pasteGETWebInterface)
 | 
			
		||||
	// Show RAW representation of private paste.
 | 
			
		||||
	c.Echo.GET("/paste/:id/:timestamp/raw", pasteRawGETWebInterface)
 | 
			
		||||
	ctx.Echo.GET("/paste/:id/:timestamp/raw", pasteRawGETWebInterface)
 | 
			
		||||
	// Verify access to passworded paste.
 | 
			
		||||
	c.Echo.GET("/paste/:id/:timestamp/verify", pastePasswordedVerifyGet)
 | 
			
		||||
	c.Echo.POST("/paste/:id/:timestamp/verify", pastePasswordedVerifyPost)
 | 
			
		||||
	ctx.Echo.GET("/paste/:id/:timestamp/verify", pastePasswordedVerifyGet)
 | 
			
		||||
	ctx.Echo.POST("/paste/:id/:timestamp/verify", pastePasswordedVerifyPost)
 | 
			
		||||
	// Pastes list.
 | 
			
		||||
	c.Echo.GET("/pastes/", pastesGET)
 | 
			
		||||
	c.Echo.GET("/pastes/:page", pastesGET)
 | 
			
		||||
	ctx.Echo.GET("/pastes/", pastesGET)
 | 
			
		||||
	ctx.Echo.GET("/pastes/:page", pastesGET)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -31,16 +31,16 @@ const (
 | 
			
		||||
// value (they both will be ignored), but private will.
 | 
			
		||||
func pasteGetData(pasteID int, timestamp int64, cookieValue string) (*structs.Paste, string) {
 | 
			
		||||
	// Get paste.
 | 
			
		||||
	paste, err1 := c.Database.GetPaste(pasteID)
 | 
			
		||||
	paste, err1 := ctx.Database.GetPaste(pasteID)
 | 
			
		||||
	if err1 != nil {
 | 
			
		||||
		c.Logger.Error().Err(err1).Int("paste ID", pasteID).Msg("Failed to get paste")
 | 
			
		||||
		ctx.Logger.Error().Err(err1).Int("paste ID", pasteID).Msg("Failed to get paste")
 | 
			
		||||
 | 
			
		||||
		return nil, pasteNotFound
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check if paste is expired.
 | 
			
		||||
	if paste.IsExpired() {
 | 
			
		||||
		c.Logger.Error().Int("paste ID", pasteID).Msg("Paste is expired")
 | 
			
		||||
		ctx.Logger.Error().Int("paste ID", pasteID).Msg("Paste is expired")
 | 
			
		||||
 | 
			
		||||
		return nil, pasteExpired
 | 
			
		||||
	}
 | 
			
		||||
@@ -49,7 +49,7 @@ func pasteGetData(pasteID int, timestamp int64, cookieValue string) (*structs.Pa
 | 
			
		||||
	if paste.Private {
 | 
			
		||||
		pasteTS := paste.CreatedAt.Unix()
 | 
			
		||||
		if timestamp != pasteTS {
 | 
			
		||||
			c.Logger.Error().Int("paste ID", pasteID).Int64("paste timestamp", pasteTS).Int64("provided timestamp", timestamp).Msg("Incorrect timestamp provided for private paste")
 | 
			
		||||
			ctx.Logger.Error().Int("paste ID", pasteID).Int64("paste timestamp", pasteTS).Int64("provided timestamp", timestamp).Msg("Incorrect timestamp provided for private paste")
 | 
			
		||||
 | 
			
		||||
			return nil, pasteTimestampInvalid
 | 
			
		||||
		}
 | 
			
		||||
@@ -78,7 +78,7 @@ func pasteGETWebInterface(ectx echo.Context) error {
 | 
			
		||||
	// error.
 | 
			
		||||
	pasteID, _ := strconv.Atoi(regexInts.FindAllString(pasteIDRaw, 1)[0])
 | 
			
		||||
	pasteIDStr := strconv.Itoa(pasteID)
 | 
			
		||||
	c.Logger.Debug().Int("paste ID", pasteID).Msg("Trying to get paste data")
 | 
			
		||||
	ctx.Logger.Debug().Int("paste ID", pasteID).Msg("Trying to get paste data")
 | 
			
		||||
 | 
			
		||||
	// Check if we have timestamp passed.
 | 
			
		||||
	// If passed timestamp is invalid (isn't a real UNIX timestamp) we
 | 
			
		||||
@@ -89,7 +89,7 @@ func pasteGETWebInterface(ectx echo.Context) error {
 | 
			
		||||
	if tsProvidedStr != "" {
 | 
			
		||||
		tsProvided, err := strconv.ParseInt(tsProvidedStr, 10, 64)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			c.Logger.Error().Err(err).Int("paste ID", pasteID).Int64("provided timestamp", tsProvided).Msg("Invalid timestamp provided for getting private paste")
 | 
			
		||||
			ctx.Logger.Error().Err(err).Int("paste ID", pasteID).Int64("provided timestamp", tsProvided).Msg("Invalid timestamp provided for getting private paste")
 | 
			
		||||
 | 
			
		||||
			errtpl := templater.GetErrorTemplate(ectx, "Paste #"+pasteIDStr+" not found")
 | 
			
		||||
 | 
			
		||||
@@ -122,7 +122,7 @@ func pasteGETWebInterface(ectx echo.Context) error {
 | 
			
		||||
	// If passed cookie value was invalid - go to paste authorization
 | 
			
		||||
	// page.
 | 
			
		||||
	if err == pasteCookieInvalid {
 | 
			
		||||
		c.Logger.Info().Int("paste ID", pasteID).Msg("Invalid cookie, redirecting to auth page")
 | 
			
		||||
		ctx.Logger.Info().Int("paste ID", pasteID).Msg("Invalid cookie, redirecting to auth page")
 | 
			
		||||
 | 
			
		||||
		// nolint:wrapcheck
 | 
			
		||||
		return ectx.Redirect(http.StatusMovedPermanently, "/paste/"+pasteIDStr+"/"+ectx.Param("timestamp")+"/verify")
 | 
			
		||||
@@ -159,7 +159,7 @@ func pasteGETWebInterface(ectx echo.Context) error {
 | 
			
		||||
	// Tokenize paste data.
 | 
			
		||||
	lexered, err3 := lexer.Tokenise(nil, paste.Data)
 | 
			
		||||
	if err3 != nil {
 | 
			
		||||
		c.Logger.Error().Err(err3).Msg("Failed to tokenize paste data")
 | 
			
		||||
		ctx.Logger.Error().Err(err3).Msg("Failed to tokenize paste data")
 | 
			
		||||
	}
 | 
			
		||||
	// Get style for HTML output.
 | 
			
		||||
	style := styles.Get("monokai")
 | 
			
		||||
@@ -174,7 +174,7 @@ func pasteGETWebInterface(ectx echo.Context) error {
 | 
			
		||||
 | 
			
		||||
	err4 := formatter.Format(buf, style, lexered)
 | 
			
		||||
	if err4 != nil {
 | 
			
		||||
		c.Logger.Error().Err(err4).Msg("Failed to format paste data")
 | 
			
		||||
		ctx.Logger.Error().Err(err4).Msg("Failed to format paste data")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pasteData["pastedata"] = buf.String()
 | 
			
		||||
@@ -195,9 +195,9 @@ func pastePasswordedVerifyGet(ectx echo.Context) error {
 | 
			
		||||
	pasteID, _ := strconv.Atoi(regexInts.FindAllString(pasteIDRaw, 1)[0])
 | 
			
		||||
 | 
			
		||||
	// Get paste.
 | 
			
		||||
	paste, err1 := c.Database.GetPaste(pasteID)
 | 
			
		||||
	paste, err1 := ctx.Database.GetPaste(pasteID)
 | 
			
		||||
	if err1 != nil {
 | 
			
		||||
		c.Logger.Error().Err(err1).Int("paste ID", pasteID).Msg("Failed to get paste data")
 | 
			
		||||
		ctx.Logger.Error().Err(err1).Int("paste ID", pasteID).Msg("Failed to get paste data")
 | 
			
		||||
 | 
			
		||||
		errtpl := templater.GetErrorTemplate(ectx, "Paste #"+pasteIDRaw+" not found")
 | 
			
		||||
 | 
			
		||||
@@ -209,19 +209,19 @@ func pastePasswordedVerifyGet(ectx echo.Context) error {
 | 
			
		||||
	cookie, err := ectx.Cookie("PASTE-" + strconv.Itoa(pasteID))
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		// No cookie, redirect to auth page.
 | 
			
		||||
		c.Logger.Debug().Msg("Paste cookie found, checking it...")
 | 
			
		||||
		ctx.Logger.Debug().Msg("Paste cookie found, checking it...")
 | 
			
		||||
 | 
			
		||||
		// Generate cookie value to check.
 | 
			
		||||
		cookieValue := paste.GenerateCryptedCookieValue()
 | 
			
		||||
 | 
			
		||||
		if cookieValue == cookie.Value {
 | 
			
		||||
			c.Logger.Info().Msg("Valid cookie, redirecting to paste page...")
 | 
			
		||||
			ctx.Logger.Info().Msg("Valid cookie, redirecting to paste page...")
 | 
			
		||||
 | 
			
		||||
			// nolint:wrapcheck
 | 
			
		||||
			return ectx.Redirect(http.StatusMovedPermanently, "/paste/"+pasteIDRaw+"/"+ectx.Param("timestamp"))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		c.Logger.Debug().Msg("Invalid cookie, showing auth page")
 | 
			
		||||
		ctx.Logger.Debug().Msg("Invalid cookie, showing auth page")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// HTML data.
 | 
			
		||||
@@ -238,9 +238,9 @@ func pastePasswordedVerifyGet(ectx echo.Context) error {
 | 
			
		||||
// POST for "/paste/PASTE_ID/TIMESTAMP/verify" - a password verify page.
 | 
			
		||||
func pastePasswordedVerifyPost(ectx echo.Context) error {
 | 
			
		||||
	// We should check if database connection available.
 | 
			
		||||
	dbConn := c.Database.GetDatabaseConnection()
 | 
			
		||||
	dbConn := ctx.Database.GetDatabaseConnection()
 | 
			
		||||
 | 
			
		||||
	if c.Config.Database.Type != flatfiles.FlatFileDialect && dbConn == nil {
 | 
			
		||||
	if ctx.Config.Database.Type != flatfiles.FlatFileDialect && dbConn == nil {
 | 
			
		||||
		// nolint:wrapcheck
 | 
			
		||||
		return ectx.Redirect(http.StatusFound, "/database_not_available")
 | 
			
		||||
	}
 | 
			
		||||
@@ -250,12 +250,12 @@ func pastePasswordedVerifyPost(ectx echo.Context) error {
 | 
			
		||||
	// We already get numbers from string, so we will not check strconv.Atoi()
 | 
			
		||||
	// error.
 | 
			
		||||
	pasteID, _ := strconv.Atoi(regexInts.FindAllString(pasteIDRaw, 1)[0])
 | 
			
		||||
	c.Logger.Debug().Int("paste ID", pasteID).Msg("Requesting paste")
 | 
			
		||||
	ctx.Logger.Debug().Int("paste ID", pasteID).Msg("Requesting paste")
 | 
			
		||||
 | 
			
		||||
	// Get paste.
 | 
			
		||||
	paste, err1 := c.Database.GetPaste(pasteID)
 | 
			
		||||
	paste, err1 := ctx.Database.GetPaste(pasteID)
 | 
			
		||||
	if err1 != nil {
 | 
			
		||||
		c.Logger.Error().Err(err1).Int("paste ID", pasteID).Msg("Failed to get paste")
 | 
			
		||||
		ctx.Logger.Error().Err(err1).Int("paste ID", pasteID).Msg("Failed to get paste")
 | 
			
		||||
		errtpl := templater.GetErrorTemplate(ectx, "Paste #"+strconv.Itoa(pasteID)+" not found")
 | 
			
		||||
 | 
			
		||||
		// nolint:wrapcheck
 | 
			
		||||
@@ -264,7 +264,7 @@ func pastePasswordedVerifyPost(ectx echo.Context) error {
 | 
			
		||||
 | 
			
		||||
	params, err2 := ectx.FormParams()
 | 
			
		||||
	if err2 != nil {
 | 
			
		||||
		c.Logger.Debug().Msg("No form parameters passed")
 | 
			
		||||
		ctx.Logger.Debug().Msg("No form parameters passed")
 | 
			
		||||
 | 
			
		||||
		errtpl := templater.GetErrorTemplate(ectx, "Paste #"+strconv.Itoa(pasteID)+" not found")
 | 
			
		||||
 | 
			
		||||
@@ -295,8 +295,8 @@ func pastePasswordedVerifyPost(ectx echo.Context) error {
 | 
			
		||||
// Web interface version.
 | 
			
		||||
func pasteRawGETWebInterface(ectx echo.Context) error {
 | 
			
		||||
	// We should check if database connection available.
 | 
			
		||||
	dbConn := c.Database.GetDatabaseConnection()
 | 
			
		||||
	if c.Config.Database.Type != flatfiles.FlatFileDialect && dbConn == nil {
 | 
			
		||||
	dbConn := ctx.Database.GetDatabaseConnection()
 | 
			
		||||
	if ctx.Config.Database.Type != flatfiles.FlatFileDialect && dbConn == nil {
 | 
			
		||||
		// nolint:wrapcheck
 | 
			
		||||
		return ectx.Redirect(http.StatusFound, "/database_not_available/raw")
 | 
			
		||||
	}
 | 
			
		||||
@@ -305,19 +305,19 @@ func pasteRawGETWebInterface(ectx echo.Context) error {
 | 
			
		||||
	// We already get numbers from string, so we will not check strconv.Atoi()
 | 
			
		||||
	// error.
 | 
			
		||||
	pasteID, _ := strconv.Atoi(regexInts.FindAllString(pasteIDRaw, 1)[0])
 | 
			
		||||
	c.Logger.Debug().Int("paste ID", pasteID).Msg("Requesting paste data")
 | 
			
		||||
	ctx.Logger.Debug().Int("paste ID", pasteID).Msg("Requesting paste data")
 | 
			
		||||
 | 
			
		||||
	// Get paste.
 | 
			
		||||
	paste, err1 := c.Database.GetPaste(pasteID)
 | 
			
		||||
	paste, err1 := ctx.Database.GetPaste(pasteID)
 | 
			
		||||
	if err1 != nil {
 | 
			
		||||
		c.Logger.Error().Err(err1).Int("paste ID", pasteID).Msg("Failed to get paste from database")
 | 
			
		||||
		ctx.Logger.Error().Err(err1).Int("paste ID", pasteID).Msg("Failed to get paste from database")
 | 
			
		||||
 | 
			
		||||
		// nolint:wrapcheck
 | 
			
		||||
		return ectx.HTML(http.StatusBadRequest, "Paste #"+pasteIDRaw+" does not exist.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if paste.IsExpired() {
 | 
			
		||||
		c.Logger.Error().Int("paste ID", pasteID).Msg("Paste is expired")
 | 
			
		||||
		ctx.Logger.Error().Int("paste ID", pasteID).Msg("Paste is expired")
 | 
			
		||||
 | 
			
		||||
		// nolint:wrapcheck
 | 
			
		||||
		return ectx.HTML(http.StatusBadRequest, "Paste #"+pasteIDRaw+" does not exist.")
 | 
			
		||||
@@ -329,7 +329,7 @@ func pasteRawGETWebInterface(ectx echo.Context) error {
 | 
			
		||||
 | 
			
		||||
		tsProvided, err2 := strconv.ParseInt(tsProvidedStr, 10, 64)
 | 
			
		||||
		if err2 != nil {
 | 
			
		||||
			c.Logger.Error().Err(err2).Int("paste ID", pasteID).Str("provided timestamp", tsProvidedStr).Msg("Invalid timestamp provided for getting private paste")
 | 
			
		||||
			ctx.Logger.Error().Err(err2).Int("paste ID", pasteID).Str("provided timestamp", tsProvidedStr).Msg("Invalid timestamp provided for getting private paste")
 | 
			
		||||
 | 
			
		||||
			// nolint:wrapcheck
 | 
			
		||||
			return ectx.String(http.StatusBadRequest, "Paste #"+pasteIDRaw+" not found")
 | 
			
		||||
@@ -337,7 +337,7 @@ func pasteRawGETWebInterface(ectx echo.Context) error {
 | 
			
		||||
 | 
			
		||||
		pasteTS := paste.CreatedAt.Unix()
 | 
			
		||||
		if tsProvided != pasteTS {
 | 
			
		||||
			c.Logger.Error().Int("paste ID", pasteID).Int64("provided timestamp", tsProvided).Int64("paste timestamp", pasteTS).Msg("Incorrect timestamp provided for private paste")
 | 
			
		||||
			ctx.Logger.Error().Int("paste ID", pasteID).Int64("provided timestamp", tsProvided).Int64("paste timestamp", pasteTS).Msg("Incorrect timestamp provided for private paste")
 | 
			
		||||
 | 
			
		||||
			// nolint:wrapcheck
 | 
			
		||||
			return ectx.String(http.StatusBadRequest, "Paste #"+pasteIDRaw+" not found")
 | 
			
		||||
@@ -348,7 +348,7 @@ func pasteRawGETWebInterface(ectx echo.Context) error {
 | 
			
		||||
	// ToDo: figure out how to handle passworded pastes here.
 | 
			
		||||
	// Return error for now.
 | 
			
		||||
	if paste.Password != "" {
 | 
			
		||||
		c.Logger.Error().Int("paste ID", pasteID).Msg("Cannot render paste as raw: passworded paste. Patches welcome!")
 | 
			
		||||
		ctx.Logger.Error().Int("paste ID", pasteID).Msg("Cannot render paste as raw: passworded paste. Patches welcome!")
 | 
			
		||||
		return ectx.String(http.StatusBadRequest, "Paste #"+pasteIDRaw+" not found")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,15 +22,15 @@ const KeepPastesForever = "forever"
 | 
			
		||||
// requests comes from browsers via web interface.
 | 
			
		||||
func pastePOSTWebInterface(ectx echo.Context) error {
 | 
			
		||||
	// We should check if database connection available.
 | 
			
		||||
	dbConn := c.Database.GetDatabaseConnection()
 | 
			
		||||
	if c.Config.Database.Type != flatfiles.FlatFileDialect && dbConn == nil {
 | 
			
		||||
	dbConn := ctx.Database.GetDatabaseConnection()
 | 
			
		||||
	if ctx.Config.Database.Type != flatfiles.FlatFileDialect && dbConn == nil {
 | 
			
		||||
		// nolint:wrapcheck
 | 
			
		||||
		return ectx.Redirect(http.StatusFound, "/database_not_available")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	params, err := ectx.FormParams()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.Logger.Error().Msg("Passed paste form is empty")
 | 
			
		||||
		ctx.Logger.Error().Msg("Passed paste form is empty")
 | 
			
		||||
 | 
			
		||||
		errtpl := templater.GetErrorTemplate(ectx, "Cannot create empty paste")
 | 
			
		||||
 | 
			
		||||
@@ -38,11 +38,11 @@ func pastePOSTWebInterface(ectx echo.Context) error {
 | 
			
		||||
		return ectx.HTML(http.StatusBadRequest, errtpl)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.Logger.Debug().Msgf("Received parameters: %+v", params)
 | 
			
		||||
	ctx.Logger.Debug().Msgf("Received parameters: %+v", params)
 | 
			
		||||
 | 
			
		||||
	// Do nothing if paste contents is empty.
 | 
			
		||||
	if len(params["paste-contents"][0]) == 0 {
 | 
			
		||||
		c.Logger.Debug().Msg("Empty paste submitted, ignoring")
 | 
			
		||||
		ctx.Logger.Debug().Msg("Empty paste submitted, ignoring")
 | 
			
		||||
 | 
			
		||||
		errtpl := templater.GetErrorTemplate(ectx, "Empty pastes aren't allowed.")
 | 
			
		||||
 | 
			
		||||
@@ -51,7 +51,7 @@ func pastePOSTWebInterface(ectx echo.Context) error {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !strings.ContainsAny(params["paste-keep-for"][0], "Mmhd") && params["paste-keep-for"][0] != KeepPastesForever {
 | 
			
		||||
		c.Logger.Debug().Str("field value", params["paste-keep-for"][0]).Msg("'Keep paste for' field have invalid value")
 | 
			
		||||
		ctx.Logger.Debug().Str("field value", params["paste-keep-for"][0]).Msg("'Keep paste for' field have invalid value")
 | 
			
		||||
 | 
			
		||||
		errtpl := templater.GetErrorTemplate(ectx, "Invalid 'Paste should be available for' parameter passed. Please do not try to hack us ;).")
 | 
			
		||||
 | 
			
		||||
@@ -61,7 +61,7 @@ func pastePOSTWebInterface(ectx echo.Context) error {
 | 
			
		||||
 | 
			
		||||
	// Verify captcha.
 | 
			
		||||
	if !captcha.Verify(params["paste-captcha-id"][0], params["paste-captcha-solution"][0]) {
 | 
			
		||||
		c.Logger.Debug().Str("captcha ID", params["paste-captcha-id"][0]).Str("captcha solution", params["paste-captcha-solution"][0]).Msg("Invalid captcha solution")
 | 
			
		||||
		ctx.Logger.Debug().Str("captcha ID", params["paste-captcha-id"][0]).Str("captcha solution", params["paste-captcha-solution"][0]).Msg("Invalid captcha solution")
 | 
			
		||||
 | 
			
		||||
		errtpl := templater.GetErrorTemplate(ectx, "Invalid captcha solution.")
 | 
			
		||||
 | 
			
		||||
@@ -69,7 +69,7 @@ func pastePOSTWebInterface(ectx echo.Context) error {
 | 
			
		||||
		return ectx.HTML(http.StatusBadRequest, errtpl)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// nolint:exhaustivestruct
 | 
			
		||||
	// nolint:exhaustruct
 | 
			
		||||
	paste := &structs.Paste{
 | 
			
		||||
		Title:    params["paste-title"][0],
 | 
			
		||||
		Data:     params["paste-contents"][0],
 | 
			
		||||
@@ -95,11 +95,11 @@ func pastePOSTWebInterface(ectx echo.Context) error {
 | 
			
		||||
		keepFor, err = strconv.Atoi(keepForRaw)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if params["paste-keep-for"][0] == KeepPastesForever {
 | 
			
		||||
				c.Logger.Debug().Msg("Keeping paste forever!")
 | 
			
		||||
				ctx.Logger.Debug().Msg("Keeping paste forever!")
 | 
			
		||||
 | 
			
		||||
				keepFor = 0
 | 
			
		||||
			} else {
 | 
			
		||||
				c.Logger.Debug().Err(err).Msg("Failed to parse 'Keep for' integer")
 | 
			
		||||
				ctx.Logger.Debug().Err(err).Msg("Failed to parse 'Keep for' integer")
 | 
			
		||||
 | 
			
		||||
				errtpl := templater.GetErrorTemplate(ectx, "Invalid 'Paste should be available for' parameter passed. Please do not try to hack us ;).")
 | 
			
		||||
 | 
			
		||||
@@ -138,9 +138,9 @@ func pastePOSTWebInterface(ectx echo.Context) error {
 | 
			
		||||
		_ = paste.CreatePassword(pastePassword[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pasteID, err2 := c.Database.SavePaste(paste)
 | 
			
		||||
	pasteID, err2 := ctx.Database.SavePaste(paste)
 | 
			
		||||
	if err2 != nil {
 | 
			
		||||
		c.Logger.Error().Err(err2).Msg("Failed to save paste")
 | 
			
		||||
		ctx.Logger.Error().Err(err2).Msg("Failed to save paste")
 | 
			
		||||
 | 
			
		||||
		errtpl := templater.GetErrorTemplate(ectx, "Failed to save paste. Please, try again later.")
 | 
			
		||||
 | 
			
		||||
@@ -149,7 +149,7 @@ func pastePOSTWebInterface(ectx echo.Context) error {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	newPasteIDAsString := strconv.FormatInt(pasteID, 10)
 | 
			
		||||
	c.Logger.Debug().Msg("Paste saved, URL: /paste/" + newPasteIDAsString)
 | 
			
		||||
	ctx.Logger.Debug().Msg("Paste saved, URL: /paste/" + newPasteIDAsString)
 | 
			
		||||
 | 
			
		||||
	// Private pastes have it's timestamp in URL.
 | 
			
		||||
	if paste.Private {
 | 
			
		||||
 
 | 
			
		||||
@@ -39,8 +39,8 @@ import (
 | 
			
		||||
// Web interface version.
 | 
			
		||||
func pastesGET(ectx echo.Context) error {
 | 
			
		||||
	// We should check if database connection available.
 | 
			
		||||
	dbConn := c.Database.GetDatabaseConnection()
 | 
			
		||||
	if c.Config.Database.Type != flatfiles.FlatFileDialect && dbConn == nil {
 | 
			
		||||
	dbConn := ctx.Database.GetDatabaseConnection()
 | 
			
		||||
	if ctx.Config.Database.Type != flatfiles.FlatFileDialect && dbConn == nil {
 | 
			
		||||
		// nolint:wrapcheck
 | 
			
		||||
		return ectx.Redirect(http.StatusFound, "/database_not_available")
 | 
			
		||||
	}
 | 
			
		||||
@@ -54,17 +54,17 @@ func pastesGET(ectx echo.Context) error {
 | 
			
		||||
		page, _ = strconv.Atoi(pageRaw)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.Logger.Debug().Int("page", page).Msg("Requested page")
 | 
			
		||||
	ctx.Logger.Debug().Int("page", page).Msg("Requested page")
 | 
			
		||||
 | 
			
		||||
	// Get pastes IDs.
 | 
			
		||||
	pastes, err3 := c.Database.GetPagedPastes(page)
 | 
			
		||||
	c.Logger.Debug().Int("count", len(pastes)).Msg("Got pastes")
 | 
			
		||||
	pastes, err3 := ctx.Database.GetPagedPastes(page)
 | 
			
		||||
	ctx.Logger.Debug().Int("count", len(pastes)).Msg("Got pastes")
 | 
			
		||||
 | 
			
		||||
	pastesString := "No pastes to show."
 | 
			
		||||
 | 
			
		||||
	// Show "No pastes to show" on any error for now.
 | 
			
		||||
	if err3 != nil {
 | 
			
		||||
		c.Logger.Error().Err(err3).Msg("Failed to get pastes list from database")
 | 
			
		||||
		ctx.Logger.Error().Err(err3).Msg("Failed to get pastes list from database")
 | 
			
		||||
 | 
			
		||||
		noPastesToShowTpl := templater.GetErrorTemplate(ectx, "No pastes to show.")
 | 
			
		||||
 | 
			
		||||
@@ -100,8 +100,8 @@ func pastesGET(ectx echo.Context) error {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Pagination.
 | 
			
		||||
	pages := c.Database.GetPastesPages()
 | 
			
		||||
	c.Logger.Debug().Int("total pages", pages).Int("current page", page).Msg("Paging data")
 | 
			
		||||
	pages := ctx.Database.GetPastesPages()
 | 
			
		||||
	ctx.Logger.Debug().Int("total pages", pages).Int("current page", page).Msg("Paging data")
 | 
			
		||||
	paginationHTML := pagination.CreateHTML(page, pages, "/pastes/")
 | 
			
		||||
 | 
			
		||||
	pasteListTpl := templater.GetTemplate(ectx, "pastelist_list.html", map[string]string{"pastes": pastesString, "pagination": paginationHTML})
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								go.mod
									
									
									
									
									
								
							@@ -3,20 +3,21 @@ module go.dev.pztrn.name/fastpastebin
 | 
			
		||||
go 1.16
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/alecthomas/chroma v0.8.2
 | 
			
		||||
	github.com/alecthomas/chroma v0.10.0
 | 
			
		||||
	github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
 | 
			
		||||
	github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
 | 
			
		||||
	github.com/dlclark/regexp2 v1.4.0 // indirect
 | 
			
		||||
	github.com/go-sql-driver/mysql v1.5.0
 | 
			
		||||
	github.com/jmoiron/sqlx v1.2.0
 | 
			
		||||
	github.com/go-sql-driver/mysql v1.6.0
 | 
			
		||||
	github.com/jmoiron/sqlx v1.3.5
 | 
			
		||||
	github.com/kr/pretty v0.3.0 // indirect
 | 
			
		||||
	github.com/labstack/echo v3.3.10+incompatible
 | 
			
		||||
	github.com/labstack/gommon v0.3.0 // indirect
 | 
			
		||||
	github.com/lib/pq v1.9.0
 | 
			
		||||
	github.com/pressly/goose v2.6.0+incompatible
 | 
			
		||||
	github.com/rs/zerolog v1.20.0
 | 
			
		||||
	go.dev.pztrn.name/flagger v0.0.0-20200617193309-89bc9818b76c
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
 | 
			
		||||
	golang.org/x/net v0.0.0-20201224014010-6772e930b67b
 | 
			
		||||
	github.com/labstack/gommon v0.3.1 // indirect
 | 
			
		||||
	github.com/lib/pq v1.10.6
 | 
			
		||||
	github.com/pressly/goose v2.7.0+incompatible
 | 
			
		||||
	github.com/rs/zerolog v1.27.0
 | 
			
		||||
	go.dev.pztrn.name/flagger v0.0.0-20211119225333-c010875aa337
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
 | 
			
		||||
	golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e // indirect
 | 
			
		||||
	golang.org/x/sys v0.0.0-20220624220833-87e55d714810 // indirect
 | 
			
		||||
	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
 | 
			
		||||
	gopkg.in/yaml.v2 v2.4.0
 | 
			
		||||
	mvdan.cc/gofumpt v0.1.1 // indirect
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										163
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										163
									
								
								go.sum
									
									
									
									
									
								
							@@ -1,15 +1,7 @@
 | 
			
		||||
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
 | 
			
		||||
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
 | 
			
		||||
github.com/alecthomas/chroma v0.8.2 h1:x3zkuE2lUk/RIekyAJ3XRqSCP4zwWDfcw/YJCuCAACg=
 | 
			
		||||
github.com/alecthomas/chroma v0.8.2/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM=
 | 
			
		||||
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo=
 | 
			
		||||
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
 | 
			
		||||
github.com/alecthomas/kong v0.2.4/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE=
 | 
			
		||||
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkxI1zYWl1QLnEqAqEARBEYa8FQnQcY=
 | 
			
		||||
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
 | 
			
		||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 | 
			
		||||
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
 | 
			
		||||
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
 | 
			
		||||
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
 | 
			
		||||
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
 | 
			
		||||
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 | 
			
		||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
@@ -17,118 +9,83 @@ github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f h1:q/DpyjJjZs94bziQ
 | 
			
		||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f/go.mod h1:QGrK8vMWWHQYQ3QU9bw9Y9OPNfxccGzfb41qjvVeXtY=
 | 
			
		||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
 | 
			
		||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 | 
			
		||||
github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk=
 | 
			
		||||
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
 | 
			
		||||
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
 | 
			
		||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
 | 
			
		||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
 | 
			
		||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
 | 
			
		||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 | 
			
		||||
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
 | 
			
		||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
			
		||||
github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=
 | 
			
		||||
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
 | 
			
		||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
 | 
			
		||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 | 
			
		||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 | 
			
		||||
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
 | 
			
		||||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
 | 
			
		||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 | 
			
		||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 | 
			
		||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
 | 
			
		||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
 | 
			
		||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 | 
			
		||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 | 
			
		||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 | 
			
		||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 | 
			
		||||
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
 | 
			
		||||
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
 | 
			
		||||
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
 | 
			
		||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
 | 
			
		||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 | 
			
		||||
github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8=
 | 
			
		||||
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 | 
			
		||||
github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=
 | 
			
		||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 | 
			
		||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 | 
			
		||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
			
		||||
github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o=
 | 
			
		||||
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
 | 
			
		||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 | 
			
		||||
github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs=
 | 
			
		||||
github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 | 
			
		||||
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
 | 
			
		||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
 | 
			
		||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 | 
			
		||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
			
		||||
github.com/pressly/goose v2.6.0+incompatible h1:3f8zIQ8rfgP9tyI0Hmcs2YNAqUCL1c+diLe3iU8Qd/k=
 | 
			
		||||
github.com/pressly/goose v2.6.0+incompatible/go.mod h1:m+QHWCqxR3k8D9l7qfzuC/djtlfzxr34mozWDYEu1z8=
 | 
			
		||||
github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 | 
			
		||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
 | 
			
		||||
github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs=
 | 
			
		||||
github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
 | 
			
		||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
 | 
			
		||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 | 
			
		||||
github.com/pressly/goose v2.7.0+incompatible h1:PWejVEv07LCerQEzMMeAtjuyCKbyprZ/LBa6K5P0OCQ=
 | 
			
		||||
github.com/pressly/goose v2.7.0+incompatible/go.mod h1:m+QHWCqxR3k8D9l7qfzuC/djtlfzxr34mozWDYEu1z8=
 | 
			
		||||
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
 | 
			
		||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 | 
			
		||||
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
 | 
			
		||||
github.com/rs/zerolog v1.27.0 h1:1T7qCieN22GVc8S4Q2yuexzBb1EqjbgjSH9RohbMjKs=
 | 
			
		||||
github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Qc0U=
 | 
			
		||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
			
		||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
			
		||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 | 
			
		||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
			
		||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 | 
			
		||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 | 
			
		||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
 | 
			
		||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
			
		||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
 | 
			
		||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
			
		||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
 | 
			
		||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
 | 
			
		||||
github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
 | 
			
		||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
 | 
			
		||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
			
		||||
go.dev.pztrn.name/flagger v0.0.0-20200617193309-89bc9818b76c h1:+GgFefaTLsYDS0lXc8LNzTo6tcsA9qO3EkTAKduPAsI=
 | 
			
		||||
go.dev.pztrn.name/flagger v0.0.0-20200617193309-89bc9818b76c/go.mod h1:ttPExQNCubgqqO5Y19LfIBKqmWtBocY7P9MXQEECuZo=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
 | 
			
		||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
			
		||||
golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8=
 | 
			
		||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
			
		||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 | 
			
		||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw=
 | 
			
		||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
 | 
			
		||||
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
 | 
			
		||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
 | 
			
		||||
go.dev.pztrn.name/flagger v0.0.0-20211119225333-c010875aa337 h1:OerezdlV+T80z1tCzOQg6HSpjheU3dmxHbUbQ8NVSAs=
 | 
			
		||||
go.dev.pztrn.name/flagger v0.0.0-20211119225333-c010875aa337/go.mod h1:ttPExQNCubgqqO5Y19LfIBKqmWtBocY7P9MXQEECuZo=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 | 
			
		||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 | 
			
		||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ=
 | 
			
		||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 | 
			
		||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220624220833-87e55d714810 h1:rHZQSjJdAI4Xf5Qzeh2bBc5YJIkPFVM6oDtMFYmgws0=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
			
		||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
 | 
			
		||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
 | 
			
		||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
			
		||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 | 
			
		||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
			
		||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
 | 
			
		||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c h1:dS09fXwOFF9cXBnIzZexIuUBj95U1NyQjkEhkgidDow=
 | 
			
		||||
golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 | 
			
		||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 | 
			
		||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
mvdan.cc/gofumpt v0.1.1 h1:bi/1aS/5W00E2ny5q65w9SnKpWEF/UIOqDYBILpo9rA=
 | 
			
		||||
mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
 
 | 
			
		||||
@@ -32,18 +32,18 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	c   *context.Context
 | 
			
		||||
	ctx *context.Context
 | 
			
		||||
	log zerolog.Logger
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// New initializes captcha package and adds necessary HTTP and API
 | 
			
		||||
// endpoints.
 | 
			
		||||
func New(cc *context.Context) {
 | 
			
		||||
	c = cc
 | 
			
		||||
	log = c.Logger.With().Str("type", "internal").Str("package", "captcha").Logger()
 | 
			
		||||
	ctx = cc
 | 
			
		||||
	log = ctx.Logger.With().Str("type", "internal").Str("package", "captcha").Logger()
 | 
			
		||||
 | 
			
		||||
	// New paste.
 | 
			
		||||
	c.Echo.GET("/captcha/:id.png", echo.WrapHandler(captcha.Server(captcha.StdWidth, captcha.StdHeight)))
 | 
			
		||||
	ctx.Echo.GET("/captcha/:id.png", echo.WrapHandler(captcha.Server(captcha.StdWidth, captcha.StdHeight)))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewCaptcha creates new captcha string.
 | 
			
		||||
 
 | 
			
		||||
@@ -100,7 +100,7 @@ func (c *Context) LoadConfiguration() {
 | 
			
		||||
 | 
			
		||||
	c.Logger.Debug().Str("path", configPath).Msg("Configuration file path")
 | 
			
		||||
 | 
			
		||||
	// nolint:exhaustivestruct
 | 
			
		||||
	// nolint:exhaustruct
 | 
			
		||||
	c.Config = &config.Struct{}
 | 
			
		||||
 | 
			
		||||
	// Read configuration file.
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,6 @@ const (
 | 
			
		||||
 | 
			
		||||
// New creates new context.
 | 
			
		||||
func New() *Context {
 | 
			
		||||
	// nolint:exhaustivestruct
 | 
			
		||||
	// nolint:exhaustruct
 | 
			
		||||
	return &Context{}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,44 +12,44 @@ import (
 | 
			
		||||
 | 
			
		||||
// Puts memory usage into log lines.
 | 
			
		||||
func (c *Context) getMemoryUsage(event *zerolog.Event, level zerolog.Level, message string) {
 | 
			
		||||
	var m runtime.MemStats
 | 
			
		||||
	var memstats runtime.MemStats
 | 
			
		||||
 | 
			
		||||
	runtime.ReadMemStats(&m)
 | 
			
		||||
	runtime.ReadMemStats(&memstats)
 | 
			
		||||
 | 
			
		||||
	event.Str("memalloc", fmt.Sprintf("%dMB", m.Alloc/1024/1024))
 | 
			
		||||
	event.Str("memsys", fmt.Sprintf("%dMB", m.Sys/1024/1024))
 | 
			
		||||
	event.Str("numgc", fmt.Sprintf("%d", m.NumGC))
 | 
			
		||||
	event.Str("memalloc", fmt.Sprintf("%dMB", memstats.Alloc/1024/1024))
 | 
			
		||||
	event.Str("memsys", fmt.Sprintf("%dMB", memstats.Sys/1024/1024))
 | 
			
		||||
	event.Str("numgc", fmt.Sprintf("%d", memstats.NumGC))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Initializes logger.
 | 
			
		||||
func (c *Context) initializeLogger() {
 | 
			
		||||
	// Устанавливаем форматирование логгера.
 | 
			
		||||
	// nolint:exhaustivestruct
 | 
			
		||||
	// nolint:exhaustruct
 | 
			
		||||
	output := zerolog.ConsoleWriter{Out: os.Stdout, NoColor: false, TimeFormat: time.RFC3339}
 | 
			
		||||
	output.FormatLevel = func(lvlRaw interface{}) string {
 | 
			
		||||
		var v string
 | 
			
		||||
		var lvl string
 | 
			
		||||
 | 
			
		||||
		if lvl, ok := lvlRaw.(string); ok {
 | 
			
		||||
			lvl = strings.ToUpper(lvl)
 | 
			
		||||
			switch lvl {
 | 
			
		||||
		if lvlAsString, ok := lvlRaw.(string); ok {
 | 
			
		||||
			lvlAsString = strings.ToUpper(lvlAsString)
 | 
			
		||||
			switch lvlAsString {
 | 
			
		||||
			case "DEBUG":
 | 
			
		||||
				v = fmt.Sprintf("\x1b[30m%-5s\x1b[0m", lvl)
 | 
			
		||||
				lvl = fmt.Sprintf("\x1b[30m%-5s\x1b[0m", lvlAsString)
 | 
			
		||||
			case "ERROR":
 | 
			
		||||
				v = fmt.Sprintf("\x1b[31m%-5s\x1b[0m", lvl)
 | 
			
		||||
				lvl = fmt.Sprintf("\x1b[31m%-5s\x1b[0m", lvlAsString)
 | 
			
		||||
			case "FATAL":
 | 
			
		||||
				v = fmt.Sprintf("\x1b[35m%-5s\x1b[0m", lvl)
 | 
			
		||||
				lvl = fmt.Sprintf("\x1b[35m%-5s\x1b[0m", lvlAsString)
 | 
			
		||||
			case "INFO":
 | 
			
		||||
				v = fmt.Sprintf("\x1b[32m%-5s\x1b[0m", lvl)
 | 
			
		||||
				lvl = fmt.Sprintf("\x1b[32m%-5s\x1b[0m", lvlAsString)
 | 
			
		||||
			case "PANIC":
 | 
			
		||||
				v = fmt.Sprintf("\x1b[36m%-5s\x1b[0m", lvl)
 | 
			
		||||
				lvl = fmt.Sprintf("\x1b[36m%-5s\x1b[0m", lvlAsString)
 | 
			
		||||
			case "WARN":
 | 
			
		||||
				v = fmt.Sprintf("\x1b[33m%-5s\x1b[0m", lvl)
 | 
			
		||||
				lvl = fmt.Sprintf("\x1b[33m%-5s\x1b[0m", lvlAsString)
 | 
			
		||||
			default:
 | 
			
		||||
				v = lvl
 | 
			
		||||
				lvl = lvlAsString
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return fmt.Sprintf("| %s |", v)
 | 
			
		||||
		return fmt.Sprintf("| %s |", lvl)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.Logger = zerolog.New(output).With().Timestamp().Logger()
 | 
			
		||||
 
 | 
			
		||||
@@ -46,7 +46,7 @@ type Database struct {
 | 
			
		||||
// a subject of change in future.
 | 
			
		||||
func (db *Database) cleanup() {
 | 
			
		||||
	for {
 | 
			
		||||
		c.Logger.Info().Msg("Starting pastes cleanup procedure...")
 | 
			
		||||
		ctx.Logger.Info().Msg("Starting pastes cleanup procedure...")
 | 
			
		||||
 | 
			
		||||
		pages := db.db.GetPastesPages()
 | 
			
		||||
 | 
			
		||||
@@ -55,7 +55,7 @@ func (db *Database) cleanup() {
 | 
			
		||||
		for i := 0; i < pages; i++ {
 | 
			
		||||
			pastes, err := db.db.GetPagedPastes(i)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				c.Logger.Error().Err(err).Int("page", i).Msg("Failed to perform database cleanup")
 | 
			
		||||
				ctx.Logger.Error().Err(err).Int("page", i).Msg("Failed to perform database cleanup")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, paste := range pastes {
 | 
			
		||||
@@ -68,11 +68,11 @@ func (db *Database) cleanup() {
 | 
			
		||||
		for _, pasteID := range pasteIDsToRemove {
 | 
			
		||||
			err := db.DeletePaste(pasteID)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				c.Logger.Error().Err(err).Int("paste", pasteID).Msg("Failed to delete paste!")
 | 
			
		||||
				ctx.Logger.Error().Err(err).Int("paste", pasteID).Msg("Failed to delete paste!")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		c.Logger.Info().Msg("Pastes cleanup done.")
 | 
			
		||||
		ctx.Logger.Info().Msg("Pastes cleanup done.")
 | 
			
		||||
 | 
			
		||||
		time.Sleep(time.Hour)
 | 
			
		||||
	}
 | 
			
		||||
@@ -107,16 +107,16 @@ func (db *Database) GetPastesPages() int {
 | 
			
		||||
 | 
			
		||||
// Initialize initializes connection to database.
 | 
			
		||||
func (db *Database) Initialize() {
 | 
			
		||||
	c.Logger.Info().Msg("Initializing database connection...")
 | 
			
		||||
	ctx.Logger.Info().Msg("Initializing database connection...")
 | 
			
		||||
 | 
			
		||||
	if c.Config.Database.Type == "mysql" {
 | 
			
		||||
		mysql.New(c)
 | 
			
		||||
	} else if c.Config.Database.Type == flatfiles.FlatFileDialect {
 | 
			
		||||
		flatfiles.New(c)
 | 
			
		||||
	} else if c.Config.Database.Type == "postgresql" {
 | 
			
		||||
		postgresql.New(c)
 | 
			
		||||
	if ctx.Config.Database.Type == "mysql" {
 | 
			
		||||
		mysql.New(ctx)
 | 
			
		||||
	} else if ctx.Config.Database.Type == flatfiles.FlatFileDialect {
 | 
			
		||||
		flatfiles.New(ctx)
 | 
			
		||||
	} else if ctx.Config.Database.Type == "postgresql" {
 | 
			
		||||
		postgresql.New(ctx)
 | 
			
		||||
	} else {
 | 
			
		||||
		c.Logger.Fatal().Str("type", c.Config.Database.Type).Msg("Unknown database type")
 | 
			
		||||
		ctx.Logger.Fatal().Str("type", ctx.Config.Database.Type).Msg("Unknown database type")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go db.cleanup()
 | 
			
		||||
 
 | 
			
		||||
@@ -32,14 +32,14 @@ import (
 | 
			
		||||
const FlatFileDialect = "flatfiles"
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	c *context.Context
 | 
			
		||||
	f *FlatFiles
 | 
			
		||||
	ctx *context.Context
 | 
			
		||||
	flf *FlatFiles
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func New(cc *context.Context) {
 | 
			
		||||
	c = cc
 | 
			
		||||
	// nolint:exhaustivestruct
 | 
			
		||||
	f = &FlatFiles{}
 | 
			
		||||
	ctx = cc
 | 
			
		||||
	// nolint:exhaustruct
 | 
			
		||||
	flf = &FlatFiles{}
 | 
			
		||||
 | 
			
		||||
	c.Database.RegisterDialect(dialectinterface.Interface(Handler{}))
 | 
			
		||||
	ctx.Database.RegisterDialect(dialectinterface.Interface(Handler{}))
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -49,7 +49,7 @@ func (ff *FlatFiles) DeletePaste(pasteID int) error {
 | 
			
		||||
	// Delete from disk.
 | 
			
		||||
	err := os.Remove(filepath.Join(ff.path, "pastes", strconv.Itoa(pasteID)+".json"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.Logger.Error().Err(err).Msg("Failed to delete paste!")
 | 
			
		||||
		ctx.Logger.Error().Err(err).Msg("Failed to delete paste!")
 | 
			
		||||
 | 
			
		||||
		// nolint:wrapcheck
 | 
			
		||||
		return err
 | 
			
		||||
@@ -83,25 +83,25 @@ func (ff *FlatFiles) GetDatabaseConnection() *sql.DB {
 | 
			
		||||
func (ff *FlatFiles) GetPaste(pasteID int) (*structs.Paste, error) {
 | 
			
		||||
	ff.writeMutex.Lock()
 | 
			
		||||
	pastePath := filepath.Join(ff.path, "pastes", strconv.Itoa(pasteID)+".json")
 | 
			
		||||
	c.Logger.Debug().Str("path", pastePath).Msg("Trying to load paste data")
 | 
			
		||||
	ctx.Logger.Debug().Str("path", pastePath).Msg("Trying to load paste data")
 | 
			
		||||
 | 
			
		||||
	pasteInBytes, err := ioutil.ReadFile(pastePath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.Logger.Debug().Err(err).Msg("Failed to read paste from storage")
 | 
			
		||||
		ctx.Logger.Debug().Err(err).Msg("Failed to read paste from storage")
 | 
			
		||||
 | 
			
		||||
		// nolint:wrapcheck
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.Logger.Debug().Int("paste bytes", len(pasteInBytes)).Msg("Loaded paste")
 | 
			
		||||
	ctx.Logger.Debug().Int("paste bytes", len(pasteInBytes)).Msg("Loaded paste")
 | 
			
		||||
	ff.writeMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	// nolint:exhaustivestruct
 | 
			
		||||
	// nolint:exhaustruct
 | 
			
		||||
	paste := &structs.Paste{}
 | 
			
		||||
 | 
			
		||||
	err1 := json.Unmarshal(pasteInBytes, paste)
 | 
			
		||||
	if err1 != nil {
 | 
			
		||||
		c.Logger.Error().Err(err1).Msgf("Failed to parse paste")
 | 
			
		||||
		ctx.Logger.Error().Err(err1).Msgf("Failed to parse paste")
 | 
			
		||||
 | 
			
		||||
		// nolint:wrapcheck
 | 
			
		||||
		return nil, err1
 | 
			
		||||
@@ -114,7 +114,7 @@ func (ff *FlatFiles) GetPagedPastes(page int) ([]structs.Paste, error) {
 | 
			
		||||
	// Pagination.
 | 
			
		||||
	startPagination := 0
 | 
			
		||||
	if page > 1 {
 | 
			
		||||
		startPagination = (page - 1) * c.Config.Pastes.Pagination
 | 
			
		||||
		startPagination = (page - 1) * ctx.Config.Pastes.Pagination
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Iteration one - get only public pastes.
 | 
			
		||||
@@ -130,38 +130,38 @@ func (ff *FlatFiles) GetPagedPastes(page int) ([]structs.Paste, error) {
 | 
			
		||||
	pastesData := make([]structs.Paste, 0)
 | 
			
		||||
 | 
			
		||||
	for idx, paste := range publicPastes {
 | 
			
		||||
		if len(pastesData) == c.Config.Pastes.Pagination {
 | 
			
		||||
		if len(pastesData) == ctx.Config.Pastes.Pagination {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if idx < startPagination {
 | 
			
		||||
			c.Logger.Debug().Int("paste index", idx).Msg("Paste isn't in pagination query: too low index")
 | 
			
		||||
			ctx.Logger.Debug().Int("paste index", idx).Msg("Paste isn't in pagination query: too low index")
 | 
			
		||||
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (idx-1 >= startPagination && page > 1 && idx > startPagination+((page-1)*c.Config.Pastes.Pagination)) || (idx-1 >= startPagination && page == 1 && idx > startPagination+(page*c.Config.Pastes.Pagination)) {
 | 
			
		||||
			c.Logger.Debug().Int("paste index", idx).Msg("Paste isn't in pagination query: too high index")
 | 
			
		||||
		if (idx-1 >= startPagination && page > 1 && idx > startPagination+((page-1)*ctx.Config.Pastes.Pagination)) || (idx-1 >= startPagination && page == 1 && idx > startPagination+(page*ctx.Config.Pastes.Pagination)) {
 | 
			
		||||
			ctx.Logger.Debug().Int("paste index", idx).Msg("Paste isn't in pagination query: too high index")
 | 
			
		||||
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		c.Logger.Debug().Int("ID", paste.ID).Int("index", idx).Msg("Getting paste data")
 | 
			
		||||
		ctx.Logger.Debug().Int("ID", paste.ID).Int("index", idx).Msg("Getting paste data")
 | 
			
		||||
 | 
			
		||||
		// Get paste data.
 | 
			
		||||
		// nolint:exhaustivestruct
 | 
			
		||||
		// nolint:exhaustruct
 | 
			
		||||
		pasteData := &structs.Paste{}
 | 
			
		||||
 | 
			
		||||
		pasteRawData, err := ioutil.ReadFile(filepath.Join(ff.path, "pastes", strconv.Itoa(paste.ID)+".json"))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			c.Logger.Error().Err(err).Msg("Failed to read paste data")
 | 
			
		||||
			ctx.Logger.Error().Err(err).Msg("Failed to read paste data")
 | 
			
		||||
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		err1 := json.Unmarshal(pasteRawData, pasteData)
 | 
			
		||||
		if err1 != nil {
 | 
			
		||||
			c.Logger.Error().Err(err1).Msg("Failed to parse paste data")
 | 
			
		||||
			ctx.Logger.Error().Err(err1).Msg("Failed to parse paste data")
 | 
			
		||||
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
@@ -185,9 +185,9 @@ func (ff *FlatFiles) GetPastesPages() int {
 | 
			
		||||
	ff.writeMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	// Calculate pages.
 | 
			
		||||
	pages := len(publicPastes) / c.Config.Pastes.Pagination
 | 
			
		||||
	pages := len(publicPastes) / ctx.Config.Pastes.Pagination
 | 
			
		||||
	// Check if we have any remainder. Add 1 to pages count if so.
 | 
			
		||||
	if len(publicPastes)%c.Config.Pastes.Pagination > 0 {
 | 
			
		||||
	if len(publicPastes)%ctx.Config.Pastes.Pagination > 0 {
 | 
			
		||||
		pages++
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -195,14 +195,14 @@ func (ff *FlatFiles) GetPastesPages() int {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ff *FlatFiles) Initialize() {
 | 
			
		||||
	c.Logger.Info().Msg("Initializing flatfiles storage...")
 | 
			
		||||
	ctx.Logger.Info().Msg("Initializing flatfiles storage...")
 | 
			
		||||
 | 
			
		||||
	path := c.Config.Database.Path
 | 
			
		||||
	path := ctx.Config.Database.Path
 | 
			
		||||
	// Get proper paste file path.
 | 
			
		||||
	if strings.Contains(c.Config.Database.Path, "~") {
 | 
			
		||||
	if strings.Contains(ctx.Config.Database.Path, "~") {
 | 
			
		||||
		curUser, err := user.Current()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			c.Logger.Error().Msg("Failed to get current user. Will replace '~' for '/' in storage path!")
 | 
			
		||||
			ctx.Logger.Error().Msg("Failed to get current user. Will replace '~' for '/' in storage path!")
 | 
			
		||||
 | 
			
		||||
			path = strings.Replace(path, "~", "/", -1)
 | 
			
		||||
		}
 | 
			
		||||
@@ -213,40 +213,40 @@ func (ff *FlatFiles) Initialize() {
 | 
			
		||||
	path, _ = filepath.Abs(path)
 | 
			
		||||
	ff.path = path
 | 
			
		||||
 | 
			
		||||
	c.Logger.Debug().Msgf("Storage path is now: %s", ff.path)
 | 
			
		||||
	ctx.Logger.Debug().Msgf("Storage path is now: %s", ff.path)
 | 
			
		||||
 | 
			
		||||
	// Create directory if necessary.
 | 
			
		||||
	if _, err := os.Stat(ff.path); err != nil {
 | 
			
		||||
		c.Logger.Debug().Str("directory", ff.path).Msg("Directory does not exist, creating...")
 | 
			
		||||
		ctx.Logger.Debug().Str("directory", ff.path).Msg("Directory does not exist, creating...")
 | 
			
		||||
		_ = os.MkdirAll(ff.path, os.ModePerm)
 | 
			
		||||
	} else {
 | 
			
		||||
		c.Logger.Debug().Str("directory", ff.path).Msg("Directory already exists")
 | 
			
		||||
		ctx.Logger.Debug().Str("directory", ff.path).Msg("Directory already exists")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Create directory for pastes.
 | 
			
		||||
	if _, err := os.Stat(filepath.Join(ff.path, "pastes")); err != nil {
 | 
			
		||||
		c.Logger.Debug().Str("directory", ff.path).Msg("Directory does not exist, creating...")
 | 
			
		||||
		ctx.Logger.Debug().Str("directory", ff.path).Msg("Directory does not exist, creating...")
 | 
			
		||||
		_ = os.MkdirAll(filepath.Join(ff.path, "pastes"), os.ModePerm)
 | 
			
		||||
	} else {
 | 
			
		||||
		c.Logger.Debug().Str("directory", ff.path).Msg("Directory already exists")
 | 
			
		||||
		ctx.Logger.Debug().Str("directory", ff.path).Msg("Directory already exists")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Load pastes index.
 | 
			
		||||
	ff.pastesIndex = []Index{}
 | 
			
		||||
	if _, err := os.Stat(filepath.Join(ff.path, "pastes", "index.json")); err != nil {
 | 
			
		||||
		c.Logger.Warn().Msg("Pastes index file does not exist, will create new one")
 | 
			
		||||
		ctx.Logger.Warn().Msg("Pastes index file does not exist, will create new one")
 | 
			
		||||
	} else {
 | 
			
		||||
		indexData, err := ioutil.ReadFile(filepath.Join(ff.path, "pastes", "index.json"))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			c.Logger.Fatal().Msg("Failed to read contents of index file!")
 | 
			
		||||
			ctx.Logger.Fatal().Msg("Failed to read contents of index file!")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		err1 := json.Unmarshal(indexData, &ff.pastesIndex)
 | 
			
		||||
		if err1 != nil {
 | 
			
		||||
			c.Logger.Error().Err(err1).Msg("Failed to parse index file contents from JSON into internal structure. Will create new index file. All of your previous pastes will became unavailable.")
 | 
			
		||||
			ctx.Logger.Error().Err(err1).Msg("Failed to parse index file contents from JSON into internal structure. Will create new index file. All of your previous pastes will became unavailable.")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		c.Logger.Debug().Int("pastes count", len(ff.pastesIndex)).Msg("Parsed pastes index")
 | 
			
		||||
		ctx.Logger.Debug().Int("pastes count", len(ff.pastesIndex)).Msg("Parsed pastes index")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -257,7 +257,7 @@ func (ff *FlatFiles) SavePaste(paste *structs.Paste) (int64, error) {
 | 
			
		||||
	pasteID := len(filesOnDisk) + 1
 | 
			
		||||
	paste.ID = pasteID
 | 
			
		||||
 | 
			
		||||
	c.Logger.Debug().Int("new paste ID", pasteID).Msg("Writing paste to disk")
 | 
			
		||||
	ctx.Logger.Debug().Int("new paste ID", pasteID).Msg("Writing paste to disk")
 | 
			
		||||
 | 
			
		||||
	data, err := json.Marshal(paste)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -276,7 +276,7 @@ func (ff *FlatFiles) SavePaste(paste *structs.Paste) (int64, error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Add it to cache.
 | 
			
		||||
	// nolint:exhaustivestruct
 | 
			
		||||
	// nolint:exhaustruct
 | 
			
		||||
	indexData := Index{}
 | 
			
		||||
	indexData.ID = pasteID
 | 
			
		||||
	indexData.Private = paste.Private
 | 
			
		||||
@@ -287,18 +287,18 @@ func (ff *FlatFiles) SavePaste(paste *structs.Paste) (int64, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ff *FlatFiles) Shutdown() {
 | 
			
		||||
	c.Logger.Info().Msg("Saving indexes...")
 | 
			
		||||
	ctx.Logger.Info().Msg("Saving indexes...")
 | 
			
		||||
 | 
			
		||||
	indexData, err := json.Marshal(ff.pastesIndex)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.Logger.Error().Err(err).Msg("Failed to encode index data into JSON")
 | 
			
		||||
		ctx.Logger.Error().Err(err).Msg("Failed to encode index data into JSON")
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err1 := ioutil.WriteFile(filepath.Join(ff.path, "pastes", "index.json"), indexData, 0o600)
 | 
			
		||||
	if err1 != nil {
 | 
			
		||||
		c.Logger.Error().Err(err1).Msg("Failed to write index data to file. Pretty sure that you've lost your pastes.")
 | 
			
		||||
		ctx.Logger.Error().Err(err1).Msg("Failed to write index data to file. Pretty sure that you've lost your pastes.")
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -33,33 +33,33 @@ import (
 | 
			
		||||
type Handler struct{}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) DeletePaste(pasteID int) error {
 | 
			
		||||
	return f.DeletePaste(pasteID)
 | 
			
		||||
	return flf.DeletePaste(pasteID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) GetDatabaseConnection() *sql.DB {
 | 
			
		||||
	return f.GetDatabaseConnection()
 | 
			
		||||
	return flf.GetDatabaseConnection()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) GetPaste(pasteID int) (*structs.Paste, error) {
 | 
			
		||||
	return f.GetPaste(pasteID)
 | 
			
		||||
	return flf.GetPaste(pasteID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) GetPagedPastes(page int) ([]structs.Paste, error) {
 | 
			
		||||
	return f.GetPagedPastes(page)
 | 
			
		||||
	return flf.GetPagedPastes(page)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) GetPastesPages() int {
 | 
			
		||||
	return f.GetPastesPages()
 | 
			
		||||
	return flf.GetPastesPages()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) Initialize() {
 | 
			
		||||
	f.Initialize()
 | 
			
		||||
	flf.Initialize()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) SavePaste(p *structs.Paste) (int64, error) {
 | 
			
		||||
	return f.SavePaste(p)
 | 
			
		||||
	return flf.SavePaste(p)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) Shutdown() {
 | 
			
		||||
	f.Shutdown()
 | 
			
		||||
	flf.Shutdown()
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -30,14 +30,14 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	c *context.Context
 | 
			
		||||
	d *Database
 | 
			
		||||
	ctx       *context.Context
 | 
			
		||||
	dbAdapter *Database
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func New(cc *context.Context) {
 | 
			
		||||
	c = cc
 | 
			
		||||
	// nolint:exhaustivestruct
 | 
			
		||||
	d = &Database{}
 | 
			
		||||
	ctx = cc
 | 
			
		||||
	// nolint:exhaustruct
 | 
			
		||||
	dbAdapter = &Database{}
 | 
			
		||||
 | 
			
		||||
	c.Database.RegisterDialect(dialectinterface.Interface(Handler{}))
 | 
			
		||||
	ctx.Database.RegisterDialect(dialectinterface.Interface(Handler{}))
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -33,33 +33,33 @@ import (
 | 
			
		||||
type Handler struct{}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) DeletePaste(pasteID int) error {
 | 
			
		||||
	return d.DeletePaste(pasteID)
 | 
			
		||||
	return dbAdapter.DeletePaste(pasteID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) GetDatabaseConnection() *sql.DB {
 | 
			
		||||
	return d.GetDatabaseConnection()
 | 
			
		||||
	return dbAdapter.GetDatabaseConnection()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) GetPaste(pasteID int) (*structs.Paste, error) {
 | 
			
		||||
	return d.GetPaste(pasteID)
 | 
			
		||||
	return dbAdapter.GetPaste(pasteID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) GetPagedPastes(page int) ([]structs.Paste, error) {
 | 
			
		||||
	return d.GetPagedPastes(page)
 | 
			
		||||
	return dbAdapter.GetPagedPastes(page)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) GetPastesPages() int {
 | 
			
		||||
	return d.GetPastesPages()
 | 
			
		||||
	return dbAdapter.GetPastesPages()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) Initialize() {
 | 
			
		||||
	d.Initialize()
 | 
			
		||||
	dbAdapter.Initialize()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) SavePaste(p *structs.Paste) (int64, error) {
 | 
			
		||||
	return d.SavePaste(p)
 | 
			
		||||
	return dbAdapter.SavePaste(p)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) Shutdown() {
 | 
			
		||||
	d.Shutdown()
 | 
			
		||||
	dbAdapter.Shutdown()
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -29,16 +29,16 @@ import (
 | 
			
		||||
	"go.dev.pztrn.name/fastpastebin/internal/context"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var c *context.Context
 | 
			
		||||
var ctx *context.Context
 | 
			
		||||
 | 
			
		||||
// New initializes migrations.
 | 
			
		||||
func New(cc *context.Context) {
 | 
			
		||||
	c = cc
 | 
			
		||||
	ctx = cc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Migrate launching migrations.
 | 
			
		||||
func Migrate() {
 | 
			
		||||
	c.Logger.Info().Msg("Migrating database...")
 | 
			
		||||
	ctx.Logger.Info().Msg("Migrating database...")
 | 
			
		||||
 | 
			
		||||
	_ = goose.SetDialect("mysql")
 | 
			
		||||
	goose.AddNamedMigration("1_initial.go", InitialUp, nil)
 | 
			
		||||
@@ -47,13 +47,13 @@ func Migrate() {
 | 
			
		||||
	goose.AddNamedMigration("4_passworded_pastes.go", PasswordedPastesUp, PasswordedPastesDown)
 | 
			
		||||
	// Add new migrations BEFORE this message.
 | 
			
		||||
 | 
			
		||||
	dbConn := c.Database.GetDatabaseConnection()
 | 
			
		||||
	dbConn := ctx.Database.GetDatabaseConnection()
 | 
			
		||||
	if dbConn != nil {
 | 
			
		||||
		err := goose.Up(dbConn, ".")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			c.Logger.Panic().Msgf("Failed to migrate database to latest version: %s", err.Error())
 | 
			
		||||
			ctx.Logger.Panic().Msgf("Failed to migrate database to latest version: %s", err.Error())
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		c.Logger.Warn().Msg("Current database dialect isn't supporting migrations, skipping")
 | 
			
		||||
		ctx.Logger.Warn().Msg("Current database dialect isn't supporting migrations, skipping")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -80,7 +80,7 @@ func (db *Database) GetDatabaseConnection() *sql.DB {
 | 
			
		||||
func (db *Database) GetPaste(pasteID int) (*structs.Paste, error) {
 | 
			
		||||
	db.check()
 | 
			
		||||
 | 
			
		||||
	// nolint:exhaustivestruct
 | 
			
		||||
	// nolint:exhaustruct
 | 
			
		||||
	paste := &structs.Paste{}
 | 
			
		||||
 | 
			
		||||
	err := db.db.Get(paste, db.db.Rebind("SELECT * FROM `pastes` WHERE id=?"), pasteID)
 | 
			
		||||
@@ -103,10 +103,10 @@ func (db *Database) GetPagedPastes(page int) ([]structs.Paste, error) {
 | 
			
		||||
	// Pagination.
 | 
			
		||||
	startPagination := 0
 | 
			
		||||
	if page > 1 {
 | 
			
		||||
		startPagination = (page - 1) * c.Config.Pastes.Pagination
 | 
			
		||||
		startPagination = (page - 1) * ctx.Config.Pastes.Pagination
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := db.db.Select(&pastesRaw, db.db.Rebind("SELECT * FROM `pastes` WHERE private != true ORDER BY id DESC LIMIT ? OFFSET ?"), c.Config.Pastes.Pagination, startPagination)
 | 
			
		||||
	err := db.db.Select(&pastesRaw, db.db.Rebind("SELECT * FROM `pastes` WHERE private != true ORDER BY id DESC LIMIT ? OFFSET ?"), ctx.Config.Pastes.Pagination, startPagination)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// nolint:wrapcheck
 | 
			
		||||
		return nil, err
 | 
			
		||||
@@ -142,9 +142,9 @@ func (db *Database) GetPastesPages() int {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Calculate pages.
 | 
			
		||||
	pages := len(pastes) / c.Config.Pastes.Pagination
 | 
			
		||||
	pages := len(pastes) / ctx.Config.Pastes.Pagination
 | 
			
		||||
	// Check if we have any remainder. Add 1 to pages count if so.
 | 
			
		||||
	if len(pastes)%c.Config.Pastes.Pagination > 0 {
 | 
			
		||||
	if len(pastes)%ctx.Config.Pastes.Pagination > 0 {
 | 
			
		||||
		pages++
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -153,23 +153,23 @@ func (db *Database) GetPastesPages() int {
 | 
			
		||||
 | 
			
		||||
// Initialize initializes MySQL/MariaDB connection.
 | 
			
		||||
func (db *Database) Initialize() {
 | 
			
		||||
	c.Logger.Info().Msg("Initializing database connection...")
 | 
			
		||||
	ctx.Logger.Info().Msg("Initializing database connection...")
 | 
			
		||||
 | 
			
		||||
	// There might be only user, without password. MySQL/MariaDB driver
 | 
			
		||||
	// in DSN wants "user" or "user:password", "user:" is invalid.
 | 
			
		||||
	var userpass string
 | 
			
		||||
	if c.Config.Database.Password == "" {
 | 
			
		||||
		userpass = c.Config.Database.Username
 | 
			
		||||
	if ctx.Config.Database.Password == "" {
 | 
			
		||||
		userpass = ctx.Config.Database.Username
 | 
			
		||||
	} else {
 | 
			
		||||
		userpass = c.Config.Database.Username + ":" + c.Config.Database.Password
 | 
			
		||||
		userpass = ctx.Config.Database.Username + ":" + ctx.Config.Database.Password
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbConnString := fmt.Sprintf("%s@tcp(%s:%s)/%s?parseTime=true&collation=utf8mb4_unicode_ci&charset=utf8mb4", userpass, c.Config.Database.Address, c.Config.Database.Port, c.Config.Database.Database)
 | 
			
		||||
	c.Logger.Debug().Str("DSN", dbConnString).Msgf("Database connection string composed")
 | 
			
		||||
	dbConnString := fmt.Sprintf("%s@tcp(%s:%s)/%s?parseTime=true&collation=utf8mb4_unicode_ci&charset=utf8mb4", userpass, ctx.Config.Database.Address, ctx.Config.Database.Port, ctx.Config.Database.Database)
 | 
			
		||||
	ctx.Logger.Debug().Str("DSN", dbConnString).Msgf("Database connection string composed")
 | 
			
		||||
 | 
			
		||||
	dbConn, err := sqlx.Connect("mysql", dbConnString)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.Logger.Error().Err(err).Msg("Failed to connect to database")
 | 
			
		||||
		ctx.Logger.Error().Err(err).Msg("Failed to connect to database")
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@@ -177,12 +177,12 @@ func (db *Database) Initialize() {
 | 
			
		||||
	// Force UTC for current connection.
 | 
			
		||||
	_ = dbConn.MustExec("SET @@session.time_zone='+00:00';")
 | 
			
		||||
 | 
			
		||||
	c.Logger.Info().Msg("Database connection established")
 | 
			
		||||
	ctx.Logger.Info().Msg("Database connection established")
 | 
			
		||||
 | 
			
		||||
	db.db = dbConn
 | 
			
		||||
 | 
			
		||||
	// Perform migrations.
 | 
			
		||||
	migrations.New(c)
 | 
			
		||||
	migrations.New(ctx)
 | 
			
		||||
	migrations.Migrate()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -208,7 +208,7 @@ func (db *Database) Shutdown() {
 | 
			
		||||
	if db.db != nil {
 | 
			
		||||
		err := db.db.Close()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			c.Logger.Error().Err(err).Msg("Failed to close database connection")
 | 
			
		||||
			ctx.Logger.Error().Err(err).Msg("Failed to close database connection")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -30,14 +30,14 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	c *context.Context
 | 
			
		||||
	d *Database
 | 
			
		||||
	ctx       *context.Context
 | 
			
		||||
	dbAdapter *Database
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func New(cc *context.Context) {
 | 
			
		||||
	c = cc
 | 
			
		||||
	// nolint:exhaustivestruct
 | 
			
		||||
	d = &Database{}
 | 
			
		||||
	ctx = cc
 | 
			
		||||
	// nolint:exhaustruct
 | 
			
		||||
	dbAdapter = &Database{}
 | 
			
		||||
 | 
			
		||||
	c.Database.RegisterDialect(dialectinterface.Interface(Handler{}))
 | 
			
		||||
	ctx.Database.RegisterDialect(dialectinterface.Interface(Handler{}))
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -33,33 +33,33 @@ import (
 | 
			
		||||
type Handler struct{}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) DeletePaste(pasteID int) error {
 | 
			
		||||
	return d.DeletePaste(pasteID)
 | 
			
		||||
	return dbAdapter.DeletePaste(pasteID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) GetDatabaseConnection() *sql.DB {
 | 
			
		||||
	return d.GetDatabaseConnection()
 | 
			
		||||
	return dbAdapter.GetDatabaseConnection()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) GetPaste(pasteID int) (*structs.Paste, error) {
 | 
			
		||||
	return d.GetPaste(pasteID)
 | 
			
		||||
	return dbAdapter.GetPaste(pasteID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) GetPagedPastes(page int) ([]structs.Paste, error) {
 | 
			
		||||
	return d.GetPagedPastes(page)
 | 
			
		||||
	return dbAdapter.GetPagedPastes(page)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) GetPastesPages() int {
 | 
			
		||||
	return d.GetPastesPages()
 | 
			
		||||
	return dbAdapter.GetPastesPages()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) Initialize() {
 | 
			
		||||
	d.Initialize()
 | 
			
		||||
	dbAdapter.Initialize()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) SavePaste(p *structs.Paste) (int64, error) {
 | 
			
		||||
	return d.SavePaste(p)
 | 
			
		||||
	return dbAdapter.SavePaste(p)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) Shutdown() {
 | 
			
		||||
	d.Shutdown()
 | 
			
		||||
	dbAdapter.Shutdown()
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -29,16 +29,16 @@ import (
 | 
			
		||||
	"go.dev.pztrn.name/fastpastebin/internal/context"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var c *context.Context
 | 
			
		||||
var ctx *context.Context
 | 
			
		||||
 | 
			
		||||
// New initializes migrations.
 | 
			
		||||
func New(cc *context.Context) {
 | 
			
		||||
	c = cc
 | 
			
		||||
	ctx = cc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Migrate launching migrations.
 | 
			
		||||
func Migrate() {
 | 
			
		||||
	c.Logger.Info().Msg("Migrating database...")
 | 
			
		||||
	ctx.Logger.Info().Msg("Migrating database...")
 | 
			
		||||
 | 
			
		||||
	_ = goose.SetDialect("postgres")
 | 
			
		||||
	goose.AddNamedMigration("1_initial.go", InitialUp, nil)
 | 
			
		||||
@@ -47,14 +47,14 @@ func Migrate() {
 | 
			
		||||
	goose.AddNamedMigration("4_passworded_pastes.go", PasswordedPastesUp, PasswordedPastesDown)
 | 
			
		||||
	// Add new migrations BEFORE this message.
 | 
			
		||||
 | 
			
		||||
	dbConn := c.Database.GetDatabaseConnection()
 | 
			
		||||
	dbConn := ctx.Database.GetDatabaseConnection()
 | 
			
		||||
	if dbConn != nil {
 | 
			
		||||
		err := goose.Up(dbConn, ".")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			c.Logger.Info().Msgf("%+v", err)
 | 
			
		||||
			c.Logger.Panic().Msgf("Failed to migrate database to latest version: %s", err.Error())
 | 
			
		||||
			ctx.Logger.Info().Msgf("%+v", err)
 | 
			
		||||
			ctx.Logger.Panic().Msgf("Failed to migrate database to latest version: %s", err.Error())
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		c.Logger.Warn().Msg("Current database dialect isn't supporting migrations, skipping")
 | 
			
		||||
		ctx.Logger.Warn().Msg("Current database dialect isn't supporting migrations, skipping")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@ package postgresql
 | 
			
		||||
import (
 | 
			
		||||
	"database/sql"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	// PostgreSQL driver.
 | 
			
		||||
@@ -83,7 +84,7 @@ func (db *Database) GetDatabaseConnection() *sql.DB {
 | 
			
		||||
func (db *Database) GetPaste(pasteID int) (*structs.Paste, error) {
 | 
			
		||||
	db.check()
 | 
			
		||||
 | 
			
		||||
	// nolint:exhaustivestruct
 | 
			
		||||
	// nolint:exhaustruct
 | 
			
		||||
	paste := &structs.Paste{}
 | 
			
		||||
 | 
			
		||||
	err := db.db.Get(paste, db.db.Rebind("SELECT * FROM pastes WHERE id=$1"), pasteID)
 | 
			
		||||
@@ -113,10 +114,10 @@ func (db *Database) GetPagedPastes(page int) ([]structs.Paste, error) {
 | 
			
		||||
	// Pagination.
 | 
			
		||||
	startPagination := 0
 | 
			
		||||
	if page > 1 {
 | 
			
		||||
		startPagination = (page - 1) * c.Config.Pastes.Pagination
 | 
			
		||||
		startPagination = (page - 1) * ctx.Config.Pastes.Pagination
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := db.db.Select(&pastesRaw, db.db.Rebind("SELECT * FROM pastes WHERE private != true ORDER BY id DESC LIMIT $1 OFFSET $2"), c.Config.Pastes.Pagination, startPagination)
 | 
			
		||||
	err := db.db.Select(&pastesRaw, db.db.Rebind("SELECT * FROM pastes WHERE private != true ORDER BY id DESC LIMIT $1 OFFSET $2"), ctx.Config.Pastes.Pagination, startPagination)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// nolint:wrapcheck
 | 
			
		||||
		return nil, err
 | 
			
		||||
@@ -158,9 +159,9 @@ func (db *Database) GetPastesPages() int {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Calculate pages.
 | 
			
		||||
	pages := len(pastes) / c.Config.Pastes.Pagination
 | 
			
		||||
	pages := len(pastes) / ctx.Config.Pastes.Pagination
 | 
			
		||||
	// Check if we have any remainder. Add 1 to pages count if so.
 | 
			
		||||
	if len(pastes)%c.Config.Pastes.Pagination > 0 {
 | 
			
		||||
	if len(pastes)%ctx.Config.Pastes.Pagination > 0 {
 | 
			
		||||
		pages++
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -169,31 +170,31 @@ func (db *Database) GetPastesPages() int {
 | 
			
		||||
 | 
			
		||||
// Initialize initializes MySQL/MariaDB connection.
 | 
			
		||||
func (db *Database) Initialize() {
 | 
			
		||||
	c.Logger.Info().Msg("Initializing database connection...")
 | 
			
		||||
	ctx.Logger.Info().Msg("Initializing database connection...")
 | 
			
		||||
 | 
			
		||||
	var userpass string
 | 
			
		||||
	if c.Config.Database.Password == "" {
 | 
			
		||||
		userpass = c.Config.Database.Username
 | 
			
		||||
	if ctx.Config.Database.Password == "" {
 | 
			
		||||
		userpass = ctx.Config.Database.Username
 | 
			
		||||
	} else {
 | 
			
		||||
		userpass = c.Config.Database.Username + ":" + c.Config.Database.Password
 | 
			
		||||
		userpass = ctx.Config.Database.Username + ":" + ctx.Config.Database.Password
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbConnString := fmt.Sprintf("postgres://%s@%s:%s/%s?connect_timeout=10&fallback_application_name=fastpastebin&sslmode=disable", userpass, c.Config.Database.Address, c.Config.Database.Port, c.Config.Database.Database)
 | 
			
		||||
	c.Logger.Debug().Str("DSN", dbConnString).Msg("Database connection string composed")
 | 
			
		||||
	dbConnString := fmt.Sprintf("postgres://%s@%s/%s?connect_timeout=10&fallback_application_name=fastpastebin&sslmode=disable", userpass, net.JoinHostPort(ctx.Config.Database.Address, ctx.Config.Database.Port), ctx.Config.Database.Database)
 | 
			
		||||
	ctx.Logger.Debug().Str("DSN", dbConnString).Msg("Database connection string composed")
 | 
			
		||||
 | 
			
		||||
	dbConn, err := sqlx.Connect("postgres", dbConnString)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.Logger.Error().Err(err).Msg("Failed to connect to database")
 | 
			
		||||
		ctx.Logger.Error().Err(err).Msg("Failed to connect to database")
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.Logger.Info().Msg("Database connection established")
 | 
			
		||||
	ctx.Logger.Info().Msg("Database connection established")
 | 
			
		||||
 | 
			
		||||
	db.db = dbConn
 | 
			
		||||
 | 
			
		||||
	// Perform migrations.
 | 
			
		||||
	migrations.New(c)
 | 
			
		||||
	migrations.New(ctx)
 | 
			
		||||
	migrations.Migrate()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -206,22 +207,22 @@ func (db *Database) SavePaste(paste *structs.Paste) (int64, error) {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var id int64
 | 
			
		||||
	var newPasteID int64
 | 
			
		||||
 | 
			
		||||
	err = stmt.Get(&id, paste)
 | 
			
		||||
	err = stmt.Get(&newPasteID, paste)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// nolint:wrapcheck
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return id, nil
 | 
			
		||||
	return newPasteID, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (db *Database) Shutdown() {
 | 
			
		||||
	if db.db != nil {
 | 
			
		||||
		err := db.db.Close()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			c.Logger.Error().Err(err).Msg("Failed to close database connection")
 | 
			
		||||
			ctx.Logger.Error().Err(err).Msg("Failed to close database connection")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -30,15 +30,15 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	c *context.Context
 | 
			
		||||
	d *Database
 | 
			
		||||
	ctx       *context.Context
 | 
			
		||||
	dbAdapter *Database
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// New initializes database structure.
 | 
			
		||||
func New(cc *context.Context) {
 | 
			
		||||
	c = cc
 | 
			
		||||
	// nolint:exhaustivestruct
 | 
			
		||||
	d = &Database{}
 | 
			
		||||
	ctx = cc
 | 
			
		||||
	// nolint:exhaustruct
 | 
			
		||||
	dbAdapter = &Database{}
 | 
			
		||||
 | 
			
		||||
	c.RegisterDatabaseInterface(databaseinterface.Interface(Handler{}))
 | 
			
		||||
	ctx.RegisterDatabaseInterface(databaseinterface.Interface(Handler{}))
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,38 +36,38 @@ import (
 | 
			
		||||
type Handler struct{}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) DeletePaste(pasteID int) error {
 | 
			
		||||
	return d.DeletePaste(pasteID)
 | 
			
		||||
	return dbAdapter.DeletePaste(pasteID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) GetDatabaseConnection() *sql.DB {
 | 
			
		||||
	return d.GetDatabaseConnection()
 | 
			
		||||
	return dbAdapter.GetDatabaseConnection()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) GetPaste(pasteID int) (*structs.Paste, error) {
 | 
			
		||||
	return d.GetPaste(pasteID)
 | 
			
		||||
	return dbAdapter.GetPaste(pasteID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) GetPagedPastes(page int) ([]structs.Paste, error) {
 | 
			
		||||
	return d.GetPagedPastes(page)
 | 
			
		||||
	return dbAdapter.GetPagedPastes(page)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) GetPastesPages() int {
 | 
			
		||||
	return d.GetPastesPages()
 | 
			
		||||
	return dbAdapter.GetPastesPages()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Initialize initializes connection to database.
 | 
			
		||||
func (dbh Handler) Initialize() {
 | 
			
		||||
	d.Initialize()
 | 
			
		||||
	dbAdapter.Initialize()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) RegisterDialect(di dialectinterface.Interface) {
 | 
			
		||||
	d.RegisterDialect(di)
 | 
			
		||||
	dbAdapter.RegisterDialect(di)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) SavePaste(p *structs.Paste) (int64, error) {
 | 
			
		||||
	return d.SavePaste(p)
 | 
			
		||||
	return dbAdapter.SavePaste(p)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbh Handler) Shutdown() {
 | 
			
		||||
	d.Shutdown()
 | 
			
		||||
	dbAdapter.Shutdown()
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -42,7 +42,8 @@ func CreateHTML(currentPage int, pages int, linksBase string) string {
 | 
			
		||||
	var (
 | 
			
		||||
		ellipsisStartAdded = false
 | 
			
		||||
		ellipsisEndAdded   = false
 | 
			
		||||
		i                  = 2
 | 
			
		||||
		// nolint:varnamelen
 | 
			
		||||
		i = 2
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	for i <= pages {
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,7 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	c   *context.Context
 | 
			
		||||
	ctx *context.Context
 | 
			
		||||
	log zerolog.Logger
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -123,6 +123,6 @@ func GetTemplate(ectx echo.Context, name string, data map[string]string) string
 | 
			
		||||
 | 
			
		||||
// Initialize initializes package.
 | 
			
		||||
func Initialize(cc *context.Context) {
 | 
			
		||||
	c = cc
 | 
			
		||||
	log = c.Logger.With().Str("type", "internal").Str("package", "templater").Logger()
 | 
			
		||||
	ctx = cc
 | 
			
		||||
	log = ctx.Logger.With().Str("type", "internal").Str("package", "templater").Logger()
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user