Pagination, readable error messages to user, syntax highlighting started.

Pagination now works. Temporary hardcoded 10 pastes per page, will be put
in configuration later. Maybe.

From now user will receive readable error message if error occured.

Started to work on syntax highlighting, tried to make lexers detection
work but apparently to no avail.
This commit is contained in:
2018-05-01 02:37:51 +05:00
parent 79c7d39759
commit 48d43ca097
221 changed files with 30321 additions and 29 deletions

View File

@@ -34,8 +34,10 @@ import (
// local
"github.com/pztrn/fastpastebin/api/http/static"
"github.com/pztrn/fastpastebin/pagination"
// other
"github.com/alecthomas/chroma/lexers"
"github.com/labstack/echo"
)
@@ -60,7 +62,8 @@ func pasteGET(ec echo.Context) error {
paste, err1 := GetByID(pasteID)
if err1 != nil {
c.Logger.Error().Msgf("Failed to get paste #%d from database: %s", pasteID, err1.Error())
return ec.HTML(http.StatusBadRequest, string(errhtml))
errhtmlAsString := strings.Replace(string(errhtml), "{error}", "Paste #"+strconv.Itoa(pasteID)+" not found", 1)
return ec.HTML(http.StatusBadRequest, errhtmlAsString)
}
pasteHTML, err2 := static.ReadFile("paste.html")
@@ -88,14 +91,23 @@ func pastePOST(ec echo.Context) error {
}
c.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")
errhtmlAsString := strings.Replace(string(errhtml), "{error}", "Empty pastes aren't allowed.", 1)
return ec.HTML(http.StatusBadRequest, errhtmlAsString)
}
if !strings.ContainsAny(params["paste-keep-for"][0], "Mmhd") {
c.Logger.Debug().Msgf("'Keep paste for' field have invalid value: %s", params["paste-keep-for"][0])
return ec.HTML(http.StatusBadRequest, string(errhtml))
errhtmlAsString := strings.Replace(string(errhtml), "{error}", "Invalid 'Paste should be available for' parameter passed. Please do not try to hack us ;).", 1)
return ec.HTML(http.StatusBadRequest, errhtmlAsString)
}
paste := &Paste{
Title: params["paste-title"][0],
Data: params["paste-contents"][0],
Title: params["paste-title"][0],
Data: params["paste-contents"][0],
Language: params["paste-language"][0],
}
// Paste creation time in UTC.
@@ -111,7 +123,8 @@ func pastePOST(ec echo.Context) error {
keepFor, err1 := strconv.Atoi(keepForRaw)
if err1 != nil {
c.Logger.Debug().Msgf("Failed to parse 'Keep for' integer: %s", err1.Error())
return ec.HTML(http.StatusBadRequest, string(errhtml))
errhtmlAsString := strings.Replace(string(errhtml), "{error}", "Invalid 'Paste should be available for' parameter passed. Please do not try to hack us ;).", 1)
return ec.HTML(http.StatusBadRequest, errhtmlAsString)
}
paste.KeepFor = keepFor
@@ -119,10 +132,21 @@ func pastePOST(ec echo.Context) error {
keepForUnit := PASTE_KEEPS_CORELLATION[keepForUnitRaw]
paste.KeepForUnitType = keepForUnit
// Try to autodetect if it was selected.
if params["paste-language"][0] == "autodetect" {
lexer := lexers.Analyse(params["paste-language"][0])
if lexer != nil {
paste.Language = lexer.Config().Name
} else {
paste.Language = "text"
}
}
id, err2 := Save(paste)
if err2 != nil {
c.Logger.Debug().Msgf("Failed to save paste: %s", err2.Error())
return ec.HTML(http.StatusBadRequest, string(errhtml))
errhtmlAsString := strings.Replace(string(errhtml), "{error}", "Failed to save paste. Please, try again later.", 1)
return ec.HTML(http.StatusBadRequest, errhtmlAsString)
}
newPasteIDAsString := strconv.FormatInt(id, 10)
@@ -142,7 +166,7 @@ func pastesGET(ec echo.Context) error {
return ec.String(http.StatusNotFound, "pastelist_paste.html wasn't found!")
}
pageFromParamRaw := ec.Param("id")
pageFromParamRaw := ec.Param("page")
var page = 1
if pageFromParamRaw != "" {
pageRaw := regexInts.FindAllString(pageFromParamRaw, 1)[0]
@@ -187,5 +211,10 @@ func pastesGET(ec echo.Context) error {
pasteListHTMLAsString := strings.Replace(string(pasteListHTML), "{pastes}", pastesString, 1)
pages := GetPastesPages()
c.Logger.Debug().Msgf("Total pages: %d, current: %d", pages, page)
paginationHTML := pagination.CreateHTML(page, pages, "/pastes/")
pasteListHTMLAsString = strings.Replace(pasteListHTMLAsString, "{pagination}", paginationHTML, -1)
return ec.HTML(http.StatusOK, string(pasteListHTMLAsString))
}

View File

@@ -27,6 +27,8 @@ package pastes
import (
// stdlib
"time"
// other
//"github.com/alecthomas/chroma"
)
const (
@@ -53,4 +55,7 @@ type Paste struct {
CreatedAt *time.Time `db:"created_at"`
KeepFor int `db:"keep_for"`
KeepForUnitType int `db:"keep_for_unit_type"`
Language string `db:"language"`
}
func (p *Paste) Highlight() {}

View File

@@ -24,6 +24,11 @@
package pastes
const (
// Pagination. Hardcoded for 30 for now.
PAGINATION = 10
)
// GetByID returns a single paste by ID.
func GetByID(id int) (*Paste, error) {
p := &Paste{}
@@ -44,10 +49,10 @@ func GetPagedPastes(page int) ([]Paste, error) {
// Pagination - 30 pastes on page.
var startPagination = 0
if page > 1 {
startPagination = (page - 1) * 30
startPagination = (page - 1) * PAGINATION
}
err := dbConn.Select(&pastes, dbConn.Rebind("SELECT * FROM `pastes` ORDER BY id DESC LIMIT 30 OFFSET ?"), startPagination)
err := dbConn.Select(&pastes, dbConn.Rebind("SELECT * FROM `pastes` ORDER BY id DESC LIMIT ? OFFSET ?"), PAGINATION, startPagination)
if err != nil {
return nil, err
}
@@ -56,10 +61,30 @@ func GetPagedPastes(page int) ([]Paste, error) {
}
// GetPastesPages returns an integer that represents quantity of pages
// that can be requested (or drawn in paginator).
func GetPastesPages() int {
var pastesCount int
dbConn := c.Database.GetDatabaseConnection()
err := dbConn.Get(&pastesCount, "SELECT COUNT(id) FROM `pastes`")
if err != nil {
return 1
}
// Calculate pages.
pages := pastesCount / PAGINATION
// Check if we have any remainder. Add 1 to pages count if so.
if pastesCount%PAGINATION != 0 {
pages += 1
}
return pages
}
// Save saves paste to database and returns it's ID.
func Save(p *Paste) (int64, error) {
dbConn := c.Database.GetDatabaseConnection()
result, err := dbConn.NamedExec("INSERT INTO `pastes` (title, data, created_at, keep_for, keep_for_unit_type) VALUES (:title, :data, :created_at, :keep_for, :keep_for_unit_type)", p)
result, err := dbConn.NamedExec("INSERT INTO `pastes` (title, data, created_at, keep_for, keep_for_unit_type, language) VALUES (:title, :data, :created_at, :keep_for, :keep_for_unit_type, :language)", p)
if err != nil {
return 0, err
}