diff --git a/api/.env b/api/.env index dd906d00..8c6b2726 100644 --- a/api/.env +++ b/api/.env @@ -1,8 +1,8 @@ NEO4J_URI=bolt://localhost:7687 NEO4J_USER=neo4j NEO4J_PASSWORD=letmein -GRAPHQL_LISTEN_PORT=4001 -GRAPHQL_URI=http://localhost:4001/graphql -HOST=http://localhost:4001 -JWT_SECRET=topSecretEpicJWTKEYThing \ No newline at end of file +GRAPHQL_LISTEN_PORT=4001 +GRAPHQL_LISTEN_HOST=http://localhost/ + +JWT_SECRET=You-sh0uld_Change-Th1s \ No newline at end of file diff --git a/api/src/config.js b/api/src/config.js index d708f693..02b9dc3c 100644 --- a/api/src/config.js +++ b/api/src/config.js @@ -2,5 +2,5 @@ import path from 'path' export default { cachePath: path.resolve(__dirname, 'cache'), - host: process.env.HOST || 'http://localhost:4001/', + host: new URL(process.env.GRAPHQL_LISTEN_HOST || 'http://localhost:4001/'), } diff --git a/api/src/index.js b/api/src/index.js index 3b49dcba..ecf0f659 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -10,7 +10,6 @@ import dotenv from 'dotenv' import http from 'http' import PhotoScanner from './scanner/Scanner' import _ from 'lodash' -import config from './config' import { getUserFromToken, getTokenFromBearer } from './token' @@ -39,7 +38,6 @@ import usersResolver from './resolvers/users' import scannerResolver from './resolvers/scanner' import photosResolver from './resolvers/photos' import siteInfoResolver from './resolvers/siteInfo' -import { isRawImage, getImageCachePath } from './scanner/utils' const resolvers = [ usersResolver, @@ -87,9 +85,13 @@ const scanner = new PhotoScanner(driver) setInterval(scanner.scanAll, 1000 * 60 * 60 * 4) // Specify port and path for GraphQL endpoint -const port = process.env.GRAPHQL_LISTEN_PORT || 4001 const graphPath = '/graphql' +const endpointUrl = new URL( + process.env.GRAPHQL_LISTEN_HOST || 'http://localhost/' +) +endpointUrl.port = process.env.GRAPHQL_LISTEN_PORT || 4001 + /* * Create a new ApolloServer instance, serving the GraphQL schema * created using makeAugmentedSchema above and injecting the Neo4j driver @@ -112,7 +114,7 @@ const server = new ApolloServer({ scanner, user, token, - endpoint: `http://localhost:${port}`, + endpoint: endpointUrl.toString(), } }, schema: schema, @@ -133,82 +135,25 @@ const server = new ApolloServer({ server.applyMiddleware({ app, graphPath }) -app.use('/images/:id/:image', async function(req, res) { - const { id, image } = req.params +import loadImageRoutes from './routes/images' - let user = null - - try { - const token = req.cookies.token - user = await getUserFromToken(token, driver) - } catch (err) { - return res.status(401).send(err.message) - } - - const session = driver.session() - - const result = await session.run( - 'MATCH (p:Photo { id: {id} })<-[:CONTAINS]-(a:Album)<-[:OWNS]-(u:User) RETURN p as photo, u.id as userId, a.id as albumId', - { - id, - } - ) - - if (result.records.length == 0) { - return res.status(404).send(`Image not found`) - } - - const userId = result.records[0].get('userId') - const albumId = result.records[0].get('albumId') - const photo = result.records[0].get('photo').properties - - if (userId != user.id) { - return res.status(401).send(`Image not owned by you`) - } - - session.close() - - let imagePath = path.resolve(getImageCachePath(id, albumId), image) - - if (!(await fs.exists(imagePath))) { - if (image == 'thumbnail.jpg') { - console.log('Thumbnail not found, generating', photo.path) - await scanner.processImage(id) - - if (!(await fs.exists(imagePath))) { - throw new Error('Thumbnail not found after image processing') - } - - return res.sendFile(imagePath) - } - - imagePath = photo.path - } - - if (await isRawImage(imagePath)) { - console.log('RAW preview image not found, generating', imagePath) - await scanner.processImage(id) - - imagePath = path.resolve(config.cachePath, 'images', id, image) - - if (!(await fs.exists(imagePath))) { - throw new Error('RAW preview not found after image processing') - } - - return res.sendFile(imagePath) - } - - res.sendFile(imagePath) -}) +loadImageRoutes({ app, driver }) const httpServer = http.createServer(app) server.installSubscriptionHandlers(httpServer) -httpServer.listen({ port, graphPath }, () => { +httpServer.listen({ port: endpointUrl.port, graphPath }, () => { console.log( - `🚀 GraphQL endpoint ready at http://localhost:${port}${server.graphqlPath}` + `🚀 GraphQL endpoint ready at ${new URL(server.graphqlPath, endpointUrl)}` ) + + let subscriptionUrl = endpointUrl + subscriptionUrl.protocol = 'ws' + console.log( - `🚀 Subscriptions ready at ws://localhost:${port}${server.subscriptionsPath}` + `🚀 Subscriptions ready at ${new URL( + server.subscriptionsPath, + endpointUrl + )}` ) }) diff --git a/api/src/routes/images.js b/api/src/routes/images.js new file mode 100644 index 00000000..a915f025 --- /dev/null +++ b/api/src/routes/images.js @@ -0,0 +1,78 @@ +import fs from 'fs-extra' +import path from 'path' +import _ from 'lodash' +import config from '../config' +import { isRawImage, getImageCachePath } from '../scanner/utils' +import { getUserFromToken } from '../token' + +function loadImageRoutes({ app, driver }) { + app.use('/images/:id/:image', async function(req, res) { + const { id, image } = req.params + + let user = null + + try { + const token = req.cookies.token + user = await getUserFromToken(token, driver) + } catch (err) { + return res.status(401).send(err.message) + } + + const session = driver.session() + + const result = await session.run( + 'MATCH (p:Photo { id: {id} })<-[:CONTAINS]-(a:Album)<-[:OWNS]-(u:User) RETURN p as photo, u.id as userId, a.id as albumId', + { + id, + } + ) + + if (result.records.length == 0) { + return res.status(404).send(`Image not found`) + } + + const userId = result.records[0].get('userId') + const albumId = result.records[0].get('albumId') + const photo = result.records[0].get('photo').properties + + if (userId != user.id) { + return res.status(401).send(`Image not owned by you`) + } + + session.close() + + let imagePath = path.resolve(getImageCachePath(id, albumId), image) + + if (!(await fs.exists(imagePath))) { + if (image == 'thumbnail.jpg') { + console.log('Thumbnail not found, generating', photo.path) + await scanner.processImage(id) + + if (!(await fs.exists(imagePath))) { + throw new Error('Thumbnail not found after image processing') + } + + return res.sendFile(imagePath) + } + + imagePath = photo.path + } + + if (await isRawImage(imagePath)) { + console.log('RAW preview image not found, generating', imagePath) + await scanner.processImage(id) + + imagePath = path.resolve(config.cachePath, 'images', id, image) + + if (!(await fs.exists(imagePath))) { + throw new Error('RAW preview not found after image processing') + } + + return res.sendFile(imagePath) + } + + res.sendFile(imagePath) + }) +} + +export default loadImageRoutes diff --git a/docker-compose.example.yml b/docker-compose.example.yml index bdd9bf59..cb9c88a6 100644 --- a/docker-compose.example.yml +++ b/docker-compose.example.yml @@ -20,6 +20,9 @@ services: - neo4j environment: - NEO4J_URI=bolt://neo4j:7687 + # Change This: The publicly exposed url for the api + - GRAPHQL_LISTEN_HOST=http://localhost/ + - GRAPHQL_LISTEN_PORT=4001 volumes: # Change This: Link photo paths from the host machine - ./photos_path:/photos @@ -28,8 +31,8 @@ services: build: context: ./ui args: - # Change This: The publicly exposed url for the api - endpoint: http://localhost:4001/graphql + # Change This: The publicly exposed url for the graphql api + GRAPHQL_ENDPOINT: http://localhost:4001/graphql ports: - 3000:80 depends_on: diff --git a/ui/Dockerfile b/ui/Dockerfile index 50b55da6..5dd35680 100644 --- a/ui/Dockerfile +++ b/ui/Dockerfile @@ -1,8 +1,8 @@ # Build the app FROM node:10 -ARG endpoint -ENV REACT_APP_GRAPHQL_URI=${endpoint} +ARG GRAPHQL_ENDPOINT +ENV REACT_APP_GRAPHQL_URI=${GRAPHQL_ENDPOINT} RUN mkdir -p /app WORKDIR /app