410 lines
11 KiB
Go
410 lines
11 KiB
Go
package graph
|
|
|
|
// This file will be automatically regenerated based on the schema, any resolver implementations
|
|
// will be copied through when generating and any unknown code will be moved to the end.
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gabriel-vasile/mimetype"
|
|
"github.com/google/uuid"
|
|
"reichard.io/imagini/graph/generated"
|
|
"reichard.io/imagini/graph/model"
|
|
)
|
|
|
|
func (r *mutationResolver) CreateMediaItem(ctx context.Context, input model.NewMediaItem) (*model.MediaItem, error) {
|
|
// Get Context
|
|
authContext := ctx.Value("auth").(*model.AuthContext)
|
|
accessToken := *authContext.AccessToken
|
|
userID, ok := accessToken.Get("sub")
|
|
if !ok {
|
|
return nil, errors.New("Upload Failed")
|
|
}
|
|
|
|
// File header placeholder
|
|
fileHeader := make([]byte, 64)
|
|
|
|
// Copy headers into the buffer
|
|
if _, err := input.File.File.Read(fileHeader); err != nil {
|
|
return nil, errors.New("Upload Failed")
|
|
}
|
|
|
|
// Determine media type
|
|
fileMime := mimetype.Detect(fileHeader)
|
|
contentType := fileMime.String()
|
|
var isVideo bool
|
|
if strings.HasPrefix(contentType, "image/") {
|
|
isVideo = false
|
|
} else if strings.HasPrefix(contentType, "video/") {
|
|
isVideo = true
|
|
} else {
|
|
return nil, errors.New("Upload Failed")
|
|
}
|
|
|
|
// Derive Folder & File Path
|
|
mediaItemID := uuid.New().String()
|
|
fileName := mediaItemID + fileMime.Extension()
|
|
folderPath := path.Join("/" + r.Config.DataPath + "/media/" + userID.(string))
|
|
os.MkdirAll(folderPath, 0700)
|
|
filePath := path.Join(folderPath + "/" + fileName)
|
|
|
|
// Create File
|
|
f, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0600)
|
|
if err != nil {
|
|
return nil, errors.New("Upload Failed")
|
|
}
|
|
defer f.Close()
|
|
|
|
// Copy header to file
|
|
_, err = io.Copy(f, bytes.NewReader(fileHeader))
|
|
if err != nil {
|
|
return nil, errors.New("Upload Failed")
|
|
}
|
|
|
|
// Copy remaining file
|
|
_, err = io.Copy(f, input.File.File)
|
|
if err != nil {
|
|
return nil, errors.New("Upload Failed")
|
|
}
|
|
|
|
// Create MediaItem From EXIF Data
|
|
mediaItem, err := mediaItemFromEXIFData(filePath)
|
|
if err != nil {
|
|
return nil, errors.New("Upload Failed")
|
|
}
|
|
|
|
// Add Additional MediaItem Fields
|
|
mediaItem.ID = mediaItemID
|
|
mediaItem.UserID = userID.(string)
|
|
mediaItem.IsVideo = isVideo
|
|
mediaItem.FileName = fileName
|
|
mediaItem.OrigName = input.File.Filename
|
|
|
|
// Create MediaItem in DB
|
|
err = r.DB.CreateMediaItem(mediaItem)
|
|
if err != nil {
|
|
return nil, errors.New("Upload Failed")
|
|
}
|
|
|
|
// Success
|
|
return mediaItem, nil
|
|
}
|
|
|
|
func (r *mutationResolver) CreateAlbum(ctx context.Context, input model.NewAlbum) (*model.Album, error) {
|
|
// Get Context
|
|
authContext := ctx.Value("auth").(*model.AuthContext)
|
|
accessToken := *authContext.AccessToken
|
|
_, ok := accessToken.Get("sub")
|
|
if !ok {
|
|
return nil, errors.New("Upload Failed")
|
|
}
|
|
|
|
album := &model.Album{
|
|
Name: input.Name,
|
|
}
|
|
|
|
err := r.DB.CreateAlbum(album)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return album, nil
|
|
}
|
|
|
|
func (r *mutationResolver) CreateTag(ctx context.Context, input model.NewTag) (*model.Tag, error) {
|
|
tag := &model.Tag{
|
|
Name: input.Name,
|
|
}
|
|
|
|
err := r.DB.CreateTag(tag)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return tag, nil
|
|
}
|
|
|
|
func (r *mutationResolver) CreateUser(ctx context.Context, input model.NewUser) (*model.User, error) {
|
|
user := &model.User{
|
|
Email: input.Email,
|
|
Username: input.Username,
|
|
FirstName: input.FirstName,
|
|
LastName: input.LastName,
|
|
Role: input.Role,
|
|
AuthType: input.AuthType,
|
|
Password: input.Password,
|
|
}
|
|
|
|
err := r.DB.CreateUser(user)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return user, nil
|
|
}
|
|
|
|
func (r *queryResolver) Login(ctx context.Context, user string, password string, deviceID *string) (*model.AuthResponse, error) {
|
|
// Get Context
|
|
authContext := ctx.Value("auth").(*model.AuthContext)
|
|
resp := authContext.AuthResponse
|
|
req := authContext.AuthRequest
|
|
|
|
// Clear All Cookies By Default
|
|
accessCookie := http.Cookie{Name: "AccessToken", Path: "/", HttpOnly: true, MaxAge: -1, Expires: time.Now().Add(-100 * time.Hour)}
|
|
refreshCookie := http.Cookie{Name: "RefreshToken", Path: "/", HttpOnly: true, MaxAge: -1, Expires: time.Now().Add(-100 * time.Hour)}
|
|
http.SetCookie(*resp, &accessCookie)
|
|
http.SetCookie(*resp, &refreshCookie)
|
|
|
|
// Do Login
|
|
foundUser, success := r.Auth.AuthenticateUser(user, password)
|
|
if !success {
|
|
return &model.AuthResponse{Result: model.AuthResultFailure}, nil
|
|
}
|
|
|
|
// Upsert Device
|
|
foundDevice := model.Device{UserID: foundUser.ID}
|
|
if deviceID != nil {
|
|
parsedDeviceID, err := uuid.Parse(*deviceID)
|
|
if err != nil {
|
|
return &model.AuthResponse{Result: model.AuthResultFailure}, nil
|
|
}
|
|
foundDevice.ID = parsedDeviceID.String()
|
|
count, err := r.DB.Device(&foundDevice)
|
|
if count != 1 || err != nil {
|
|
return &model.AuthResponse{Result: model.AuthResultFailure}, nil
|
|
}
|
|
} else {
|
|
foundDevice.Type = deriveDeviceType(req)
|
|
err := r.DB.CreateDevice(&foundDevice)
|
|
if err != nil {
|
|
return &model.AuthResponse{Result: model.AuthResultFailure}, nil
|
|
}
|
|
}
|
|
|
|
// Create Tokens
|
|
accessToken, err := r.Auth.CreateJWTAccessToken(foundUser, foundDevice)
|
|
if err != nil {
|
|
return &model.AuthResponse{Result: model.AuthResultFailure}, nil
|
|
}
|
|
refreshToken, err := r.Auth.CreateJWTRefreshToken(foundUser, foundDevice)
|
|
if err != nil {
|
|
return &model.AuthResponse{Result: model.AuthResultFailure}, nil
|
|
}
|
|
|
|
// Set appropriate cookies
|
|
accessCookie = http.Cookie{Name: "AccessToken", Value: accessToken, Path: "/", HttpOnly: true}
|
|
refreshCookie = http.Cookie{Name: "RefreshToken", Value: refreshToken, Path: "/", HttpOnly: true}
|
|
http.SetCookie(*resp, &accessCookie)
|
|
http.SetCookie(*resp, &refreshCookie)
|
|
|
|
return &model.AuthResponse{Result: model.AuthResultSuccess, Device: &foundDevice}, nil
|
|
}
|
|
|
|
func (r *queryResolver) Logout(ctx context.Context) (*model.AuthResponse, error) {
|
|
// Set Cookie From Context
|
|
authContext := ctx.Value("auth").(*model.AuthContext)
|
|
resp := authContext.AuthResponse
|
|
|
|
// Clear All Cookies
|
|
accessCookie := http.Cookie{Name: "AccessToken", Path: "/", HttpOnly: true, MaxAge: -1, Expires: time.Now().Add(-100 * time.Hour)}
|
|
refreshCookie := http.Cookie{Name: "RefreshToken", Path: "/", HttpOnly: true, MaxAge: -1, Expires: time.Now().Add(-100 * time.Hour)}
|
|
http.SetCookie(*resp, &accessCookie)
|
|
http.SetCookie(*resp, &refreshCookie)
|
|
|
|
return &model.AuthResponse{Result: model.AuthResultSuccess}, nil
|
|
}
|
|
|
|
func (r *queryResolver) MediaItem(ctx context.Context, id string, delete *bool) (*model.MediaItem, error) {
|
|
// TODO: User Specific
|
|
deviceID, err := uuid.Parse(id)
|
|
if err != nil {
|
|
return nil, errors.New("Invalid ID Format")
|
|
}
|
|
|
|
foundMediaItem := &model.MediaItem{ID: deviceID.String()}
|
|
count, err := r.DB.MediaItem(foundMediaItem)
|
|
if err != nil {
|
|
return nil, errors.New("DB Error")
|
|
} else if count != 1 {
|
|
return nil, errors.New("MediaItem Not Found")
|
|
}
|
|
return foundMediaItem, nil
|
|
}
|
|
|
|
func (r *queryResolver) Device(ctx context.Context, id string, delete *bool) (*model.Device, error) {
|
|
// TODO: User Specific
|
|
deviceID, err := uuid.Parse(id)
|
|
if err != nil {
|
|
return nil, errors.New("Invalid ID Format")
|
|
}
|
|
|
|
foundDevice := &model.Device{ID: deviceID.String()}
|
|
count, err := r.DB.Device(foundDevice)
|
|
if err != nil {
|
|
return nil, errors.New("DB Error")
|
|
} else if count != 1 {
|
|
return nil, errors.New("Device Not Found")
|
|
}
|
|
return foundDevice, nil
|
|
}
|
|
|
|
func (r *queryResolver) Album(ctx context.Context, id string, delete *bool) (*model.Album, error) {
|
|
// TODO: User Specific
|
|
albumID, err := uuid.Parse(id)
|
|
if err != nil {
|
|
return nil, errors.New("Invalid ID Format")
|
|
}
|
|
|
|
foundAlbum := &model.Album{ID: albumID.String()}
|
|
count, err := r.DB.Album(foundAlbum)
|
|
if err != nil {
|
|
return nil, errors.New("DB Error")
|
|
} else if count != 1 {
|
|
return nil, errors.New("Album Not Found")
|
|
}
|
|
return foundAlbum, nil
|
|
}
|
|
|
|
func (r *queryResolver) User(ctx context.Context, id string, delete *bool) (*model.User, error) {
|
|
userID, err := uuid.Parse(id)
|
|
if err != nil {
|
|
return nil, errors.New("Invalid ID Format")
|
|
}
|
|
|
|
foundUser := &model.User{ID: userID.String()}
|
|
count, err := r.DB.User(foundUser)
|
|
if err != nil {
|
|
return nil, errors.New("DB Error")
|
|
} else if count != 1 {
|
|
return nil, errors.New("User Not Found")
|
|
}
|
|
return foundUser, nil
|
|
}
|
|
|
|
func (r *queryResolver) Tag(ctx context.Context, id string, delete *bool) (*model.Tag, error) {
|
|
tagID, err := uuid.Parse(id)
|
|
if err != nil {
|
|
return nil, errors.New("Invalid ID Format")
|
|
}
|
|
|
|
foundTag := &model.Tag{ID: tagID.String()}
|
|
count, err := r.DB.Tag(foundTag)
|
|
if err != nil {
|
|
return nil, errors.New("DB Error")
|
|
} else if count != 1 {
|
|
return nil, errors.New("Tag Not Found")
|
|
}
|
|
return foundTag, nil
|
|
}
|
|
|
|
func (r *queryResolver) Me(ctx context.Context, delete *bool) (*model.User, error) {
|
|
// Get Context
|
|
authContext := ctx.Value("auth").(*model.AuthContext)
|
|
accessToken := *authContext.AccessToken
|
|
userID, ok := accessToken.Get("sub")
|
|
if !ok {
|
|
return nil, errors.New("Context Error")
|
|
}
|
|
|
|
foundUser := &model.User{ID: userID.(string)}
|
|
count, err := r.DB.User(foundUser)
|
|
if err != nil || count != 1 {
|
|
return nil, errors.New("DB Error")
|
|
}
|
|
return foundUser, nil
|
|
}
|
|
|
|
func (r *queryResolver) MediaItems(ctx context.Context, delete *bool, filter *model.MediaItemFilter, count *int, page *int) (*model.MediaItemResponse, error) {
|
|
resp, totalCount, err := r.DB.MediaItems()
|
|
if err != nil {
|
|
return nil, errors.New("Context Error")
|
|
}
|
|
return &model.MediaItemResponse{
|
|
Data: resp,
|
|
PageInfo: &model.PageInfo{
|
|
Count: int(totalCount),
|
|
Page: 0,
|
|
Total: int(totalCount),
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func (r *queryResolver) Devices(ctx context.Context, delete *bool, filter *model.DeviceFilter, count *int, page *int) (*model.DeviceResponse, error) {
|
|
// TODO: User Specific
|
|
resp, totalCount, err := r.DB.Devices()
|
|
if err != nil {
|
|
return nil, errors.New("Context Error")
|
|
}
|
|
return &model.DeviceResponse{
|
|
Data: resp,
|
|
PageInfo: &model.PageInfo{
|
|
Count: int(totalCount),
|
|
Page: 0,
|
|
Total: int(totalCount),
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func (r *queryResolver) Albums(ctx context.Context, delete *bool, filter *model.AlbumFilter, count *int, page *int) (*model.AlbumResponse, error) {
|
|
// TODO: User Specific
|
|
resp, totalCount, err := r.DB.Albums()
|
|
if err != nil {
|
|
return nil, errors.New("Context Error")
|
|
}
|
|
return &model.AlbumResponse{
|
|
Data: resp,
|
|
PageInfo: &model.PageInfo{
|
|
Count: int(totalCount),
|
|
Page: 0,
|
|
Total: int(totalCount),
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func (r *queryResolver) Tags(ctx context.Context, delete *bool, filter *model.TagFilter, count *int, page *int) (*model.TagResponse, error) {
|
|
resp, totalCount, err := r.DB.Tags()
|
|
if err != nil {
|
|
return nil, errors.New("Context Error")
|
|
}
|
|
return &model.TagResponse{
|
|
Data: resp,
|
|
PageInfo: &model.PageInfo{
|
|
Count: int(totalCount),
|
|
Page: 0,
|
|
Total: int(totalCount),
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func (r *queryResolver) Users(ctx context.Context, delete *bool, filter *model.UserFilter, count *int, page *int) (*model.UserResponse, error) {
|
|
resp, totalCount, err := r.DB.Users()
|
|
if err != nil {
|
|
return nil, errors.New("Context Error")
|
|
}
|
|
return &model.UserResponse{
|
|
Data: resp,
|
|
PageInfo: &model.PageInfo{
|
|
Count: int(totalCount),
|
|
Page: 0,
|
|
Total: int(totalCount),
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// Mutation returns generated.MutationResolver implementation.
|
|
func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResolver{r} }
|
|
|
|
// Query returns generated.QueryResolver implementation.
|
|
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
|
|
|
|
type mutationResolver struct{ *Resolver }
|
|
type queryResolver struct{ *Resolver }
|