2024-01-25 17:58:55 +01:00
|
|
|
package plak
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2024-02-16 22:36:13 +01:00
|
|
|
"embed"
|
2024-04-10 19:34:36 +02:00
|
|
|
"html/template"
|
2024-01-25 17:58:55 +01:00
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
|
|
|
"time"
|
|
|
|
|
2024-02-24 01:26:27 +01:00
|
|
|
"git.gnous.eu/gnouseu/plakken/internal/constant"
|
2024-01-25 17:58:55 +01:00
|
|
|
"git.gnous.eu/gnouseu/plakken/internal/database"
|
2024-02-25 00:29:39 +01:00
|
|
|
"git.gnous.eu/gnouseu/plakken/internal/secret"
|
2024-01-25 17:58:55 +01:00
|
|
|
"git.gnous.eu/gnouseu/plakken/internal/utils"
|
|
|
|
"github.com/redis/go-redis/v9"
|
|
|
|
)
|
|
|
|
|
2024-04-10 19:34:36 +02:00
|
|
|
var ctx = context.Background() //nolint:gochecknoglobals
|
2024-01-25 17:58:55 +01:00
|
|
|
|
|
|
|
type WebConfig struct {
|
|
|
|
DB *redis.Client
|
2024-04-10 19:34:36 +02:00
|
|
|
URLLength uint8
|
2024-02-16 22:36:13 +01:00
|
|
|
Templates embed.FS
|
2024-01-25 17:58:55 +01:00
|
|
|
}
|
|
|
|
|
2024-04-10 19:34:36 +02:00
|
|
|
// plak "Object" for plak.
|
2024-02-25 00:29:39 +01:00
|
|
|
type plak struct {
|
2024-01-25 17:58:55 +01:00
|
|
|
Key string
|
|
|
|
Content string
|
|
|
|
Expiration time.Duration
|
|
|
|
DB *redis.Client
|
|
|
|
}
|
|
|
|
|
2024-04-10 19:34:36 +02:00
|
|
|
// create a plak.
|
2024-02-25 00:29:39 +01:00
|
|
|
func (plak plak) create() (string, error) {
|
|
|
|
dbConf := database.DBConfig{
|
|
|
|
DB: plak.DB,
|
|
|
|
}
|
|
|
|
|
|
|
|
token, err := secret.GenerateToken()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2024-04-10 19:34:36 +02:00
|
|
|
if dbConf.URLExist(plak.Key) {
|
2024-02-25 00:29:39 +01:00
|
|
|
return "", &createError{message: "key already exist"}
|
|
|
|
}
|
|
|
|
|
|
|
|
var hashedSecret string
|
|
|
|
hashedSecret, err = secret.Password(token)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
dbConf.InsertPaste(plak.Key, plak.Content, hashedSecret, plak.Expiration)
|
|
|
|
|
|
|
|
return token, nil
|
|
|
|
}
|
|
|
|
|
2024-04-10 19:34:36 +02:00
|
|
|
// PostCreate manage POST request for create plak.
|
2024-02-25 00:29:39 +01:00
|
|
|
func (config WebConfig) PostCreate(w http.ResponseWriter, r *http.Request) {
|
2024-01-25 17:58:55 +01:00
|
|
|
content := r.FormValue("content")
|
|
|
|
if content == "" {
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
2024-04-10 19:34:36 +02:00
|
|
|
|
2024-02-18 23:27:14 +01:00
|
|
|
return
|
2024-01-25 17:58:55 +01:00
|
|
|
}
|
|
|
|
|
2024-02-18 23:27:14 +01:00
|
|
|
filename := r.FormValue("filename")
|
|
|
|
var key string
|
|
|
|
if len(filename) == 0 {
|
2024-04-10 19:34:36 +02:00
|
|
|
key = utils.GenerateURL(config.URLLength)
|
2024-02-18 23:27:14 +01:00
|
|
|
} else {
|
|
|
|
if !utils.ValidKey(filename) {
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
2024-04-10 19:34:36 +02:00
|
|
|
|
2024-02-18 23:27:14 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
key = filename
|
|
|
|
}
|
|
|
|
|
2024-02-25 00:29:39 +01:00
|
|
|
plak := plak{
|
|
|
|
Key: key,
|
|
|
|
Content: content,
|
|
|
|
DB: config.DB,
|
|
|
|
}
|
2024-02-18 23:27:14 +01:00
|
|
|
|
2024-02-25 00:29:39 +01:00
|
|
|
rawExpiration := r.FormValue("exp")
|
2024-01-25 17:58:55 +01:00
|
|
|
expiration, err := utils.ParseExpiration(rawExpiration)
|
|
|
|
if err != nil {
|
2024-02-25 00:29:39 +01:00
|
|
|
log.Println(err)
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
2024-04-10 19:34:36 +02:00
|
|
|
|
2024-02-18 23:27:14 +01:00
|
|
|
return
|
2024-02-25 00:29:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if expiration == 0 {
|
|
|
|
plak.Expiration = -1
|
2024-01-25 17:58:55 +01:00
|
|
|
} else {
|
2024-02-25 00:29:39 +01:00
|
|
|
plak.Expiration = time.Duration(expiration * int(time.Second))
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = plak.create()
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
2024-04-10 19:34:36 +02:00
|
|
|
|
2024-02-25 00:29:39 +01:00
|
|
|
return
|
2024-01-25 17:58:55 +01:00
|
|
|
}
|
|
|
|
|
2024-02-24 01:26:27 +01:00
|
|
|
http.Redirect(w, r, "/"+key, http.StatusSeeOther)
|
|
|
|
}
|
|
|
|
|
2024-04-10 19:34:36 +02:00
|
|
|
// CurlCreate PostCreate plak with minimum param, ideal for curl. Force 7 day expiration.
|
2024-02-24 01:26:27 +01:00
|
|
|
func (config WebConfig) CurlCreate(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if r.ContentLength == 0 {
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
2024-04-10 19:34:36 +02:00
|
|
|
|
2024-02-24 01:26:27 +01:00
|
|
|
return
|
|
|
|
}
|
2024-02-25 00:29:39 +01:00
|
|
|
|
2024-02-24 01:26:27 +01:00
|
|
|
content, _ := io.ReadAll(r.Body)
|
|
|
|
err := r.Body.Close()
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
}
|
2024-02-25 00:29:39 +01:00
|
|
|
|
2024-04-10 19:34:36 +02:00
|
|
|
key := utils.GenerateURL(config.URLLength)
|
2024-02-25 00:29:39 +01:00
|
|
|
|
|
|
|
plak := plak{
|
|
|
|
Key: key,
|
|
|
|
Content: string(content),
|
|
|
|
Expiration: constant.ExpirationCurlCreate,
|
|
|
|
DB: config.DB,
|
|
|
|
}
|
|
|
|
|
|
|
|
var token string
|
|
|
|
token, err = plak.create()
|
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
2024-04-10 19:34:36 +02:00
|
|
|
|
2024-02-25 00:29:39 +01:00
|
|
|
return
|
2024-02-24 01:26:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
var baseURL string
|
|
|
|
if r.TLS == nil {
|
|
|
|
baseURL = "http://" + r.Host + "/" + key
|
|
|
|
} else {
|
|
|
|
baseURL = "https://" + r.Host + "/" + key
|
|
|
|
}
|
|
|
|
|
2024-02-25 00:29:39 +01:00
|
|
|
message := baseURL + "\n" + "Delete with : 'curl -X DELETE " + baseURL + "?secret\\=" + token + "'" + "\n"
|
2024-02-24 01:26:27 +01:00
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
|
|
|
_, err = io.WriteString(w, message)
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
}
|
2024-01-25 17:58:55 +01:00
|
|
|
}
|
|
|
|
|
2024-04-10 19:34:36 +02:00
|
|
|
// View for plak.
|
2024-01-25 17:58:55 +01:00
|
|
|
func (config WebConfig) View(w http.ResponseWriter, r *http.Request) {
|
|
|
|
dbConf := database.DBConfig{
|
|
|
|
DB: config.DB,
|
|
|
|
}
|
2024-02-25 00:29:39 +01:00
|
|
|
var currentPlak plak
|
2024-01-25 17:58:55 +01:00
|
|
|
key := r.PathValue("key")
|
|
|
|
|
2024-04-10 19:34:36 +02:00
|
|
|
//nolint:nestif
|
|
|
|
if dbConf.URLExist(key) {
|
2024-02-25 00:29:39 +01:00
|
|
|
currentPlak = plak{
|
2024-01-25 17:58:55 +01:00
|
|
|
Key: key,
|
|
|
|
DB: config.DB,
|
|
|
|
}
|
2024-02-25 00:29:39 +01:00
|
|
|
currentPlak = currentPlak.getContent()
|
2024-01-25 17:58:55 +01:00
|
|
|
if r.PathValue("settings") == "raw" {
|
|
|
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
2024-02-25 00:29:39 +01:00
|
|
|
_, err := io.WriteString(w, currentPlak.Content)
|
2024-01-25 17:58:55 +01:00
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
}
|
|
|
|
} else {
|
2024-02-16 22:36:13 +01:00
|
|
|
t, err := template.ParseFS(config.Templates, "templates/paste.html")
|
2024-01-25 17:58:55 +01:00
|
|
|
if err != nil {
|
2024-02-16 22:36:13 +01:00
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
2024-01-25 17:58:55 +01:00
|
|
|
log.Println(err)
|
2024-04-10 19:34:36 +02:00
|
|
|
|
2024-02-25 00:29:39 +01:00
|
|
|
return
|
2024-01-25 17:58:55 +01:00
|
|
|
}
|
2024-02-25 00:29:39 +01:00
|
|
|
err = t.Execute(w, currentPlak)
|
2024-01-25 17:58:55 +01:00
|
|
|
if err != nil {
|
2024-02-25 00:29:39 +01:00
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
2024-01-25 17:58:55 +01:00
|
|
|
log.Println(err)
|
2024-04-10 19:34:36 +02:00
|
|
|
|
2024-02-25 00:29:39 +01:00
|
|
|
return
|
2024-01-25 17:58:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
w.WriteHeader(http.StatusNotFound)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-10 19:34:36 +02:00
|
|
|
// DeleteRequest manage plak deletion endpoint.
|
2024-02-25 00:29:39 +01:00
|
|
|
func (config WebConfig) DeleteRequest(w http.ResponseWriter, r *http.Request) {
|
2024-01-25 17:58:55 +01:00
|
|
|
dbConf := database.DBConfig{
|
|
|
|
DB: config.DB,
|
|
|
|
}
|
|
|
|
key := r.PathValue("key")
|
2024-02-25 00:29:39 +01:00
|
|
|
var valid bool
|
2024-01-25 17:58:55 +01:00
|
|
|
|
2024-04-10 19:34:36 +02:00
|
|
|
//nolint:nestif
|
|
|
|
if dbConf.URLExist(key) {
|
2024-02-25 00:29:39 +01:00
|
|
|
var err error
|
|
|
|
token := r.URL.Query().Get("secret")
|
|
|
|
|
|
|
|
valid, err = dbConf.VerifySecret(key, token)
|
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
log.Println(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if valid {
|
|
|
|
plak := plak{
|
2024-01-25 17:58:55 +01:00
|
|
|
Key: key,
|
|
|
|
DB: config.DB,
|
|
|
|
}
|
2024-02-25 00:29:39 +01:00
|
|
|
err := plak.delete()
|
2024-01-25 17:58:55 +01:00
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
}
|
2024-02-25 00:29:39 +01:00
|
|
|
|
2024-01-25 17:58:55 +01:00
|
|
|
w.WriteHeader(http.StatusNoContent)
|
|
|
|
} else {
|
|
|
|
w.WriteHeader(http.StatusForbidden)
|
|
|
|
}
|
2024-04-10 19:34:36 +02:00
|
|
|
|
|
|
|
return
|
2024-01-25 17:58:55 +01:00
|
|
|
}
|
2024-02-25 00:29:39 +01:00
|
|
|
|
2024-01-25 17:58:55 +01:00
|
|
|
w.WriteHeader(http.StatusNotFound)
|
|
|
|
}
|
|
|
|
|
2024-04-10 19:34:36 +02:00
|
|
|
// delete DeleteRequest plak from database.
|
2024-02-25 00:29:39 +01:00
|
|
|
func (plak plak) delete() error {
|
2024-01-25 17:58:55 +01:00
|
|
|
err := plak.DB.Del(ctx, plak.Key).Err()
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
2024-04-10 19:34:36 +02:00
|
|
|
|
2024-02-25 00:29:39 +01:00
|
|
|
return &deletePlakError{name: plak.Key, err: err}
|
2024-01-25 17:58:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-04-10 19:34:36 +02:00
|
|
|
// getContent get plak content.
|
2024-02-25 00:29:39 +01:00
|
|
|
func (plak plak) getContent() plak {
|
2024-01-25 17:58:55 +01:00
|
|
|
plak.Content = plak.DB.HGet(ctx, plak.Key, "content").Val()
|
2024-04-10 19:34:36 +02:00
|
|
|
|
2024-01-25 17:58:55 +01:00
|
|
|
return plak
|
|
|
|
}
|