From 9a8701ecd06fdb1b4fb45c7755caac296995cb1e Mon Sep 17 00:00:00 2001 From: viktorstrate Date: Sun, 9 Feb 2020 12:53:21 +0100 Subject: [PATCH] Improve image processing --- api/database/migrations/0002_photo.up.sql | 27 +- api/graphql/generated.go | 500 ++++++++++++++-------- api/graphql/models/photo.go | 36 +- api/graphql/resolvers/photo.go | 30 +- api/graphql/schema.graphql | 37 +- api/routes/images.go | 18 + api/scanner/process_image.go | 53 ++- api/server/server.go | 3 + 8 files changed, 453 insertions(+), 251 deletions(-) create mode 100644 api/routes/images.go diff --git a/api/database/migrations/0002_photo.up.sql b/api/database/migrations/0002_photo.up.sql index 4b4046fd..a93dfbd8 100644 --- a/api/database/migrations/0002_photo.up.sql +++ b/api/database/migrations/0002_photo.up.sql @@ -1,12 +1,3 @@ -CREATE TABLE IF NOT EXISTS photo_url ( - url_id int NOT NULL AUTO_INCREMENT, - token varchar(256) NOT NULL, - width int NOT NULL, - height int NOT NULL, - - PRIMARY KEY (url_id) -); - CREATE TABLE IF NOT EXISTS photo_exif ( exif_id int NOT NULL AUTO_INCREMENT, camera varchar(256), @@ -39,14 +30,22 @@ CREATE TABLE IF NOT EXISTS photo ( photo_id int NOT NULL AUTO_INCREMENT, title varchar(256) NOT NULL, path varchar(512) NOT NULL UNIQUE, - original_url int NOT NULL, - thumbnail_url int NOT NULL, album_id int NOT NULL, exif_id int, PRIMARY KEY (photo_id), FOREIGN KEY (album_id) REFERENCES album(album_id), - FOREIGN KEY (exif_id) REFERENCES photo_exif(exif_id), - FOREIGN KEY (original_url) REFERENCES photo_url(url_id), - FOREIGN KEY (thumbnail_url) REFERENCES photo_url(url_id) + FOREIGN KEY (exif_id) REFERENCES photo_exif(exif_id) ); + +CREATE TABLE IF NOT EXISTS photo_url ( + url_id int NOT NULL AUTO_INCREMENT, + photo_id int NOT NULL, + photo_name varchar(256) NOT NULL, + width int NOT NULL, + height int NOT NULL, + purpose varchar(64) NOT NULL, + + PRIMARY KEY (url_id), + FOREIGN KEY (photo_id) REFERENCES photo(photo_id) +); \ No newline at end of file diff --git a/api/graphql/generated.go b/api/graphql/generated.go index 74eede1a..eb0dc623 100644 --- a/api/graphql/generated.go +++ b/api/graphql/generated.go @@ -75,6 +75,7 @@ type ComplexityRoot struct { Photo struct { Album func(childComplexity int) int Exif func(childComplexity int) int + HighRes func(childComplexity int) int ID func(childComplexity int) int Original func(childComplexity int) int Path func(childComplexity int) int @@ -147,6 +148,7 @@ type MutationResolver interface { type PhotoResolver interface { Original(ctx context.Context, obj *models.Photo) (*models.PhotoURL, error) Thumbnail(ctx context.Context, obj *models.Photo) (*models.PhotoURL, error) + HighRes(ctx context.Context, obj *models.Photo) (*models.PhotoURL, error) Album(ctx context.Context, obj *models.Photo) (*models.Album, error) Exif(ctx context.Context, obj *models.Photo) (*models.PhotoExif, error) } @@ -314,6 +316,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Photo.Exif(childComplexity), true + case "Photo.highRes": + if e.complexity.Photo.HighRes == nil { + break + } + + return e.complexity.Photo.HighRes(childComplexity), true + case "Photo.id": if e.complexity.Photo.ID == nil { break @@ -636,22 +645,22 @@ var parsedSchema = gqlparser.MustLoadSchema( scalar Time type Query { - siteInfo: SiteInfo + siteInfo: SiteInfo! "List of registered users, must be admin to call" users: [User!]! @isAdmin "Information about the currently logged in user" - myUser: User + myUser: User! "List of albums owned by the logged in user" - myAlbums: [Album] + myAlbums: [Album!]! "Get album by id, user must own the album or be admin" - album(id: ID): Album + album(id: ID): Album! "List of photos owned by the logged in user" - myPhotos: [Photo] + myPhotos: [Photo!]! "Get photo by id, user must own the photo or be admin" - photo(id: ID!): Photo + photo(id: ID!): Photo! } type Mutation { @@ -662,7 +671,7 @@ type Mutation { username: String! password: String! rootPath: String! - ): AuthorizeResult! @isAdmin + ): AuthorizeResult! "Registers the initial user, can only be called if initialSetup from SiteInfo is true" initialSetupWizard( @@ -707,34 +716,35 @@ type User { type Album { id: ID! - title: String - photos: [Photo] - subAlbums: [Album] + title: String! + photos: [Photo!]! + subAlbums: [Album!]! parentAlbum: Album owner: User! - path: String + path: String! # shares: [ShareToken] } type PhotoURL { "URL for previewing the image" - url: String + url: String! "Width of the image in pixels" - width: Int + width: Int! "Height of the image in pixels" - height: Int + height: Int! } type Photo { id: ID! - title: String + title: String! "Local filepath for the photo" - path: String + path: String! "URL to display the photo in full resolution" - original: PhotoURL + original: PhotoURL! "URL to display the photo in a smaller resolution" - thumbnail: PhotoURL + thumbnail: PhotoURL! + highRes: PhotoURL! "The album that holds the photo" album: Album! exif: PhotoEXIF @@ -1010,12 +1020,15 @@ func (ec *executionContext) _Album_title(ctx context.Context, field graphql.Coll return graphql.Null } if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } res := resTmp.(string) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalOString2string(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } func (ec *executionContext) _Album_photos(ctx context.Context, field graphql.CollectedField, obj *models.Album) (ret graphql.Marshaler) { @@ -1044,12 +1057,15 @@ func (ec *executionContext) _Album_photos(ctx context.Context, field graphql.Col return graphql.Null } if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } res := resTmp.([]*models.Photo) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalOPhoto2ᚕᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhoto(ctx, field.Selections, res) + return ec.marshalNPhoto2ᚕᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhotoᚄ(ctx, field.Selections, res) } func (ec *executionContext) _Album_subAlbums(ctx context.Context, field graphql.CollectedField, obj *models.Album) (ret graphql.Marshaler) { @@ -1078,12 +1094,15 @@ func (ec *executionContext) _Album_subAlbums(ctx context.Context, field graphql. return graphql.Null } if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } res := resTmp.([]*models.Album) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalOAlbum2ᚕᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbum(ctx, field.Selections, res) + return ec.marshalNAlbum2ᚕᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbumᚄ(ctx, field.Selections, res) } func (ec *executionContext) _Album_parentAlbum(ctx context.Context, field graphql.CollectedField, obj *models.Album) (ret graphql.Marshaler) { @@ -1183,12 +1202,15 @@ func (ec *executionContext) _Album_path(ctx context.Context, field graphql.Colle return graphql.Null } if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } res := resTmp.(string) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalOString2string(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } func (ec *executionContext) _AuthorizeResult_success(ctx context.Context, field graphql.CollectedField, obj *models.AuthorizeResult) (ret graphql.Marshaler) { @@ -1368,28 +1390,8 @@ func (ec *executionContext) _Mutation_registerUser(ctx context.Context, field gr rctx.Args = args ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - directive0 := func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().RegisterUser(rctx, args["username"].(string), args["password"].(string), args["rootPath"].(string)) - } - directive1 := func(ctx context.Context) (interface{}, error) { - if ec.directives.IsAdmin == nil { - return nil, errors.New("directive isAdmin is not implemented") - } - return ec.directives.IsAdmin(ctx, nil, directive0) - } - - tmp, err := directive1(rctx) - if err != nil { - return nil, err - } - if tmp == nil { - return nil, nil - } - if data, ok := tmp.(*models.AuthorizeResult); ok { - return data, nil - } - return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/viktorstrate/photoview/api/graphql/models.AuthorizeResult`, tmp) + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().RegisterUser(rctx, args["username"].(string), args["password"].(string), args["rootPath"].(string)) }) if err != nil { ec.Error(ctx, err) @@ -1592,12 +1594,15 @@ func (ec *executionContext) _Photo_title(ctx context.Context, field graphql.Coll return graphql.Null } if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } res := resTmp.(string) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalOString2string(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } func (ec *executionContext) _Photo_path(ctx context.Context, field graphql.CollectedField, obj *models.Photo) (ret graphql.Marshaler) { @@ -1626,12 +1631,15 @@ func (ec *executionContext) _Photo_path(ctx context.Context, field graphql.Colle return graphql.Null } if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } res := resTmp.(string) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalOString2string(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } func (ec *executionContext) _Photo_original(ctx context.Context, field graphql.CollectedField, obj *models.Photo) (ret graphql.Marshaler) { @@ -1660,12 +1668,15 @@ func (ec *executionContext) _Photo_original(ctx context.Context, field graphql.C return graphql.Null } if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } res := resTmp.(*models.PhotoURL) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalOPhotoURL2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhotoURL(ctx, field.Selections, res) + return ec.marshalNPhotoURL2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhotoURL(ctx, field.Selections, res) } func (ec *executionContext) _Photo_thumbnail(ctx context.Context, field graphql.CollectedField, obj *models.Photo) (ret graphql.Marshaler) { @@ -1694,12 +1705,52 @@ func (ec *executionContext) _Photo_thumbnail(ctx context.Context, field graphql. return graphql.Null } if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } res := resTmp.(*models.PhotoURL) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalOPhotoURL2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhotoURL(ctx, field.Selections, res) + return ec.marshalNPhotoURL2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhotoURL(ctx, field.Selections, res) +} + +func (ec *executionContext) _Photo_highRes(ctx context.Context, field graphql.CollectedField, obj *models.Photo) (ret graphql.Marshaler) { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + ec.Tracer.EndFieldExecution(ctx) + }() + rctx := &graphql.ResolverContext{ + Object: "Photo", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Photo().HighRes(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*models.PhotoURL) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalNPhotoURL2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhotoURL(ctx, field.Selections, res) } func (ec *executionContext) _Photo_album(ctx context.Context, field graphql.CollectedField, obj *models.Photo) (ret graphql.Marshaler) { @@ -2173,12 +2224,15 @@ func (ec *executionContext) _PhotoURL_url(ctx context.Context, field graphql.Col return graphql.Null } if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } res := resTmp.(string) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalOString2string(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } func (ec *executionContext) _PhotoURL_width(ctx context.Context, field graphql.CollectedField, obj *models.PhotoURL) (ret graphql.Marshaler) { @@ -2207,12 +2261,15 @@ func (ec *executionContext) _PhotoURL_width(ctx context.Context, field graphql.C return graphql.Null } if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } res := resTmp.(int) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalOInt2int(ctx, field.Selections, res) + return ec.marshalNInt2int(ctx, field.Selections, res) } func (ec *executionContext) _PhotoURL_height(ctx context.Context, field graphql.CollectedField, obj *models.PhotoURL) (ret graphql.Marshaler) { @@ -2241,12 +2298,15 @@ func (ec *executionContext) _PhotoURL_height(ctx context.Context, field graphql. return graphql.Null } if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } res := resTmp.(int) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalOInt2int(ctx, field.Selections, res) + return ec.marshalNInt2int(ctx, field.Selections, res) } func (ec *executionContext) _Query_siteInfo(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { @@ -2275,12 +2335,15 @@ func (ec *executionContext) _Query_siteInfo(ctx context.Context, field graphql.C return graphql.Null } if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } res := resTmp.(*models.SiteInfo) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalOSiteInfo2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐSiteInfo(ctx, field.Selections, res) + return ec.marshalNSiteInfo2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐSiteInfo(ctx, field.Selections, res) } func (ec *executionContext) _Query_users(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { @@ -2366,12 +2429,15 @@ func (ec *executionContext) _Query_myUser(ctx context.Context, field graphql.Col return graphql.Null } if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } res := resTmp.(*models.User) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalOUser2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐUser(ctx, field.Selections, res) + return ec.marshalNUser2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐUser(ctx, field.Selections, res) } func (ec *executionContext) _Query_myAlbums(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { @@ -2400,12 +2466,15 @@ func (ec *executionContext) _Query_myAlbums(ctx context.Context, field graphql.C return graphql.Null } if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } res := resTmp.([]*models.Album) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalOAlbum2ᚕᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbum(ctx, field.Selections, res) + return ec.marshalNAlbum2ᚕᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbumᚄ(ctx, field.Selections, res) } func (ec *executionContext) _Query_album(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { @@ -2441,12 +2510,15 @@ func (ec *executionContext) _Query_album(ctx context.Context, field graphql.Coll return graphql.Null } if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } res := resTmp.(*models.Album) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalOAlbum2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbum(ctx, field.Selections, res) + return ec.marshalNAlbum2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbum(ctx, field.Selections, res) } func (ec *executionContext) _Query_myPhotos(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { @@ -2475,12 +2547,15 @@ func (ec *executionContext) _Query_myPhotos(ctx context.Context, field graphql.C return graphql.Null } if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } res := resTmp.([]*models.Photo) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalOPhoto2ᚕᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhoto(ctx, field.Selections, res) + return ec.marshalNPhoto2ᚕᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhotoᚄ(ctx, field.Selections, res) } func (ec *executionContext) _Query_photo(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { @@ -2516,12 +2591,15 @@ func (ec *executionContext) _Query_photo(ctx context.Context, field graphql.Coll return graphql.Null } if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } res := resTmp.(*models.Photo) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalOPhoto2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhoto(ctx, field.Selections, res) + return ec.marshalNPhoto2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhoto(ctx, field.Selections, res) } func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { @@ -4123,6 +4201,9 @@ func (ec *executionContext) _Album(ctx context.Context, sel ast.SelectionSet, ob } case "title": out.Values[i] = ec._Album_title(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } case "photos": field := field out.Concurrently(i, func() (res graphql.Marshaler) { @@ -4132,6 +4213,9 @@ func (ec *executionContext) _Album(ctx context.Context, sel ast.SelectionSet, ob } }() res = ec._Album_photos(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } return res }) case "subAlbums": @@ -4143,6 +4227,9 @@ func (ec *executionContext) _Album(ctx context.Context, sel ast.SelectionSet, ob } }() res = ec._Album_subAlbums(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } return res }) case "parentAlbum": @@ -4172,6 +4259,9 @@ func (ec *executionContext) _Album(ctx context.Context, sel ast.SelectionSet, ob }) case "path": out.Values[i] = ec._Album_path(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -4283,8 +4373,14 @@ func (ec *executionContext) _Photo(ctx context.Context, sel ast.SelectionSet, ob } case "title": out.Values[i] = ec._Photo_title(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } case "path": out.Values[i] = ec._Photo_path(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } case "original": field := field out.Concurrently(i, func() (res graphql.Marshaler) { @@ -4294,6 +4390,9 @@ func (ec *executionContext) _Photo(ctx context.Context, sel ast.SelectionSet, ob } }() res = ec._Photo_original(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } return res }) case "thumbnail": @@ -4305,6 +4404,23 @@ func (ec *executionContext) _Photo(ctx context.Context, sel ast.SelectionSet, ob } }() res = ec._Photo_thumbnail(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) + case "highRes": + field := field + out.Concurrently(i, func() (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Photo_highRes(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } return res }) case "album": @@ -4400,10 +4516,19 @@ func (ec *executionContext) _PhotoURL(ctx context.Context, sel ast.SelectionSet, out.Values[i] = graphql.MarshalString("PhotoURL") case "url": out.Values[i] = ec._PhotoURL_url(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } case "width": out.Values[i] = ec._PhotoURL_width(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } case "height": out.Values[i] = ec._PhotoURL_height(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -4439,6 +4564,9 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } }() res = ec._Query_siteInfo(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } return res }) case "users": @@ -4464,6 +4592,9 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } }() res = ec._Query_myUser(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } return res }) case "myAlbums": @@ -4475,6 +4606,9 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } }() res = ec._Query_myAlbums(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } return res }) case "album": @@ -4486,6 +4620,9 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } }() res = ec._Query_album(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } return res }) case "myPhotos": @@ -4497,6 +4634,9 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } }() res = ec._Query_myPhotos(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } return res }) case "photo": @@ -4508,6 +4648,9 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } }() res = ec._Query_photo(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } return res }) case "__type": @@ -4879,6 +5022,43 @@ func (ec *executionContext) marshalNAlbum2githubᚗcomᚋviktorstrateᚋphotovie return ec._Album(ctx, sel, &v) } +func (ec *executionContext) marshalNAlbum2ᚕᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbumᚄ(ctx context.Context, sel ast.SelectionSet, v []*models.Album) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + rctx := &graphql.ResolverContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithResolverContext(ctx, rctx) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNAlbum2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbum(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + return ret +} + func (ec *executionContext) marshalNAlbum2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbum(ctx context.Context, sel ast.SelectionSet, v *models.Album) graphql.Marshaler { if v == nil { if !ec.HasError(graphql.GetResolverContext(ctx)) { @@ -4931,6 +5111,85 @@ func (ec *executionContext) marshalNID2string(ctx context.Context, sel ast.Selec return res } +func (ec *executionContext) unmarshalNInt2int(ctx context.Context, v interface{}) (int, error) { + return graphql.UnmarshalInt(v) +} + +func (ec *executionContext) marshalNInt2int(ctx context.Context, sel ast.SelectionSet, v int) graphql.Marshaler { + res := graphql.MarshalInt(v) + if res == graphql.Null { + if !ec.HasError(graphql.GetResolverContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + } + return res +} + +func (ec *executionContext) marshalNPhoto2githubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhoto(ctx context.Context, sel ast.SelectionSet, v models.Photo) graphql.Marshaler { + return ec._Photo(ctx, sel, &v) +} + +func (ec *executionContext) marshalNPhoto2ᚕᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhotoᚄ(ctx context.Context, sel ast.SelectionSet, v []*models.Photo) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + rctx := &graphql.ResolverContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithResolverContext(ctx, rctx) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNPhoto2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhoto(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + return ret +} + +func (ec *executionContext) marshalNPhoto2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhoto(ctx context.Context, sel ast.SelectionSet, v *models.Photo) graphql.Marshaler { + if v == nil { + if !ec.HasError(graphql.GetResolverContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec._Photo(ctx, sel, v) +} + +func (ec *executionContext) marshalNPhotoURL2githubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhotoURL(ctx context.Context, sel ast.SelectionSet, v models.PhotoURL) graphql.Marshaler { + return ec._PhotoURL(ctx, sel, &v) +} + +func (ec *executionContext) marshalNPhotoURL2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhotoURL(ctx context.Context, sel ast.SelectionSet, v *models.PhotoURL) graphql.Marshaler { + if v == nil { + if !ec.HasError(graphql.GetResolverContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec._PhotoURL(ctx, sel, v) +} + func (ec *executionContext) marshalNScannerResult2githubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐScannerResult(ctx context.Context, sel ast.SelectionSet, v models.ScannerResult) graphql.Marshaler { return ec._ScannerResult(ctx, sel, &v) } @@ -4945,6 +5204,20 @@ func (ec *executionContext) marshalNScannerResult2ᚖgithubᚗcomᚋviktorstrate return ec._ScannerResult(ctx, sel, v) } +func (ec *executionContext) marshalNSiteInfo2githubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐSiteInfo(ctx context.Context, sel ast.SelectionSet, v models.SiteInfo) graphql.Marshaler { + return ec._SiteInfo(ctx, sel, &v) +} + +func (ec *executionContext) marshalNSiteInfo2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐSiteInfo(ctx context.Context, sel ast.SelectionSet, v *models.SiteInfo) graphql.Marshaler { + if v == nil { + if !ec.HasError(graphql.GetResolverContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec._SiteInfo(ctx, sel, v) +} + func (ec *executionContext) unmarshalNString2string(ctx context.Context, v interface{}) (string, error) { return graphql.UnmarshalString(v) } @@ -5240,46 +5513,6 @@ func (ec *executionContext) marshalOAlbum2githubᚗcomᚋviktorstrateᚋphotovie return ec._Album(ctx, sel, &v) } -func (ec *executionContext) marshalOAlbum2ᚕᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbum(ctx context.Context, sel ast.SelectionSet, v []*models.Album) graphql.Marshaler { - if v == nil { - return graphql.Null - } - ret := make(graphql.Array, len(v)) - var wg sync.WaitGroup - isLen1 := len(v) == 1 - if !isLen1 { - wg.Add(len(v)) - } - for i := range v { - i := i - rctx := &graphql.ResolverContext{ - Index: &i, - Result: &v[i], - } - ctx := graphql.WithResolverContext(ctx, rctx) - f := func(i int) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = nil - } - }() - if !isLen1 { - defer wg.Done() - } - ret[i] = ec.marshalOAlbum2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbum(ctx, sel, v[i]) - } - if isLen1 { - f(i) - } else { - go f(i) - } - - } - wg.Wait() - return ret -} - func (ec *executionContext) marshalOAlbum2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbum(ctx context.Context, sel ast.SelectionSet, v *models.Album) graphql.Marshaler { if v == nil { return graphql.Null @@ -5394,46 +5627,6 @@ func (ec *executionContext) marshalOPhoto2githubᚗcomᚋviktorstrateᚋphotovie return ec._Photo(ctx, sel, &v) } -func (ec *executionContext) marshalOPhoto2ᚕᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhoto(ctx context.Context, sel ast.SelectionSet, v []*models.Photo) graphql.Marshaler { - if v == nil { - return graphql.Null - } - ret := make(graphql.Array, len(v)) - var wg sync.WaitGroup - isLen1 := len(v) == 1 - if !isLen1 { - wg.Add(len(v)) - } - for i := range v { - i := i - rctx := &graphql.ResolverContext{ - Index: &i, - Result: &v[i], - } - ctx := graphql.WithResolverContext(ctx, rctx) - f := func(i int) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = nil - } - }() - if !isLen1 { - defer wg.Done() - } - ret[i] = ec.marshalOPhoto2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhoto(ctx, sel, v[i]) - } - if isLen1 { - f(i) - } else { - go f(i) - } - - } - wg.Wait() - return ret -} - func (ec *executionContext) marshalOPhoto2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhoto(ctx context.Context, sel ast.SelectionSet, v *models.Photo) graphql.Marshaler { if v == nil { return graphql.Null @@ -5452,28 +5645,6 @@ func (ec *executionContext) marshalOPhotoEXIF2ᚖgithubᚗcomᚋviktorstrateᚋp return ec._PhotoEXIF(ctx, sel, v) } -func (ec *executionContext) marshalOPhotoURL2githubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhotoURL(ctx context.Context, sel ast.SelectionSet, v models.PhotoURL) graphql.Marshaler { - return ec._PhotoURL(ctx, sel, &v) -} - -func (ec *executionContext) marshalOPhotoURL2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhotoURL(ctx context.Context, sel ast.SelectionSet, v *models.PhotoURL) graphql.Marshaler { - if v == nil { - return graphql.Null - } - return ec._PhotoURL(ctx, sel, v) -} - -func (ec *executionContext) marshalOSiteInfo2githubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐSiteInfo(ctx context.Context, sel ast.SelectionSet, v models.SiteInfo) graphql.Marshaler { - return ec._SiteInfo(ctx, sel, &v) -} - -func (ec *executionContext) marshalOSiteInfo2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐSiteInfo(ctx context.Context, sel ast.SelectionSet, v *models.SiteInfo) graphql.Marshaler { - if v == nil { - return graphql.Null - } - return ec._SiteInfo(ctx, sel, v) -} - func (ec *executionContext) unmarshalOString2string(ctx context.Context, v interface{}) (string, error) { return graphql.UnmarshalString(v) } @@ -5520,17 +5691,6 @@ func (ec *executionContext) marshalOTime2ᚖtimeᚐTime(ctx context.Context, sel return ec.marshalOTime2timeᚐTime(ctx, sel, *v) } -func (ec *executionContext) marshalOUser2githubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐUser(ctx context.Context, sel ast.SelectionSet, v models.User) graphql.Marshaler { - return ec._User(ctx, sel, &v) -} - -func (ec *executionContext) marshalOUser2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐUser(ctx context.Context, sel ast.SelectionSet, v *models.User) graphql.Marshaler { - if v == nil { - return graphql.Null - } - return ec._User(ctx, sel, v) -} - func (ec *executionContext) marshalO__EnumValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValueᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.EnumValue) graphql.Marshaler { if v == nil { return graphql.Null diff --git a/api/graphql/models/photo.go b/api/graphql/models/photo.go index 34a8f7d1..03b007cd 100644 --- a/api/graphql/models/photo.go +++ b/api/graphql/models/photo.go @@ -6,20 +6,28 @@ import ( ) type Photo struct { - PhotoID int - Title string - Path string - OriginalUrl int - ThumbnailUrl int - AlbumId int - ExifId *int + PhotoID int + Title string + Path string + AlbumId int + ExifId *int } +type PhotoPurpose string + +const ( + PhotoThumbnail PhotoPurpose = "thumbnail" + PhotoHighRes PhotoPurpose = "high-res" + PhotoOriginal PhotoPurpose = "original" +) + type PhotoURL struct { - UrlID int - Token string - Width int - Height int + UrlID int + PhotoId int + PhotoName string + Width int + Height int + purpose PhotoPurpose } func (p *Photo) ID() string { @@ -29,7 +37,7 @@ func (p *Photo) ID() string { func NewPhotoFromRow(row *sql.Row) (*Photo, error) { photo := Photo{} - if err := row.Scan(&photo.PhotoID, &photo.Title, &photo.Path, &photo.OriginalUrl, &photo.ThumbnailUrl, &photo.AlbumId, &photo.ExifId); err != nil { + if err := row.Scan(&photo.PhotoID, &photo.Title, &photo.Path, &photo.AlbumId, &photo.ExifId); err != nil { return nil, err } @@ -41,7 +49,7 @@ func NewPhotosFromRows(rows *sql.Rows) ([]*Photo, error) { for rows.Next() { var photo Photo - if err := rows.Scan(&photo.PhotoID, &photo.Title, &photo.Path, &photo.OriginalUrl, &photo.ThumbnailUrl, &photo.AlbumId, &photo.ExifId); err != nil { + if err := rows.Scan(&photo.PhotoID, &photo.Title, &photo.Path, &photo.AlbumId, &photo.ExifId); err != nil { return nil, err } photos = append(photos, &photo) @@ -51,5 +59,5 @@ func NewPhotosFromRows(rows *sql.Rows) ([]*Photo, error) { } func (p *PhotoURL) URL() string { - return "URL:" + p.Token + return "URL:" + p.PhotoName } diff --git a/api/graphql/resolvers/photo.go b/api/graphql/resolvers/photo.go index 6a657404..9e0d4ac6 100644 --- a/api/graphql/resolvers/photo.go +++ b/api/graphql/resolvers/photo.go @@ -40,26 +40,32 @@ func (r *Resolver) Photo() api.PhotoResolver { return &photoResolver{r} } +func (r *photoResolver) HighRes(ctx context.Context, obj *models.Photo) (*models.PhotoURL, error) { + panic("not implemented") +} + func (r *photoResolver) Original(ctx context.Context, obj *models.Photo) (*models.PhotoURL, error) { - row := r.Database.QueryRow("SELECT photo_url.* FROM photo, photo_url WHERE photo.photo_id = ? AND photo.original_url = photo_url.url_id", obj.PhotoID) + panic("not implemented") + // row := r.Database.QueryRow("SELECT photo_url.* FROM photo, photo_url WHERE photo.photo_id = ? AND photo.original_url = photo_url.url_id", obj.PhotoID) - var photoUrl models.PhotoURL - if err := row.Scan(&photoUrl.UrlID, &photoUrl.Token, &photoUrl.Width, &photoUrl.Height); err != nil { - return nil, err - } + // var photoUrl models.PhotoURL + // if err := row.Scan(&photoUrl.UrlID, &photoUrl.Token, &photoUrl.Width, &photoUrl.Height); err != nil { + // return nil, err + // } - return &photoUrl, nil + // return &photoUrl, nil } func (r *photoResolver) Thumbnail(ctx context.Context, obj *models.Photo) (*models.PhotoURL, error) { - row := r.Database.QueryRow("SELECT photo_url.* FROM photo, photo_url WHERE photo.photo_id = ? AND photo.thumbnail_url = photo_url.url_id", obj.PhotoID) + panic("not implemented") + // row := r.Database.QueryRow("SELECT photo_url.* FROM photo, photo_url WHERE photo.photo_id = ? AND photo.thumbnail_url = photo_url.url_id", obj.PhotoID) - var photoUrl models.PhotoURL - if err := row.Scan(&photoUrl.UrlID, &photoUrl.Token, &photoUrl.Width, &photoUrl.Height); err != nil { - return nil, err - } + // var photoUrl models.PhotoURL + // if err := row.Scan(&photoUrl.UrlID, &photoUrl.Token, &photoUrl.Width, &photoUrl.Height); err != nil { + // return nil, err + // } - return &photoUrl, nil + // return &photoUrl, nil } func (r *photoResolver) Album(ctx context.Context, obj *models.Photo) (*models.Album, error) { diff --git a/api/graphql/schema.graphql b/api/graphql/schema.graphql index 7df5f8f2..6815373a 100644 --- a/api/graphql/schema.graphql +++ b/api/graphql/schema.graphql @@ -3,22 +3,22 @@ directive @isAdmin on FIELD_DEFINITION scalar Time type Query { - siteInfo: SiteInfo + siteInfo: SiteInfo! "List of registered users, must be admin to call" users: [User!]! @isAdmin "Information about the currently logged in user" - myUser: User + myUser: User! "List of albums owned by the logged in user" - myAlbums: [Album] + myAlbums: [Album!]! "Get album by id, user must own the album or be admin" - album(id: ID): Album + album(id: ID): Album! "List of photos owned by the logged in user" - myPhotos: [Photo] + myPhotos: [Photo!]! "Get photo by id, user must own the photo or be admin" - photo(id: ID!): Photo + photo(id: ID!): Photo! } type Mutation { @@ -29,7 +29,7 @@ type Mutation { username: String! password: String! rootPath: String! - ): AuthorizeResult! @isAdmin + ): AuthorizeResult! "Registers the initial user, can only be called if initialSetup from SiteInfo is true" initialSetupWizard( @@ -74,34 +74,35 @@ type User { type Album { id: ID! - title: String - photos: [Photo] - subAlbums: [Album] + title: String! + photos: [Photo!]! + subAlbums: [Album!]! parentAlbum: Album owner: User! - path: String + path: String! # shares: [ShareToken] } type PhotoURL { "URL for previewing the image" - url: String + url: String! "Width of the image in pixels" - width: Int + width: Int! "Height of the image in pixels" - height: Int + height: Int! } type Photo { id: ID! - title: String + title: String! "Local filepath for the photo" - path: String + path: String! "URL to display the photo in full resolution" - original: PhotoURL + original: PhotoURL! "URL to display the photo in a smaller resolution" - thumbnail: PhotoURL + thumbnail: PhotoURL! + highRes: PhotoURL! "The album that holds the photo" album: Album! exif: PhotoEXIF diff --git a/api/routes/images.go b/api/routes/images.go new file mode 100644 index 00000000..e53b2b2c --- /dev/null +++ b/api/routes/images.go @@ -0,0 +1,18 @@ +package routes + +import ( + "fmt" + "net/http" + + "github.com/go-chi/chi" +) + +func ImageRoutes() chi.Router { + router := chi.NewRouter() + router.Get("/{name}", func(w http.ResponseWriter, r *http.Request) { + image_name := chi.URLParam(r, "name") + w.Write([]byte(fmt.Sprintf("Image: %s", image_name))) + }) + + return router +} diff --git a/api/scanner/process_image.go b/api/scanner/process_image.go index 88838ac0..71ae10f1 100644 --- a/api/scanner/process_image.go +++ b/api/scanner/process_image.go @@ -2,6 +2,7 @@ package scanner import ( "database/sql" + "fmt" "image" "image/jpeg" "log" @@ -9,8 +10,10 @@ import ( "os" "path" "strconv" + "strings" "github.com/nfnt/resize" + "github.com/viktorstrate/photoview/api/graphql/models" // Image decoders _ "golang.org/x/image/bmp" @@ -29,8 +32,8 @@ func ProcessImage(tx *sql.Tx, photoPath string, albumId int) error { // Check if image already exists row := tx.QueryRow("SELECT (photo_id) FROM photo WHERE path = ?", photoPath) - var id int - if err := row.Scan(&id); err != sql.ErrNoRows { + var photo_id int64 + if err := row.Scan(&photo_id); err != sql.ErrNoRows { if err == nil { log.Printf("Image already processed: %s\n", photoPath) return nil @@ -39,6 +42,16 @@ func ProcessImage(tx *sql.Tx, photoPath string, albumId int) error { } } + result, err := tx.Exec("INSERT INTO photo (title, path, album_id) VALUES (?, ?, ?)", photoName, photoPath, albumId) + if err != nil { + log.Printf("ERROR: Could not insert photo into database") + return err + } + photo_id, err = result.LastInsertId() + if err != nil { + return err + } + thumbFile, err := os.Open(photoPath) if err != nil { return err @@ -50,6 +63,13 @@ func ProcessImage(tx *sql.Tx, photoPath string, albumId int) error { log.Println("ERROR: decoding image") return err } + + _, err = tx.Exec("INSERT INTO photo_url (photo_id, photo_name, width, height, purpose) VALUES (?, ?, ?, ?, ?)", photo_id, photoName, image.Bounds().Max.X, image.Bounds().Max.Y, models.PhotoOriginal) + if err != nil { + log.Printf("Could not insert original photo url: %d, %s\n", photo_id, photoName) + return err + } + thumbnailImage := resize.Thumbnail(1024, 1024, image, resize.Bilinear) if _, err := os.Stat("image-cache"); os.IsNotExist(err) { @@ -69,10 +89,14 @@ func ProcessImage(tx *sql.Tx, photoPath string, albumId int) error { } // Generate image token name thumbnailToken := generateToken() - originalToken := generateToken() // Save thumbnail as jpg - thumbFile, err = os.Create(path.Join(albumCachePath, thumbnailToken+".jpg")) + thumbnail_name := fmt.Sprintf("thumbnail_%s_%s", photoName, thumbnailToken) + thumbnail_name = strings.ReplaceAll(thumbnail_name, ".", "_") + thumbnail_name = strings.ReplaceAll(thumbnail_name, " ", "_") + thumbnail_name = thumbnail_name + ".jpg" + + thumbFile, err = os.Create(path.Join(albumCachePath, thumbnail_name)) if err != nil { log.Println("ERROR: Could not make thumbnail file") return err @@ -82,34 +106,17 @@ func ProcessImage(tx *sql.Tx, photoPath string, albumId int) error { jpeg.Encode(thumbFile, thumbnailImage, &jpeg.Options{Quality: 70}) thumbSize := thumbnailImage.Bounds().Max - thumbRes, err := tx.Exec("INSERT INTO photo_url (token, width, height) VALUES (?, ?, ?)", thumbnailToken, thumbSize.X, thumbSize.Y) + _, err = tx.Exec("INSERT INTO photo_url (photo_id, photo_name, width, height, purpose) VALUES (?, ?, ?, ?, ?)", photo_id, thumbnail_name, thumbSize.X, thumbSize.Y, models.PhotoThumbnail) if err != nil { return err } - thumbUrlId, err := thumbRes.LastInsertId() - if err != nil { - return err - } - - origSize := image.Bounds().Max - origRes, err := tx.Exec("INSERT INTO photo_url (token, width, height) VALUES (?, ?, ?)", originalToken, origSize.X, origSize.Y) - if err != nil { - return err - } - origUrlId, err := origRes.LastInsertId() - - _, err = tx.Exec("INSERT INTO photo (title, path, album_id, original_url, thumbnail_url) VALUES (?, ?, ?, ?, ?)", photoName, photoPath, albumId, origUrlId, thumbUrlId) - if err != nil { - log.Printf("ERROR: Could not insert photo into database") - return err - } return nil } func generateToken() string { const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - const length = 24 + const length = 8 b := make([]byte, length) for i := range b { diff --git a/api/server/server.go b/api/server/server.go index 62544a4c..1c93e162 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -11,6 +11,7 @@ import ( "github.com/viktorstrate/photoview/api/database" "github.com/viktorstrate/photoview/api/graphql/auth" + "github.com/viktorstrate/photoview/api/routes" "github.com/99designs/gqlgen/handler" photoview_graphql "github.com/viktorstrate/photoview/api/graphql" @@ -61,6 +62,8 @@ func main() { router.Handle("/", handler.Playground("GraphQL playground", "/graphql")) router.Handle("/graphql", handler.GraphQL(photoview_graphql.NewExecutableSchema(graphqlConfig))) + router.Mount("/image", routes.ImageRoutes()) + log.Printf("🚀 Graphql playground ready at http://localhost:%s/", port) log.Fatal(http.ListenAndServe(":"+port, router)) }