1
Fork 0

Work on shares

This commit is contained in:
viktorstrate 2020-02-11 14:32:35 +01:00
parent 5a0f03268d
commit 687b4f4f7f
11 changed files with 221 additions and 178 deletions

View File

@ -2,11 +2,11 @@ CREATE TABLE IF NOT EXISTS share_token (
token_id int AUTO_INCREMENT,
value char(24) NOT NULL UNIQUE,
owner_id int NOT NULL,
expire timestamp,
password varchar(256) NOT NULL,
expire timestamp NULL DEFAULT NULL,
password varchar(256),
album_id int,
photo_id int,
PRIMARY KEY (token_id),
CHECK (album_id IS NOT NULL OR photo_id IS NOT NULL)
PRIMARY KEY (token_id)
-- CHECK (album_id IS NOT NULL OR photo_id IS NOT NULL)
);

View File

@ -25,6 +25,7 @@ func SetupDatabase() *sql.DB {
queryValues := address.Query()
queryValues.Add("multiStatements", "true")
queryValues.Add("parseTime", "true")
address.RawQuery = queryValues.Encode()

View File

@ -55,6 +55,7 @@ type ComplexityRoot struct {
ParentAlbum func(childComplexity int) int
Path func(childComplexity int) int
Photos func(childComplexity int, filter *models.Filter) int
Shares func(childComplexity int) int
SubAlbums func(childComplexity int, filter *models.Filter) int
Thumbnail func(childComplexity int) int
Title func(childComplexity int) int
@ -114,15 +115,14 @@ type ComplexityRoot struct {
}
Query struct {
Album func(childComplexity int, id int) int
AlbumShares func(childComplexity int, id int, password *string) int
MyAlbums func(childComplexity int, filter *models.Filter) int
MyPhotos func(childComplexity int, filter *models.Filter) int
MyUser func(childComplexity int) int
Photo func(childComplexity int, id int) int
PhotoShares func(childComplexity int, id int, password *string) int
SiteInfo func(childComplexity int) int
Users func(childComplexity int, filter *models.Filter) int
Album func(childComplexity int, id int) int
MyAlbums func(childComplexity int, filter *models.Filter) int
MyPhotos func(childComplexity int, filter *models.Filter) int
MyUser func(childComplexity int) int
Photo func(childComplexity int, id int) int
ShareToken func(childComplexity int, token string, password *string) int
SiteInfo func(childComplexity int) int
Users func(childComplexity int, filter *models.Filter) int
}
ScannerResult struct {
@ -160,6 +160,7 @@ type AlbumResolver interface {
Owner(ctx context.Context, obj *models.Album) (*models.User, error)
Thumbnail(ctx context.Context, obj *models.Album) (*models.Photo, error)
Shares(ctx context.Context, obj *models.Album) ([]*models.ShareToken, error)
}
type MutationResolver interface {
AuthorizeUser(ctx context.Context, username string, password string) (*models.AuthorizeResult, error)
@ -186,8 +187,7 @@ type QueryResolver interface {
Album(ctx context.Context, id int) (*models.Album, error)
MyPhotos(ctx context.Context, filter *models.Filter) ([]*models.Photo, error)
Photo(ctx context.Context, id int) (*models.Photo, error)
AlbumShares(ctx context.Context, id int, password *string) ([]*models.ShareToken, error)
PhotoShares(ctx context.Context, id int, password *string) ([]*models.ShareToken, error)
ShareToken(ctx context.Context, token string, password *string) (*models.ShareToken, error)
}
type ShareTokenResolver interface {
Owner(ctx context.Context, obj *models.ShareToken) (*models.User, error)
@ -251,6 +251,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Album.Photos(childComplexity, args["filter"].(*models.Filter)), true
case "Album.shares":
if e.complexity.Album.Shares == nil {
break
}
return e.complexity.Album.Shares(childComplexity), true
case "Album.subAlbums":
if e.complexity.Album.SubAlbums == nil {
break
@ -564,18 +571,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Query.Album(childComplexity, args["id"].(int)), true
case "Query.albumShares":
if e.complexity.Query.AlbumShares == nil {
break
}
args, err := ec.field_Query_albumShares_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Query.AlbumShares(childComplexity, args["id"].(int), args["password"].(*string)), true
case "Query.myAlbums":
if e.complexity.Query.MyAlbums == nil {
break
@ -619,17 +614,17 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Query.Photo(childComplexity, args["id"].(int)), true
case "Query.photoShares":
if e.complexity.Query.PhotoShares == nil {
case "Query.shareToken":
if e.complexity.Query.ShareToken == nil {
break
}
args, err := ec.field_Query_photoShares_args(context.TODO(), rawArgs)
args, err := ec.field_Query_shareToken_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Query.PhotoShares(childComplexity, args["id"].(int), args["password"].(*string)), true
return e.complexity.Query.ShareToken(childComplexity, args["token"].(string), args["password"].(*string)), true
case "Query.siteInfo":
if e.complexity.Query.SiteInfo == nil {
@ -851,8 +846,7 @@ type Query {
"Get photo by id, user must own the photo or be admin"
photo(id: Int!): Photo!
albumShares(id: Int!, password: String): [ShareToken!]!
photoShares(id: Int!, password: String): [ShareToken!]!
shareToken(token: String!, password: String): ShareToken!
}
type Mutation {
@ -942,7 +936,7 @@ type Album {
"An image in this album used for previewing this album"
thumbnail: Photo
# shares: [ShareToken]
shares: [ShareToken]
}
type PhotoURL {
@ -1204,28 +1198,6 @@ func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs
return args, nil
}
func (ec *executionContext) field_Query_albumShares_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
var arg0 int
if tmp, ok := rawArgs["id"]; ok {
arg0, err = ec.unmarshalNInt2int(ctx, tmp)
if err != nil {
return nil, err
}
}
args["id"] = arg0
var arg1 *string
if tmp, ok := rawArgs["password"]; ok {
arg1, err = ec.unmarshalOString2ᚖstring(ctx, tmp)
if err != nil {
return nil, err
}
}
args["password"] = arg1
return args, nil
}
func (ec *executionContext) field_Query_album_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
@ -1268,28 +1240,6 @@ func (ec *executionContext) field_Query_myPhotos_args(ctx context.Context, rawAr
return args, nil
}
func (ec *executionContext) field_Query_photoShares_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
var arg0 int
if tmp, ok := rawArgs["id"]; ok {
arg0, err = ec.unmarshalNInt2int(ctx, tmp)
if err != nil {
return nil, err
}
}
args["id"] = arg0
var arg1 *string
if tmp, ok := rawArgs["password"]; ok {
arg1, err = ec.unmarshalOString2ᚖstring(ctx, tmp)
if err != nil {
return nil, err
}
}
args["password"] = arg1
return args, nil
}
func (ec *executionContext) field_Query_photo_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
@ -1304,6 +1254,28 @@ func (ec *executionContext) field_Query_photo_args(ctx context.Context, rawArgs
return args, nil
}
func (ec *executionContext) field_Query_shareToken_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
var arg0 string
if tmp, ok := rawArgs["token"]; ok {
arg0, err = ec.unmarshalNString2string(ctx, tmp)
if err != nil {
return nil, err
}
}
args["token"] = arg0
var arg1 *string
if tmp, ok := rawArgs["password"]; ok {
arg1, err = ec.unmarshalOString2ᚖstring(ctx, tmp)
if err != nil {
return nil, err
}
}
args["password"] = arg1
return args, nil
}
func (ec *executionContext) field_Query_users_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
@ -1658,6 +1630,40 @@ func (ec *executionContext) _Album_thumbnail(ctx context.Context, field graphql.
return ec.marshalOPhoto2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhoto(ctx, field.Selections, res)
}
func (ec *executionContext) _Album_shares(ctx context.Context, field graphql.CollectedField, obj *models.Album) (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: "Album",
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.Album().Shares(rctx, obj)
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.([]*models.ShareToken)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
return ec.marshalOShareToken2ᚕᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐShareToken(ctx, field.Selections, res)
}
func (ec *executionContext) _AuthorizeResult_success(ctx context.Context, field graphql.CollectedField, obj *models.AuthorizeResult) (ret graphql.Marshaler) {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() {
@ -3261,7 +3267,7 @@ func (ec *executionContext) _Query_photo(ctx context.Context, field graphql.Coll
return ec.marshalNPhoto2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhoto(ctx, field.Selections, res)
}
func (ec *executionContext) _Query_albumShares(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
func (ec *executionContext) _Query_shareToken(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() {
if r := recover(); r != nil {
@ -3278,7 +3284,7 @@ func (ec *executionContext) _Query_albumShares(ctx context.Context, field graphq
}
ctx = graphql.WithResolverContext(ctx, rctx)
rawArgs := field.ArgumentMap(ec.Variables)
args, err := ec.field_Query_albumShares_args(ctx, rawArgs)
args, err := ec.field_Query_shareToken_args(ctx, rawArgs)
if err != nil {
ec.Error(ctx, err)
return graphql.Null
@ -3287,7 +3293,7 @@ func (ec *executionContext) _Query_albumShares(ctx context.Context, field graphq
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.Query().AlbumShares(rctx, args["id"].(int), args["password"].(*string))
return ec.resolvers.Query().ShareToken(rctx, args["token"].(string), args["password"].(*string))
})
if err != nil {
ec.Error(ctx, err)
@ -3299,54 +3305,10 @@ func (ec *executionContext) _Query_albumShares(ctx context.Context, field graphq
}
return graphql.Null
}
res := resTmp.([]*models.ShareToken)
res := resTmp.(*models.ShareToken)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
return ec.marshalNShareToken2ᚕᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐShareTokenᚄ(ctx, field.Selections, res)
}
func (ec *executionContext) _Query_photoShares(ctx context.Context, field graphql.CollectedField) (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: "Query",
Field: field,
Args: nil,
IsMethod: true,
}
ctx = graphql.WithResolverContext(ctx, rctx)
rawArgs := field.ArgumentMap(ec.Variables)
args, err := ec.field_Query_photoShares_args(ctx, rawArgs)
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
rctx.Args = args
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.Query().PhotoShares(rctx, args["id"].(int), args["password"].(*string))
})
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.ShareToken)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
return ec.marshalNShareToken2ᚕᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐShareTokenᚄ(ctx, field.Selections, res)
return ec.marshalNShareToken2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐShareToken(ctx, field.Selections, res)
}
func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
@ -5269,6 +5231,17 @@ func (ec *executionContext) _Album(ctx context.Context, sel ast.SelectionSet, ob
res = ec._Album_thumbnail(ctx, field, obj)
return res
})
case "shares":
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._Album_shares(ctx, field, obj)
return res
})
default:
panic("unknown field " + strconv.Quote(field.Name))
}
@ -5710,7 +5683,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
}
return res
})
case "albumShares":
case "shareToken":
field := field
out.Concurrently(i, func() (res graphql.Marshaler) {
defer func() {
@ -5718,21 +5691,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._Query_albumShares(ctx, field)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res
})
case "photoShares":
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._Query_photoShares(ctx, field)
res = ec._Query_shareToken(ctx, field)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
@ -6905,6 +6864,46 @@ func (ec *executionContext) marshalOShareToken2githubᚗcomᚋviktorstrateᚋpho
return ec._ShareToken(ctx, sel, &v)
}
func (ec *executionContext) marshalOShareToken2ᚕᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐShareToken(ctx context.Context, sel ast.SelectionSet, v []*models.ShareToken) 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.marshalOShareToken2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐShareToken(ctx, sel, v[i])
}
if isLen1 {
f(i)
} else {
go f(i)
}
}
wg.Wait()
return ret
}
func (ec *executionContext) marshalOShareToken2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐShareToken(ctx context.Context, sel ast.SelectionSet, v *models.ShareToken) graphql.Marshaler {
if v == nil {
return graphql.Null

View File

@ -1,6 +1,9 @@
package models
import "time"
import (
"database/sql"
"time"
)
type ShareToken struct {
TokenID int
@ -19,3 +22,27 @@ func (share *ShareToken) Token() string {
func (share *ShareToken) ID() int {
return share.TokenID
}
func NewShareTokenFromRow(row *sql.Row) (*ShareToken, error) {
token := ShareToken{}
if err := row.Scan(&token.TokenID, &token.Value, &token.OwnerID, &token.Expire, &token.Password, &token.AlbumID, &token.PhotoID); err != nil {
return nil, err
}
return &token, nil
}
func NewShareTokensFromRows(rows *sql.Rows) ([]*ShareToken, error) {
tokens := make([]*ShareToken, 0)
for rows.Next() {
var token ShareToken
if err := rows.Scan(&token.TokenID, &token.Value, &token.OwnerID, &token.Expire, &token.Password, &token.AlbumID, &token.PhotoID); err != nil {
return nil, err
}
tokens = append(tokens, &token)
}
return tokens, nil
}

View File

@ -117,3 +117,13 @@ func (r *albumResolver) ParentAlbum(ctx context.Context, obj *models.Album) (*mo
func (r *albumResolver) Owner(ctx context.Context, obj *models.Album) (*models.User, error) {
panic("not implemented")
}
func (r *albumResolver) Shares(ctx context.Context, obj *models.Album) ([]*models.ShareToken, error) {
rows, err := r.Database.Query("SELECT * FROM share_token WHERE album_id = ?", obj.ID())
if err != nil {
return nil, err
}
defer rows.Close()
return models.NewShareTokensFromRows(rows)
}

View File

@ -54,14 +54,22 @@ func (r *shareTokenResolver) Photo(ctx context.Context, obj *models.ShareToken)
return photo, nil
}
func (r *queryResolver) AlbumShares(ctx context.Context, id int, password *string) ([]*models.ShareToken, error) {
log.Println("Query AlbumShares: not implemented")
func (r *queryResolver) ShareToken(ctx context.Context, token string, password *string) (*models.ShareToken, error) {
tokens := make([]*models.ShareToken, 0)
return tokens, nil
row := r.Database.QueryRow("SELECT * FROM share_token WHERE value = ? AND (password = ? OR password IS NULL)", token, password)
result, err := models.NewShareTokenFromRow(row)
if err != nil {
if err == sql.ErrNoRows {
return nil, nil
} else {
return nil, err
}
}
return result, nil
}
func (r *queryResolver) PhotoShares(ctx context.Context, id int, password *string) ([]*models.ShareToken, error) {
func (r *queryResolver) PhotoShares(ctx context.Context, token string, password *string) ([]*models.ShareToken, error) {
log.Println("Query PhotoShares: not implemented")
tokens := make([]*models.ShareToken, 0)

View File

@ -32,8 +32,7 @@ type Query {
"Get photo by id, user must own the photo or be admin"
photo(id: Int!): Photo!
albumShares(id: Int!, password: String): [ShareToken!]!
photoShares(id: Int!, password: String): [ShareToken!]!
shareToken(token: String!, password: String): ShareToken!
}
type Mutation {
@ -123,7 +122,7 @@ type Album {
"An image in this album used for previewing this album"
thumbnail: Photo
# shares: [ShareToken]
shares: [ShareToken]
}
type PhotoURL {

View File

@ -7,7 +7,7 @@ import { Query } from 'react-apollo'
import gql from 'graphql-tag'
const tokenQuery = gql`
query SharePageToken($token: Int!) {
query SharePageToken($token: String!) {
shareToken(token: $token) {
token
album {
@ -28,7 +28,7 @@ const tokenQuery = gql`
fragment AlbumProps on Album {
id
title
photos(orderBy: title_desc) {
photos(filter: { order_by: "title", order_direction: DESC }) {
...PhotoProps
}
}
@ -41,16 +41,14 @@ const tokenQuery = gql`
width
height
}
original {
highRes {
url
}
exif {
camera
maker
lens
dateShot {
formatted
}
dateShot
fileSize
exposure
aperture

View File

@ -9,10 +9,13 @@ export async function fetchProtectedImage(src, { signal } = { signal: null }) {
return imageCache[src]
}
let headers = {}
if (localStorage.getItem('token')) {
headers['Authorization'] = `Bearer ${localStorage.getItem('token')}`
}
let image = await fetch(src, {
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
},
headers,
signal,
})

View File

@ -37,7 +37,7 @@ const AlbumSidebar = ({ albumId }) => {
}
AlbumSidebar.propTypes = {
albumId: PropTypes.string.isRequired,
albumId: PropTypes.number.isRequired,
}
export default AlbumSidebar

View File

@ -7,26 +7,28 @@ import copy from 'copy-to-clipboard'
const sharePhotoQuery = gql`
query sidbarGetPhotoShares($id: Int!) {
photoShares(id: $id) {
token
photo(id: $id) {
id
shares {
token
}
}
}
`
const shareAlbumQuery = gql`
query sidbarGetAlbumShares($id: Int!) {
albumShares(id: $id) {
token
album(id: $id) {
id
shares {
token
}
}
}
`
const addPhotoShareMutation = gql`
mutation sidebarPhotoAddShare(
$id: Int!
$password: String
$expire: _Neo4jDateInput
) {
mutation sidebarPhotoAddShare($id: Int!, $password: String, $expire: Time) {
sharePhoto(photoId: $id, password: $password, expire: $expire) {
token
}
@ -34,11 +36,7 @@ const addPhotoShareMutation = gql`
`
const addAlbumShareMutation = gql`
mutation sidebarAlbumAddShare(
$id: Int!
$password: String
$expire: _Neo4jDateInput
) {
mutation sidebarAlbumAddShare($id: Int!, $password: String, $expire: Time) {
shareAlbum(albumId: $id, password: $password, expire: $expire) {
token
}
@ -71,9 +69,9 @@ const SidebarShare = ({ photo, album }) => {
<Query query={query} variables={{ id }}>
{({ loading, error, data, refetch }) => {
if (loading) return <div>Loading...</div>
if (error) return <div>Error: {error}</div>
if (error) return <div>Error: {error.message}</div>
let shares = isPhoto ? data.photoShares : data.albumShares
let shares = isPhoto ? data.photo.shares : data.album.shares
const rows = shares.map(share => (
<Table.Row key={share.token}>