diff --git a/.gitignore b/.gitignore
index d209a838..2be7de69 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,8 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
/api/src/cache/
+/docker-compose.yml
+/photos_path
# dependencies
node_modules/
diff --git a/api/package-lock.json b/api/package-lock.json
index ae4e4a33..d40c1136 100644
--- a/api/package-lock.json
+++ b/api/package-lock.json
@@ -2201,22 +2201,6 @@
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
},
- "cookie-parser": {
- "version": "1.4.4",
- "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.4.tgz",
- "integrity": "sha512-lo13tqF3JEtFO7FyA49CqbhaFkskRJ0u/UAiINgrIXeRCY41c88/zxtrECl8AKH3B0hj9q10+h3Kt8I7KlW4tw==",
- "requires": {
- "cookie": "0.3.1",
- "cookie-signature": "1.0.6"
- },
- "dependencies": {
- "cookie": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
- "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
- }
- }
- },
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
diff --git a/api/package.json b/api/package.json
index d9c15598..e2112b32 100644
--- a/api/package.json
+++ b/api/package.json
@@ -23,7 +23,6 @@
"apollo-server": "^2.8.1",
"babel-runtime": "^6.26.0",
"body-parser": "^1.19.0",
- "cookie-parser": "^1.4.4",
"core-js": "^3.1.4",
"dotenv": "^8.0.0",
"exiftool-vendored": "^8.19.0",
diff --git a/api/src/index.js b/api/src/index.js
index ecf0f659..c23a0c24 100644
--- a/api/src/index.js
+++ b/api/src/index.js
@@ -3,7 +3,7 @@ import path from 'path'
import { ApolloServer } from 'apollo-server-express'
import express from 'express'
import bodyParser from 'body-parser'
-import cookieParser from 'cookie-parser'
+import cors from 'cors'
import { v1 as neo4j } from 'neo4j-driver'
import { makeAugmentedSchema } from 'neo4j-graphql-js'
import dotenv from 'dotenv'
@@ -18,7 +18,7 @@ dotenv.config()
const app = express()
app.use(bodyParser.json())
-app.use(cookieParser())
+app.use(cors())
/*
* Create an executable GraphQL schema object from GraphQL type definitions
@@ -137,7 +137,7 @@ server.applyMiddleware({ app, graphPath })
import loadImageRoutes from './routes/images'
-loadImageRoutes({ app, driver })
+loadImageRoutes({ app, driver, scanner })
const httpServer = http.createServer(app)
server.installSubscriptionHandlers(httpServer)
diff --git a/api/src/routes/images.js b/api/src/routes/images.js
index a915f025..8c514a14 100644
--- a/api/src/routes/images.js
+++ b/api/src/routes/images.js
@@ -3,21 +3,23 @@ import path from 'path'
import _ from 'lodash'
import config from '../config'
import { isRawImage, getImageCachePath } from '../scanner/utils'
-import { getUserFromToken } from '../token'
+import { getUserFromToken, getTokenFromBearer } from '../token'
-function loadImageRoutes({ app, driver }) {
+function loadImageRoutes({ app, driver, scanner }) {
app.use('/images/:id/:image', async function(req, res) {
const { id, image } = req.params
let user = null
try {
- const token = req.cookies.token
+ const token = getTokenFromBearer(req.headers.authorization)
user = await getUserFromToken(token, driver)
} catch (err) {
return res.status(401).send(err.message)
}
+ console.log('Getting image for user', user.username)
+
const session = driver.session()
const result = await session.run(
diff --git a/ui/Dockerfile b/ui/Dockerfile
index 5dd35680..11b3be8d 100644
--- a/ui/Dockerfile
+++ b/ui/Dockerfile
@@ -17,5 +17,6 @@ RUN npm run build
FROM nginx:stable
COPY --from=0 /app/dist /usr/share/nginx/html
+COPY ./docker-nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
\ No newline at end of file
diff --git a/ui/docker-nginx.conf b/ui/docker-nginx.conf
new file mode 100644
index 00000000..2ff0b308
--- /dev/null
+++ b/ui/docker-nginx.conf
@@ -0,0 +1,9 @@
+server {
+ listen 80 default_server;
+
+ root /usr/share/nginx/html;
+
+ location / {
+ try_files $uri /index.html;
+ }
+}
\ No newline at end of file
diff --git a/ui/package-lock.json b/ui/package-lock.json
index c19c2650..9067f6a6 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -81,12 +81,6 @@
"lodash": "^4.17.13",
"to-fast-properties": "^2.0.0"
}
- },
- "lodash": {
- "version": "4.17.15",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
- "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
- "dev": true
}
}
},
@@ -788,6 +782,18 @@
"@babel/helper-plugin-utils": "^7.0.0"
}
},
+ "@babel/plugin-transform-runtime": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.5.5.tgz",
+ "integrity": "sha512-6Xmeidsun5rkwnGfMOp6/z9nSzWpHFNVr2Jx7kwoq4mVatQfQx5S56drBgEHF+XQbKOdIaOiMIINvp/kAwMN+w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-imports": "^7.0.0",
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "resolve": "^1.8.1",
+ "semver": "^5.5.1"
+ }
+ },
"@babel/plugin-transform-shorthand-properties": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz",
@@ -2131,11 +2137,6 @@
"safe-buffer": "~5.1.1"
}
},
- "cookie": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
- "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
- },
"copy-descriptor": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
diff --git a/ui/package.json b/ui/package.json
index d3a1dad8..bbf34143 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -12,7 +12,6 @@
"apollo-link-http": "^1.5.15",
"apollo-link-ws": "^1.0.18",
"babel-plugin-styled-components": "^1.10.6",
- "cookie": "^0.4.0",
"graphql": "^14.2.1",
"graphql-tag": "^2.10.1",
"parcel-bundler": "^1.12.3",
@@ -38,6 +37,7 @@
},
"devDependencies": {
"@babel/core": "^7.5.5",
+ "@babel/plugin-transform-runtime": "^7.5.5",
"husky": "^3.0.2",
"isarray": "^2.0.5",
"lint-staged": "^9.2.1"
@@ -63,7 +63,8 @@
]
],
"plugins": [
- "babel-plugin-styled-components"
+ "babel-plugin-styled-components",
+ "@babel/plugin-transform-runtime"
]
},
"lint-staged": {
diff --git a/ui/src/Pages/AllAlbumsPage/AlbumBox.js b/ui/src/Pages/AllAlbumsPage/AlbumBox.js
index 8f6f2ecb..ee4b5fbe 100644
--- a/ui/src/Pages/AllAlbumsPage/AlbumBox.js
+++ b/ui/src/Pages/AllAlbumsPage/AlbumBox.js
@@ -1,6 +1,8 @@
import React from 'react'
import styled from 'styled-components'
import { Link } from 'react-router-dom'
+import ProtectedImage from '../../components/photoGallery/ProtectedImage'
+import { Icon } from 'semantic-ui-react'
const AlbumBoxLink = styled(Link)`
width: 240px;
@@ -10,16 +12,30 @@ const AlbumBoxLink = styled(Link)`
color: #222;
`
-const AlbumBoxImage = styled.div`
- width: 220px;
- height: 220px;
- margin: auto;
- border-radius: 4%;
- background-image: url('${props => props.image}');
- background-color: #eee;
- background-size: cover;
- background-position: center;
-`
+const AlbumBoxImage = ({ src, ...props }) => {
+ const Image = styled(ProtectedImage)`
+ width: 220px;
+ height: 220px;
+ margin: auto;
+ border-radius: 4%;
+ object-fit: cover;
+ object-position: center;
+ `
+
+ const Placeholder = styled.div`
+ width: 220px;
+ height: 220px;
+ border-radius: 4%;
+ margin: auto;
+ background: linear-gradient(#f7f7f7 0%, #eee 100%);
+ `
+
+ if (src) {
+ return
+ }
+
+ return
+}
export const AlbumBox = ({ album, ...props }) => {
if (!album) {
@@ -33,7 +49,7 @@ export const AlbumBox = ({ album, ...props }) => {
return (
{
- const StyledPhoto = styled(animated.img)`
+ const StyledPhoto = styled(animated(ProtectedImage))`
height: 200px;
min-width: 100%;
position: relative;
diff --git a/ui/src/components/photoGallery/PresentView.js b/ui/src/components/photoGallery/PresentView.js
index 1511fe69..bd7893d6 100644
--- a/ui/src/components/photoGallery/PresentView.js
+++ b/ui/src/components/photoGallery/PresentView.js
@@ -2,6 +2,7 @@ import React from 'react'
import styled, { createGlobalStyle } from 'styled-components'
import { Query } from 'react-apollo'
import gql from 'graphql-tag'
+import ProtectedImage from './ProtectedImage'
const PresentContainer = styled.div`
position: fixed;
@@ -34,7 +35,7 @@ const imageQuery = gql`
}
`
-const PresentImage = styled.img`
+const PresentImage = styled(ProtectedImage)`
width: 100vw;
height: 100vh;
object-fit: contain;
diff --git a/ui/src/components/photoGallery/ProtectedImage.js b/ui/src/components/photoGallery/ProtectedImage.js
new file mode 100644
index 00000000..857d0e5c
--- /dev/null
+++ b/ui/src/components/photoGallery/ProtectedImage.js
@@ -0,0 +1,49 @@
+import React, { useState, useEffect } from 'react'
+
+/**
+ * An image that needs a authorization header to load
+ */
+class ProtectedImage extends React.Component {
+ constructor(props) {
+ super(props)
+
+ this.state = {
+ imgSrc: null,
+ }
+
+ this.shouldRefresh = true
+ }
+
+ shouldComponentUpdate(newProps) {
+ if (newProps.src != this.props.src) this.shouldRefresh = true
+
+ return true
+ }
+
+ async fetchImage() {
+ if (this.props.src && this.shouldRefresh) {
+ this.shouldRefresh = false
+
+ let image = await fetch(this.props.src, {
+ headers: {
+ Authorization: `Bearer ${localStorage.getItem('token')}`,
+ },
+ })
+
+ image = await image.blob()
+ const url = URL.createObjectURL(image)
+
+ this.setState({
+ imgSrc: url,
+ loadedSrc: this.props.src,
+ })
+ }
+ }
+
+ render() {
+ this.fetchImage()
+ return
+ }
+}
+
+export default ProtectedImage
diff --git a/ui/src/components/sidebar/PhotoSidebar.js b/ui/src/components/sidebar/PhotoSidebar.js
index cf1761ca..24ca5282 100644
--- a/ui/src/components/sidebar/PhotoSidebar.js
+++ b/ui/src/components/sidebar/PhotoSidebar.js
@@ -4,6 +4,7 @@ import { Query } from 'react-apollo'
import gql from 'graphql-tag'
import { SidebarItem } from './SidebarItem'
import { Loader } from 'semantic-ui-react'
+import ProtectedImage from '../photoGallery/ProtectedImage'
const photoQuery = gql`
query sidebarPhoto($id: ID) {
@@ -43,7 +44,7 @@ const RightSidebar = styled.div`
border-left: 1px solid #eee;
`
-const PreviewImage = styled.img`
+const PreviewImage = styled(ProtectedImage)`
width: 100%;
height: 333px;
object-fit: contain;