Initial commit
This commit is contained in:
353
internal/controller/handler.go
Normal file
353
internal/controller/handler.go
Normal file
@@ -0,0 +1,353 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"image/jpeg"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"photodisk/internal/albums"
|
||||
"photodisk/internal/auth"
|
||||
"photodisk/internal/config"
|
||||
"photodisk/internal/fs"
|
||||
"photodisk/internal/watermark"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Login
|
||||
|
||||
type LoginRequest struct {
|
||||
Username string `form:"username" json:"username" binding:"required"`
|
||||
Password string `form:"password" json:"password" binding:"required"`
|
||||
}
|
||||
|
||||
func Login(c *gin.Context) {
|
||||
var req LoginRequest
|
||||
if err := c.Bind(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
user, err := auth.Login(req.Username, req.Password)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
expiresAt := time.Now().Add(config.Get().SessionTtl)
|
||||
sessionId, err := auth.CreateSession(user.Id, expiresAt)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
cookie := &http.Cookie{
|
||||
Name: "session_id",
|
||||
Value: sessionId,
|
||||
Expires: expiresAt,
|
||||
Path: "/",
|
||||
}
|
||||
http.SetCookie(c.Writer, cookie)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"session": sessionId})
|
||||
}
|
||||
|
||||
// Create
|
||||
|
||||
type CreateAlbumRequest struct {
|
||||
Name string `form:"name" binding:"required"`
|
||||
Password string `form:"password"`
|
||||
}
|
||||
|
||||
type CreateAlbumResponse struct {
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
func CreateAlbum(c *gin.Context) {
|
||||
var req CreateAlbumRequest
|
||||
if err := c.Bind(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
id := albums.GenerateAlbumId()
|
||||
if err := albums.CreateAlbum(albums.Album{
|
||||
ID: id,
|
||||
Name: req.Name,
|
||||
Password: req.Password,
|
||||
}); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, CreateAlbumResponse{Id: id})
|
||||
}
|
||||
|
||||
// Delete
|
||||
|
||||
type DeleteAlbumRequest struct {
|
||||
Id string `form:"id" binding:"required"`
|
||||
}
|
||||
|
||||
type DeleteAlbumResponse struct {
|
||||
}
|
||||
|
||||
func DeleteAlbum(c *gin.Context) {
|
||||
var req DeleteAlbumRequest
|
||||
if err := c.Bind(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if err := albums.DeleteAlbum(req.Id); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, DeleteAlbumResponse{})
|
||||
}
|
||||
|
||||
// List
|
||||
|
||||
type ListAlbumsRequest struct {
|
||||
}
|
||||
|
||||
type ListAlbumsResponse struct {
|
||||
Albums []albums.Album `json:"albums"`
|
||||
}
|
||||
|
||||
func ListAlbums(c *gin.Context) {
|
||||
var req ListAlbumsRequest
|
||||
if err := c.Bind(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
list, err := albums.ListAlbums()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, ListAlbumsResponse{Albums: list})
|
||||
}
|
||||
|
||||
// List Images
|
||||
|
||||
type ListImagesRequest struct {
|
||||
Password string `form:"password"`
|
||||
}
|
||||
|
||||
type ListImagesResponse struct {
|
||||
Images []string `json:"images"`
|
||||
}
|
||||
|
||||
func ListImages(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
var req ListImagesRequest
|
||||
if err := c.Bind(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
album, err := albums.GetAlbum(id, req.Password)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
files, err := fs.ListFiles(album.ID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
var images []string
|
||||
for _, file := range files {
|
||||
suffix := ""
|
||||
if filepath.Ext(file) != ".mp4" {
|
||||
suffix = "?width=200"
|
||||
}
|
||||
images = append(images, fmt.Sprintf("/albums/%s/%s%s", album.ID, file, suffix))
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, ListImagesResponse{Images: images})
|
||||
}
|
||||
|
||||
// Get Album
|
||||
|
||||
type GetAlbumRequest struct {
|
||||
Id string `form:"id" binding:"required"`
|
||||
Password string `form:"password"`
|
||||
}
|
||||
|
||||
type GetAlbumResponse struct {
|
||||
Album albums.Album `json:"album"`
|
||||
}
|
||||
|
||||
func GetAlbum(c *gin.Context) {
|
||||
var req GetAlbumRequest
|
||||
if err := c.Bind(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
album, err := albums.GetAlbum(req.Id, req.Password)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, GetAlbumResponse{Album: album})
|
||||
}
|
||||
|
||||
// Update Album
|
||||
|
||||
type UpdateAlbumRequest struct {
|
||||
Id string `form:"id" binding:"required"`
|
||||
Name string `form:"name" binding:"required"`
|
||||
Password string `form:"password"`
|
||||
IsActive bool `form:"is_active"`
|
||||
}
|
||||
|
||||
type UpdateAlbumResponse struct {
|
||||
Album albums.Album `json:"album"`
|
||||
}
|
||||
|
||||
func UpdateAlbum(c *gin.Context) {
|
||||
var req UpdateAlbumRequest
|
||||
if err := c.Bind(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
album, err := albums.GetAlbum(req.Id, req.Password)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
album.Name = req.Name
|
||||
album.IsActive = req.IsActive
|
||||
|
||||
if err := albums.UpdateAlbum(album); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, UpdateAlbumResponse{Album: album})
|
||||
}
|
||||
|
||||
// Serve Image
|
||||
|
||||
type ServeImageRequest struct {
|
||||
Width int `form:"width"`
|
||||
}
|
||||
|
||||
func ServeImage(c *gin.Context) {
|
||||
albumID := c.Param("id")
|
||||
fileName := c.Param("image")
|
||||
|
||||
if albumID == "" || fileName == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "album and file query parameters are required"})
|
||||
return
|
||||
}
|
||||
|
||||
var req ServeImageRequest
|
||||
if err := c.Bind(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// just to check access
|
||||
album, err := albums.GetAlbum(albumID, "")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err})
|
||||
return
|
||||
}
|
||||
|
||||
path, exists := fs.FileExists(album.ID, fileName)
|
||||
if exists {
|
||||
/*
|
||||
// Cache control
|
||||
data := []byte(time.Now().String())
|
||||
etag := fmt.Sprintf("%x", md5.Sum(data))
|
||||
c.Header("Cache-Control", "public, max-age=3600")
|
||||
c.Header("ETag", etag)
|
||||
if match := c.GetHeader("If-None-Match"); match != "" {
|
||||
if strings.Contains(match, etag) {
|
||||
c.Status(http.StatusNotModified)
|
||||
return
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if album.Watermarked && fs.IsImageFile(fileName) {
|
||||
watermarkedImg, err := watermark.AddWatermark(path, config.Get().Watermark)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.Header("Content-Type", "image/jpeg")
|
||||
|
||||
// Resize image
|
||||
if req.Width > 0 {
|
||||
watermarkedImg = watermark.Resize(watermarkedImg, req.Width)
|
||||
}
|
||||
|
||||
jpeg.Encode(c.Writer, watermarkedImg, nil)
|
||||
return
|
||||
}
|
||||
|
||||
c.File(path)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "File not found"})
|
||||
}
|
||||
|
||||
// Upload Image
|
||||
|
||||
func UploadImage(c *gin.Context) {
|
||||
albumID := c.Param("id")
|
||||
|
||||
// Parse the multipart form
|
||||
err := c.Request.ParseMultipartForm(10 << 20) // Max size: 10MB
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the album exists
|
||||
_, err = albums.GetAlbum(albumID, "") // FIXME: add password support
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Album not found"})
|
||||
} else {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
files := c.Request.MultipartForm.File["files[]"]
|
||||
for _, fileHeader := range files {
|
||||
file, err := fileHeader.Open()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
file.Close()
|
||||
return
|
||||
}
|
||||
|
||||
if err := fs.CreateFile(albumID, fileHeader.Filename, file); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
file.Close()
|
||||
return
|
||||
}
|
||||
|
||||
file.Close()
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Images uploaded successfully"})
|
||||
}
|
||||
Reference in New Issue
Block a user