uLinkShortener/internal/api/handlers/url.go
2025-04-02 08:37:01 +02:00

132 lines
No EOL
3.9 KiB
Go

package handlers
import (
"context"
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/gorilla/mux"
"github.com/marcus7i/ulinkshortener/internal/database"
"github.com/marcus7i/ulinkshortener/internal/models"
"github.com/marcus7i/ulinkshortener/internal/utils"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
)
type CreateLinkRequest struct {
AccountID string `json:"account_id"`
URL string `json:"url"`
}
type CreateLinkResponse struct {
ShortURL string `json:"short_url"`
}
func (h *Handler) CreateLink(w http.ResponseWriter, r *http.Request) {
var req CreateLinkRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid request")
return
}
ctx := context.Background()
user := h.DB.Collection(database.UsersCollection).FindOne(ctx, bson.M{"account_id": req.AccountID})
if user.Err() != nil {
respondWithError(w, http.StatusUnauthorized, "Invalid account")
return
}
if !utils.IsValidURL(req.URL) {
respondWithError(w, http.StatusBadRequest, "Invalid URL. Please provide a valid URL with scheme (e.g., http:// or https://)")
return
}
shortID := utils.GenerateShortID()
link := models.Link{
ShortID: shortID,
TargetURL: req.URL,
AccountID: req.AccountID,
CreatedAt: time.Now(),
}
_, err := h.DB.Collection(database.LinksCollection).InsertOne(ctx, link)
if err != nil {
respondWithError(w, http.StatusInternalServerError, "Failed to create link")
return
}
respondWithJSON(w, http.StatusOK, CreateLinkResponse{
ShortURL: fmt.Sprintf("/l/%s", shortID),
})
}
func (h *Handler) RedirectLink(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
shortID := vars["shortID"]
ctx := context.Background()
var link models.Link
err := h.DB.Collection(database.LinksCollection).FindOne(ctx, bson.M{"short_id": shortID}).Decode(&link)
if err != nil {
if err == mongo.ErrNoDocuments {
http.Error(w, "Link not found", http.StatusNotFound)
} else {
http.Error(w, "Server error", http.StatusInternalServerError)
}
return
}
clientInfo := utils.GetClientInfo(r)
clientInfo["link_id"] = shortID
clientInfo["account_id"] = link.AccountID
_, err = h.DB.Collection(database.AnalyticsCollection).InsertOne(ctx, clientInfo)
if err != nil {
fmt.Printf("Failed to log analytics: %v\n", err)
}
http.Redirect(w, r, link.TargetURL, http.StatusFound)
}
func (h *Handler) DeleteLink(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
shortID := vars["shortID"]
accountID := getAccountIDFromCookie(r)
if accountID == "" {
respondWithError(w, http.StatusUnauthorized, "Not logged in")
return
}
ctx := context.Background()
var link models.Link
err := h.DB.Collection(database.LinksCollection).FindOne(ctx, bson.M{
"short_id": shortID,
"account_id": accountID,
}).Decode(&link)
if err != nil {
if err == mongo.ErrNoDocuments {
respondWithError(w, http.StatusNotFound, "Link not found or unauthorized")
} else {
respondWithError(w, http.StatusInternalServerError, "Server error")
}
return
}
_, err = h.DB.Collection(database.LinksCollection).DeleteOne(ctx, bson.M{"short_id": shortID})
if err != nil {
respondWithError(w, http.StatusInternalServerError, "Failed to delete link")
return
}
_, err = h.DB.Collection(database.AnalyticsCollection).DeleteMany(ctx, bson.M{"link_id": shortID})
if err != nil {
fmt.Printf("Failed to delete analytics: %v\n", err)
}
respondWithJSON(w, http.StatusOK, map[string]bool{"success": true})
}