generated from tsivinsky/go-template
183 lines
4.6 KiB
Go
183 lines
4.6 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"image-storage/auth"
|
|
"image-storage/model"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/jmoiron/sqlx"
|
|
"golang.org/x/crypto/bcrypt"
|
|
_ "modernc.org/sqlite"
|
|
)
|
|
|
|
func main() {
|
|
db, err := sqlx.Connect("sqlite", "./db/sqlite.db")
|
|
if err != nil {
|
|
log.Fatalf("failed to connect to db: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
srv := NewServer(":5000")
|
|
|
|
srv.Handle("POST /api/auth/register", func(w http.ResponseWriter, r *http.Request) error {
|
|
var body struct {
|
|
Email string `json:"email"`
|
|
Password string `json:"password"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
|
return srv.Error("empty body", err, 400)
|
|
}
|
|
|
|
if body.Email == "" || body.Password == "" {
|
|
return srv.Error("email or password missing", nil, 400)
|
|
}
|
|
|
|
hash, err := bcrypt.GenerateFromPassword([]byte(body.Password), 10)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to generate password hash: %v", err)
|
|
}
|
|
|
|
user := &model.User{
|
|
Email: body.Email,
|
|
Password: string(hash),
|
|
}
|
|
|
|
if err := user.Create(db); err != nil {
|
|
return srv.Error("failed to create user", err, 400)
|
|
}
|
|
|
|
if err := user.FindByID(db); err != nil {
|
|
return fmt.Errorf("failed to populate user object after creating it: %v", err)
|
|
}
|
|
|
|
expiryTime := time.Now().Add(time.Hour * 24 * 30)
|
|
token, err := auth.GenerateUserToken(user.ID, expiryTime)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to generate access token: %v", err)
|
|
}
|
|
auth.SetUserCookie(w, token, expiryTime)
|
|
|
|
return srv.JSON(w, user, 201)
|
|
})
|
|
|
|
srv.Handle("POST /api/auth/login", func(w http.ResponseWriter, r *http.Request) error {
|
|
var body struct {
|
|
Email string `json:"email"`
|
|
Password string `json:"password"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
|
return srv.Error("empty body", err, 400)
|
|
}
|
|
|
|
if body.Email == "" || body.Password == "" {
|
|
return srv.Error("email or password missing", nil, 400)
|
|
}
|
|
|
|
user := &model.User{Email: body.Email}
|
|
if err := user.FindByEmail(db); err != nil {
|
|
return srv.Error("user not found", err, 404)
|
|
}
|
|
|
|
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(body.Password)); err != nil {
|
|
return srv.Error("invalid password", nil, 400)
|
|
}
|
|
|
|
expiryTime := time.Now().Add(time.Hour * 24 * 30)
|
|
token, err := auth.GenerateUserToken(user.ID, expiryTime)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to generate access token: %v", err)
|
|
}
|
|
auth.SetUserCookie(w, token, expiryTime)
|
|
|
|
return srv.JSON(w, user, 200)
|
|
})
|
|
|
|
srv.Handle("POST /api/images", func(w http.ResponseWriter, r *http.Request) error {
|
|
userId, err := auth.GetUserIdFromRequest(r)
|
|
if err != nil {
|
|
return srv.Error("unauthorized", err, 401)
|
|
}
|
|
|
|
user := &model.User{Model: model.Model{ID: userId}}
|
|
if err := user.FindByID(db); err != nil {
|
|
return srv.Error("user not found", nil, 401)
|
|
}
|
|
|
|
data, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read request body: %v", err)
|
|
}
|
|
|
|
img := &model.Image{
|
|
UserID: user.ID,
|
|
Data: data,
|
|
ContentType: r.Header.Get("Content-Type"),
|
|
}
|
|
if err := img.Create(db); err != nil {
|
|
return srv.Error("failed to save image to database: %v", err, 400)
|
|
}
|
|
|
|
return srv.JSON(w, img, 201)
|
|
})
|
|
|
|
srv.Handle("GET /api/images", func(w http.ResponseWriter, r *http.Request) error {
|
|
userId, err := auth.GetUserIdFromRequest(r)
|
|
if err != nil {
|
|
return srv.Error("unauthorized", err, 401)
|
|
}
|
|
|
|
rows, err := db.Queryx("SELECT * FROM images WHERE user_id = ?", userId)
|
|
if err != nil {
|
|
return srv.Error("images not found", err, 400)
|
|
}
|
|
|
|
images := []model.Image{}
|
|
for rows.Next() {
|
|
img := model.Image{}
|
|
if err := rows.StructScan(&img); err != nil {
|
|
log.Printf("failed to save image to struct: %v", err)
|
|
continue
|
|
}
|
|
images = append(images, img)
|
|
}
|
|
|
|
return srv.JSON(w, images, 200)
|
|
})
|
|
|
|
srv.Handle("GET /images/{id}", func(w http.ResponseWriter, r *http.Request) error {
|
|
img := &model.Image{ID: r.PathValue("id")}
|
|
if err := img.FindByID(db); err != nil {
|
|
return srv.Error("image not found", nil, 404)
|
|
}
|
|
|
|
w.Header().Set("Content-Type", img.ContentType)
|
|
w.Write(img.Data)
|
|
|
|
return nil
|
|
})
|
|
|
|
srv.Handle("DELETE /api/images/{id}", func(w http.ResponseWriter, r *http.Request) error {
|
|
img := &model.Image{ID: r.PathValue("id")}
|
|
if err := img.FindByID(db); err != nil {
|
|
return srv.Error("image not found", nil, 404)
|
|
}
|
|
|
|
if err := img.DeleteByID(db); err != nil {
|
|
return srv.Error("failed to delete image: %v", err, 500)
|
|
}
|
|
|
|
return srv.JSON(w, struct {
|
|
Ok bool `json:"ok"`
|
|
}{true}, 200)
|
|
})
|
|
|
|
if err := srv.ListenAndServe(); err != nil {
|
|
log.Fatalf("failed to start http server: %v", err)
|
|
}
|
|
}
|