Initial Commit
This commit is contained in:
9712
graph/generated/generated.go
Normal file
9712
graph/generated/generated.go
Normal file
File diff suppressed because it is too large
Load Diff
120
graph/helpers.go
Normal file
120
graph/helpers.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package graph
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dsoprea/go-exif/v3"
|
||||
exifcommon "github.com/dsoprea/go-exif/v3/common"
|
||||
"github.com/google/uuid"
|
||||
"reichard.io/imagini/graph/model"
|
||||
)
|
||||
|
||||
func getContextHTTP(ctx context.Context) (*http.ResponseWriter, *http.Request, error) {
|
||||
authContext := ctx.Value("auth").(*model.AuthContext)
|
||||
|
||||
resp := authContext.AuthResponse
|
||||
if resp == nil {
|
||||
return nil, nil, errors.New("Context Error")
|
||||
}
|
||||
|
||||
req := authContext.AuthRequest
|
||||
if resp == nil {
|
||||
return nil, nil, errors.New("Context Error")
|
||||
}
|
||||
|
||||
return resp, req, nil
|
||||
}
|
||||
|
||||
func getContextIDs(ctx context.Context) (string, string, error) {
|
||||
authContext := ctx.Value("auth").(*model.AuthContext)
|
||||
accessToken := *authContext.AccessToken
|
||||
|
||||
uid, ok := accessToken.Get("sub")
|
||||
if !ok {
|
||||
return "", "", errors.New("Context Error")
|
||||
}
|
||||
|
||||
did, ok := accessToken.Get("did")
|
||||
if !ok {
|
||||
return "", "", errors.New("Context Error")
|
||||
}
|
||||
|
||||
userID, err := uuid.Parse(uid.(string))
|
||||
if err != nil {
|
||||
return "", "", errors.New("Context Error")
|
||||
}
|
||||
|
||||
deviceID, err := uuid.Parse(did.(string))
|
||||
if err != nil {
|
||||
return "", "", errors.New("Context Error")
|
||||
}
|
||||
|
||||
return userID.String(), deviceID.String(), nil
|
||||
}
|
||||
|
||||
func deriveDeviceType(r *http.Request) model.DeviceType {
|
||||
userAgent := strings.ToLower(r.Header.Get("User-Agent"))
|
||||
if strings.Contains(userAgent, "ios-imagini") {
|
||||
return model.DeviceTypeIOs
|
||||
} else if strings.Contains(userAgent, "android-imagini") {
|
||||
return model.DeviceTypeAndroid
|
||||
} else if strings.Contains(userAgent, "chrome") {
|
||||
return model.DeviceTypeChrome
|
||||
} else if strings.Contains(userAgent, "firefox") {
|
||||
return model.DeviceTypeFirefox
|
||||
} else if strings.Contains(userAgent, "msie") {
|
||||
return model.DeviceTypeInternetExplorer
|
||||
} else if strings.Contains(userAgent, "edge") {
|
||||
return model.DeviceTypeEdge
|
||||
} else if strings.Contains(userAgent, "safari") {
|
||||
return model.DeviceTypeSafari
|
||||
}
|
||||
return model.DeviceTypeUnknown
|
||||
}
|
||||
|
||||
func mediaItemFromEXIFData(filePath string) (*model.MediaItem, error) {
|
||||
rawExif, err := exif.SearchFileAndExtractExif(filePath)
|
||||
entries, _, err := exif.GetFlatExifData(rawExif, nil)
|
||||
|
||||
decLong := float64(1)
|
||||
decLat := float64(1)
|
||||
|
||||
mediaItem := &model.MediaItem{}
|
||||
for _, v := range entries {
|
||||
if v.TagName == "DateTimeOriginal" {
|
||||
formattedTime, _ := time.Parse("2006:01:02 15:04:05", v.Formatted)
|
||||
mediaItem.ExifDate = &formattedTime
|
||||
} else if v.TagName == "GPSLatitude" {
|
||||
latStruct := v.Value.([]exifcommon.Rational)
|
||||
decLat *= deriveDecimalCoordinate(
|
||||
latStruct[0].Numerator/latStruct[0].Denominator,
|
||||
latStruct[1].Numerator/latStruct[1].Denominator,
|
||||
float64(latStruct[2].Numerator)/float64(latStruct[2].Denominator),
|
||||
)
|
||||
} else if v.TagName == "GPSLongitude" {
|
||||
longStruct := v.Value.([]exifcommon.Rational)
|
||||
decLong *= deriveDecimalCoordinate(
|
||||
longStruct[0].Numerator/longStruct[0].Denominator,
|
||||
longStruct[1].Numerator/longStruct[1].Denominator,
|
||||
float64(longStruct[2].Numerator)/float64(longStruct[2].Denominator),
|
||||
)
|
||||
} else if v.TagName == "GPSLatitudeRef" && v.Formatted == "S" {
|
||||
decLat *= -1
|
||||
} else if v.TagName == "GPSLongitudeRef" && v.Formatted == "W" {
|
||||
decLong *= -1
|
||||
}
|
||||
}
|
||||
|
||||
mediaItem.Latitude = &decLat
|
||||
mediaItem.Longitude = &decLong
|
||||
|
||||
return mediaItem, err
|
||||
}
|
||||
|
||||
func deriveDecimalCoordinate(degrees, minutes uint32, seconds float64) float64 {
|
||||
return float64(degrees) + (float64(minutes) / 60) + (seconds / 3600)
|
||||
}
|
||||
13
graph/model/models_auth.go
Normal file
13
graph/model/models_auth.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/lestrrat-go/jwx/jwt"
|
||||
)
|
||||
|
||||
type AuthContext struct {
|
||||
AccessToken *jwt.Token
|
||||
AuthResponse *http.ResponseWriter
|
||||
AuthRequest *http.Request
|
||||
}
|
||||
36
graph/model/models_db.go
Normal file
36
graph/model/models_db.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
|
||||
newID := uuid.New().String()
|
||||
u.ID = newID
|
||||
return
|
||||
}
|
||||
|
||||
func (a *Album) BeforeCreate(tx *gorm.DB) (err error) {
|
||||
newID := uuid.New().String()
|
||||
a.ID = newID
|
||||
return
|
||||
}
|
||||
|
||||
func (m *MediaItem) BeforeCreate(tx *gorm.DB) (err error) {
|
||||
newID := uuid.New().String()
|
||||
m.ID = newID
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Tag) BeforeCreate(tx *gorm.DB) (err error) {
|
||||
newID := uuid.New().String()
|
||||
t.ID = newID
|
||||
return
|
||||
}
|
||||
|
||||
func (d *Device) BeforeCreate(tx *gorm.DB) (err error) {
|
||||
newID := uuid.New().String()
|
||||
d.ID = newID
|
||||
return
|
||||
}
|
||||
465
graph/model/models_gen.go
Normal file
465
graph/model/models_gen.go
Normal file
@@ -0,0 +1,465 @@
|
||||
// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql"
|
||||
)
|
||||
|
||||
type Album struct {
|
||||
ID string `json:"id" gorm:"primaryKey;not null"`
|
||||
CreatedAt *time.Time `json:"createdAt" `
|
||||
UpdatedAt *time.Time `json:"updatedAt" `
|
||||
Name string `json:"name" gorm:"unique;not null"`
|
||||
UserID string `json:"userID" gorm:"not null"`
|
||||
}
|
||||
|
||||
type AlbumFilter struct {
|
||||
ID *IDFilter `json:"id" `
|
||||
CreatedAt *TimeFilter `json:"createdAt" `
|
||||
UpdatedAt *TimeFilter `json:"updatedAt" `
|
||||
Name *StringFilter `json:"name" `
|
||||
}
|
||||
|
||||
type AlbumResponse struct {
|
||||
Data []*Album `json:"data" `
|
||||
Page *PageResponse `json:"page" `
|
||||
}
|
||||
|
||||
type AuthResponse struct {
|
||||
Result AuthResult `json:"result" `
|
||||
Device *Device `json:"device" `
|
||||
Error *string `json:"error" `
|
||||
}
|
||||
|
||||
type AuthTypeFilter struct {
|
||||
EqualTo *AuthType `json:"equalTo" `
|
||||
NotEqualTo *AuthType `json:"notEqualTo" `
|
||||
}
|
||||
|
||||
type BooleanFilter struct {
|
||||
EqualTo *bool `json:"equalTo" `
|
||||
NotEqualTo *bool `json:"notEqualTo" `
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
ID string `json:"id" gorm:"primaryKey;not null"`
|
||||
CreatedAt *time.Time `json:"createdAt" `
|
||||
UpdatedAt *time.Time `json:"updatedAt" `
|
||||
Name string `json:"name" gorm:"not null"`
|
||||
Type DeviceType `json:"type" gorm:"default:Unknown;not null"`
|
||||
RefreshKey *string `json:"refreshKey" `
|
||||
UserID string `json:"userID" gorm:"not null"`
|
||||
}
|
||||
|
||||
type DeviceFilter struct {
|
||||
ID *IDFilter `json:"id" `
|
||||
CreatedAt *TimeFilter `json:"createdAt" `
|
||||
UpdatedAt *TimeFilter `json:"updatedAt" `
|
||||
Name *StringFilter `json:"name" `
|
||||
Type *DeviceTypeFilter `json:"type" `
|
||||
}
|
||||
|
||||
type DeviceResponse struct {
|
||||
Data []*Device `json:"data" `
|
||||
Page *PageResponse `json:"page" `
|
||||
}
|
||||
|
||||
type DeviceTypeFilter struct {
|
||||
EqualTo *DeviceType `json:"equalTo" `
|
||||
NotEqualTo *DeviceType `json:"notEqualTo" `
|
||||
}
|
||||
|
||||
type FloatFilter struct {
|
||||
EqualTo *float64 `json:"equalTo" `
|
||||
NotEqualTo *float64 `json:"notEqualTo" `
|
||||
LessThan *float64 `json:"lessThan" `
|
||||
LessThanOrEqualTo *float64 `json:"lessThanOrEqualTo" `
|
||||
GreaterThan *float64 `json:"greaterThan" `
|
||||
GreaterThanOrEqualTo *float64 `json:"greaterThanOrEqualTo" `
|
||||
}
|
||||
|
||||
type IDFilter struct {
|
||||
EqualTo *string `json:"equalTo" `
|
||||
NotEqualTo *string `json:"notEqualTo" `
|
||||
}
|
||||
|
||||
type IntFilter struct {
|
||||
EqualTo *int `json:"equalTo" `
|
||||
NotEqualTo *int `json:"notEqualTo" `
|
||||
LessThan *int `json:"lessThan" `
|
||||
LessThanOrEqualTo *int `json:"lessThanOrEqualTo" `
|
||||
GreaterThan *int `json:"greaterThan" `
|
||||
GreaterThanOrEqualTo *int `json:"greaterThanOrEqualTo" `
|
||||
}
|
||||
|
||||
type MediaItem struct {
|
||||
ID string `json:"id" gorm:"primaryKey;not null"`
|
||||
CreatedAt *time.Time `json:"createdAt" `
|
||||
UpdatedAt *time.Time `json:"updatedAt" `
|
||||
ExifDate *time.Time `json:"exifDate" `
|
||||
Latitude *float64 `json:"latitude" gorm:"precision:5"`
|
||||
Longitude *float64 `json:"longitude" gorm:"precision:5"`
|
||||
IsVideo bool `json:"isVideo" gorm:"default:false;not null"`
|
||||
FileName string `json:"fileName" gorm:"not null"`
|
||||
OrigName string `json:"origName" gorm:"not null"`
|
||||
Tags []*Tag `json:"tags" gorm:"many2many:media_tags;foreignKey:ID,UserID;References:ID"`
|
||||
Albums []*Album `json:"albums" gorm:"many2many:media_albums;foreignKey:ID,UserID;Refrences:ID"`
|
||||
UserID string `json:"userID" gorm:"not null"`
|
||||
}
|
||||
|
||||
type MediaItemFilter struct {
|
||||
ID *IDFilter `json:"id" `
|
||||
CreatedAt *TimeFilter `json:"createdAt" `
|
||||
UpdatedAt *TimeFilter `json:"updatedAt" `
|
||||
ExifDate *TimeFilter `json:"exifDate" `
|
||||
Latitude *FloatFilter `json:"latitude" `
|
||||
Longitude *FloatFilter `json:"longitude" `
|
||||
IsVideo *BooleanFilter `json:"isVideo" `
|
||||
OrigName *StringFilter `json:"origName" `
|
||||
Tags *TagFilter `json:"tags" `
|
||||
Albums *AlbumFilter `json:"albums" `
|
||||
}
|
||||
|
||||
type MediaItemResponse struct {
|
||||
Data []*MediaItem `json:"data" `
|
||||
Page *PageResponse `json:"page" `
|
||||
}
|
||||
|
||||
type NewAlbum struct {
|
||||
Name string `json:"name" `
|
||||
}
|
||||
|
||||
type NewMediaItem struct {
|
||||
File graphql.Upload `json:"file" `
|
||||
Tags []string `json:"tags" `
|
||||
Albums []string `json:"albums" `
|
||||
}
|
||||
|
||||
type NewTag struct {
|
||||
Name string `json:"name" `
|
||||
}
|
||||
|
||||
type NewUser struct {
|
||||
Email string `json:"email" `
|
||||
Username string `json:"username" `
|
||||
FirstName *string `json:"firstName" `
|
||||
LastName *string `json:"lastName" `
|
||||
Role Role `json:"role" `
|
||||
AuthType AuthType `json:"authType" `
|
||||
Password *string `json:"password" `
|
||||
}
|
||||
|
||||
type Order struct {
|
||||
By *string `json:"by" `
|
||||
Direction *OrderDirection `json:"direction" `
|
||||
}
|
||||
|
||||
type Page struct {
|
||||
Size *int `json:"size" `
|
||||
Page *int `json:"page" `
|
||||
}
|
||||
|
||||
type PageResponse struct {
|
||||
Size int `json:"size" `
|
||||
Page int `json:"page" `
|
||||
Total int `json:"total" `
|
||||
}
|
||||
|
||||
type RoleFilter struct {
|
||||
EqualTo *Role `json:"equalTo" `
|
||||
NotEqualTo *Role `json:"notEqualTo" `
|
||||
}
|
||||
|
||||
type StringFilter struct {
|
||||
EqualTo *string `json:"equalTo" `
|
||||
NotEqualTo *string `json:"notEqualTo" `
|
||||
StartsWith *string `json:"startsWith" `
|
||||
NotStartsWith *string `json:"notStartsWith" `
|
||||
EndsWith *string `json:"endsWith" `
|
||||
NotEndsWith *string `json:"notEndsWith" `
|
||||
Contains *string `json:"contains" `
|
||||
NotContains *string `json:"notContains" `
|
||||
}
|
||||
|
||||
type Tag struct {
|
||||
ID string `json:"id" gorm:"primaryKey;not null"`
|
||||
CreatedAt *time.Time `json:"createdAt" `
|
||||
UpdatedAt *time.Time `json:"updatedAt" `
|
||||
Name string `json:"name" gorm:"unique;not null"`
|
||||
UserID string `json:"userID" gorm:"not null"`
|
||||
}
|
||||
|
||||
type TagFilter struct {
|
||||
ID *IDFilter `json:"id" `
|
||||
CreatedAt *TimeFilter `json:"createdAt" `
|
||||
UpdatedAt *TimeFilter `json:"updatedAt" `
|
||||
Name *StringFilter `json:"name" `
|
||||
}
|
||||
|
||||
type TagResponse struct {
|
||||
Data []*Tag `json:"data" `
|
||||
Page *PageResponse `json:"page" `
|
||||
}
|
||||
|
||||
type TimeFilter struct {
|
||||
EqualTo *time.Time `json:"equalTo" `
|
||||
NotEqualTo *time.Time `json:"notEqualTo" `
|
||||
LessThan *time.Time `json:"lessThan" `
|
||||
LessThanOrEqualTo *time.Time `json:"lessThanOrEqualTo" `
|
||||
GreaterThan *time.Time `json:"greaterThan" `
|
||||
GreaterThanOrEqualTo *time.Time `json:"greaterThanOrEqualTo" `
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID string `json:"id" gorm:"primaryKey;not null"`
|
||||
CreatedAt *time.Time `json:"createdAt" `
|
||||
UpdatedAt *time.Time `json:"updatedAt" `
|
||||
Email string `json:"email" gorm:"not null;unique"`
|
||||
Username string `json:"username" gorm:"not null;unique"`
|
||||
FirstName *string `json:"firstName" `
|
||||
LastName *string `json:"lastName" `
|
||||
Role Role `json:"role" gorm:"default:User;not null"`
|
||||
AuthType AuthType `json:"authType" gorm:"default:Local;not null"`
|
||||
Password *string `json:"password" `
|
||||
Devices []*Device `json:"devices" gorm:"foreignKey:UserID"`
|
||||
MediaItems []*MediaItem `json:"mediaItems" gorm:"foreignKey:UserID"`
|
||||
}
|
||||
|
||||
type UserFilter struct {
|
||||
ID *IDFilter `json:"id" `
|
||||
CreatedAt *TimeFilter `json:"createdAt" `
|
||||
UpdatedAt *TimeFilter `json:"updatedAt" `
|
||||
Username *StringFilter `json:"username" `
|
||||
FirstName *StringFilter `json:"firstName" `
|
||||
LastName *StringFilter `json:"lastName" `
|
||||
Role *RoleFilter `json:"role" `
|
||||
AuthType *AuthTypeFilter `json:"authType" `
|
||||
}
|
||||
|
||||
type UserResponse struct {
|
||||
Data []*User `json:"data" `
|
||||
Page *PageResponse `json:"page" `
|
||||
}
|
||||
|
||||
type AuthResult string
|
||||
|
||||
const (
|
||||
AuthResultSuccess AuthResult = "Success"
|
||||
AuthResultFailure AuthResult = "Failure"
|
||||
)
|
||||
|
||||
var AllAuthResult = []AuthResult{
|
||||
AuthResultSuccess,
|
||||
AuthResultFailure,
|
||||
}
|
||||
|
||||
func (e AuthResult) IsValid() bool {
|
||||
switch e {
|
||||
case AuthResultSuccess, AuthResultFailure:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e AuthResult) String() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
func (e *AuthResult) UnmarshalGQL(v interface{}) error {
|
||||
str, ok := v.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("enums must be strings")
|
||||
}
|
||||
|
||||
*e = AuthResult(str)
|
||||
if !e.IsValid() {
|
||||
return fmt.Errorf("%s is not a valid AuthResult", str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e AuthResult) MarshalGQL(w io.Writer) {
|
||||
fmt.Fprint(w, strconv.Quote(e.String()))
|
||||
}
|
||||
|
||||
type AuthType string
|
||||
|
||||
const (
|
||||
AuthTypeLocal AuthType = "Local"
|
||||
AuthTypeLdap AuthType = "LDAP"
|
||||
)
|
||||
|
||||
var AllAuthType = []AuthType{
|
||||
AuthTypeLocal,
|
||||
AuthTypeLdap,
|
||||
}
|
||||
|
||||
func (e AuthType) IsValid() bool {
|
||||
switch e {
|
||||
case AuthTypeLocal, AuthTypeLdap:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e AuthType) String() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
func (e *AuthType) UnmarshalGQL(v interface{}) error {
|
||||
str, ok := v.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("enums must be strings")
|
||||
}
|
||||
|
||||
*e = AuthType(str)
|
||||
if !e.IsValid() {
|
||||
return fmt.Errorf("%s is not a valid AuthType", str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e AuthType) MarshalGQL(w io.Writer) {
|
||||
fmt.Fprint(w, strconv.Quote(e.String()))
|
||||
}
|
||||
|
||||
type DeviceType string
|
||||
|
||||
const (
|
||||
DeviceTypeIOs DeviceType = "iOS"
|
||||
DeviceTypeAndroid DeviceType = "Android"
|
||||
DeviceTypeChrome DeviceType = "Chrome"
|
||||
DeviceTypeFirefox DeviceType = "Firefox"
|
||||
DeviceTypeInternetExplorer DeviceType = "InternetExplorer"
|
||||
DeviceTypeEdge DeviceType = "Edge"
|
||||
DeviceTypeSafari DeviceType = "Safari"
|
||||
DeviceTypeUnknown DeviceType = "Unknown"
|
||||
)
|
||||
|
||||
var AllDeviceType = []DeviceType{
|
||||
DeviceTypeIOs,
|
||||
DeviceTypeAndroid,
|
||||
DeviceTypeChrome,
|
||||
DeviceTypeFirefox,
|
||||
DeviceTypeInternetExplorer,
|
||||
DeviceTypeEdge,
|
||||
DeviceTypeSafari,
|
||||
DeviceTypeUnknown,
|
||||
}
|
||||
|
||||
func (e DeviceType) IsValid() bool {
|
||||
switch e {
|
||||
case DeviceTypeIOs, DeviceTypeAndroid, DeviceTypeChrome, DeviceTypeFirefox, DeviceTypeInternetExplorer, DeviceTypeEdge, DeviceTypeSafari, DeviceTypeUnknown:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e DeviceType) String() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
func (e *DeviceType) UnmarshalGQL(v interface{}) error {
|
||||
str, ok := v.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("enums must be strings")
|
||||
}
|
||||
|
||||
*e = DeviceType(str)
|
||||
if !e.IsValid() {
|
||||
return fmt.Errorf("%s is not a valid DeviceType", str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e DeviceType) MarshalGQL(w io.Writer) {
|
||||
fmt.Fprint(w, strconv.Quote(e.String()))
|
||||
}
|
||||
|
||||
type OrderDirection string
|
||||
|
||||
const (
|
||||
OrderDirectionAsc OrderDirection = "ASC"
|
||||
OrderDirectionDesc OrderDirection = "DESC"
|
||||
)
|
||||
|
||||
var AllOrderDirection = []OrderDirection{
|
||||
OrderDirectionAsc,
|
||||
OrderDirectionDesc,
|
||||
}
|
||||
|
||||
func (e OrderDirection) IsValid() bool {
|
||||
switch e {
|
||||
case OrderDirectionAsc, OrderDirectionDesc:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e OrderDirection) String() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
func (e *OrderDirection) UnmarshalGQL(v interface{}) error {
|
||||
str, ok := v.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("enums must be strings")
|
||||
}
|
||||
|
||||
*e = OrderDirection(str)
|
||||
if !e.IsValid() {
|
||||
return fmt.Errorf("%s is not a valid OrderDirection", str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e OrderDirection) MarshalGQL(w io.Writer) {
|
||||
fmt.Fprint(w, strconv.Quote(e.String()))
|
||||
}
|
||||
|
||||
type Role string
|
||||
|
||||
const (
|
||||
RoleAdmin Role = "Admin"
|
||||
RoleUser Role = "User"
|
||||
)
|
||||
|
||||
var AllRole = []Role{
|
||||
RoleAdmin,
|
||||
RoleUser,
|
||||
}
|
||||
|
||||
func (e Role) IsValid() bool {
|
||||
switch e {
|
||||
case RoleAdmin, RoleUser:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e Role) String() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
func (e *Role) UnmarshalGQL(v interface{}) error {
|
||||
str, ok := v.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("enums must be strings")
|
||||
}
|
||||
|
||||
*e = Role(str)
|
||||
if !e.IsValid() {
|
||||
return fmt.Errorf("%s is not a valid Role", str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e Role) MarshalGQL(w io.Writer) {
|
||||
fmt.Fprint(w, strconv.Quote(e.String()))
|
||||
}
|
||||
17
graph/resolver.go
Normal file
17
graph/resolver.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package graph
|
||||
|
||||
import (
|
||||
"reichard.io/imagini/internal/auth"
|
||||
"reichard.io/imagini/internal/config"
|
||||
"reichard.io/imagini/internal/db"
|
||||
)
|
||||
|
||||
// This file will not be regenerated automatically.
|
||||
//
|
||||
// It serves as dependency injection for your app, add any dependencies you require here.
|
||||
|
||||
type Resolver struct {
|
||||
Config *config.Config
|
||||
Auth *auth.AuthManager
|
||||
DB *db.DBManager
|
||||
}
|
||||
384
graph/schema.graphqls
Normal file
384
graph/schema.graphqls
Normal file
@@ -0,0 +1,384 @@
|
||||
|
||||
# https://gqlgen.com/reference/scalars/
|
||||
scalar Time
|
||||
scalar Upload
|
||||
|
||||
# https://gqlgen.com/reference/directives/
|
||||
directive @hasMinRole(role: Role!) on FIELD_DEFINITION
|
||||
directive @isPrivate on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
|
||||
|
||||
directive @meta(
|
||||
gorm: String,
|
||||
) on OBJECT | FIELD_DEFINITION | ENUM_VALUE | INPUT_FIELD_DEFINITION | ENUM | INPUT_OBJECT | ARGUMENT_DEFINITION
|
||||
|
||||
enum Role {
|
||||
Admin
|
||||
User
|
||||
}
|
||||
|
||||
enum DeviceType {
|
||||
iOS
|
||||
Android
|
||||
Chrome
|
||||
Firefox
|
||||
InternetExplorer
|
||||
Edge
|
||||
Safari
|
||||
Unknown
|
||||
}
|
||||
|
||||
enum AuthType {
|
||||
Local
|
||||
LDAP
|
||||
}
|
||||
|
||||
enum OrderDirection {
|
||||
ASC
|
||||
DESC
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# ---------------------- Authentication ----------------------
|
||||
# ------------------------------------------------------------
|
||||
|
||||
enum AuthResult {
|
||||
Success
|
||||
Failure
|
||||
}
|
||||
|
||||
type AuthResponse {
|
||||
result: AuthResult!
|
||||
device: Device
|
||||
error: String
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# ----------------------- Type Filters -----------------------
|
||||
# ------------------------------------------------------------
|
||||
|
||||
input TimeFilter {
|
||||
equalTo: Time
|
||||
notEqualTo: Time
|
||||
lessThan: Time
|
||||
lessThanOrEqualTo: Time
|
||||
greaterThan: Time
|
||||
greaterThanOrEqualTo: Time
|
||||
}
|
||||
|
||||
input IntFilter {
|
||||
equalTo: Int
|
||||
notEqualTo: Int
|
||||
lessThan: Int
|
||||
lessThanOrEqualTo: Int
|
||||
greaterThan: Int
|
||||
greaterThanOrEqualTo: Int
|
||||
}
|
||||
|
||||
input FloatFilter {
|
||||
equalTo: Float
|
||||
notEqualTo: Float
|
||||
lessThan: Float
|
||||
lessThanOrEqualTo: Float
|
||||
greaterThan: Float
|
||||
greaterThanOrEqualTo: Float
|
||||
}
|
||||
|
||||
input BooleanFilter {
|
||||
equalTo: Boolean
|
||||
notEqualTo: Boolean
|
||||
}
|
||||
|
||||
input IDFilter {
|
||||
equalTo: ID
|
||||
notEqualTo: ID
|
||||
}
|
||||
|
||||
input StringFilter {
|
||||
equalTo: String
|
||||
notEqualTo: String
|
||||
startsWith: String
|
||||
notStartsWith: String
|
||||
endsWith: String
|
||||
notEndsWith: String
|
||||
contains: String
|
||||
notContains: String
|
||||
}
|
||||
|
||||
input RoleFilter {
|
||||
equalTo: Role
|
||||
notEqualTo: Role
|
||||
}
|
||||
|
||||
input DeviceTypeFilter {
|
||||
equalTo: DeviceType
|
||||
notEqualTo: DeviceType
|
||||
}
|
||||
|
||||
input AuthTypeFilter {
|
||||
equalTo: AuthType
|
||||
notEqualTo: AuthType
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# -------------------- Object Definitions --------------------
|
||||
# ------------------------------------------------------------
|
||||
|
||||
type User {
|
||||
id: ID! @meta(gorm: "primaryKey;not null")
|
||||
createdAt: Time
|
||||
updatedAt: Time
|
||||
email: String! @meta(gorm: "not null;unique")
|
||||
username: String! @meta(gorm: "not null;unique")
|
||||
firstName: String
|
||||
lastName: String
|
||||
role: Role! @meta(gorm: "default:User;not null")
|
||||
authType: AuthType! @meta(gorm: "default:Local;not null")
|
||||
password: String @isPrivate
|
||||
devices: [Device!] @meta(gorm: "foreignKey:UserID")
|
||||
mediaItems: [MediaItem!] @meta(gorm: "foreignKey:UserID")
|
||||
}
|
||||
|
||||
type Device {
|
||||
id: ID! @meta(gorm: "primaryKey;not null")
|
||||
createdAt: Time
|
||||
updatedAt: Time
|
||||
name: String! @meta(gorm: "not null")
|
||||
type: DeviceType! @meta(gorm: "default:Unknown;not null")
|
||||
refreshKey: String @isPrivate
|
||||
userID: ID! @meta(gorm: "not null")
|
||||
}
|
||||
|
||||
type MediaItem {
|
||||
id: ID! @meta(gorm: "primaryKey;not null")
|
||||
createdAt: Time
|
||||
updatedAt: Time
|
||||
exifDate: Time
|
||||
latitude: Float @meta(gorm: "precision:5")
|
||||
longitude: Float @meta(gorm: "precision:5")
|
||||
isVideo: Boolean! @meta(gorm: "default:false;not null")
|
||||
fileName: String! @meta(gorm: "not null")
|
||||
origName: String! @meta(gorm: "not null")
|
||||
tags: [Tag] @meta(gorm: "many2many:media_tags;foreignKey:ID,UserID;References:ID")
|
||||
albums: [Album] @meta(gorm: "many2many:media_albums;foreignKey:ID,UserID;Refrences:ID")
|
||||
userID: ID! @meta(gorm: "not null")
|
||||
}
|
||||
|
||||
type Tag {
|
||||
id: ID! @meta(gorm: "primaryKey;not null")
|
||||
createdAt: Time
|
||||
updatedAt: Time
|
||||
name: String! @meta(gorm: "unique;not null")
|
||||
userID: ID! @meta(gorm: "not null")
|
||||
}
|
||||
|
||||
type Album {
|
||||
id: ID! @meta(gorm: "primaryKey;not null")
|
||||
createdAt: Time
|
||||
updatedAt: Time
|
||||
name: String! @meta(gorm: "unique;not null")
|
||||
userID: ID! @meta(gorm: "not null")
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# ---------------------- Object Filters ----------------------
|
||||
# ------------------------------------------------------------
|
||||
|
||||
input UserFilter {
|
||||
id: IDFilter
|
||||
createdAt: TimeFilter
|
||||
updatedAt: TimeFilter
|
||||
username: StringFilter
|
||||
firstName: StringFilter
|
||||
lastName: StringFilter
|
||||
role: RoleFilter
|
||||
authType: AuthTypeFilter
|
||||
|
||||
# and: UserFilter
|
||||
# or: UserFilter
|
||||
}
|
||||
|
||||
input MediaItemFilter {
|
||||
id: IDFilter
|
||||
createdAt: TimeFilter
|
||||
updatedAt: TimeFilter
|
||||
exifDate: TimeFilter
|
||||
latitude: FloatFilter
|
||||
longitude: FloatFilter
|
||||
isVideo: BooleanFilter
|
||||
origName: StringFilter
|
||||
tags: TagFilter
|
||||
albums: AlbumFilter
|
||||
|
||||
# and: MediaItemFilter
|
||||
# or: MediaItemFilter
|
||||
}
|
||||
|
||||
input DeviceFilter {
|
||||
id: IDFilter
|
||||
createdAt: TimeFilter
|
||||
updatedAt: TimeFilter
|
||||
name: StringFilter
|
||||
type: DeviceTypeFilter
|
||||
|
||||
# and: MediaItemFilter
|
||||
# or: MediaItemFilter
|
||||
}
|
||||
|
||||
input TagFilter {
|
||||
id: IDFilter
|
||||
createdAt: TimeFilter
|
||||
updatedAt: TimeFilter
|
||||
name: StringFilter
|
||||
|
||||
# and: MediaItemFilter
|
||||
# or: MediaItemFilter
|
||||
}
|
||||
|
||||
input AlbumFilter {
|
||||
id: IDFilter
|
||||
createdAt: TimeFilter
|
||||
updatedAt: TimeFilter
|
||||
name: StringFilter
|
||||
|
||||
# and: MediaItemFilter
|
||||
# or: MediaItemFilter
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# -------------------------- Inputs --------------------------
|
||||
# ------------------------------------------------------------
|
||||
|
||||
input NewUser {
|
||||
email: String!
|
||||
username: String!
|
||||
firstName: String
|
||||
lastName: String
|
||||
role: Role!
|
||||
authType: AuthType!
|
||||
password: String
|
||||
}
|
||||
|
||||
input NewMediaItem {
|
||||
file: Upload!
|
||||
tags: [ID!]
|
||||
albums: [ID!]
|
||||
}
|
||||
|
||||
input NewTag {
|
||||
name: String!
|
||||
}
|
||||
|
||||
input NewAlbum {
|
||||
name: String!
|
||||
}
|
||||
|
||||
input Page {
|
||||
size: Int
|
||||
page: Int
|
||||
}
|
||||
|
||||
input Order {
|
||||
by: String
|
||||
direction: OrderDirection
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# ------------------------ Responses -------------------------
|
||||
# ------------------------------------------------------------
|
||||
|
||||
type PageResponse {
|
||||
size: Int!
|
||||
page: Int!
|
||||
total: Int!
|
||||
}
|
||||
|
||||
type MediaItemResponse {
|
||||
data: [MediaItem]
|
||||
page: PageResponse!
|
||||
}
|
||||
|
||||
type UserResponse {
|
||||
data: [User]
|
||||
page: PageResponse!
|
||||
}
|
||||
|
||||
type DeviceResponse {
|
||||
data: [Device]
|
||||
page: PageResponse!
|
||||
}
|
||||
|
||||
type TagResponse {
|
||||
data: [Tag]
|
||||
page: PageResponse!
|
||||
}
|
||||
|
||||
type AlbumResponse {
|
||||
data: [Album]
|
||||
page: PageResponse!
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# --------------------- Query & Mutations --------------------
|
||||
# ------------------------------------------------------------
|
||||
|
||||
type Query {
|
||||
# Authentication
|
||||
login(
|
||||
user: String!
|
||||
password: String!
|
||||
deviceID: ID
|
||||
): AuthResponse!
|
||||
logout: AuthResponse! @hasMinRole(role: User)
|
||||
|
||||
# Single Item
|
||||
mediaItem(
|
||||
id: ID!
|
||||
): MediaItem! @hasMinRole(role: User)
|
||||
device(
|
||||
id: ID!
|
||||
): Device! @hasMinRole(role: User)
|
||||
album(
|
||||
id: ID!
|
||||
): Album! @hasMinRole(role: User)
|
||||
user(
|
||||
id: ID!
|
||||
): User! @hasMinRole(role: Admin)
|
||||
tag(
|
||||
id: ID!
|
||||
): Tag! @hasMinRole(role: User)
|
||||
me: User! @hasMinRole(role: User)
|
||||
|
||||
# All
|
||||
mediaItems(
|
||||
filter: MediaItemFilter
|
||||
page: Page
|
||||
order: Order
|
||||
): MediaItemResponse! @hasMinRole(role: User)
|
||||
devices(
|
||||
filter: DeviceFilter
|
||||
page: Page
|
||||
order: Order
|
||||
): DeviceResponse! @hasMinRole(role: User)
|
||||
albums(
|
||||
filter: AlbumFilter
|
||||
page: Page
|
||||
order: Order
|
||||
): AlbumResponse! @hasMinRole(role: User)
|
||||
tags(
|
||||
filter: TagFilter
|
||||
page: Page
|
||||
order: Order
|
||||
): TagResponse! @hasMinRole(role: User)
|
||||
users(
|
||||
filter: UserFilter
|
||||
page: Page
|
||||
order: Order
|
||||
): UserResponse! @hasMinRole(role: Admin)
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
createMediaItem(input: NewMediaItem!): MediaItem! @hasMinRole(role: User)
|
||||
createAlbum(input: NewAlbum!): Album! @hasMinRole(role: User)
|
||||
createTag(input: NewTag!): Tag! @hasMinRole(role: User)
|
||||
createUser(input: NewUser!): User! @hasMinRole(role: Admin)
|
||||
}
|
||||
441
graph/schema.resolvers.go
Normal file
441
graph/schema.resolvers.go
Normal file
@@ -0,0 +1,441 @@
|
||||
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) {
|
||||
// Acquire Context
|
||||
userID, _, err := getContextIDs(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 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)
|
||||
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
|
||||
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) {
|
||||
// Acquire Context
|
||||
userID, _, err := getContextIDs(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
album := &model.Album{
|
||||
Name: input.Name,
|
||||
UserID: userID,
|
||||
}
|
||||
|
||||
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) {
|
||||
// Acquire Context
|
||||
userID, _, err := getContextIDs(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tag := &model.Tag{
|
||||
Name: input.Name,
|
||||
UserID: userID,
|
||||
}
|
||||
|
||||
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) {
|
||||
// Acquire Context
|
||||
resp, req, err := getContextHTTP(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 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 (TODO: Only for web!)
|
||||
accessCookie = http.Cookie{Name: "AccessToken", Value: accessToken, Path: "/", HttpOnly: false}
|
||||
refreshCookie = http.Cookie{Name: "RefreshToken", Value: refreshToken, Path: "/", HttpOnly: false}
|
||||
http.SetCookie(*resp, &accessCookie)
|
||||
http.SetCookie(*resp, &refreshCookie)
|
||||
|
||||
// Only for iOS & Android (TODO: Remove for web! Only cause affected by CORS during development)
|
||||
(*resp).Header().Set("X-Imagini-AccessToken", accessToken)
|
||||
(*resp).Header().Set("X-Imagini-RefreshToken", refreshToken)
|
||||
|
||||
return &model.AuthResponse{Result: model.AuthResultSuccess, Device: &foundDevice}, nil
|
||||
}
|
||||
|
||||
func (r *queryResolver) Logout(ctx context.Context) (*model.AuthResponse, error) {
|
||||
// Acquire Context
|
||||
resp, _, err := getContextHTTP(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 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) (*model.MediaItem, error) {
|
||||
// Acquire Context
|
||||
userID, _, err := getContextIDs(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mediaItemID, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
return nil, errors.New("Invalid ID Format")
|
||||
}
|
||||
|
||||
foundMediaItem := &model.MediaItem{ID: mediaItemID.String(), UserID: userID}
|
||||
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) (*model.Device, error) {
|
||||
// Acquire Context
|
||||
userID, _, err := getContextIDs(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deviceID, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
return nil, errors.New("Invalid ID Format")
|
||||
}
|
||||
|
||||
foundDevice := &model.Device{ID: deviceID.String(), UserID: userID}
|
||||
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) (*model.Album, error) {
|
||||
// Acquire Context
|
||||
userID, _, err := getContextIDs(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
albumID, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
return nil, errors.New("Invalid ID Format")
|
||||
}
|
||||
|
||||
foundAlbum := &model.Album{ID: albumID.String(), UserID: userID}
|
||||
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) (*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) (*model.Tag, error) {
|
||||
// Acquire Context
|
||||
userID, _, err := getContextIDs(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tagID, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
return nil, errors.New("Invalid ID Format")
|
||||
}
|
||||
|
||||
foundTag := &model.Tag{ID: tagID.String(), UserID: userID}
|
||||
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) (*model.User, error) {
|
||||
// Acquire Context
|
||||
userID, _, err := getContextIDs(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
foundUser := &model.User{ID: userID}
|
||||
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, filter *model.MediaItemFilter, page *model.Page, order *model.Order) (*model.MediaItemResponse, error) {
|
||||
// Acquire Context
|
||||
userID, _, err := getContextIDs(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, pageResponse, err := r.DB.MediaItems(userID, filter, page, order)
|
||||
if err != nil {
|
||||
return nil, errors.New("DB Error")
|
||||
}
|
||||
return &model.MediaItemResponse{
|
||||
Data: resp,
|
||||
Page: &pageResponse,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *queryResolver) Devices(ctx context.Context, filter *model.DeviceFilter, page *model.Page, order *model.Order) (*model.DeviceResponse, error) {
|
||||
// Acquire Context
|
||||
userID, _, err := getContextIDs(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, pageResponse, err := r.DB.Devices(userID, filter, page, order)
|
||||
if err != nil {
|
||||
return nil, errors.New("DB Error")
|
||||
}
|
||||
return &model.DeviceResponse{
|
||||
Data: resp,
|
||||
Page: &pageResponse,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *queryResolver) Albums(ctx context.Context, filter *model.AlbumFilter, page *model.Page, order *model.Order) (*model.AlbumResponse, error) {
|
||||
// Acquire Context
|
||||
userID, _, err := getContextIDs(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, pageResponse, err := r.DB.Albums(userID, filter, page, order)
|
||||
if err != nil {
|
||||
return nil, errors.New("Context Error")
|
||||
}
|
||||
return &model.AlbumResponse{
|
||||
Data: resp,
|
||||
Page: &pageResponse,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *queryResolver) Tags(ctx context.Context, filter *model.TagFilter, page *model.Page, order *model.Order) (*model.TagResponse, error) {
|
||||
// Acquire Context
|
||||
userID, _, err := getContextIDs(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, pageResponse, err := r.DB.Tags(userID, filter, page, order)
|
||||
if err != nil {
|
||||
return nil, errors.New("Context Error")
|
||||
}
|
||||
return &model.TagResponse{
|
||||
Data: resp,
|
||||
Page: &pageResponse,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *queryResolver) Users(ctx context.Context, filter *model.UserFilter, page *model.Page, order *model.Order) (*model.UserResponse, error) {
|
||||
resp, pageResponse, err := r.DB.Users(filter, page, order)
|
||||
if err != nil {
|
||||
return nil, errors.New("Context Error")
|
||||
}
|
||||
return &model.UserResponse{
|
||||
Data: resp,
|
||||
Page: &pageResponse,
|
||||
}, 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 }
|
||||
Reference in New Issue
Block a user