diff --git a/cmd/cmd.go b/cmd/cmd.go deleted file mode 100644 index 7d11b7d..0000000 --- a/cmd/cmd.go +++ /dev/null @@ -1,80 +0,0 @@ -package cmd - -import ( - "net/http" - - "reichard.io/imagini/routes" - "reichard.io/imagini/internal/context" - - "github.com/urfave/cli/v2" - log "github.com/sirupsen/logrus" -) - -var CmdServe = cli.Command{ - Name: "serve", - Aliases: []string{"s"}, - Usage: "Start Imagini web server.", - Action: serveWeb, -} - -func serveWeb(cliCtx *cli.Context) error { - log.Info("Serving Web") - - ctx := context.NewImaginiContext() - routes.RegisterRoutes(ctx) - - if err := http.ListenAndServe(":" + ctx.Config.ListenPort, nil); err != nil { - log.Fatal(err) - } - - return nil -} - -// func testDatabase(cliCtx *cli.Context) error { -// log.Info("Testing Database") -// c := config.NewConfig() -// db.ConnectDB(c) -// -// err := auth.CreateUser(models.User{ -// Username: "User123", -// Email: "user26@evan.pub", -// FirstName: "User", -// LastName: "Reichard", -// AuthType: "Local", -// }, "myPassword123") -// -// if err != nil { -// fmt.Println(err) -// } -// -// resp := auth.AuthenticateUser(models.APICredentials{User:"User123", Password: "myPassword123"}) -// if resp == true { -// log.Info("USER SUCCESSFULLY AUTHENTICATED BY USERNAME") -// }else { -// log.Info("USER NOT AUTHENTICATED") -// } -// -// resp = auth.AuthenticateUser(models.APICredentials{User:"user26@evan.pub", Password: "myPassword123"}) -// if resp == true { -// log.Info("USER SUCCESSFULLY AUTHENTICATED BY EMAIL") -// }else { -// log.Info("USER NOT AUTHENTICATED") -// } -// -// resp = auth.AuthenticateUser(models.APICredentials{User:"user@evan.pub", Password: "myPassword12"}) -// if resp == true { -// log.Info("USER SUCCESSFULLY AUTHENTICATED BY EMAIL") -// }else { -// log.Info("USER NOT AUTHENTICATED") -// } -// -// // foundUser, err := db.GetUser(db.User{Username: "User123"}) -// -// // if errors.Is(err, gorm.ErrRecordNotFound) { -// // log.Warn("RECORD NOT FOUND") -// // } else { -// // log.Info("FOUND USER", foundUser) -// // } -// -// return nil -// } diff --git a/imagini.db b/cmd/imagini.db similarity index 90% rename from imagini.db rename to cmd/imagini.db index 7def2e9..eca2661 100644 Binary files a/imagini.db and b/cmd/imagini.db differ diff --git a/main.go b/cmd/main.go similarity index 53% rename from main.go rename to cmd/main.go index aa0669b..0821940 100644 --- a/main.go +++ b/cmd/main.go @@ -2,16 +2,13 @@ package main import ( "os" - + "os/signal" "github.com/urfave/cli/v2" log "github.com/sirupsen/logrus" - "reichard.io/imagini/cmd" - "reichard.io/imagini/internal/sessions" + "reichard.io/imagini/cmd/server" ) -var globalSessions *sessions.Manager - type UTCFormatter struct { log.Formatter } @@ -24,13 +21,17 @@ func (u UTCFormatter) Format(e *log.Entry) ([]byte, error) { func main() { log.SetFormatter(UTCFormatter{&log.TextFormatter{FullTimestamp: true}}) - log.Info("Starging Imagini") + log.Info("Starting Imagini") app := &cli.App{ Name: "Imagini", Usage: "A self hosted photo library.", Commands: []*cli.Command{ - &cmd.CmdServe, -// &cmd.CmdDBTest, + { + Name: "serve", + Aliases: []string{"s"}, + Usage: "Start Imagini web server.", + Action: cmdServer, + }, }, } err := app.Run(os.Args) @@ -38,3 +39,17 @@ func main() { log.Fatal(err) } } + +func cmdServer(ctx *cli.Context) error { + server := server.NewServer() + server.StartServer() + + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + <-c + + server.StopServer() + os.Exit(0) + + return nil +} diff --git a/cmd/server/server.go b/cmd/server/server.go new file mode 100644 index 0000000..ba5ed4d --- /dev/null +++ b/cmd/server/server.go @@ -0,0 +1,57 @@ +package server + +import ( + "time" + "context" + "net/http" + "reichard.io/imagini/internal/db" + "reichard.io/imagini/internal/api" + "reichard.io/imagini/internal/auth" + "reichard.io/imagini/internal/config" + log "github.com/sirupsen/logrus" +) + +type Server struct { + API *api.API + Auth *auth.AuthManager + Config *config.Config + Database *db.DBManager + httpServer *http.Server +} + +func NewServer() *Server { + config := config.Load() + db := db.NewMgr(config) + auth := auth.NewMgr(db) + api := api.NewApi(db, auth) + + return &Server{ + API: api, + Auth: auth, + Config: config, + Database: db, + } +} + +func (s *Server) StartServer() { + listenAddr := (":" + s.Config.ListenPort) + + s.httpServer = &http.Server{ + Handler: s.API.Router, + Addr: listenAddr, + } + + go func() { + err := s.httpServer.ListenAndServe() + if err != nil { + log.Error("Error starting server ", err) + return + } + }() +} + +func (s *Server) StopServer() { + ctx, cancel := context.WithTimeout(context.Background(), 5 * time.Second) + defer cancel() + s.httpServer.Shutdown(ctx) +} diff --git a/internal/api/albums.go b/internal/api/albums.go new file mode 100644 index 0000000..9f71175 --- /dev/null +++ b/internal/api/albums.go @@ -0,0 +1,9 @@ +package api + +import ( + "net/http" +) + +func (api *API) albumsHandler(w http.ResponseWriter, r *http.Request) { + +} diff --git a/internal/api/api.go b/internal/api/api.go new file mode 100644 index 0000000..b6da92d --- /dev/null +++ b/internal/api/api.go @@ -0,0 +1,24 @@ +package api + +import ( + "net/http" + + "reichard.io/imagini/internal/db" + "reichard.io/imagini/internal/auth" +) + +type API struct { + Router *http.ServeMux + Auth *auth.AuthManager + DB *db.DBManager +} + +func NewApi(db *db.DBManager, auth *auth.AuthManager) *API { + api := &API{ + Router: http.NewServeMux(), + DB: db, + Auth: auth, + } + api.registerRoutes() + return api +} diff --git a/routes/auth.go b/internal/api/auth.go similarity index 56% rename from routes/auth.go rename to internal/api/auth.go index ae72337..bde410d 100644 --- a/routes/auth.go +++ b/internal/api/auth.go @@ -1,18 +1,23 @@ -package routes +package api import ( "time" "encoding/json" "net/http" - - "reichard.io/imagini/internal/auth" "reichard.io/imagini/internal/models" + // "github.com/lestrrat-go/jwx/jwt" + // "github.com/lestrrat-go/jwx/jwa" // log "github.com/sirupsen/logrus" ) -func (ctx *ImaginiContext) loginHandler(w http.ResponseWriter, r *http.Request) { +// https://www.calhoun.io/pitfalls-of-context-values-and-how-to-avoid-or-mitigate-them/ +// https://pace.dev/blog/2018/05/09/how-I-write-http-services-after-eight-years.html +// https://medium.com/@benbjohnson/standard-package-layout-7cdbc8391fc1#333c +// https://www.alexedwards.net/blog/organising-database-access <---- best +// - TLDR: Do what you're doing, but use closeures for the handlers +func (api *API) loginHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { - JSONError(w, "Method is not supported.", http.StatusMethodNotAllowed) + errorJSON(w, "Method is not supported.", http.StatusMethodNotAllowed) return } @@ -20,20 +25,20 @@ func (ctx *ImaginiContext) loginHandler(w http.ResponseWriter, r *http.Request) var creds models.APICredentials err := json.NewDecoder(r.Body).Decode(&creds) if err != nil { - JSONError(w, "Invalid parameters.", http.StatusBadRequest) + errorJSON(w, "Invalid parameters.", http.StatusBadRequest) return } // Validate if creds.User == "" || creds.Password == "" { - JSONError(w, "Invalid parameters.", http.StatusBadRequest) + errorJSON(w, "Invalid parameters.", http.StatusBadRequest) return } // TODO: Is user already logged in? If so refresh token, if different user, kill session and log in new user? // Do login - resp := auth.AuthenticateUser(ctx.DB, creds) + resp := api.Auth.AuthenticateUser(creds) if resp == true { // Return Success cookie := http.Cookie{ @@ -41,14 +46,14 @@ func (ctx *ImaginiContext) loginHandler(w http.ResponseWriter, r *http.Request) Value: "testToken", } http.SetCookie(w, &cookie) - JSONSuccess(w, "Login success.", http.StatusOK) + successJSON(w, "Login success.", http.StatusOK) }else { // Return Failure - JSONError(w, "Invalid credentials.", http.StatusUnauthorized) + errorJSON(w, "Invalid credentials.", http.StatusUnauthorized) } } -func (ctx *ImaginiContext) logoutHandler(w http.ResponseWriter, r *http.Request) { +func (api *API) logoutHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method is not supported.", http.StatusMethodNotAllowed) return diff --git a/internal/api/info.go b/internal/api/info.go new file mode 100644 index 0000000..4958078 --- /dev/null +++ b/internal/api/info.go @@ -0,0 +1,9 @@ +package api + +import ( + "net/http" +) + +func (api *API) infoHandler(w http.ResponseWriter, r *http.Request) { + +} diff --git a/internal/api/media_items.go b/internal/api/media_items.go new file mode 100644 index 0000000..09858f5 --- /dev/null +++ b/internal/api/media_items.go @@ -0,0 +1,9 @@ +package api + +import ( + "net/http" +) + +func (api *API) mediaItemsHandler(w http.ResponseWriter, r *http.Request) { + +} diff --git a/routes/middlewares.go b/internal/api/middlewares.go similarity index 98% rename from routes/middlewares.go rename to internal/api/middlewares.go index a52593d..faeca8d 100644 --- a/routes/middlewares.go +++ b/internal/api/middlewares.go @@ -1,4 +1,4 @@ -package routes +package api import ( "net/http" diff --git a/internal/api/routes.go b/internal/api/routes.go new file mode 100644 index 0000000..7789dc9 --- /dev/null +++ b/internal/api/routes.go @@ -0,0 +1,33 @@ +package api + +import ( + "encoding/json" + "net/http" +) + +func (api *API) registerRoutes() { + api.Router.HandleFunc("/MediaItems", api.mediaItemsHandler) + api.Router.HandleFunc("/Upload", api.uploadHandler) + api.Router.HandleFunc("/Albums", api.albumsHandler) + api.Router.HandleFunc("/Logout", api.logoutHandler) + api.Router.HandleFunc("/Login", api.loginHandler) + api.Router.HandleFunc("/Users", api.usersHandler) + api.Router.HandleFunc("/Tags", api.tagsHandler) + api.Router.HandleFunc("/Info", api.infoHandler) + api.Router.HandleFunc("/Me", api.meHandler) +} + +// https://stackoverflow.com/a/59764037 +func errorJSON(w http.ResponseWriter, err string, code int) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.Header().Set("X-Content-Type-Options", "nosniff") + w.WriteHeader(code) + json.NewEncoder(w).Encode(map[string]interface{}{"error": err}) +} + +func successJSON(w http.ResponseWriter, msg string, code int) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.Header().Set("X-Content-Type-Options", "nosniff") + w.WriteHeader(code) + json.NewEncoder(w).Encode(map[string]interface{}{"success": msg}) +} diff --git a/internal/api/tags.go b/internal/api/tags.go new file mode 100644 index 0000000..fe7c7e2 --- /dev/null +++ b/internal/api/tags.go @@ -0,0 +1,9 @@ +package api + +import ( + "net/http" +) + +func (api *API) tagsHandler(w http.ResponseWriter, r *http.Request) { + +} diff --git a/internal/api/upload.go b/internal/api/upload.go new file mode 100644 index 0000000..c9767a1 --- /dev/null +++ b/internal/api/upload.go @@ -0,0 +1,9 @@ +package api + +import ( + "net/http" +) + +func (api *API) uploadHandler(w http.ResponseWriter, r *http.Request) { + +} diff --git a/routes/users.go b/internal/api/users.go similarity index 70% rename from routes/users.go rename to internal/api/users.go index a9213b6..31faa23 100644 --- a/routes/users.go +++ b/internal/api/users.go @@ -1,11 +1,11 @@ -package routes +package api import ( "net/http" log "github.com/sirupsen/logrus" ) -func (ctx *ImaginiContext) usersHandler(w http.ResponseWriter, r *http.Request) { +func (api *API) usersHandler(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodPost { // CREATE } else if r.Method == http.MethodPut { @@ -17,14 +17,14 @@ func (ctx *ImaginiContext) usersHandler(w http.ResponseWriter, r *http.Request) } else if r.Method == http.MethodGet { // GET } else { - JSONError(w, "Method is not supported.", http.StatusMethodNotAllowed) + errorJSON(w, "Method is not supported.", http.StatusMethodNotAllowed) return } } -func (ctx *ImaginiContext) meHandler(w http.ResponseWriter, r *http.Request) { +func (api *API) meHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { - JSONError(w, "Method is not supported.", http.StatusMethodNotAllowed) + errorJSON(w, "Method is not supported.", http.StatusMethodNotAllowed) return } diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 7d15aca..497526d 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -3,16 +3,26 @@ package auth import ( "errors" "gorm.io/gorm" - "reichard.io/imagini/internal/query" + "reichard.io/imagini/internal/db" "reichard.io/imagini/internal/models" log "github.com/sirupsen/logrus" ) -func AuthenticateUser(db *gorm.DB, creds models.APICredentials) bool { +type AuthManager struct { + DB *db.DBManager +} + +func NewMgr(db *db.DBManager) *AuthManager { + return &AuthManager{ + DB: db, + } +} + +func (auth *AuthManager) AuthenticateUser(creds models.APICredentials) bool { // By Username - foundUser, err := query.User(db, models.User{Username: creds.User}) + foundUser, err := auth.DB.User(models.User{Username: creds.User}) if errors.Is(err, gorm.ErrRecordNotFound) { - foundUser, err = query.User(db, models.User{Email: creds.User}) + foundUser, err = auth.DB.User(models.User{Email: creds.User}) } // Error Checking diff --git a/internal/config/config.go b/internal/config/config.go index b4e0d07..4243563 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -14,7 +14,7 @@ type Config struct { ListenPort string } -func NewConfig() *Config { +func Load() *Config { return &Config{ DBType: getEnv("DATABASE_TYPE", "SQLite"), DBName: getEnv("DATABASE_NAME", "imagini"), diff --git a/internal/context/context.go b/internal/context/context.go deleted file mode 100644 index b3ae793..0000000 --- a/internal/context/context.go +++ /dev/null @@ -1,21 +0,0 @@ -package context - -import ( - "gorm.io/gorm" - "reichard.io/imagini/internal/query" - "reichard.io/imagini/internal/config" -) - -type ImaginiContext struct { - DB *gorm.DB - Config *config.Config -} - -func NewImaginiContext() *ImaginiContext { - c := config.NewConfig() - db := query.NewDB(c) - return &ImaginiContext{ - DB: db, - Config: c, - } -} diff --git a/internal/query/query.go b/internal/db/db.go similarity index 83% rename from internal/query/query.go rename to internal/db/db.go index f33afd0..cea04dc 100644 --- a/internal/query/query.go +++ b/internal/db/db.go @@ -1,4 +1,4 @@ -package query +package db import ( "path" @@ -12,40 +12,46 @@ import ( "reichard.io/imagini/internal/models" ) -func NewDB(c *config.Config) *gorm.DB { +type DBManager struct { + db *gorm.DB +} + +func NewMgr(c *config.Config) *DBManager { gormConfig := &gorm.Config{ PrepareStmt: true, Logger: logger.Default.LogMode(logger.Silent), } - var db *gorm.DB + + // Create manager + dbm := &DBManager{} if c.DBType == "SQLite" { dbLocation := path.Join(c.ConfigPath, "imagini.db") - db, _ = gorm.Open(sqlite.Open(dbLocation), gormConfig) + dbm.db, _ = gorm.Open(sqlite.Open(dbLocation), gormConfig) } else { log.Fatal("Unsupported Database") } // Initialize database - db.AutoMigrate(&models.ServerSetting{}) - db.AutoMigrate(&models.User{}) - db.AutoMigrate(&models.MediaItem{}) - db.AutoMigrate(&models.Tag{}) - db.AutoMigrate(&models.Album{}) + dbm.db.AutoMigrate(&models.ServerSetting{}) + dbm.db.AutoMigrate(&models.User{}) + dbm.db.AutoMigrate(&models.MediaItem{}) + dbm.db.AutoMigrate(&models.Tag{}) + dbm.db.AutoMigrate(&models.Album{}) // Determine whether to bootstrap var count int64 - db.Model(&models.User{}).Count(&count) + dbm.db.Model(&models.User{}).Count(&count) if count == 0 { - bootstrapDatabase(db) + dbm.bootstrapDatabase() } - return db + return dbm } -func bootstrapDatabase(db *gorm.DB) { +func (dbm *DBManager) bootstrapDatabase() { log.Info("[query] Bootstrapping database.") - err := CreateUser(db, models.User{ + err := dbm.CreateUser(models.User{ Username: "admin", Password: "admin", AuthType: "Local", @@ -56,7 +62,7 @@ func bootstrapDatabase(db *gorm.DB) { } } -func ItemsFromAlbum(db *gorm.DB, user models.User, album models.Album) []models.MediaItem { +func (dbm *DBManager) ItemsFromAlbum(user models.User, album models.Album) []models.MediaItem { var mediaItems []models.MediaItem // db.Table("media_albums"). // Select("media_item.*"). @@ -64,7 +70,7 @@ func ItemsFromAlbum(db *gorm.DB, user models.User, album models.Album) []models. // Where("media_albums.album_id = ? AND media_items.User = ?", albumID, userID). - db. + dbm.db. //Where("album = ? AND user = ?", albumID, userID). Find(&mediaItems) return mediaItems diff --git a/internal/query/errors.go b/internal/db/errors.go similarity index 86% rename from internal/query/errors.go rename to internal/db/errors.go index 5c9b824..035b3c1 100644 --- a/internal/query/errors.go +++ b/internal/db/errors.go @@ -1,4 +1,4 @@ -package query +package db import "errors" diff --git a/internal/query/users.go b/internal/db/users.go similarity index 63% rename from internal/query/users.go rename to internal/db/users.go index 22b1d43..3ef2c64 100644 --- a/internal/query/users.go +++ b/internal/db/users.go @@ -1,4 +1,4 @@ -package query +package db import ( "errors" @@ -9,9 +9,9 @@ import ( "reichard.io/imagini/internal/models" ) -func CreateUser(db *gorm.DB, user models.User) error { +func (dbm *DBManager) CreateUser(user models.User) error { log.Info("[query] Creating user: ", user.Username) - _, err := User(db, user) + _, err := dbm.User(user) if !errors.Is(err, gorm.ErrRecordNotFound) { log.Warn("[query] User already exists: ", user.Username) return errors.New("User already exists") @@ -23,20 +23,20 @@ func CreateUser(db *gorm.DB, user models.User) error { return err } user.Password = string(hashedPassword) - return db.Create(&user).Error + return dbm.db.Create(&user).Error } -func User (db *gorm.DB, user models.User) (models.User, error) { +func (dbm *DBManager) User (user models.User) (models.User, error) { var foundUser models.User var count int64 - err := db.Where(&user).First(&foundUser).Count(&count).Error + err := dbm.db.Where(&user).First(&foundUser).Count(&count).Error return foundUser, err } -func DeleteUser (user models.User) error { +func (dbm *DBManager) DeleteUser (user models.User) error { return nil } -func UpdatePassword (user models.User, pw string) { +func (dbm *DBManager) UpdatePassword (user models.User, pw string) { } diff --git a/internal/models/db.go b/internal/models/db.go index 489467b..8543698 100644 --- a/internal/models/db.go +++ b/internal/models/db.go @@ -5,41 +5,51 @@ import ( "time" ) +// Might not even need this type ServerSetting struct { gorm.Model - Name string `json:"name"` - Description string `json:"description"` - Value string `json:"value"` + Name string `json:"name" gorm:"not null"` + Description string `json:"description" gorm:"not null"` + Value string `json:"value" gorm:"not null"` } +type Device struct { + gorm.Model + User User `json:"user" gorm:"ForeignKey:ID"` + DeviceName string `json:"name"` + Type string `json:"type"` // Android, iOS, Chrome, FireFox, Edge, etc +} + +// TODO: ID -> UUID? type User struct { gorm.Model - Email string `json:"email" gorm:"unique;not null"` - Username string `json:"username" gorm:"unique;not null"` + Email string `json:"email" gorm:"unique"` + Username string `json:"username" gorm:"unique"` FirstName string `json:"first_name"` LastName string `json:"last_name"` - AuthType string `json:"auth_type"` - Password string `json:"password"` + AuthType string `json:"auth_type" gorm:"default:Local;not null"` + Password string `json:"-"` + JWTSecret string `json:"-" gorm:"unique;not null"` // TODO: Auto Generate UUID } type MediaItem struct { gorm.Model - User User `json:"user" gorm:"ForeignKey:ID"` + User User `json:"user" gorm:"ForeignKey:ID;not null"` EXIFDate time.Time `json:"exif_date"` Latitude string `json:"latitude"` Longitude string `json:"longitude"` - MediaType uint `json:"media_type"` - RelPath string `json:"rel_path"` + MediaType string `json:"media_type" gorm:"default:Photo;not null"` // Photo, Video + RelPath string `json:"rel_path" gorm:"not null"` Tags []Tag `json:"tags" gorm:"many2many:media_tags;"` Albums []Album `json:"albums" gorm:"many2many:media_albums;"` } type Tag struct { gorm.Model - Name string `json:"name"` + Name string `json:"name" gorm:"not null"` } type Album struct { gorm.Model - Name string `json:"name"` + Name string `json:"name" gorm:"not null"` } diff --git a/internal/session/session.go b/internal/session/session.go new file mode 100644 index 0000000..38d0ba6 --- /dev/null +++ b/internal/session/session.go @@ -0,0 +1,25 @@ +package session + +import ( + "sync" +) + +type SessionManager struct { + Mutex sync.Mutex +} + +func NewMgr() *SessionManager { + return &SessionManager{} +} + +func (sm *SessionManager) Set(key, value string) { + +} + +func (sm *SessionManager) Get(key string) string { + return "" +} + +func (sm *SessionManager) Delete(key string) { + +} diff --git a/internal/sessions/sessions.go b/internal/sessions/sessions.go deleted file mode 100644 index 276d64e..0000000 --- a/internal/sessions/sessions.go +++ /dev/null @@ -1,9 +0,0 @@ -package sessions - -// import ( -// "github.com/dgrijalva/jwt-go" -// ) - -type Manager struct { - -} diff --git a/routes/albums.go b/routes/albums.go deleted file mode 100644 index faec9f0..0000000 --- a/routes/albums.go +++ /dev/null @@ -1,9 +0,0 @@ -package routes - -import ( - "net/http" -) - -func (ctx *ImaginiContext) albumsHandler(w http.ResponseWriter, r *http.Request) { - -} diff --git a/routes/info.go b/routes/info.go deleted file mode 100644 index b488b18..0000000 --- a/routes/info.go +++ /dev/null @@ -1,9 +0,0 @@ -package routes - -import ( - "net/http" -) - -func (ctx *ImaginiContext) infoHandler(w http.ResponseWriter, r *http.Request) { - -} diff --git a/routes/media_items.go b/routes/media_items.go deleted file mode 100644 index f09b5b4..0000000 --- a/routes/media_items.go +++ /dev/null @@ -1,9 +0,0 @@ -package routes - -import ( - "net/http" -) - -func (ctx *ImaginiContext) mediaItemsHandler(w http.ResponseWriter, r *http.Request) { - -} diff --git a/routes/routes.go b/routes/routes.go deleted file mode 100644 index 27140f2..0000000 --- a/routes/routes.go +++ /dev/null @@ -1,138 +0,0 @@ -package routes - -import ( - "encoding/json" - "net/http" - "reichard.io/imagini/internal/context" -) - -type ImaginiContext struct { - *context.ImaginiContext -} - -func RegisterRoutes(cctx *context.ImaginiContext) { - ctx := &ImaginiContext{cctx} - http.HandleFunc("/MediaItems", ctx.mediaItemsHandler) - http.HandleFunc("/Upload", ctx.uploadHandler) - http.HandleFunc("/Albums", ctx.albumsHandler) - http.HandleFunc("/Logout", ctx.logoutHandler) - http.HandleFunc("/Login", ctx.loginHandler) - http.HandleFunc("/Users", ctx.usersHandler) - http.HandleFunc("/Tags", ctx.tagsHandler) - http.HandleFunc("/Info", ctx.infoHandler) - http.HandleFunc("/Me", ctx.meHandler) -} - -// https://stackoverflow.com/a/59764037 -func JSONError(w http.ResponseWriter, err string, code int) { - w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.Header().Set("X-Content-Type-Options", "nosniff") - w.WriteHeader(code) - json.NewEncoder(w).Encode(map[string]interface{}{"error": err}) -} - -func JSONSuccess(w http.ResponseWriter, msg string, code int) { - w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.Header().Set("X-Content-Type-Options", "nosniff") - w.WriteHeader(code) - json.NewEncoder(w).Encode(map[string]interface{}{"success": msg}) -} - -// METHOD: -// switch r.Method { -// case http.MethodGet: -// // Serve the resource. -// case http.MethodPost: -// // Create a new record. -// case http.MethodPut: -// // Update an existing record. -// case http.MethodDelete: -// // Remove the record. -// default: -// // Give an error message. -// } - - -// commonMiddleware := []Middleware{ -// logMiddleware, -// authMiddleware, -// } -// http.Handle("/Users", MultipleMiddleware(usersHandler, commonMiddleware...)) -// http.Handle("/Uploads/", MultipleMiddleware(uploadsHandler, commonMiddleware...)) - -// // http.HandleFunc("/uploads/", uploadsHandler()) -// http.Handle("/Uploads/", func(next http.Handler) http.Handler { -// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { -// _, ok := ValidateUserToken(r) - -// if ok { -// next.ServeHTTP(w, r) -// } else { -// w.WriteHeader(http.StatusUnauthorized) -// } -// }) -// }(http.StripPrefix("/Uploads/", tusHandler))) - -// Filter Example: -// query := r.URL.Query() -// filters, present := query["filters"] - -// func uploadsHandler() http.Handler { -// store := filestore.FileStore{ -// Path: "./Uploads", -// } -// composer := tusd.NewStoreComposer() -// store.UseIn(composer) -// -// handler, err := tusd.NewHandler(tusd.Config{ -// BasePath: "/uploads/", -// StoreComposer: composer, -// NotifyCompleteUploads: true, -// }) -// -// if err != nil { -// panic(fmt.Errorf("Unable to create handler: %s", err)) -// } -// -// go func() { -// for { -// event := <-handler.CompleteUploads -// fmt.Printf("Upload %s finished\n", event.Upload.ID) -// } -// }() -// -// // return func(w http.ResponseWriter, r *http.Request) { -// // http.StripPrefix("/Uploads/", handler).ServeHTTP(w, r) -// // }; -// -// return http.StripPrefix("/Uploads/", handler) -// } - -// func processMedia() { -// var mi db.MediaItem -// - // TODO: - // - Derive Magic -> mediaType - // - Create Thumbnail - // - Pull EXIF - // - EXIFDate - // - Latitude - // - Longitude - // - TensorFlow Classification - // - https://outcrawl.com/image-recognition-api-go-tensorflow - // - Update Tags / MediaTags Table - // - Save Image - // - Update MediaItems Table - - - - // import "github.com/disintegration/imaging" - // - // img, err := imaging.Open("original.jpg", imaging.AutoOrientation(true)) - // if err != nil { - // return nil, err - // } - // - // img = imaging.Fit(img, 240, 160, imaging.Lanczos) - // err = imaging.Save(img, "thumbnail.jpg") -// } diff --git a/routes/tags.go b/routes/tags.go deleted file mode 100644 index 93dd07a..0000000 --- a/routes/tags.go +++ /dev/null @@ -1,9 +0,0 @@ -package routes - -import ( - "net/http" -) - -func (ctx *ImaginiContext) tagsHandler(w http.ResponseWriter, r *http.Request) { - -} diff --git a/routes/upload.go b/routes/upload.go deleted file mode 100644 index 91abd32..0000000 --- a/routes/upload.go +++ /dev/null @@ -1,9 +0,0 @@ -package routes - -import ( - "net/http" -) - -func (ctx *ImaginiContext) uploadHandler(w http.ResponseWriter, r *http.Request) { - -}