Fix share pages
This commit is contained in:
parent
df80802cab
commit
cacfb5edde
|
@ -3,6 +3,8 @@ package models
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ShareToken struct {
|
type ShareToken struct {
|
||||||
|
@ -27,7 +29,7 @@ func NewShareTokenFromRow(row *sql.Row) (*ShareToken, error) {
|
||||||
token := ShareToken{}
|
token := ShareToken{}
|
||||||
|
|
||||||
if err := row.Scan(&token.TokenID, &token.Value, &token.OwnerID, &token.Expire, &token.Password, &token.AlbumID, &token.MediaID); err != nil {
|
if err := row.Scan(&token.TokenID, &token.Value, &token.OwnerID, &token.Expire, &token.Password, &token.AlbumID, &token.MediaID); err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "failed to scan share token from database")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &token, nil
|
return &token, nil
|
||||||
|
@ -39,7 +41,7 @@ func NewShareTokensFromRows(rows *sql.Rows) ([]*ShareToken, error) {
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var token ShareToken
|
var token ShareToken
|
||||||
if err := rows.Scan(&token.TokenID, &token.Value, &token.OwnerID, &token.Expire, &token.Password, &token.AlbumID, &token.MediaID); err != nil {
|
if err := rows.Scan(&token.TokenID, &token.Value, &token.OwnerID, &token.Expire, &token.Password, &token.AlbumID, &token.MediaID); err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "failed to scan share tokens from database")
|
||||||
}
|
}
|
||||||
tokens = append(tokens, &token)
|
tokens = append(tokens, &token)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,12 @@ package models
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ func NewUserFromRow(row *sql.Row) (*User, error) {
|
||||||
user := User{}
|
user := User{}
|
||||||
|
|
||||||
if err := row.Scan(&user.UserID, &user.Username, &user.Password, &user.RootPath, &user.Admin); err != nil {
|
if err := row.Scan(&user.UserID, &user.Username, &user.Password, &user.RootPath, &user.Admin); err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "failed to scan user from database")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &user, nil
|
return &user, nil
|
||||||
|
@ -47,7 +47,7 @@ func NewUsersFromRows(rows *sql.Rows) ([]*User, error) {
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var user User
|
var user User
|
||||||
if err := rows.Scan(&user.UserID, &user.Username, &user.Password, &user.RootPath, &user.Admin); err != nil {
|
if err := rows.Scan(&user.UserID, &user.Username, &user.Password, &user.RootPath, &user.Admin); err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "failed to scan users from database")
|
||||||
}
|
}
|
||||||
users = append(users, &user)
|
users = append(users, &user)
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ func AuthorizeUser(database *sql.DB, username string, password string) (*User, e
|
||||||
if err == bcrypt.ErrMismatchedHashAndPassword {
|
if err == bcrypt.ErrMismatchedHashAndPassword {
|
||||||
return nil, ErrorInvalidUserCredentials
|
return nil, ErrorInvalidUserCredentials
|
||||||
} else {
|
} else {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "compare user password hash")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,16 +104,16 @@ func RegisterUser(database *sql.Tx, username string, password *string, rootPath
|
||||||
if password != nil {
|
if password != nil {
|
||||||
hashedPassBytes, err := bcrypt.GenerateFromPassword([]byte(*password), 12)
|
hashedPassBytes, err := bcrypt.GenerateFromPassword([]byte(*password), 12)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "failed to hash password")
|
||||||
}
|
}
|
||||||
hashedPass := string(hashedPassBytes)
|
hashedPass := string(hashedPassBytes)
|
||||||
|
|
||||||
if _, err := database.Exec("INSERT INTO user (username, password, root_path, admin) VALUES (?, ?, ?, ?)", username, hashedPass, rootPath, admin); err != nil {
|
if _, err := database.Exec("INSERT INTO user (username, password, root_path, admin) VALUES (?, ?, ?, ?)", username, hashedPass, rootPath, admin); err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "insert new user with password into database")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if _, err := database.Exec("INSERT INTO user (username, root_path, admin) VALUES (?, ?, ?)", username, rootPath, admin); err != nil {
|
if _, err := database.Exec("INSERT INTO user (username, root_path, admin) VALUES (?, ?, ?)", username, rootPath, admin); err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "insert user without password into database")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ func (r *shareTokenResolver) Album(ctx context.Context, obj *models.ShareToken)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
} else {
|
} else {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "could not get album of share token from database")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ func (r *shareTokenResolver) Media(ctx context.Context, obj *models.ShareToken)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
} else {
|
} else {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "could not get media of share token from database")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ func (r *queryResolver) ShareToken(ctx context.Context, tokenValue string, passw
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return nil, errors.New("share not found")
|
return nil, errors.New("share not found")
|
||||||
} else {
|
} else {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "failed to get share token from database")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ func (r *queryResolver) ShareToken(ctx context.Context, tokenValue string, passw
|
||||||
if err == bcrypt.ErrMismatchedHashAndPassword {
|
if err == bcrypt.ErrMismatchedHashAndPassword {
|
||||||
return nil, errors.New("unauthorized")
|
return nil, errors.New("unauthorized")
|
||||||
} else {
|
} else {
|
||||||
return nil, errors.New("internal server error")
|
return nil, errors.Wrap(err, "failed to compare token password hashes")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,7 @@ func (r *queryResolver) ShareTokenValidatePassword(ctx context.Context, tokenVal
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return false, errors.New("share not found")
|
return false, errors.New("share not found")
|
||||||
} else {
|
} else {
|
||||||
return false, err
|
return false, errors.Wrap(err, "failed to get share token from database")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ func (r *queryResolver) ShareTokenValidatePassword(ctx context.Context, tokenVal
|
||||||
if err == bcrypt.ErrMismatchedHashAndPassword {
|
if err == bcrypt.ErrMismatchedHashAndPassword {
|
||||||
return false, nil
|
return false, nil
|
||||||
} else {
|
} else {
|
||||||
return false, err
|
return false, errors.Wrap(err, "could not compare token password hashes")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ func (r *mutationResolver) ShareAlbum(ctx context.Context, albumID int, expire *
|
||||||
|
|
||||||
rows, err := r.Database.Query("SELECT owner_id FROM album WHERE album.album_id = ? AND album.owner_id = ?", albumID, user.UserID)
|
rows, err := r.Database.Query("SELECT owner_id FROM album WHERE album.album_id = ? AND album.owner_id = ?", albumID, user.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "failed to validate album owner with database")
|
||||||
}
|
}
|
||||||
if rows.Next() == false {
|
if rows.Next() == false {
|
||||||
return nil, auth.ErrUnauthorized
|
return nil, auth.ErrUnauthorized
|
||||||
|
@ -134,7 +134,7 @@ func (r *mutationResolver) ShareAlbum(ctx context.Context, albumID int, expire *
|
||||||
if password != nil {
|
if password != nil {
|
||||||
hashedPassBytes, err := bcrypt.GenerateFromPassword([]byte(*password), 12)
|
hashedPassBytes, err := bcrypt.GenerateFromPassword([]byte(*password), 12)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "failed to hash token password")
|
||||||
}
|
}
|
||||||
hashed_str := string(hashedPassBytes)
|
hashed_str := string(hashedPassBytes)
|
||||||
hashed_password = &hashed_str
|
hashed_password = &hashed_str
|
||||||
|
@ -143,12 +143,12 @@ func (r *mutationResolver) ShareAlbum(ctx context.Context, albumID int, expire *
|
||||||
token := utils.GenerateToken()
|
token := utils.GenerateToken()
|
||||||
res, err := r.Database.Exec("INSERT INTO share_token (value, owner_id, expire, password, album_id) VALUES (?, ?, ?, ?, ?)", token, user.UserID, expire, hashed_password, albumID)
|
res, err := r.Database.Exec("INSERT INTO share_token (value, owner_id, expire, password, album_id) VALUES (?, ?, ?, ?, ?)", token, user.UserID, expire, hashed_password, albumID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "failed to insert new share token into database")
|
||||||
}
|
}
|
||||||
|
|
||||||
token_id, err := res.LastInsertId()
|
token_id, err := res.LastInsertId()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "could not get database id of new album share token")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &models.ShareToken{
|
return &models.ShareToken{
|
||||||
|
@ -170,7 +170,7 @@ func (r *mutationResolver) ShareMedia(ctx context.Context, mediaID int, expire *
|
||||||
|
|
||||||
rows, err := r.Database.Query("SELECT owner_id FROM album, media WHERE media.media_id = ? AND media.album_id = album.album_id AND album.owner_id = ?", mediaID, user.UserID)
|
rows, err := r.Database.Query("SELECT owner_id FROM album, media WHERE media.media_id = ? AND media.album_id = album.album_id AND album.owner_id = ?", mediaID, user.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "error validating owner of media with database")
|
||||||
}
|
}
|
||||||
if rows.Next() == false {
|
if rows.Next() == false {
|
||||||
return nil, auth.ErrUnauthorized
|
return nil, auth.ErrUnauthorized
|
||||||
|
@ -185,12 +185,12 @@ func (r *mutationResolver) ShareMedia(ctx context.Context, mediaID int, expire *
|
||||||
token := utils.GenerateToken()
|
token := utils.GenerateToken()
|
||||||
res, err := r.Database.Exec("INSERT INTO share_token (value, owner_id, expire, password, media_id) VALUES (?, ?, ?, ?, ?)", token, user.UserID, expire, hashed_password, mediaID)
|
res, err := r.Database.Exec("INSERT INTO share_token (value, owner_id, expire, password, media_id) VALUES (?, ?, ?, ?, ?)", token, user.UserID, expire, hashed_password, mediaID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "failed to insert new share token into database")
|
||||||
}
|
}
|
||||||
|
|
||||||
token_id, err := res.LastInsertId()
|
token_id, err := res.LastInsertId()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "could not get database id of new media share token")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &models.ShareToken{
|
return &models.ShareToken{
|
||||||
|
@ -252,7 +252,7 @@ func hashSharePassword(password *string) (*string, error) {
|
||||||
if password != nil {
|
if password != nil {
|
||||||
hashedPassBytes, err := bcrypt.GenerateFromPassword([]byte(*password), 12)
|
hashedPassBytes, err := bcrypt.GenerateFromPassword([]byte(*password), 12)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "failed to generate hash for share password")
|
||||||
}
|
}
|
||||||
hashed_str := string(hashedPassBytes)
|
hashed_str := string(hashedPassBytes)
|
||||||
hashed_password = &hashed_str
|
hashed_password = &hashed_str
|
||||||
|
@ -271,7 +271,7 @@ func getUserToken(db *sql.DB, user *models.User, tokenValue string) (*models.Sha
|
||||||
|
|
||||||
token, err := models.NewShareTokenFromRow(row)
|
token, err := models.NewShareTokenFromRow(row)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "failed to get user share token from database")
|
||||||
}
|
}
|
||||||
|
|
||||||
return token, nil
|
return token, nil
|
||||||
|
|
|
@ -18,7 +18,7 @@ const albumQuery = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
photos(filter: { order_by: "title", order_direction: DESC }) {
|
media(filter: { order_by: "title", order_direction: DESC }) {
|
||||||
id
|
id
|
||||||
thumbnail {
|
thumbnail {
|
||||||
url
|
url
|
||||||
|
|
|
@ -30,8 +30,8 @@ const shareTokenQuery = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
photo {
|
media {
|
||||||
...PhotoProps
|
...MediaProps
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,12 +44,12 @@ const shareTokenQuery = gql`
|
||||||
url
|
url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
photos(filter: { order_by: "title", order_direction: DESC }) {
|
media(filter: { order_by: "title", order_direction: DESC }) {
|
||||||
...PhotoProps
|
...MediaProps
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment PhotoProps on Photo {
|
fragment MediaProps on Media {
|
||||||
id
|
id
|
||||||
title
|
title
|
||||||
thumbnail {
|
thumbnail {
|
||||||
|
@ -104,8 +104,8 @@ const AuthorizedTokenRoute = ({ match }) => {
|
||||||
return <AlbumSharePage album={data.shareToken.album} match={match} />
|
return <AlbumSharePage album={data.shareToken.album} match={match} />
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.shareToken.photo) {
|
if (data.shareToken.media) {
|
||||||
return <PhotoSharePage photo={data.shareToken.photo} />
|
return <PhotoSharePage photo={data.shareToken.media} />
|
||||||
}
|
}
|
||||||
|
|
||||||
return <h1>Share not found</h1>
|
return <h1>Share not found</h1>
|
||||||
|
|
|
@ -33,12 +33,12 @@ const AlbumGallery = ({ album, loading = false, customAlbumLink }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextImage = () => {
|
const nextImage = () => {
|
||||||
setActiveImage((imageState.activeImage + 1) % album.photos.length)
|
setActiveImage((imageState.activeImage + 1) % album.media.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
const previousImage = () => {
|
const previousImage = () => {
|
||||||
if (imageState.activeImage <= 0) {
|
if (imageState.activeImage <= 0) {
|
||||||
setActiveImage(album.photos.length - 1)
|
setActiveImage(album.media.length - 1)
|
||||||
} else {
|
} else {
|
||||||
setActiveImage(imageState.activeImage - 1)
|
setActiveImage(imageState.activeImage - 1)
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,7 @@ const AlbumGallery = ({ album, loading = false, customAlbumLink }) => {
|
||||||
}
|
}
|
||||||
<PhotoGallery
|
<PhotoGallery
|
||||||
loading={loading}
|
loading={loading}
|
||||||
photos={album && album.photos}
|
media={album && album.media}
|
||||||
activeIndex={imageState.activeImage}
|
activeIndex={imageState.activeImage}
|
||||||
presenting={imageState.presenting}
|
presenting={imageState.presenting}
|
||||||
onSelectImage={index => {
|
onSelectImage={index => {
|
||||||
|
|
|
@ -130,7 +130,7 @@ export const Photo = ({
|
||||||
if (typeof photo.favorite == 'boolean') {
|
if (typeof photo.favorite == 'boolean') {
|
||||||
heartIcon = (
|
heartIcon = (
|
||||||
<FavoriteIcon
|
<FavoriteIcon
|
||||||
favorite={photo.favorite}
|
favorite={photo.favorite.toString()}
|
||||||
name={photo.favorite ? 'heart' : 'heart outline'}
|
name={photo.favorite ? 'heart' : 'heart outline'}
|
||||||
onClick={event => {
|
onClick={event => {
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
|
|
|
@ -10,7 +10,7 @@ import SidebarDownload from './SidebarDownload'
|
||||||
|
|
||||||
const photoQuery = gql`
|
const photoQuery = gql`
|
||||||
query sidebarPhoto($id: Int!) {
|
query sidebarPhoto($id: Int!) {
|
||||||
photo(id: $id) {
|
media(id: $id) {
|
||||||
id
|
id
|
||||||
title
|
title
|
||||||
highRes {
|
highRes {
|
||||||
|
@ -168,7 +168,7 @@ class PhotoSidebar extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SidebarContent photo={data.photo} hidePreview={hidePreview} />
|
<SidebarContent photo={data.media} hidePreview={hidePreview} />
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
</Query>
|
</Query>
|
||||||
|
|
|
@ -14,7 +14,7 @@ import copy from 'copy-to-clipboard'
|
||||||
|
|
||||||
const sharePhotoQuery = gql`
|
const sharePhotoQuery = gql`
|
||||||
query sidbarGetPhotoShares($id: Int!) {
|
query sidbarGetPhotoShares($id: Int!) {
|
||||||
photo(id: $id) {
|
media(id: $id) {
|
||||||
id
|
id
|
||||||
shares {
|
shares {
|
||||||
token
|
token
|
||||||
|
@ -38,7 +38,7 @@ const shareAlbumQuery = gql`
|
||||||
|
|
||||||
const addPhotoShareMutation = gql`
|
const addPhotoShareMutation = gql`
|
||||||
mutation sidebarPhotoAddShare($id: Int!, $password: String, $expire: Time) {
|
mutation sidebarPhotoAddShare($id: Int!, $password: String, $expire: Time) {
|
||||||
sharePhoto(photoId: $id, password: $password, expire: $expire) {
|
shareMedia(mediaId: $id, password: $password, expire: $expire) {
|
||||||
token
|
token
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -254,7 +254,7 @@ const SidebarShare = ({ photo, album }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!content) {
|
if (!content) {
|
||||||
const shares = isPhoto ? sharesData.photo.shares : sharesData.album.shares
|
const shares = isPhoto ? sharesData.media.shares : sharesData.album.shares
|
||||||
|
|
||||||
const optionsRows = shares.map(share => (
|
const optionsRows = shares.map(share => (
|
||||||
<Table.Row key={share.token}>
|
<Table.Row key={share.token}>
|
||||||
|
|
|
@ -8,8 +8,8 @@ import gql from 'graphql-tag'
|
||||||
import download from 'downloadjs'
|
import download from 'downloadjs'
|
||||||
|
|
||||||
const downloadQuery = gql`
|
const downloadQuery = gql`
|
||||||
query sidebarDownloadQuery($photoId: Int!) {
|
query sidebarDownloadQuery($mediaId: Int!) {
|
||||||
photo(id: $photoId) {
|
media(id: $mediaId) {
|
||||||
id
|
id
|
||||||
downloads {
|
downloads {
|
||||||
title
|
title
|
||||||
|
@ -149,13 +149,13 @@ const SidebarDownload = ({ photo }) => {
|
||||||
const [
|
const [
|
||||||
loadPhotoDownloads,
|
loadPhotoDownloads,
|
||||||
{ called, loading, data },
|
{ called, loading, data },
|
||||||
] = useLazyQuery(downloadQuery, { variables: { photoId: photo.id } })
|
] = useLazyQuery(downloadQuery, { variables: { mediaId: photo.id } })
|
||||||
|
|
||||||
let downloads = []
|
let downloads = []
|
||||||
|
|
||||||
if (called) {
|
if (called) {
|
||||||
if (!loading) {
|
if (!loading) {
|
||||||
downloads = data && data.photo.downloads
|
downloads = data && data.media.downloads
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!photo.downloads) {
|
if (!photo.downloads) {
|
||||||
|
|
Loading…
Reference in New Issue