mirror of
https://github.com/ThisTine/Snip.git
synced 2026-08-18 23:18:47 +07:00
149 lines
3.5 KiB
Go
149 lines
3.5 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/snip/backend/internal/domain"
|
|
"github.com/snip/backend/internal/service"
|
|
)
|
|
|
|
// /api/v1/links — POST create (auth optional), GET list (auth required)
|
|
func (a *API) linksCollection(w http.ResponseWriter, r *http.Request) {
|
|
switch r.Method {
|
|
case http.MethodPost:
|
|
a.createLink(w, r)
|
|
case http.MethodGet:
|
|
a.listLinks(w, r)
|
|
default:
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
}
|
|
}
|
|
|
|
func (a *API) createLink(w http.ResponseWriter, r *http.Request) {
|
|
var req createReq
|
|
if err := decode(r, &req); err != nil {
|
|
writeError(w, domain.ErrInvalidURL)
|
|
return
|
|
}
|
|
mode := domain.Mode(req.Mode)
|
|
if mode != domain.ModeRandom && mode != domain.ModeMemorable && mode != domain.ModeCustom {
|
|
mode = domain.ModeRandom
|
|
}
|
|
link, err := a.links.Create(r.Context(), service.CreateInput{
|
|
LongURL: req.LongURL,
|
|
Mode: mode,
|
|
CustomAlias: req.CustomAlias,
|
|
Pin: req.Pin,
|
|
}, a.userID(r))
|
|
if err != nil {
|
|
writeError(w, err)
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusCreated, toDTO(link))
|
|
}
|
|
|
|
func (a *API) listLinks(w http.ResponseWriter, r *http.Request) {
|
|
q := r.URL.Query()
|
|
page, _ := strconv.Atoi(q.Get("page"))
|
|
pageSize, _ := strconv.Atoi(q.Get("pageSize"))
|
|
|
|
result, err := a.links.List(r.Context(), a.userID(r), q.Get("q"), page, pageSize)
|
|
if err != nil {
|
|
writeError(w, err)
|
|
return
|
|
}
|
|
items := make([]linkDTO, 0, len(result.Items))
|
|
for i := range result.Items {
|
|
items = append(items, toDTO(&result.Items[i]))
|
|
}
|
|
writeJSON(w, http.StatusOK, listResponse{
|
|
Items: items,
|
|
Total: result.Total,
|
|
Page: result.Page,
|
|
PageSize: result.PageSize,
|
|
})
|
|
}
|
|
|
|
func (a *API) linkStats(w http.ResponseWriter, r *http.Request) {
|
|
stats, err := a.links.Stats(r.Context(), a.userID(r))
|
|
if err != nil {
|
|
writeError(w, err)
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, stats)
|
|
}
|
|
|
|
// /api/v1/links/{id} — PATCH, DELETE
|
|
// /api/v1/links/{id}/pin — PUT, DELETE
|
|
func (a *API) linkItem(w http.ResponseWriter, r *http.Request) {
|
|
rest := pathTail(r.URL.Path, "/api/v1/links/")
|
|
parts := strings.Split(strings.Trim(rest, "/"), "/")
|
|
id := parts[0]
|
|
if id == "" {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
return
|
|
}
|
|
owner := a.userID(r)
|
|
|
|
// /{id}/pin
|
|
if len(parts) == 2 && parts[1] == "pin" {
|
|
a.managePin(w, r, id, owner)
|
|
return
|
|
}
|
|
if len(parts) != 1 {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
switch r.Method {
|
|
case http.MethodPatch:
|
|
var req updateReq
|
|
if err := decode(r, &req); err != nil {
|
|
writeError(w, domain.ErrInvalidURL)
|
|
return
|
|
}
|
|
link, err := a.links.UpdateDestination(r.Context(), id, owner, req.LongURL)
|
|
if err != nil {
|
|
writeError(w, err)
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, toDTO(link))
|
|
case http.MethodDelete:
|
|
if err := a.links.Delete(r.Context(), id, owner); err != nil {
|
|
writeError(w, err)
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusNoContent)
|
|
default:
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
}
|
|
}
|
|
|
|
func (a *API) managePin(w http.ResponseWriter, r *http.Request, id, owner string) {
|
|
switch r.Method {
|
|
case http.MethodPut:
|
|
var req pinReq
|
|
if err := decode(r, &req); err != nil {
|
|
writeError(w, domain.ErrInvalidPin)
|
|
return
|
|
}
|
|
link, err := a.links.SetPin(r.Context(), id, owner, req.Pin)
|
|
if err != nil {
|
|
writeError(w, err)
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, toDTO(link))
|
|
case http.MethodDelete:
|
|
link, err := a.links.SetPin(r.Context(), id, owner, "")
|
|
if err != nil {
|
|
writeError(w, err)
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, toDTO(link))
|
|
default:
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
}
|
|
}
|