Compare commits
17 Commits
d85c9cb53c
...
v0.4.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
74ea9b6863
|
|||
|
30f8b4975c
|
|||
|
dcd361de1f
|
|||
|
2ec0e28243
|
|||
|
df5671586e
|
|||
|
59dafc373f
|
|||
|
25489dc103
|
|||
|
1dc6dfe00e
|
|||
|
02e933efed
|
|||
|
d2b3304a5a
|
|||
|
9164c53c54
|
|||
|
18734ebd18
|
|||
|
6b04a1dcd5
|
|||
|
1cb93c7d97
|
|||
|
f8f0302564
|
|||
|
7b6a425908
|
|||
|
591c24bab7
|
38
.drone.yml
38
.drone.yml
@@ -1,32 +1,58 @@
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: build
|
||||
name: lint and test
|
||||
|
||||
steps:
|
||||
- name: lint
|
||||
image: golangci/golangci-lint:v1.46.2
|
||||
image: code.pztrn.name/containers/mirror/golangci/golangci-lint:v1.48.0
|
||||
pull: if-not-exists
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
commands:
|
||||
- golangci-lint run
|
||||
|
||||
- name: test
|
||||
image: golang:1.18.3-alpine
|
||||
image: code.pztrn.name/containers/mirror/golang:1.19.0-alpine
|
||||
pull: if-not-exists
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
commands:
|
||||
- go test ./...
|
||||
|
||||
- name: docker
|
||||
image: plugins/docker
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: build docker images
|
||||
|
||||
depends_on:
|
||||
- "lint and test"
|
||||
|
||||
steps:
|
||||
- name: build master image
|
||||
image: code.pztrn.name/containers/mirror/plugins/docker:20.13.0
|
||||
pull: if-not-exists
|
||||
privileged: true
|
||||
when:
|
||||
branch: ["master"]
|
||||
settings:
|
||||
registry: code.pztrn.name
|
||||
username: drone
|
||||
password:
|
||||
from_secret: drone_secret
|
||||
repo: code.pztrn.name/apps/fastpastebin
|
||||
auto_tag: true
|
||||
|
||||
- name: build tagged image
|
||||
image: code.pztrn.name/containers/mirror/plugins/docker:20.13.0
|
||||
pull: if-not-exists
|
||||
privileged: true
|
||||
when:
|
||||
event: ["tag"]
|
||||
settings:
|
||||
registry: code.pztrn.name
|
||||
username: drone
|
||||
password:
|
||||
from_secret: drone_secret
|
||||
repo: code.pztrn.name/pztrn/fastpastebin
|
||||
repo: code.pztrn.name/apps/fastpastebin
|
||||
auto_tag: true
|
||||
|
||||
@@ -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
|
||||
|
||||
5
.markdownlint.json
Normal file
5
.markdownlint.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"line-length": false,
|
||||
"first-line-h1": false,
|
||||
"no-duplicate-header": false
|
||||
}
|
||||
82
CHANGELOG.md
82
CHANGELOG.md
@@ -1,12 +1,84 @@
|
||||
# Changelog
|
||||
|
||||
``[A]`` - added
|
||||
``[F]`` - fixed
|
||||
``[R]`` - removed
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
---
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## 0.1.0
|
||||
## [Unreleased]
|
||||
|
||||
## [0.4.1] - 2022-08-14
|
||||
|
||||
### Changed
|
||||
|
||||
* Update docker images - alpine to 3.16.1, golang to 1.19, golangci-lint to 1.48.0.
|
||||
* Update chroma to v2.2.0.
|
||||
* Update bulma to 0.9.4.
|
||||
* Update bulma-tooltip to 1.2 (was 3.0.0, but repo was switched to CreativeBulma).
|
||||
* Update github.com/dchest/captcha to v1.0.0.
|
||||
|
||||
### Fixed
|
||||
|
||||
* Put valid repository's link in footer.
|
||||
|
||||
### Removed
|
||||
|
||||
* Removed `flagger` dependency.
|
||||
|
||||
## [0.4.0] - 2021-01-09
|
||||
|
||||
### Added
|
||||
|
||||
* PostgreSQL support.
|
||||
* Docker containerization for every commit and tag.
|
||||
* Pastes cleanup procedure.
|
||||
|
||||
### Changed
|
||||
|
||||
* Updated bulma to v 0.7.5.
|
||||
* Moved from `io/ioutil` to `os` package for reading/writing files and directories.
|
||||
|
||||
### Fixed
|
||||
|
||||
* Dirty hack to get database connection reestablish (for sure).
|
||||
|
||||
## 0.3.0
|
||||
|
||||
Release changelogs lost :(.
|
||||
|
||||
## [0.2.0] - 2018-05-27
|
||||
|
||||
### Added
|
||||
|
||||
* Possibility to create different database backends. Currently `mysql` and `flatfiles` are available.
|
||||
|
||||
### Changed
|
||||
|
||||
* De-hardcoded pagination configuration, it is now configurable via configuration file.
|
||||
|
||||
## [0.1.1] - 2018-05-26
|
||||
|
||||
### Added
|
||||
|
||||
* Footer copyrights.
|
||||
|
||||
### Changed
|
||||
|
||||
* Refactored templates: now they're included in each other if neccessary.
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed nasty bugs with private pastes that causing fastpastebin to crash.
|
||||
* Logger level from configuration now properly set.
|
||||
|
||||
## [0.1.0] - 2018-05-19
|
||||
|
||||
First normal release. Fast Paste Bin is able to handle public, private
|
||||
and passworded pastes.
|
||||
|
||||
[Unreleased]: https://code.pztrn.name/apps/fastpastebin/compare/v0.4.1...HEAD
|
||||
[0.4.1]: https://code.pztrn.name/apps/fastpastebin/compare/0.4.0...v0.4.1
|
||||
[0.4.0]: https://code.pztrn.name/apps/fastpastebin/compare/v0.2.0...0.4.0
|
||||
[0.2.0]: https://code.pztrn.name/apps/fastpastebin/compare/v0.1.1...v0.2.0
|
||||
[0.1.1]: https://code.pztrn.name/apps/fastpastebin/compare/v0.1.0...v0.1.1
|
||||
[0.1.0]: https://code.pztrn.name/apps/fastpastebin/src/tag/v0.1.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.16.5-alpine AS build
|
||||
FROM code.pztrn.name/containers/mirror/golang:1.19.0-alpine AS build
|
||||
|
||||
WORKDIR /fastpastebin
|
||||
COPY . .
|
||||
@@ -7,7 +7,7 @@ WORKDIR /fastpastebin/cmd/fastpastebin
|
||||
|
||||
RUN CGO_ENABLED=0 go build -tags netgo
|
||||
|
||||
FROM alpine:3.13
|
||||
FROM code.pztrn.name/containers/mirror/alpine:3.16.1
|
||||
LABEL maintainer "Stanislav N. <pztrn@pztrn.name>"
|
||||
|
||||
COPY --from=build /fastpastebin/cmd/fastpastebin/fastpastebin /app/fastpastebin
|
||||
|
||||
21
README.md
21
README.md
@@ -1,9 +1,11 @@
|
||||
# Fast Pastebin
|
||||
|
||||
[](https://github-ci.pztrn.name/pztrn/fastpastebin) 
|
||||
[](https://ci.code.pztrn.name/apps/fastpastebin)
|
||||
|
||||
Easy-to-use-and-install pastebin software written in Go. No bells or whistles, no websockets and even NO JAVASCRIPT!
|
||||
|
||||
**Please, use [my gitea](https://code.pztrn.name/apps/fastpastebin) for bug reporting. All other places are mirrors!**
|
||||
|
||||
## Current functionality
|
||||
|
||||
* Create and view public and private pastes.
|
||||
@@ -14,14 +16,14 @@ Easy-to-use-and-install pastebin software written in Go. No bells or whistles, n
|
||||
|
||||
## Caveats
|
||||
|
||||
* No links at lines numbers. See [this Chroma bug](https://github.com/alecthomas/chroma/issues/132)
|
||||
* Not known at this moment.
|
||||
|
||||
## Installation and updating
|
||||
|
||||
Just issue:
|
||||
|
||||
```bash
|
||||
CGO_ENABLED=0 go get -u -v go.dev.pztrn.name/fastpastebin/cmd/fastpastebin
|
||||
CGO_ENABLED=0 go install go.dev.pztrn.name/fastpastebin/cmd/fastpastebin@latest
|
||||
```
|
||||
|
||||
This command can be used to update Fast Paste Bin.
|
||||
@@ -34,18 +36,13 @@ Configuration file position is irrelevant, there is no hardcoded paths where Fas
|
||||
|
||||
## Developing
|
||||
|
||||
Developers should install [fileb0x](https://github.com/UnnoTed/fileb0x/) which is used as replacement to go-bindata for embedding assets into binary. After changing assets they should be recompiled into Go code. At repository root execute this command and you'll be fine:
|
||||
Use linters, formatters, etc. VSCode with Go plugin is recommended for developing as it will perform most of linting-formatting
|
||||
actions automagically.
|
||||
|
||||
```bash
|
||||
fileb0x fileb0x.yml
|
||||
```
|
||||
Also, Sublime Text with LSP-gopls will also work just fine.
|
||||
|
||||
Also if you're changed list of assets (by creating or deleting them) be sure to fix files list in ``fileb0x.yml`` file!
|
||||
Try to follow [Go's code review comments](https://github.com/golang/go/wiki/CodeReviewComments) with few exceptions:
|
||||
|
||||
The rest is default - use linters, formatters, etc. VSCode with Go plugin is recommended for developing as it will perform most of linting-formatting
|
||||
actions automagically. Try to follow [Go's code review comments](https://github.com/golang/go/wiki/CodeReviewComments) with few exceptions:
|
||||
|
||||
* Imports should be organized in 3 groups: stdlib, local, other. See [this file](https://sources.dev.pztrn.name/fastpastebin/fastpastebin/src/branch/master/domains/pastes/paste_get.go) for example.
|
||||
* We're not forcing any limits on line length for code, only for comments, they should be 72-76 chars long.
|
||||
|
||||
## ToDo
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//nolint:gofmt,gofumpt,goimports
|
||||
package assets
|
||||
|
||||
import "embed"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<strong>{version}</strong> by
|
||||
<a href="https://pztrn.name">Stanislav N. aka pztrn</a>. The source code is licensed
|
||||
<a href="http://opensource.org/licenses/mit-license.php">MIT</a>. Get
|
||||
<a href="https://gitlab.pztrn.name/fastpastebin/fastpastebin">source or binary releases here</a>!
|
||||
<a href="https://code.pztrn.name/apps/fastpastebin">source or binary releases here</a>!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -38,13 +38,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field tooltip is-tooltip-bottom is-tooltip-multiline" data-tooltip="Should this paste be accessible only with special URL and not shown in pastes list? WARNING: If you'll enter password into 'Password for paste' field this checkbox will be assumed as checked!">
|
||||
<div class="field tooltip is-tooltip-bottom is-tooltip-multiline has-tooltip-arrow" data-tooltip="Should this paste be accessible only with special URL and not shown in pastes list? WARNING: If you'll enter password into 'Password for paste' field this checkbox will be assumed as checked!">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" name="paste-private" id="paste-private"> Private paste with unique URL?
|
||||
</label>
|
||||
</div>
|
||||
<div>OR</div>
|
||||
<div class="field tooltip is-tooltip-bottom is-tooltip-multiline" data-tooltip="If you'll enter password here - 'Private paste with unique URL' checkbox will be assumed as checked.">
|
||||
<div class="field tooltip is-tooltip-bottom is-tooltip-multiline has-tooltip-arrow" data-tooltip="If you'll enter password here - 'Private paste with unique URL' checkbox will be assumed as checked.">
|
||||
<label for="paste-password">Password for paste:</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" placeholder="Enter password." name="paste-password" id="paste-password">
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Fast Paste Bin</title>
|
||||
<link rel="stylesheet" href="/static/css/bulma-0.7.5.min.css">
|
||||
<link rel="stylesheet" href="/static/css/bulma-tooltip-3.0.0.min.css">
|
||||
<link rel="stylesheet" href="/static/css/bulma-0.9.4.min.css">
|
||||
<link rel="stylesheet" href="/static/css/bulma-tooltip-1.2.min.css">
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
</head>
|
||||
|
||||
|
||||
1
assets/static/css/bulma-0.7.5.min.css
vendored
1
assets/static/css/bulma-0.7.5.min.css
vendored
File diff suppressed because one or more lines are too long
1
assets/static/css/bulma-0.9.4.min.css
vendored
Normal file
1
assets/static/css/bulma-0.9.4.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2
assets/static/css/bulma-tooltip-1.2.min.css
vendored
Executable file
2
assets/static/css/bulma-tooltip-1.2.min.css
vendored
Executable file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -25,6 +25,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
@@ -48,7 +49,7 @@ func main() {
|
||||
// to be added.
|
||||
|
||||
// Parse flags.
|
||||
appCtx.Flagger.Parse()
|
||||
flag.Parse()
|
||||
|
||||
// Continue loading.
|
||||
appCtx.LoadConfiguration()
|
||||
|
||||
@@ -35,11 +35,11 @@ import (
|
||||
func dbNotAvailableGet(ec echo.Context) error {
|
||||
htmlData := templater.GetTemplate(ec, "database_not_available.html", nil)
|
||||
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return ec.HTML(http.StatusInternalServerError, htmlData)
|
||||
}
|
||||
|
||||
func dbNotAvailableRawGet(ec echo.Context) error {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return ec.String(http.StatusInternalServerError, "Database not available\nSomething went wrong while trying to connect to database. Check logs for details.")
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ package indexpage
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/alecthomas/chroma/lexers"
|
||||
"github.com/alecthomas/chroma/v2/lexers"
|
||||
"github.com/labstack/echo"
|
||||
"go.dev.pztrn.name/fastpastebin/internal/captcha"
|
||||
"go.dev.pztrn.name/fastpastebin/internal/database/dialects/flatfiles"
|
||||
@@ -37,9 +37,9 @@ 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 {
|
||||
// nolint:wrapcheck
|
||||
dbConn := ctx.Database.GetDatabaseConnection()
|
||||
if ctx.Config.Database.Type != flatfiles.FlatFileDialect && dbConn == nil {
|
||||
//nolint:wrapcheck
|
||||
return ectx.Redirect(http.StatusFound, "/database_not_available")
|
||||
}
|
||||
|
||||
@@ -56,6 +56,6 @@ func indexGet(ectx echo.Context) error {
|
||||
|
||||
htmlData := templater.GetTemplate(ectx, "index.html", map[string]string{"lexers": availableLexersSelectOpts, "captchaString": captchaString})
|
||||
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return ectx.HTML(http.StatusOK, htmlData)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -6,10 +6,9 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/alecthomas/chroma"
|
||||
htmlfmt "github.com/alecthomas/chroma/formatters/html"
|
||||
"github.com/alecthomas/chroma/lexers"
|
||||
"github.com/alecthomas/chroma/styles"
|
||||
htmlfmt "github.com/alecthomas/chroma/v2/formatters/html"
|
||||
"github.com/alecthomas/chroma/v2/lexers"
|
||||
"github.com/alecthomas/chroma/v2/styles"
|
||||
"github.com/labstack/echo"
|
||||
"go.dev.pztrn.name/fastpastebin/internal/database/dialects/flatfiles"
|
||||
"go.dev.pztrn.name/fastpastebin/internal/structs"
|
||||
@@ -31,16 +30,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 +48,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 +77,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,11 +88,11 @@ 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")
|
||||
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return ectx.HTML(http.StatusBadRequest, errtpl)
|
||||
}
|
||||
|
||||
@@ -115,16 +114,16 @@ func pasteGETWebInterface(ectx echo.Context) error {
|
||||
if err == pasteExpired || err == pasteNotFound || err == pasteTimestampInvalid {
|
||||
errtpl := templater.GetErrorTemplate(ectx, "Paste #"+pasteIDRaw+" not found")
|
||||
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return ectx.HTML(http.StatusNotFound, errtpl)
|
||||
}
|
||||
|
||||
// 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
|
||||
//nolint:wrapcheck
|
||||
return ectx.Redirect(http.StatusMovedPermanently, "/paste/"+pasteIDStr+"/"+ectx.Param("timestamp")+"/verify")
|
||||
}
|
||||
|
||||
@@ -159,7 +158,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")
|
||||
@@ -167,14 +166,14 @@ func pasteGETWebInterface(ectx echo.Context) error {
|
||||
style = styles.Fallback
|
||||
}
|
||||
// Get HTML formatter.
|
||||
formatter := chroma.Formatter(htmlfmt.New(htmlfmt.WithLineNumbers(true), htmlfmt.LineNumbersInTable(true), htmlfmt.LinkableLineNumbers(true, "L")))
|
||||
formatter := htmlfmt.New(htmlfmt.WithLineNumbers(true), htmlfmt.LineNumbersInTable(true), htmlfmt.LinkableLineNumbers(true, "L"))
|
||||
|
||||
// Create buffer and format into it.
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
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()
|
||||
@@ -182,7 +181,7 @@ func pasteGETWebInterface(ectx echo.Context) error {
|
||||
// Get template and format it.
|
||||
pasteHTML := templater.GetTemplate(ectx, "paste.html", pasteData)
|
||||
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return ectx.HTML(http.StatusOK, pasteHTML)
|
||||
}
|
||||
|
||||
@@ -195,13 +194,13 @@ 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")
|
||||
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return ectx.HTML(http.StatusBadRequest, errtpl)
|
||||
}
|
||||
|
||||
@@ -209,19 +208,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
|
||||
//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.
|
||||
@@ -231,17 +230,17 @@ func pastePasswordedVerifyGet(ectx echo.Context) error {
|
||||
|
||||
verifyHTML := templater.GetTemplate(ectx, "passworded_paste_verify.html", htmlData)
|
||||
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return ectx.HTML(http.StatusOK, verifyHTML)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// nolint:wrapcheck
|
||||
if ctx.Config.Database.Type != flatfiles.FlatFileDialect && dbConn == nil {
|
||||
//nolint:wrapcheck
|
||||
return ectx.Redirect(http.StatusFound, "/database_not_available")
|
||||
}
|
||||
|
||||
@@ -250,25 +249,25 @@ 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
|
||||
//nolint:wrapcheck
|
||||
return ectx.HTML(http.StatusBadRequest, errtpl)
|
||||
}
|
||||
|
||||
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")
|
||||
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return ectx.HTML(http.StatusBadRequest, errtpl)
|
||||
}
|
||||
|
||||
@@ -281,13 +280,13 @@ func pastePasswordedVerifyPost(ectx echo.Context) error {
|
||||
cookie.Expires = time.Now().Add(24 * time.Hour)
|
||||
ectx.SetCookie(cookie)
|
||||
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return ectx.Redirect(http.StatusFound, "/paste/"+strconv.Itoa(pasteID)+"/"+timestampRaw)
|
||||
}
|
||||
|
||||
errtpl := templater.GetErrorTemplate(ectx, "Invalid password. Please, try again.")
|
||||
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return ectx.HTML(http.StatusBadRequest, errtpl)
|
||||
}
|
||||
|
||||
@@ -295,9 +294,9 @@ 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 {
|
||||
// nolint:wrapcheck
|
||||
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,21 +304,21 @@ 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
|
||||
//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
|
||||
//nolint:wrapcheck
|
||||
return ectx.HTML(http.StatusBadRequest, "Paste #"+pasteIDRaw+" does not exist.")
|
||||
}
|
||||
|
||||
@@ -329,29 +328,29 @@ 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
|
||||
//nolint:wrapcheck
|
||||
return ectx.String(http.StatusBadRequest, "Paste #"+pasteIDRaw+" not found")
|
||||
}
|
||||
|
||||
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
|
||||
//nolint:wrapcheck
|
||||
return ectx.String(http.StatusBadRequest, "Paste #"+pasteIDRaw+" not found")
|
||||
}
|
||||
}
|
||||
|
||||
// nolint
|
||||
//nolint
|
||||
// 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")
|
||||
}
|
||||
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return ectx.String(http.StatusOK, paste.Data)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/alecthomas/chroma/lexers"
|
||||
"github.com/alecthomas/chroma/v2/lexers"
|
||||
"github.com/labstack/echo"
|
||||
"go.dev.pztrn.name/fastpastebin/internal/captcha"
|
||||
"go.dev.pztrn.name/fastpastebin/internal/database/dialects/flatfiles"
|
||||
@@ -22,54 +22,54 @@ 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 {
|
||||
// nolint:wrapcheck
|
||||
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")
|
||||
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
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.")
|
||||
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return ectx.HTML(http.StatusBadRequest, errtpl)
|
||||
}
|
||||
|
||||
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 ;).")
|
||||
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return ectx.HTML(http.StatusBadRequest, errtpl)
|
||||
}
|
||||
|
||||
// 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.")
|
||||
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return ectx.HTML(http.StatusBadRequest, errtpl)
|
||||
}
|
||||
|
||||
// nolint:exhaustivestruct
|
||||
//nolint:exhaustruct
|
||||
paste := &structs.Paste{
|
||||
Title: params["paste-title"][0],
|
||||
Data: params["paste-contents"][0],
|
||||
@@ -95,15 +95,15 @@ 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 ;).")
|
||||
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return ectx.HTML(http.StatusBadRequest, errtpl)
|
||||
}
|
||||
}
|
||||
@@ -138,25 +138,25 @@ 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.")
|
||||
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return ectx.HTML(http.StatusBadRequest, errtpl)
|
||||
}
|
||||
|
||||
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 {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return ectx.Redirect(http.StatusFound, "/paste/"+newPasteIDAsString+"/"+strconv.FormatInt(paste.CreatedAt.Unix(), 10))
|
||||
}
|
||||
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return ectx.Redirect(http.StatusFound, "/paste/"+newPasteIDAsString)
|
||||
}
|
||||
|
||||
@@ -39,9 +39,9 @@ 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 {
|
||||
// nolint:wrapcheck
|
||||
dbConn := ctx.Database.GetDatabaseConnection()
|
||||
if ctx.Config.Database.Type != flatfiles.FlatFileDialect && dbConn == nil {
|
||||
//nolint:wrapcheck
|
||||
return ectx.Redirect(http.StatusFound, "/database_not_available")
|
||||
}
|
||||
|
||||
@@ -54,21 +54,21 @@ 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.")
|
||||
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return ectx.HTML(http.StatusOK, noPastesToShowTpl)
|
||||
}
|
||||
|
||||
@@ -100,12 +100,12 @@ 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})
|
||||
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return ectx.HTML(http.StatusOK, pasteListTpl)
|
||||
}
|
||||
|
||||
112
fileb0x.yml
112
fileb0x.yml
@@ -1,112 +0,0 @@
|
||||
# all folders and files are relative to the path
|
||||
# where fileb0x was run at!
|
||||
|
||||
# default: main
|
||||
pkg: static
|
||||
|
||||
# destination
|
||||
dest: "./assets/static/"
|
||||
|
||||
# gofmt
|
||||
# type: bool
|
||||
# default: false
|
||||
fmt: false
|
||||
|
||||
# compress files
|
||||
# at the moment, only supports gzip
|
||||
#
|
||||
# type: object
|
||||
compression:
|
||||
# activates the compression
|
||||
#
|
||||
# type: bool
|
||||
# default: false
|
||||
compress: false
|
||||
|
||||
# valid values are:
|
||||
# -> "NoCompression"
|
||||
# -> "BestSpeed"
|
||||
# -> "BestCompression"
|
||||
# -> "DefaultCompression" or ""
|
||||
#
|
||||
# type: string
|
||||
# default: "DefaultCompression" # when: Compress == true && Method == ""
|
||||
method: ""
|
||||
|
||||
# true = do it yourself (the file is written as gzip compressed file into the memory file system)
|
||||
# false = decompress files at run time (while writing file into memory file system)
|
||||
#
|
||||
# type: bool
|
||||
# default: false
|
||||
keep: false
|
||||
|
||||
# ---------------
|
||||
# -- DANGEROUS --
|
||||
# ---------------
|
||||
#
|
||||
# cleans the destination folder (only b0xfiles)
|
||||
# you should use this when using the spread function
|
||||
# type: bool
|
||||
# default: false
|
||||
clean: true
|
||||
|
||||
# default: ab0x.go
|
||||
output: "ab0x.go"
|
||||
|
||||
# [unexporTed] builds non-exporTed functions, variables and types...
|
||||
# type: bool
|
||||
# default: false
|
||||
unexporTed: false
|
||||
|
||||
# [spread] means it will make a file to hold all fileb0x data
|
||||
# and each file into a separaTed .go file
|
||||
#
|
||||
# example:
|
||||
# theres 2 files in the folder assets, they're: hello.json and world.txt
|
||||
# when spread is activaTed, fileb0x will make a file:
|
||||
# b0x.go or [output]'s data, assets_hello.json.go and assets_world.txt.go
|
||||
#
|
||||
#
|
||||
# type: bool
|
||||
# default: false
|
||||
spread: true
|
||||
|
||||
# [lcf] log changed files when spread is active
|
||||
lcf: true
|
||||
|
||||
# type: array of objects
|
||||
custom:
|
||||
|
||||
# type: array of strings
|
||||
- files:
|
||||
- "assets/css/bulma-0.7.5.min.css"
|
||||
- "assets/css/bulma-tooltip-3.0.0.min.css"
|
||||
- "assets/css/bulma.css.map"
|
||||
- "assets/css/style.css"
|
||||
- "assets/js/fontawesome-5.0.7.js"
|
||||
|
||||
# base is the path that will be removed from all files' path
|
||||
# type: string
|
||||
base: "assets"
|
||||
|
||||
# prefix is the path that will be added to all files' path
|
||||
# type: string
|
||||
prefix: "static/"
|
||||
# end: files
|
||||
- files:
|
||||
- "assets/database_not_available.html"
|
||||
- "assets/error.html"
|
||||
- "assets/footer.html"
|
||||
- "assets/index.html"
|
||||
- "assets/main.html"
|
||||
- "assets/navigation.html"
|
||||
- "assets/pagination_ellipsis.html"
|
||||
- "assets/pagination_link_current.html"
|
||||
- "assets/pagination_link.html"
|
||||
- "assets/pagination.html"
|
||||
- "assets/passworded_paste_verify.html"
|
||||
- "assets/paste.html"
|
||||
- "assets/pastelist_list.html"
|
||||
- "assets/pastelist_paste.html"
|
||||
base: "assets"
|
||||
prefix: ""
|
||||
27
go.mod
27
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/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||
github.com/alecthomas/chroma/v2 v2.2.0
|
||||
github.com/dchest/captcha v1.0.0
|
||||
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/dlclark/regexp2 v1.7.0 // indirect
|
||||
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
|
||||
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
|
||||
)
|
||||
|
||||
171
go.sum
171
go.sum
@@ -1,134 +1,91 @@
|
||||
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/v2 v2.2.0 h1:Aten8jfQwUqEdadVFFjNyjx7HTexhKP0XuqBG67mRDY=
|
||||
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
|
||||
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae h1:zzGwJfFlFGD94CyyYwCJeSuD32Gj9GTaSi5y9hoVzdY=
|
||||
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
||||
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=
|
||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f h1:q/DpyjJjZs94bziQ7YkBmIlpqbVP7yw179rnzoNVX1M=
|
||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f/go.mod h1:QGrK8vMWWHQYQ3QU9bw9Y9OPNfxccGzfb41qjvVeXtY=
|
||||
github.com/dchest/captcha v1.0.0 h1:vw+bm/qMFvTgcjQlYVTuQBJkarm5R0YSsDKhm1HZI2o=
|
||||
github.com/dchest/captcha v1.0.0/go.mod h1:7zoElIawLp7GUMLcj54K9kbw+jEyvz2K0FDdRRYhvWo=
|
||||
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/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
|
||||
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
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=
|
||||
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.
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"flag"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -33,7 +33,6 @@ import (
|
||||
"github.com/rs/zerolog"
|
||||
"go.dev.pztrn.name/fastpastebin/internal/config"
|
||||
databaseinterface "go.dev.pztrn.name/fastpastebin/internal/database/interface"
|
||||
"go.dev.pztrn.name/flagger"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
@@ -42,26 +41,18 @@ import (
|
||||
// contains everything every part of application need, like configuration
|
||||
// access, logger, etc.
|
||||
type Context struct {
|
||||
Config *config.Struct
|
||||
Database databaseinterface.Interface
|
||||
Echo *echo.Echo
|
||||
Flagger *flagger.Flagger
|
||||
Logger zerolog.Logger
|
||||
Config *config.Struct
|
||||
Database databaseinterface.Interface
|
||||
Echo *echo.Echo
|
||||
Logger zerolog.Logger
|
||||
configPathFromCLI string
|
||||
}
|
||||
|
||||
// Initialize initializes context.
|
||||
func (c *Context) Initialize() {
|
||||
c.initializeLogger()
|
||||
|
||||
c.Flagger = flagger.New("fastpastebin", nil)
|
||||
c.Flagger.Initialize()
|
||||
|
||||
_ = c.Flagger.AddFlag(&flagger.Flag{
|
||||
Name: "config",
|
||||
Description: "Configuration file path. Can be overridded with FASTPASTEBIN_CONFIG environment variable (this is what used in tests).",
|
||||
Type: "string",
|
||||
DefaultValue: "NO_CONFIG",
|
||||
})
|
||||
flag.StringVar(&c.configPathFromCLI, "config", "NO_CONFIG", "Configuration file path. Can be overridded with FASTPASTEBIN_CONFIG environment variable.")
|
||||
}
|
||||
|
||||
// InitializePost initializes everything that needs a configuration.
|
||||
@@ -76,20 +67,18 @@ func (c *Context) InitializePost() {
|
||||
func (c *Context) LoadConfiguration() {
|
||||
c.Logger.Info().Msg("Loading configuration...")
|
||||
|
||||
configPath := ""
|
||||
configPath := c.configPathFromCLI
|
||||
|
||||
// We're accepting configuration path from "-config" CLI parameter
|
||||
// and FASTPASTEBIN_CONFIG environment variable. Later have higher
|
||||
// weight and can override "-config" value.
|
||||
configPathFromCLI, err := c.Flagger.GetStringValue("config")
|
||||
configPathFromEnv, configPathFromEnvFound := os.LookupEnv("FASTPASTEBIN_CONFIG")
|
||||
|
||||
if err != nil && configPathFromEnvFound || err == nil && configPathFromEnvFound {
|
||||
if configPathFromEnvFound {
|
||||
configPath = configPathFromEnv
|
||||
} else if err != nil && !configPathFromEnvFound || err == nil && configPathFromCLI == "NO_CONFIG" {
|
||||
}
|
||||
|
||||
if configPath == "NO_CONFIG" {
|
||||
c.Logger.Panic().Msg("Configuration file path wasn't passed via '-config' or 'FASTPASTEBIN_CONFIG' environment variable. Cannot continue.")
|
||||
} else if err == nil && !configPathFromEnvFound {
|
||||
configPath = configPathFromCLI
|
||||
}
|
||||
|
||||
// Normalize file path.
|
||||
@@ -100,11 +89,11 @@ 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.
|
||||
fileData, err2 := ioutil.ReadFile(normalizedConfigPath)
|
||||
fileData, err2 := os.ReadFile(normalizedConfigPath)
|
||||
if err2 != nil {
|
||||
c.Logger.Panic().Err(err2).Msg("Failed to read configuration file")
|
||||
}
|
||||
|
||||
@@ -26,11 +26,11 @@ package context
|
||||
|
||||
const (
|
||||
// Version .
|
||||
Version = "0.4.0"
|
||||
Version = "0.4.1"
|
||||
)
|
||||
|
||||
// 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,18 +68,18 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
func (db *Database) DeletePaste(pasteID int) error {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return db.db.DeletePaste(pasteID)
|
||||
}
|
||||
|
||||
@@ -92,12 +92,12 @@ func (db *Database) GetDatabaseConnection() *sql.DB {
|
||||
}
|
||||
|
||||
func (db *Database) GetPaste(pasteID int) (*structs.Paste, error) {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return db.db.GetPaste(pasteID)
|
||||
}
|
||||
|
||||
func (db *Database) GetPagedPastes(page int) ([]structs.Paste, error) {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return db.db.GetPagedPastes(page)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
@@ -128,7 +128,7 @@ func (db *Database) RegisterDialect(di dialectinterface.Interface) {
|
||||
}
|
||||
|
||||
func (db *Database) SavePaste(p *structs.Paste) (int64, error) {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return db.db.SavePaste(p)
|
||||
}
|
||||
|
||||
|
||||
@@ -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{}))
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ package flatfiles
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
@@ -49,9 +48,9 @@ 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
|
||||
//nolint:wrapcheck
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -83,27 +82,27 @@ 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)
|
||||
pasteInBytes, err := os.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
|
||||
//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
|
||||
//nolint:wrapcheck
|
||||
return nil, err1
|
||||
}
|
||||
|
||||
@@ -114,7 +113,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 +129,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"))
|
||||
pasteRawData, err := os.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 +184,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 +194,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,70 +212,70 @@ 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"))
|
||||
indexData, err := os.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")
|
||||
}
|
||||
}
|
||||
|
||||
func (ff *FlatFiles) SavePaste(paste *structs.Paste) (int64, error) {
|
||||
ff.writeMutex.Lock()
|
||||
// Write paste data on disk.
|
||||
filesOnDisk, _ := ioutil.ReadDir(filepath.Join(ff.path, "pastes"))
|
||||
filesOnDisk, _ := os.ReadDir(filepath.Join(ff.path, "pastes"))
|
||||
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 {
|
||||
ff.writeMutex.Unlock()
|
||||
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return 0, err
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(filepath.Join(ff.path, "pastes", strconv.Itoa(pasteID)+".json"), data, 0o600)
|
||||
err = os.WriteFile(filepath.Join(ff.path, "pastes", strconv.Itoa(pasteID)+".json"), data, 0o600)
|
||||
if err != nil {
|
||||
ff.writeMutex.Unlock()
|
||||
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Add it to cache.
|
||||
// nolint:exhaustivestruct
|
||||
//nolint:exhaustruct
|
||||
indexData := Index{}
|
||||
indexData.ID = pasteID
|
||||
indexData.Private = paste.Private
|
||||
@@ -287,18 +286,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)
|
||||
err1 := os.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()
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ func InitialUp(tx *sql.Tx) error {
|
||||
keep_for_unit_type int(1) NOT NULL DEFAULT 1 COMMENT 'Keep for unit type. 1 - minutes, 2 - hours, 3 - days, 4 - months.',
|
||||
PRIMARY KEY (id), UNIQUE KEY id (id)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='Pastes';`)
|
||||
if err != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
func PasteLangUp(tx *sql.Tx) error {
|
||||
_, err := tx.Exec("ALTER TABLE `pastes` ADD `language` VARCHAR(191) NOT NULL DEFAULT 'text' COMMENT 'Paste language'")
|
||||
if err != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ func PasteLangUp(tx *sql.Tx) error {
|
||||
func PasteLangDown(tx *sql.Tx) error {
|
||||
_, err := tx.Exec("ALTER TABLE `pastes` DROP COLUMN `language`")
|
||||
if err != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
func PrivatePastesUp(tx *sql.Tx) error {
|
||||
_, err := tx.Exec("ALTER TABLE `pastes` ADD `private` BOOL NOT NULL DEFAULT false COMMENT 'Private paste? If true - additional URL parameter (UNIX TIMESTAMP) of paste will be required to access.'")
|
||||
if err != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ func PrivatePastesUp(tx *sql.Tx) error {
|
||||
func PrivatePastesDown(tx *sql.Tx) error {
|
||||
_, err := tx.Exec("ALTER TABLE `pastes` DROP COLUMN `private`")
|
||||
if err != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -31,13 +31,13 @@ import (
|
||||
func PasswordedPastesUp(txn *sql.Tx) error {
|
||||
_, err := txn.Exec("ALTER TABLE `pastes` ADD `password` varchar(64) NOT NULL DEFAULT '' COMMENT 'Password for paste (scrypted and sha256ed).'")
|
||||
if err != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return err
|
||||
}
|
||||
|
||||
_, err1 := txn.Exec("ALTER TABLE `pastes` ADD `password_salt` varchar(64) NOT NULL DEFAULT '' COMMENT 'Password salt (sha256ed).'")
|
||||
if err1 != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return err1
|
||||
}
|
||||
|
||||
@@ -47,13 +47,13 @@ func PasswordedPastesUp(txn *sql.Tx) error {
|
||||
func PasswordedPastesDown(txn *sql.Tx) error {
|
||||
_, err := txn.Exec("ALTER TABLE `pastes` DROP COLUMN `password`")
|
||||
if err != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return err
|
||||
}
|
||||
|
||||
_, err1 := txn.Exec("ALTER TABLE `pastes` DROP COLUMN `password_salt`")
|
||||
if err1 != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return err1
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ func (db *Database) DeletePaste(pasteID int) error {
|
||||
|
||||
_, err := db.db.Exec(db.db.Rebind("DELETE FROM pastes WHERE id=?"), pasteID)
|
||||
if err != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -80,12 +80,12 @@ 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)
|
||||
if err != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -103,12 +103,12 @@ 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
|
||||
//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()
|
||||
}
|
||||
|
||||
@@ -191,13 +191,13 @@ func (db *Database) SavePaste(p *structs.Paste) (int64, error) {
|
||||
|
||||
result, err := db.db.NamedExec("INSERT INTO `pastes` (title, data, created_at, keep_for, keep_for_unit_type, language, private, password, password_salt) VALUES (:title, :data, :created_at, :keep_for, :keep_for_unit_type, :language, :private, :password, :password_salt)", p)
|
||||
if err != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return 0, err
|
||||
}
|
||||
|
||||
lastInsertID, err1 := result.LastInsertId()
|
||||
if err1 != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ func InitialUp(tx *sql.Tx) error {
|
||||
COMMENT ON COLUMN pastes.keep_for_unit_type IS 'Keep for unit type. 0 - forever, 1 - minutes, 2 - hours, 3 - days, 4 - months.';
|
||||
`)
|
||||
if err != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
func PasteLangUp(tx *sql.Tx) error {
|
||||
_, err := tx.Exec("ALTER TABLE pastes ADD COLUMN language VARCHAR(191) NOT NULL DEFAULT 'text'; COMMENT ON COLUMN pastes.language IS 'Paste language';")
|
||||
if err != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ func PasteLangUp(tx *sql.Tx) error {
|
||||
func PasteLangDown(tx *sql.Tx) error {
|
||||
_, err := tx.Exec("ALTER TABLE pastes DROP COLUMN language")
|
||||
if err != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
func PrivatePastesUp(tx *sql.Tx) error {
|
||||
_, err := tx.Exec("ALTER TABLE pastes ADD COLUMN private BOOLEAN NOT NULL DEFAULT false; COMMENT ON COLUMN pastes.private IS 'Private paste? If true - additional URL parameter (UNIX TIMESTAMP) of paste will be required to access.';")
|
||||
if err != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ func PrivatePastesUp(tx *sql.Tx) error {
|
||||
func PrivatePastesDown(tx *sql.Tx) error {
|
||||
_, err := tx.Exec("ALTER TABLE pastes DROP COLUMN private")
|
||||
if err != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -31,13 +31,13 @@ import (
|
||||
func PasswordedPastesUp(txn *sql.Tx) error {
|
||||
_, err := txn.Exec("ALTER TABLE pastes ADD COLUMN password VARCHAR(64) NOT NULL DEFAULT ''; COMMENT ON COLUMN pastes.password IS 'Password for paste (scrypted and sha256ed).';")
|
||||
if err != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return err
|
||||
}
|
||||
|
||||
_, err1 := txn.Exec("ALTER TABLE pastes ADD COLUMN password_salt VARCHAR(64) NOT NULL DEFAULT ''; COMMENT ON COLUMN pastes.password_salt IS 'Password salt (sha256ed).';")
|
||||
if err1 != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return err1
|
||||
}
|
||||
|
||||
@@ -47,13 +47,13 @@ func PasswordedPastesUp(txn *sql.Tx) error {
|
||||
func PasswordedPastesDown(txn *sql.Tx) error {
|
||||
_, err := txn.Exec("ALTER TABLE pastes DROP COLUMN password")
|
||||
if err != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return err
|
||||
}
|
||||
|
||||
_, err1 := txn.Exec("ALTER TABLE pastes DROP COLUMN password_salt")
|
||||
if err1 != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return err1
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,10 +24,11 @@
|
||||
|
||||
package postgresql
|
||||
|
||||
// nolint:gci
|
||||
//nolint:gci
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
// PostgreSQL driver.
|
||||
@@ -62,7 +63,7 @@ func (db *Database) DeletePaste(pasteID int) error {
|
||||
|
||||
_, err := db.db.Exec(db.db.Rebind("DELETE FROM pastes WHERE id=?"), pasteID)
|
||||
if err != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -83,12 +84,12 @@ 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)
|
||||
if err != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -113,12 +114,12 @@ 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
|
||||
//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()
|
||||
}
|
||||
|
||||
@@ -202,26 +203,26 @@ func (db *Database) SavePaste(paste *structs.Paste) (int64, error) {
|
||||
|
||||
stmt, err := db.db.PrepareNamed("INSERT INTO pastes (title, data, created_at, keep_for, keep_for_unit_type, language, private, password, password_salt) VALUES (:title, :data, :created_at, :keep_for, :keep_for_unit_type, :language, :private, :password, :password_salt) RETURNING id")
|
||||
if err != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var id int64
|
||||
var newPasteID int64
|
||||
|
||||
err = stmt.Get(&id, paste)
|
||||
err = stmt.Get(&newPasteID, paste)
|
||||
if err != nil {
|
||||
// nolint:wrapcheck
|
||||
//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,12 +42,13 @@ func CreateHTML(currentPage int, pages int, linksBase string) string {
|
||||
var (
|
||||
ellipsisStartAdded = false
|
||||
ellipsisEndAdded = false
|
||||
i = 2
|
||||
//nolint:varnamelen
|
||||
i = 2
|
||||
)
|
||||
|
||||
for i <= pages {
|
||||
// ToDo: fix it!
|
||||
// nolint:nestif
|
||||
//nolint:nestif
|
||||
if pages > 5 {
|
||||
if currentPage-3 < i && currentPage+3 > i || i == pages {
|
||||
paginationItemRaw := string(paginationLinkRaw)
|
||||
|
||||
@@ -76,7 +76,7 @@ type Paste struct {
|
||||
func (p *Paste) CreatePassword(password string) error {
|
||||
// Create salt - random string.
|
||||
// Yes, it is insecure. Should be refactored!
|
||||
// nolint:gosec
|
||||
//nolint:gosec
|
||||
seededRand := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
saltBytes := make([]byte, 64)
|
||||
|
||||
@@ -90,7 +90,7 @@ func (p *Paste) CreatePassword(password string) error {
|
||||
// Create crypted password and hash it.
|
||||
passwordCrypted, err := scrypt.Key([]byte(password), []byte(p.PasswordSalt), 131072, 8, 1, 64)
|
||||
if err != nil {
|
||||
// nolint:wrapcheck
|
||||
//nolint:wrapcheck
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -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