Started big code refactoring
parent
75a4b715d9
commit
b913f7b5ed
@ -1,2 +1,4 @@
|
||||
github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0=
|
||||
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.13/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/yuin/goldmark v1.4.12/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
|
@ -0,0 +1,37 @@
|
||||
// Copyright (C) 2021-2022 Leonid Maslakov.
|
||||
|
||||
// This file is part of Lenpaste.
|
||||
|
||||
// Lenpaste is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the
|
||||
// GNU Affero Public License as published by the
|
||||
// Free Software Foundation, either version 3 of the License,
|
||||
// or (at your option) any later version.
|
||||
|
||||
// Lenpaste is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU Affero Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero Public License along with Lenpaste.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package logger
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
}
|
||||
|
||||
func (config Config) HttpRequest(req *http.Request) {
|
||||
now := time.Now()
|
||||
println(now.Format("2006/01/02 15:04:05"), "[REQUEST]", req.RemoteAddr, req.Method, req.URL.Path)
|
||||
}
|
||||
|
||||
func (config Config) HttpError(req *http.Request, err error) {
|
||||
now := time.Now()
|
||||
println(now.Format("2006/01/02 15:04:05"), "[ERROR]", req.RemoteAddr, req.Method, req.URL.Path, "Error:", err.Error())
|
||||
}
|
@ -1,385 +0,0 @@
|
||||
// Copyright (C) 2021-2022 Leonid Maslakov.
|
||||
|
||||
// This file is part of Lenpaste.
|
||||
|
||||
// Lenpaste is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the
|
||||
// GNU Affero Public License as published by the
|
||||
// Free Software Foundation, either version 3 of the License,
|
||||
// or (at your option) any later version.
|
||||
|
||||
// Lenpaste is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU Affero Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero Public License along with Lenpaste.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package pages
|
||||
|
||||
import (
|
||||
"git.lcomrade.su/root/lenpaste/internal/config"
|
||||
"git.lcomrade.su/root/lenpaste/internal/storage"
|
||||
"bytes"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
webDir = "./web"
|
||||
)
|
||||
|
||||
type PastePageType struct {
|
||||
Title string
|
||||
Text string
|
||||
OneUse bool
|
||||
ExpiresMin int64
|
||||
ExpiresHour int64
|
||||
ExpiresDay int64
|
||||
//Syntax string
|
||||
//Password string
|
||||
}
|
||||
|
||||
type errorPageType struct {
|
||||
Code int
|
||||
Error string
|
||||
}
|
||||
|
||||
//Style
|
||||
func Style(rw http.ResponseWriter, req *http.Request) {
|
||||
//Return response
|
||||
rw.Header().Set("Content-Type", "text/css")
|
||||
|
||||
io.WriteString(rw, pages.StyleCSS)
|
||||
}
|
||||
|
||||
//New paste
|
||||
func NewPaste(rw http.ResponseWriter, req *http.Request) {
|
||||
//Return response
|
||||
rw.Header().Set("Content-Type", "text/html")
|
||||
|
||||
io.WriteString(rw, pages.New)
|
||||
}
|
||||
|
||||
//New paste done
|
||||
type NewPasteType struct {
|
||||
Name string
|
||||
Host string
|
||||
}
|
||||
|
||||
func NewPasteDone(rw http.ResponseWriter, req *http.Request) {
|
||||
//Get form data
|
||||
req.ParseForm()
|
||||
|
||||
text := req.Form.Get("text")
|
||||
expiration := req.Form.Get("expiration")
|
||||
title := req.Form.Get("title")
|
||||
|
||||
oneUse := false
|
||||
if req.Form.Get("oneUse") == "true" {
|
||||
oneUse = true
|
||||
}
|
||||
|
||||
//Create paste
|
||||
paste, err := storage.NewPaste(text, expiration, oneUse, title)
|
||||
if err != nil {
|
||||
errorHandler(rw, err, 400)
|
||||
return
|
||||
}
|
||||
|
||||
//Total paste info
|
||||
pasteTotal := NewPasteType{
|
||||
Name: paste.Name,
|
||||
Host: req.Host,
|
||||
}
|
||||
|
||||
//Set Header
|
||||
rw.Header().Set("Content-Type", "text/html")
|
||||
|
||||
//Filling the html page template
|
||||
tmpl := pages.NewDoneTmpl
|
||||
|
||||
err = tmpl.Execute(rw, pasteTotal)
|
||||
if err != nil {
|
||||
errorHandler(rw, err, 400)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
//About API page
|
||||
func API(rw http.ResponseWriter, req *http.Request) {
|
||||
//Return response
|
||||
rw.Header().Set("Content-Type", "text/html")
|
||||
|
||||
io.WriteString(rw, pages.API)
|
||||
}
|
||||
|
||||
//Server rules page
|
||||
func Rules(rw http.ResponseWriter, req *http.Request) {
|
||||
//Return response
|
||||
rw.Header().Set("Content-Type", "text/html")
|
||||
|
||||
io.WriteString(rw, pages.Rules)
|
||||
}
|
||||
|
||||
//Server version page
|
||||
func Version(rw http.ResponseWriter, req *http.Request) {
|
||||
//Return response
|
||||
rw.Header().Set("Content-Type", "text/html")
|
||||
|
||||
io.WriteString(rw, pages.Version)
|
||||
}
|
||||
|
||||
//Get paste
|
||||
func GetPaste(rw http.ResponseWriter, req *http.Request) {
|
||||
//Set Header
|
||||
rw.Header().Set("Content-Type", "text/html")
|
||||
|
||||
//Main page
|
||||
if req.URL.Path == "/" {
|
||||
io.WriteString(rw, pages.Main)
|
||||
return
|
||||
}
|
||||
|
||||
//Get paste name
|
||||
name := filepath.Base(req.URL.Path)
|
||||
|
||||
//Get paste
|
||||
pasteInfo, err := storage.GetPaste(name)
|
||||
if err != nil {
|
||||
errorHandler(rw, err, 404)
|
||||
return
|
||||
}
|
||||
|
||||
//Convert paste info
|
||||
deltaTime := pasteInfo.Info.DeleteTime - time.Now().Unix()
|
||||
deltaTime = deltaTime / 60
|
||||
pastePage := PastePageType{
|
||||
Title: pasteInfo.Info.Title,
|
||||
Text: pasteInfo.Text,
|
||||
OneUse: pasteInfo.Info.OneUse,
|
||||
ExpiresMin: deltaTime % 60,
|
||||
ExpiresHour: deltaTime / 60 % 24,
|
||||
ExpiresDay: deltaTime / 60 / 24,
|
||||
}
|
||||
|
||||
//Filling the html page template
|
||||
tmpl := pages.GetTmpl
|
||||
|
||||
err = tmpl.Execute(rw, pastePage)
|
||||
if err != nil {
|
||||
errorHandler(rw, err, 400)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
//Error page
|
||||
func errorHandler(rw http.ResponseWriter, err error, code int) {
|
||||
//Set Header
|
||||
rw.WriteHeader(code)
|
||||
rw.Header().Set("Content-Type", "text/html")
|
||||
|
||||
//Get error info
|
||||
errorInfo := errorPageType{
|
||||
Code: code,
|
||||
Error: err.Error(),
|
||||
}
|
||||
|
||||
//Filling the html page template
|
||||
tmpl := pages.ErrorTmpl
|
||||
|
||||
err = tmpl.Execute(rw, errorInfo)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), 400)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
//Load pages (init)
|
||||
func loadFile(path string) (string, error) {
|
||||
var out string
|
||||
|
||||
//Open file
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
//Read file
|
||||
fileByte, err := ioutil.ReadAll(file)
|
||||
out = string(fileByte)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func loadMain() (string, error) {
|
||||
var mainHTML string
|
||||
|
||||
//Load HTML template
|
||||
tmpl, err := template.ParseFiles(filepath.Join(webDir, "main.tmpl"))
|
||||
if err != nil {
|
||||
return mainHTML, err
|
||||
}
|
||||
|
||||
//Read about file
|
||||
about, err := config.ReadAbout()
|
||||
if err != nil {
|
||||
return mainHTML, err
|
||||
}
|
||||
|
||||
//Execute template
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
err = tmpl.Execute(buf, about)
|
||||
if err != nil {
|
||||
return mainHTML, err
|
||||
}
|
||||
|
||||
mainHTML = buf.String()
|
||||
|
||||
return mainHTML, nil
|
||||
}
|
||||
|
||||
func loadRules() (string, error) {
|
||||
var rulesHTML string
|
||||
|
||||
//Load HTML template
|
||||
tmpl, err := template.ParseFiles(filepath.Join(webDir, "rules.tmpl"))
|
||||
if err != nil {
|
||||
return rulesHTML, err
|
||||
}
|
||||
|
||||
//Read rules file
|
||||
rules, err := config.ReadRules()
|
||||
if err != nil {
|
||||
return rulesHTML, err
|
||||
}
|
||||
|
||||
if rules.Exist == false {
|
||||
rules.Text = "This server has no rules."
|
||||
}
|
||||
|
||||
//Execute template
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
err = tmpl.Execute(buf, rules)
|
||||
if err != nil {
|
||||
return rulesHTML, err
|
||||
}
|
||||
|
||||
rulesHTML = buf.String()
|
||||
|
||||
return rulesHTML, nil
|
||||
}
|
||||
|
||||
func loadVersion() (string, error) {
|
||||
var versionHTML string
|
||||
|
||||
//Load HTML template
|
||||
tmpl, err := template.ParseFiles(filepath.Join(webDir, "version.tmpl"))
|
||||
if err != nil {
|
||||
return versionHTML, err
|
||||
}
|
||||
|
||||
//Read version file
|
||||
version, err := config.ReadVersion()
|
||||
if err != nil {
|
||||
return versionHTML, err
|
||||
}
|
||||
|
||||
//Execute template
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
err = tmpl.Execute(buf, version)
|
||||
if err != nil {
|
||||
return versionHTML, err
|
||||
}
|
||||
|
||||
versionHTML = buf.String()
|
||||
|
||||
return versionHTML, nil
|
||||
}
|
||||
|
||||
type pagesType struct {
|
||||
StyleCSS string
|
||||
Main string
|
||||
API string
|
||||
Rules string
|
||||
Version string
|
||||
New string
|
||||
NewDoneTmpl *template.Template
|
||||
GetTmpl *template.Template
|
||||
ErrorTmpl *template.Template
|
||||
}
|
||||
|
||||
var pages pagesType
|
||||
|
||||
func Load() error {
|
||||
var err error
|
||||
|
||||
//Style
|
||||
pages.StyleCSS, err = loadFile(filepath.Join(webDir, "style.css"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//Main page
|
||||
pages.Main, err = loadMain()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//About API page
|
||||
pages.API, err = loadFile(filepath.Join(webDir, "api.html"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//Rules page
|
||||
pages.Rules, err = loadRules()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//Version page
|
||||
pages.Version, err = loadVersion()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//New page
|
||||
pages.New, err = loadFile(filepath.Join(webDir, "new.html"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//New done tmpl
|
||||
pages.NewDoneTmpl, err = template.ParseFiles(filepath.Join(webDir, "new_done.tmpl"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//Get tmpl
|
||||
pages.GetTmpl, err = template.ParseFiles(filepath.Join(webDir, "get.tmpl"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//Error tmpl
|
||||
pages.ErrorTmpl, err = template.ParseFiles(filepath.Join(webDir, "error.tmpl"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
// Copyright (C) 2021-2022 Leonid Maslakov.
|
||||
|
||||
// This file is part of Lenpaste.
|
||||
|
||||
// Lenpaste is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the
|
||||
// GNU Affero Public License as published by the
|
||||
// Free Software Foundation, either version 3 of the License,
|
||||
// or (at your option) any later version.
|
||||
|
||||
// Lenpaste is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU Affero Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero Public License along with Lenpaste.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package storage
|
||||
|
||||
import(
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
func genTokenCrypto(tokenLen int) (string, error) {
|
||||
// Generate token
|
||||
var chars = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
||||
charsLen := int64(len(chars))
|
||||
charsLenBig := big.NewInt(charsLen)
|
||||
|
||||
token := ""
|
||||
|
||||
for i := 0; i < tokenLen; i++ {
|
||||
randInt, err := rand.Int(rand.Reader, charsLenBig)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
token = token + string(chars[randInt.Int64()])
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
// Copyright (C) 2021-2022 Leonid Maslakov.
|
||||
|
||||
// This file is part of Lenpaste.
|
||||
|
||||
// Lenpaste is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the
|
||||
// GNU Affero Public License as published by the
|
||||
// Free Software Foundation, either version 3 of the License,
|
||||
// or (at your option) any later version.
|
||||
|
||||
// Lenpaste is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU Affero Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero Public License along with Lenpaste.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package storage
|
||||
|
||||
|
||||
import(
|
||||
"errors"
|
||||
)
|
||||
|
||||
var(
|
||||
ErrUnknowExpir = errors.New("unknown expiration")
|
||||
)
|
||||
|
||||
func ExpirationToTime(expiration string) (int64, error) {
|
||||
switch expiration {
|
||||
// Minutes
|
||||
case "5m":
|
||||
return 60 * 5, nil
|
||||
|
||||
case "10m":
|
||||
return 60 * 10, nil
|
||||
|
||||
case "20m":
|
||||
return 60 * 20, nil
|
||||
|
||||
case "30m":
|
||||
return 60 * 30, nil
|
||||
|
||||
case "40m":
|
||||
return 60 * 40, nil
|
||||
|
||||
case "50m":
|
||||
return 60 * 50, nil
|
||||
|
||||
// Hours
|
||||
case "1h":
|
||||
return 60 * 60, nil
|
||||
|
||||
case "2h":
|
||||
return 60 * 60 * 2, nil
|
||||
|
||||
case "4h":
|
||||
return 60 * 60 * 4, nil
|
||||
|
||||
case "12h":
|
||||
return 60 * 60 * 12, nil
|
||||
|
||||
// Days
|
||||
case "1d":
|
||||
return 60 * 60 * 24, nil
|
||||
|
||||
case "2d":
|
||||
return 60 * 60 * 24 * 2, nil
|
||||
|
||||
case "3d":
|
||||
return 60 * 60 * 24 * 3, nil
|
||||
|
||||
case "4d":
|
||||
return 60 * 60 * 24 * 4, nil
|
||||
|
||||
case "5d":
|
||||
return 60 * 60 * 24 * 5, nil
|
||||
|
||||
case "6d":
|
||||
return 60 * 60 * 24 * 6, nil
|
||||
|
||||
// Weeks
|
||||
case "1w":
|
||||
return 60 * 60 * 24 * 7, nil
|
||||
|
||||
case "2w":
|
||||
return 60 * 60 * 24 * 7 * 2, nil
|
||||
|
||||
case "3w":
|
||||
return 60 * 60 * 24 * 7 * 3, nil
|
||||
}
|
||||
|
||||
return 0, ErrUnknowExpir
|
||||
}
|
@ -0,0 +1,174 @@
|
||||
// Copyright (C) 2021-2022 Leonid Maslakov.
|
||||
|
||||
// This file is part of Lenpaste.
|
||||
|
||||
// Lenpaste is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the
|
||||
// GNU Affero Public License as published by the
|
||||
// Free Software Foundation, either version 3 of the License,
|
||||
// or (at your option) any later version.
|
||||
|
||||
// Lenpaste is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU Affero Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero Public License along with Lenpaste.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package storage
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Paste struct {
|
||||
ID string `json:"id"` // Ignored when creating
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
CreateTime int64 `json:"createTime"` // Ignored when creating
|
||||
DeleteTime int64 `json:"deleteTime"`
|
||||
OneUse bool `json:"oneUse"`
|
||||
//Syntax string `json:"syntax"`
|
||||
//Password string `json:"password"`
|
||||
}
|
||||
|
||||
func (dbInfo DB) PasteAdd(paste Paste) (Paste, error) {
|
||||
// Open DB
|
||||
db, err := dbInfo.openDB()
|
||||
if err != nil {
|
||||
return paste, err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Generate ID
|
||||
paste.ID, err = genTokenCrypto(8)
|
||||
if err != nil {
|
||||
return paste, err
|
||||
}
|
||||
|
||||
// Add
|
||||
_, err = db.Exec(
|
||||
`INSERT INTO "pastes" ("id", "title", "body", "create_time", "delete_time", "one_use") VALUES (?, ?, ?, ?, ?, ?)`,
|
||||
paste.ID, paste.Title, paste.Body, time.Now().Unix(), paste.DeleteTime, paste.OneUse,
|
||||
)
|
||||
if err != nil {
|
||||
return paste, err
|
||||
}
|
||||
|
||||
return paste, nil
|
||||
}
|
||||
|
||||
func (dbInfo DB) PasteDelete(id string) error {
|
||||
// Open DB
|
||||
db, err := dbInfo.openDB()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Delete
|
||||
result, err := db.Exec(
|
||||
`DELETE FROM "pastes" WHERE id = ?`,
|
||||
id,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check result
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if rowsAffected == 0 {
|
||||
return ErrNotFoundID
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbInfo DB) PasteGet(id string) (Paste, error) {
|
||||
var paste Paste
|
||||
|
||||
// Open DB
|
||||
db, err := dbInfo.openDB()
|
||||
if err != nil {
|
||||
return paste, err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Make query
|
||||
row := db.QueryRow(
|
||||
`SELECT "id", "title", "body", "create_time", "delete_time", "one_use" FROM "pastes" WHERE "id" = ?`,
|
||||
id,
|
||||
)
|
||||
|
||||
// Read query
|
||||
err = row.Scan(&paste.ID, &paste.Title, &paste.Body, &paste.CreateTime, &paste.DeleteTime, &paste.OneUse)
|
||||
if err != nil {
|
||||
return paste, err
|
||||
}
|
||||
|
||||
return paste, nil
|
||||
}
|
||||
|
||||
func (dbInfo DB) PasteGetList() ([]Paste, error) {
|
||||
var pastes []Paste
|
||||
|
||||
// Open DB
|
||||
db, err := dbInfo.openDB()
|
||||
if err != nil {
|
||||
return pastes, err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Make query
|
||||
rows, err := db.Query(
|
||||
`SELECT "id", "title", "body", "create_time", "delete_time", "one_use" FROM "pastes"`,
|
||||
)
|
||||
if err != nil {
|
||||
return pastes, err
|
||||
}
|
||||
|
||||
// Read query
|
||||
for rows.Next() {
|
||||
var paste Paste
|
||||
|
||||
err = rows.Scan(&paste.ID, &paste.Title, &paste.Body, &paste.CreateTime, &paste.DeleteTime, &paste.OneUse)
|
||||
if err != nil {
|
||||
return pastes, err
|
||||
}
|
||||
|
||||
pastes = append(pastes, paste)
|
||||
}
|
||||
|
||||
return pastes, nil
|
||||
}
|
||||
|
||||
func (dbInfo DB) PasteDeleteExpired() (int64, error) {
|
||||
// Open DB
|
||||
db, err := dbInfo.openDB()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Delete
|
||||
result, err := db.Exec(
|
||||
`DELETE FROM "pastes" WHERE delete_time < ?`,
|
||||
time.Now().Unix(),
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Check result
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return rowsAffected, err
|
||||
}
|
||||
|
||||
return rowsAffected, nil
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
// Copyright (C) 2021-2022 Leonid Maslakov.
|
||||
|
||||
// This file is part of Lenpaste.
|
||||
|
||||
// Lenpaste is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the
|
||||
// GNU Affero Public License as published by the
|
||||
// Free Software Foundation, either version 3 of the License,
|
||||
// or (at your option) any later version.
|
||||
|
||||
// Lenpaste is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU Affero Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero Public License along with Lenpaste.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package web
|
||||
|
||||
import(
|
||||
"os"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func readFile(path string) ([]byte, error) {
|
||||
// Open file
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Read file
|
||||
fileByte, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return fileByte, nil
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
// Copyright (C) 2021-2022 Leonid Maslakov.
|
||||
|
||||
// This file is part of Lenpaste.
|
||||
|
||||
// Lenpaste is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the
|
||||
// GNU Affero Public License as published by the
|
||||
// Free Software Foundation, either version 3 of the License,
|
||||
// or (at your option) any later version.
|
||||
|
||||
// Lenpaste is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU Affero Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero Public License along with Lenpaste.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package web
|
||||
|
||||
import (
|
||||
"git.lcomrade.su/root/lenpaste/internal/logger"
|
||||
"git.lcomrade.su/root/lenpaste/internal/storage"
|
||||
"html/template"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type Data struct {
|
||||
DB storage.DB
|
||||
Log logger.Config
|
||||
|
||||
StyleCSS []byte
|
||||
Main *template.Template
|
||||
Docs *template.Template
|
||||
DocsApiV1 *template.Template
|
||||
CreatePaste *template.Template
|
||||
PastePage *template.Template
|
||||
ErrorPage *template.Template
|
||||
}
|
||||
|
||||
func Load(db storage.DB, webDir string) (Data, error) {
|
||||
var data Data
|
||||
var err error
|
||||
|
||||
// Save DB info
|
||||
data.DB = db
|
||||
|
||||
// style.css file
|
||||
data.StyleCSS, err = readFile(filepath.Join(webDir, "style.css"))
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
// main.tmpl
|
||||
data.Main, err = template.ParseFiles(
|
||||
filepath.Join(webDir, "base.tmpl"),
|
||||
filepath.Join(webDir, "main.tmpl"),
|
||||
)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
// new.tmpl
|
||||
data.CreatePaste, err = template.ParseFiles(
|
||||
filepath.Join(webDir, "base.tmpl"),
|
||||
filepath.Join(webDir, "new.tmpl"),
|
||||
)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
// paste.tmpl
|
||||
data.PastePage, err = template.ParseFiles(
|
||||
filepath.Join(webDir, "base.tmpl"),
|
||||
filepath.Join(webDir, "paste.tmpl"),
|
||||
)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
// docs.tmpl
|
||||
data.Docs, err = template.ParseFiles(
|
||||
filepath.Join(webDir, "base.tmpl"),
|
||||
filepath.Join(webDir, "docs.tmpl"),
|
||||
)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
// docs_apiv1.tmpl
|
||||
data.DocsApiV1, err = template.ParseFiles(
|
||||
filepath.Join(webDir, "base.tmpl"),
|
||||
filepath.Join(webDir, "docs_apiv1.tmpl"),
|
||||
)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
// error.tmpl
|
||||
data.ErrorPage, err = template.ParseFiles(
|
||||
filepath.Join(webDir, "base.tmpl"),
|
||||
filepath.Join(webDir, "error.tmpl"),
|
||||
)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
// Copyright (C) 2021-2022 Leonid Maslakov.
|
||||
|
||||
// This file is part of Lenpaste.
|
||||
|
||||
// Lenpaste is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the
|
||||
// GNU Affero Public License as published by the
|
||||
// Free Software Foundation, either version 3 of the License,
|
||||
// or (at your option) any later version.
|
||||
|
||||
// Lenpaste is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU Affero Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero Public License along with Lenpaste.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package web
|
||||
|
||||
import(
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Pattern: /docs
|
||||
func (data Data) DocsHand(rw http.ResponseWriter, req *http.Request) {
|
||||
data.Log.HttpRequest(req)
|
||||
|
||||
rw.Header().Set("Content-Type", "text/html")
|
||||
data.Docs.Execute(rw, "")
|
||||
}
|
||||
|
||||
// Pattern: /docs/apiv1
|
||||
func (data Data) DocsAPIV1Hand(rw http.ResponseWriter, req *http.Request) {
|
||||
data.Log.HttpRequest(req)
|
||||
|
||||
rw.Header().Set("Content-Type", "text/html")
|
||||
data.DocsApiV1.Execute(rw, "")
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
// Copyright (C) 2021-2022 Leonid Maslakov.
|
||||
|
||||
// This file is part of Lenpaste.
|
||||
|
||||
// Lenpaste is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the
|
||||
// GNU Affero Public License as published by the
|
||||
// Free Software Foundation, either version 3 of the License,
|
||||
// or (at your option) any later version.
|
||||
|
||||
// Lenpaste is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU Affero Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero Public License along with Lenpaste.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package web
|
||||
|
||||
import(
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type errorTmpl struct {
|
||||
Code int
|
||||
Error string
|
||||
}
|
||||
|
||||
func (data Data) errorBadRequest(rw http.ResponseWriter, req *http.Request) {
|
||||
// Write response header
|
||||
rw.Header().Set("Content-type", "text/html")
|
||||
rw.WriteHeader(400)
|
||||
|
||||
// Render template
|
||||
errData := errorTmpl{
|
||||
Error: "Bad Request",
|
||||
Code: 400,
|
||||
}
|
||||
|
||||
e := data.ErrorPage.Execute(rw, errData)
|
||||
if e != nil {
|
||||
data.Log.HttpError(req, e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func (data Data) errorNotFound(rw http.ResponseWriter, req *http.Request) {
|
||||
// Write response header
|
||||
rw.Header().Set("Content-type", "text/html")
|
||||
rw.WriteHeader(404)
|
||||
|
||||
// Render template
|
||||
errData := errorTmpl{
|
||||
Error: "Not Found",
|
||||
Code: 404,
|
||||
}
|
||||
|
||||
e := data.ErrorPage.Execute(rw, errData)
|
||||
if e != nil {
|
||||
data.Log.HttpError(req, e)
|
||||
}
|
||||
}
|
||||
|
||||
func (data Data) errorInternal(rw http.ResponseWriter, req *http.Request, err error) {
|
||||
// Write to log
|
||||
data.Log.HttpError(req, err)
|
||||
|
||||
// Write response header
|
||||
rw.Header().Set("Content-type", "text/html")
|
||||
rw.WriteHeader(500)
|
||||
|
||||
// Render template
|
||||
errData := errorTmpl{
|
||||
Error: "Internal Server Error",
|
||||
Code: 500,
|
||||
}
|
||||
|
||||
e := data.ErrorPage.Execute(rw, errData)
|
||||
if e != nil {
|
||||
data.Log.HttpError(req, e)
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
// Copyright (C) 2021-2022 Leonid Maslakov.
|
||||
|
||||
// This file is part of Lenpaste.
|
||||
|
||||
// Lenpaste is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the
|
||||
// GNU Affero Public License as published by the
|
||||
// Free Software Foundation, either version 3 of the License,
|
||||
// or (at your option) any later version.
|
||||
|
||||
// Lenpaste is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU Affero Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero Public License along with Lenpaste.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package web
|
||||
|
||||
import (
|
||||
"git.lcomrade.su/root/lenpaste/internal/storage"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Pattern: /
|
||||
func (data Data) MainHand(rw http.ResponseWriter, req *http.Request) {
|
||||
// Log request
|
||||
data.Log.HttpRequest(req)
|
||||
|
||||
// If main page
|
||||
if req.URL.Path == "/" {
|
||||
rw.Header().Set("Content-Type", "text/html")
|
||||
data.Main.Execute(rw, "")
|
||||
return
|
||||
}
|
||||
|
||||
// Get paste
|
||||
pasteID := string([]rune(req.URL.Path)[1:])
|
||||
|
||||
paste, err := data.DB.PasteGet(pasteID)
|
||||
if err != nil {
|
||||
if err == storage.ErrNotFoundID {
|
||||
data.errorNotFound(rw, req)
|
||||
return
|
||||
|
||||
} else {
|
||||
data.errorInternal(rw, req, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Show paste
|
||||
err = data.PastePage.Execute(rw, paste)
|
||||
if err != nil {
|
||||
data.errorInternal(rw, req, err)
|
||||
return
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
// Copyright (C) 2021-2022 Leonid Maslakov.
|
||||
|
||||
// This file is part of Lenpaste.
|
||||
|
||||
// Lenpaste is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the
|
||||
// GNU Affero Public License as published by the
|
||||
// Free Software Foundation, either version 3 of the License,
|
||||
// or (at your option) any later version.
|
||||
|
||||
// Lenpaste is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU Affero Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero Public License along with Lenpaste.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package web
|
||||
|
||||
import(
|
||||
"git.lcomrade.su/root/lenpaste/internal/storage"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Pattern: /new
|
||||
func (data Data) CreatePasteHand(rw http.ResponseWriter, req *http.Request) {
|
||||
// Log request
|
||||
data.Log.HttpRequest(req)
|
||||
|
||||
// Read request
|
||||
req.ParseForm()
|
||||
|
||||
if req.PostForm.Get("body") != "" {
|
||||
paste := storage.Paste{
|
||||
Title: req.PostForm.Get("title"),
|
||||
Body: req.PostForm.Get("body"),
|
||||
OneUse: false,
|
||||
}
|
||||
|
||||
// Get expiration time
|
||||
expirTime, err := storage.ExpirationToTime(req.PostForm.Get("expiration"))
|
||||
if err != nil {
|
||||
data.errorBadRequest(rw, req)
|
||||
return
|
||||
}
|
||||
paste.DeleteTime = time.Now().Unix() + expirTime
|
||||
|
||||
// Get "one use" parameter
|
||||
if req.PostForm.Get("oneUse") == "true" {
|
||||
paste.OneUse = true
|
||||
}
|
||||
|
||||
// Create paste
|
||||
_, err = data.DB.PasteAdd(paste)
|
||||
if err != nil {
|
||||
data.errorInternal(rw, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Else show create page
|
||||
rw.Header().Set("Content-Type", "text/html")
|
||||
|
||||
err := data.CreatePaste.Execute(rw, "")
|
||||
if err != nil {
|
||||
data.errorInternal(rw, req, err)
|
||||
return
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
// Copyright (C) 2021-2022 Leonid Maslakov.
|
||||
|
||||
// This file is part of Lenpaste.
|
||||
|
||||
// Lenpaste is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the
|
||||
// GNU Affero Public License as published by the
|
||||
// Free Software Foundation, either version 3 of the License,
|
||||
// or (at your option) any later version.
|
||||
|
||||
// Lenpaste is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU Affero Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero Public License along with Lenpaste.
|
||||
// If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package web
|
||||
|
||||
import(
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (data Data) StyleCSSHand(rw http.ResponseWriter, req *http.Request) {
|
||||
data.Log.HttpRequest(req)
|
||||
|
||||
rw.Header().Set("Content-Type", "text/css")
|
||||
rw.Write(data.StyleCSS)
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Done | Lenpaste</title>
|
||||
<link rel="stylesheet" type="text/css" href="/style.css"/>
|
||||