mirror of
https://github.com/arkorty/Reduce.git
synced 2026-03-18 00:47:10 +00:00
feat: version 2
This commit is contained in:
116
backend/main.go
116
backend/main.go
@@ -1,102 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"time"
|
||||
"os"
|
||||
"errors"
|
||||
"strconv"
|
||||
"github.com/jinzhu/gorm"
|
||||
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/labstack/echo"
|
||||
"github.com/labstack/echo/middleware"
|
||||
)
|
||||
|
||||
func codegen(length int) string {
|
||||
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
bytes := make([]byte, length)
|
||||
for i := range bytes {
|
||||
bytes[i] = charset[rand.Intn(len(charset))]
|
||||
}
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
func shortenURL(c echo.Context) error {
|
||||
// Define a struct for binding the request body
|
||||
type RequestBody struct {
|
||||
LURL string `json:"lurl"`
|
||||
BaseURL string `json:"base_url"` // Expect base URL in the request
|
||||
}
|
||||
|
||||
// Bind request body to the RequestBody struct
|
||||
reqBody := new(RequestBody)
|
||||
if err := c.Bind(reqBody); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Validate the base URL
|
||||
if reqBody.BaseURL == "" {
|
||||
// Fallback to BASE_URL environment variable
|
||||
reqBody.BaseURL = os.Getenv("BASE_URL")
|
||||
if reqBody.BaseURL == "" {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Base URL is not configured")
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the long URL already exists in the database
|
||||
var existingURL CodeURLMap
|
||||
if err := db.Where("lurl = ?", reqBody.LURL).First(&existingURL).Error; err == nil {
|
||||
// If the long URL exists, return the existing short URL
|
||||
return c.JSON(http.StatusOK, map[string]string{
|
||||
"surl": reqBody.BaseURL + "/" + existingURL.Code,
|
||||
})
|
||||
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
// If there's an error other than record not found, return an error
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to check existing URL")
|
||||
}
|
||||
|
||||
// Generate a unique code
|
||||
codelen := 6
|
||||
if os.Getenv("CODE_LENGTH") != "" && os.Getenv("CODE_LENGTH") != "0" {
|
||||
t, err := strconv.Atoi(os.Getenv("CODE_LENGTH"))
|
||||
if err == nil {
|
||||
codelen = t
|
||||
}
|
||||
}
|
||||
code := codegen(codelen)
|
||||
|
||||
// Create URL record
|
||||
url := &CodeURLMap{
|
||||
Code: code,
|
||||
LURL: reqBody.LURL,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
// Save URL record to the database
|
||||
if err := db.Create(url).Error; err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create URL record")
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusCreated, map[string]string{
|
||||
"surl": reqBody.BaseURL + "/" + code,
|
||||
})
|
||||
}
|
||||
|
||||
func fetchLURL(c echo.Context) error {
|
||||
code := c.Param("code")
|
||||
var url CodeURLMap
|
||||
if err := db.Where("code = ?", code).First(&url).Error; err != nil {
|
||||
log.Println("Error retrieving URL:", err)
|
||||
return echo.NewHTTPError(http.StatusNotFound, "URL not found")
|
||||
}
|
||||
return c.JSON(http.StatusOK, map[string]string{"lurl": url.LURL})
|
||||
}
|
||||
|
||||
|
||||
func main() {
|
||||
godotenv.Load()
|
||||
defer db.Close()
|
||||
|
||||
e := echo.New()
|
||||
@@ -107,15 +21,33 @@ func main() {
|
||||
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
|
||||
AllowOrigins: []string{"*"},
|
||||
AllowMethods: []string{http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete},
|
||||
AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept, echo.HeaderAuthorization},
|
||||
}))
|
||||
|
||||
// Routes
|
||||
// Health
|
||||
e.GET("/", func(c echo.Context) error {
|
||||
return c.String(http.StatusOK, "Backend is running alright.\n")
|
||||
})
|
||||
|
||||
e.POST("/reduce/shorten", shortenURL)
|
||||
e.GET("/reduce/:code", fetchLURL)
|
||||
// Auth
|
||||
e.POST("/auth/register", register)
|
||||
e.POST("/auth/login", login)
|
||||
e.GET("/auth/me", getMe, JWTMiddleware)
|
||||
|
||||
e.Logger.Fatal(e.Start(":8080"))
|
||||
// Public link routes (optional auth for shorten)
|
||||
e.POST("/reduce/shorten", shortenURL, OptionalJWTMiddleware)
|
||||
e.GET("/reduce/:code", fetchLURL)
|
||||
e.POST("/reduce/:code/verify", verifyAndRedirect)
|
||||
|
||||
// Authenticated link management
|
||||
links := e.Group("/links", JWTMiddleware)
|
||||
links.GET("", listLinks)
|
||||
links.PUT("/:id", updateLink)
|
||||
links.DELETE("/:id", deleteLink)
|
||||
|
||||
port := os.Getenv("PORT")
|
||||
if port == "" {
|
||||
port = "8080"
|
||||
}
|
||||
e.Logger.Fatal(e.Start(":" + port))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user