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