1
Fork 0

Merge branch 'master' of github.com:viktorstrate/photoview into component-class-to-function-migration

This commit is contained in:
stz184 2020-11-16 22:48:00 +02:00
commit 24ea00d9ec
8 changed files with 17811 additions and 980 deletions

View File

@ -6,7 +6,7 @@ updates:
- package-ecosystem: "npm"
directory: "/ui"
schedule:
interval: "weekly"
interval: "monthly"
commit-message:
prefix: "ui (npm)"
labels:
@ -17,7 +17,7 @@ updates:
- package-ecosystem: "gomod"
directory: "/api"
schedule:
interval: "weekly"
interval: "monthly"
commit-message:
prefix: "api (gomod)"
labels:

View File

@ -4,7 +4,7 @@ go 1.13
require (
github.com/99designs/gqlgen v0.13.0
github.com/Microsoft/go-winio v0.4.14 // indirect
github.com/Microsoft/go-winio v0.4.15 // indirect
github.com/agnivade/levenshtein v1.1.0 // indirect
github.com/disintegration/imaging v1.6.2
github.com/docker/distribution v2.7.1+incompatible // indirect
@ -25,9 +25,10 @@ require (
github.com/vektah/gqlparser/v2 v2.1.0
github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0
github.com/xor-gate/goexif2 v1.1.0
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5
golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d // indirect
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b // indirect
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd // indirect
gopkg.in/vansante/go-ffprobe.v2 v2.0.2
gopkg.in/yaml.v2 v2.3.0 // indirect
)

View File

@ -3,6 +3,8 @@ github.com/99designs/gqlgen v0.13.0/go.mod h1:NV130r6f4tpRWuAI+zsrSdooO/eWUv+Gyy
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.15 h1:qkLXKzb1QoVatRyd/YlXZ/Kg0m5K3SPuoD82jjSOaBc=
github.com/Microsoft/go-winio v0.4.15/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+sxIXdQ=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/agnivade/levenshtein v1.0.3 h1:M5ZnqLOoZR8ygVq0FfkXsNOKzMCk0xRiow0R5+5VkQ0=
@ -120,6 +122,8 @@ golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rB
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9 h1:umElSU9WZirRdgu2yFHY0ayQkEnKiOC1TtM3fWXFnoU=
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200801110659-972c09e46d76 h1:U7GPaoQyQmX+CBRWXKrvRzWTbd+slqeSh8uARsIyhAw=
golang.org/x/image v0.0.0-20200801110659-972c09e46d76/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@ -135,17 +139,23 @@ golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOL
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d h1:dOiJ2n2cMwGLce/74I/QHMbnpk5GfY7InR8rczoMqRM=
golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY=
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@ -2,8 +2,6 @@ package routes
import (
"database/sql"
"fmt"
"io"
"log"
"net/http"
"os"
@ -49,7 +47,6 @@ func RegisterPhotoRoutes(db *sql.DB, router *mux.Router) {
}
var cachedPath string
var file *os.File = nil
if mediaUrl.Purpose == models.PhotoThumbnail || mediaUrl.Purpose == models.PhotoHighRes || mediaUrl.Purpose == models.VideoThumbnail {
cachedPath = path.Join(scanner.PhotoCache(), strconv.Itoa(media.AlbumId), strconv.Itoa(mediaUrl.MediaId), mediaUrl.MediaName)
@ -62,48 +59,40 @@ func RegisterPhotoRoutes(db *sql.DB, router *mux.Router) {
return
}
file, err = os.Open(cachedPath)
defer file.Close()
if err != nil {
if os.IsNotExist(err) {
tx, err := db.Begin()
if err != nil {
log.Printf("ERROR: %s\n", err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("internal server error"))
return
}
_, err = scanner.ProcessMedia(tx, media)
if err != nil {
log.Printf("ERROR: processing image not found in cache: %s\n", err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("internal server error"))
tx.Rollback()
return
}
file, err = os.Open(cachedPath)
if err != nil {
log.Printf("ERROR: after reprocessing image not found in cache: %s\n", err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("internal server error"))
tx.Rollback()
return
}
tx.Commit()
_, err = os.Stat(cachedPath)
if os.IsNotExist((err)) {
tx, err := db.Begin()
if err != nil {
log.Printf("ERROR: %s\n", err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("internal server error"))
return
}
}
w.Header().Set("Content-Type", mediaUrl.ContentType)
if stats, err := file.Stat(); err == nil {
w.Header().Set("Content-Length", fmt.Sprintf("%d", stats.Size()))
_, err = scanner.ProcessMedia(tx, media)
if err != nil {
log.Printf("ERROR: processing image not found in cache: %s\n", err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("internal server error"))
tx.Rollback()
return
}
_, err = os.Stat(cachedPath)
if err != nil {
log.Printf("ERROR: after reprocessing image not found in cache: %s\n", err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("internal server error"))
tx.Rollback()
return
}
tx.Commit()
}
// Allow caching the resource for 1 day
w.Header().Set("Cache-Control", "private, max-age=86400, immutable")
io.Copy(w, file)
http.ServeFile(w, r, cachedPath)
})
}

18616
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -5,22 +5,21 @@
"description": "UI app for Photoview",
"dependencies": {
"@babel/preset-env": "^7.12.1",
"@types/react": "^16.9.53",
"@types/react": "^16.9.56",
"babel-plugin-styled-components": "^1.11.1",
"copy-to-clipboard": "^3.3.1",
"downloadjs": "^1.4.7",
"graphql": "^14.7.0",
"graphql": "^15.4.0",
"parcel-bundler": "^1.12.4",
"prop-types": "^15.7.2",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-helmet": "^6.1.0",
"react-lazyload": "^3.1.0",
"react-router-dom": "^5.2.0",
"react-spring": "^8.0.27",
"semantic-ui-css": "^2.4.1",
"semantic-ui-react": "^2.0.1",
"styled-components": "^5.2.0",
"styled-components": "^5.2.1",
"subscriptions-transport-ws": "^0.9.18",
"url-join": "^4.0.1"
},
@ -36,21 +35,21 @@
"@babel/core": "^7.12.3",
"@babel/plugin-transform-modules-commonjs": "^7.12.1",
"@babel/plugin-transform-runtime": "^7.12.1",
"@babel/preset-react": "^7.12.1",
"@babel/preset-react": "^7.12.5",
"@testing-library/jest-dom": "^5.11.5",
"@testing-library/react": "^11.1.0",
"@testing-library/react": "^11.1.2",
"babel-eslint": "^10.1.0",
"babel-jest": "^26.6.1",
"babel-jest": "^26.6.3",
"babel-plugin-graphql-tag": "^3.1.0",
"babel-plugin-transform-semantic-ui-react-imports": "^1.4.1",
"eslint": "^7.12.0",
"eslint-plugin-jest": "^24.1.0",
"eslint": "^7.13.0",
"eslint-plugin-jest": "^24.1.3",
"eslint-plugin-jest-dom": "^3.2.4",
"eslint-plugin-react": "^7.21.5",
"eslint-plugin-react-hooks": "^4.2.0",
"husky": "^4.3.0",
"jest": "^26.6.1",
"lint-staged": "^10.5.0",
"jest": "^26.6.3",
"lint-staged": "^10.5.1",
"mapbox-gl": "^1.12.0",
"parcel-plugin-sw-cache": "^0.3.1",
"prettier": "^2.1.2",

View File

@ -7,11 +7,13 @@ import { MemoryRouter, Route } from 'react-router-dom'
import * as authentication from '../../authentication'
jest.mock('../../authentication.js')
describe('AuthorizedRoute component', () => {
const AuthorizedComponent = () => <div>authorized content</div>
test('not logged in', async () => {
authentication.authToken = jest.fn(() => null)
authentication.authToken.mockImplementation(() => null)
render(
<MemoryRouter initialEntries={['/']}>
@ -24,7 +26,7 @@ describe('AuthorizedRoute component', () => {
})
test('logged in', async () => {
authentication.authToken = jest.fn(() => 'token-here')
authentication.authToken.mockImplementation(() => 'token-here')
render(
<MemoryRouter initialEntries={['/']}>
@ -34,5 +36,6 @@ describe('AuthorizedRoute component', () => {
)
expect(screen.getByText('authorized content')).toBeInTheDocument()
expect(screen.queryByText('login redirect')).toBeNull()
})
})

View File

@ -4,7 +4,6 @@ import { Table } from 'semantic-ui-react'
import styled from 'styled-components'
import { MessageState } from '../messages/Messages'
import { useLazyQuery, gql } from '@apollo/client'
import download from 'downloadjs'
import { authToken } from '../../authentication'
export const SIDEBAR_DOWNLOAD_QUERY = gql`
@ -31,11 +30,8 @@ function formatBytes(bytes) {
return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i]
}
const downloadPhoto = async url => {
const downloadMedia = async url => {
const imgUrl = new URL(url)
// let headers = {
// Authorization: `Bearer ${localStorage.getItem('token')}`,
// }
if (authToken() == null) {
// Get share token if not authorized
@ -43,29 +39,26 @@ const downloadPhoto = async url => {
if (token) {
imgUrl.searchParams.set('token', token[1])
}
// headers = {}
}
const response = await fetch(imgUrl.href, {
// headers,
credentials: 'include',
})
const totalBytes = Number(response.headers.get('content-length'))
if (totalBytes == 0) {
MessageState.add({
key: Math.random().toString(26),
type: 'message',
props: {
header: 'Error downloading photo',
content: `Could not get size of photo from server`,
negative: true,
},
})
return
let blob = null
if (response.headers.has('content-length')) {
blob = await downloadMediaShowProgress(response)
} else {
blob = await response.blob()
}
const filename = url.match(/[^/]*$/)[0]
downloadBlob(blob, filename)
}
const downloadMediaShowProgress = async response => {
const totalBytes = Number(response.headers.get('content-length'))
const reader = response.body.getReader()
let data = new Uint8Array(totalBytes)
@ -134,9 +127,23 @@ const downloadPhoto = async url => {
const content = new Blob([data.buffer], {
type: response.headers.get('content-type'),
})
const filename = url.match(/[^/]*$/)[0]
download(content, filename)
return content
}
const downloadBlob = async (blob, filename) => {
let objectUrl = window.URL.createObjectURL(blob)
let anchor = document.createElement('a')
document.body.appendChild(anchor)
anchor.href = objectUrl
anchor.download = filename
anchor.click()
anchor.remove()
window.URL.revokeObjectURL(objectUrl)
}
const DownloadTableRow = styled(Table.Row)`
@ -172,7 +179,7 @@ const SidebarDownload = ({ photo }) => {
let downloadRows = downloads.map(x => (
<DownloadTableRow
key={x.mediaUrl.url}
onClick={() => downloadPhoto(x.mediaUrl.url)}
onClick={() => downloadMedia(x.mediaUrl.url, photo.title)}
>
<Table.Cell>{`${x.title}`}</Table.Cell>
<Table.Cell>{`${x.mediaUrl.width} x ${x.mediaUrl.height}`}</Table.Cell>