diff --git a/.gitignore b/.gitignore index 2be7de69..a5777376 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,11 @@ # See https://help.github.com/ignore-files/ for more about ignoring files. /api/src/cache/ -/docker-compose.yml /photos_path +# docker +docker-compose.yml + # dependencies node_modules/ diff --git a/api/src/index.js b/api/src/index.js index 49340ff7..a90d146e 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -1,5 +1,5 @@ import { ApolloServer } from 'apollo-server-express' -import express from 'express' +import express, { Router } from 'express' import bodyParser from 'body-parser' import cors from 'cors' import { v1 as neo4j } from 'neo4j-driver' @@ -8,6 +8,7 @@ import PhotoScanner from './scanner/Scanner' import _ from 'lodash' import config from './config' import gql from 'graphql-tag' +import path from 'path' import { getUserFromToken, getTokenFromBearer } from './token' @@ -41,7 +42,8 @@ app.use((req, res, next) => { setInterval(scanner.scanAll, 1000 * 60 * 60 * 4) // Specify port and path for GraphQL endpoint -const graphPath = '/graphql' +const graphPath = new URL(path.join(config.host.toString(), '/graphql')) + .pathname app.use(graphPath, (req, res, next) => { if (req.body.query) { @@ -102,6 +104,7 @@ const server = new ApolloServer({ introspection: true, playground: !process.env.PRODUCTION, subscriptions: { + path: graphPath, onConnect: async (connectionParams, webSocket) => { const token = getTokenFromBearer(connectionParams.Authorization) const user = await getUserFromToken(token, driver) @@ -115,12 +118,15 @@ const server = new ApolloServer({ }) server.applyMiddleware({ app, path: graphPath }) +const router = new Router() import loadImageRoutes from './routes/images' import loadDownloadRoutes from './routes/downloads' -loadImageRoutes(app) -loadDownloadRoutes(app) +loadImageRoutes(router) +loadDownloadRoutes(router) + +app.use(config.host.pathname, router) const httpServer = http.createServer(app) server.installSubscriptionHandlers(httpServer) diff --git a/api/src/resolvers/photos.js b/api/src/resolvers/photos.js index 381072b5..0f551b83 100644 --- a/api/src/resolvers/photos.js +++ b/api/src/resolvers/photos.js @@ -1,4 +1,5 @@ import { cypherQuery } from 'neo4j-graphql-js' +import path from 'path' import config from '../config' function injectAt(query, index, injection) { @@ -126,7 +127,8 @@ const Query = { const urlResolve = { url(root, args, ctx, info) { - let url = new URL(root.url, config.host) + let url = new URL(path.join(config.host.href, root.url)) + if (ctx.shareToken) url.search = `?token=${ctx.shareToken}` return url.href }, diff --git a/api/src/routes/downloads.js b/api/src/routes/downloads.js index e18f16be..e74d56c2 100644 --- a/api/src/routes/downloads.js +++ b/api/src/routes/downloads.js @@ -18,9 +18,9 @@ async function sendDownload(req, res) { throw new RequestError(404, 'Image could not be found') } -const loadDownloadRoutes = app => { - app.use('/download/:id/:image', getImageFromRequest) - app.use('/download/:id/:image', sendDownload) +const loadDownloadRoutes = router => { + router.use('/download/:id/:image', getImageFromRequest) + router.use('/download/:id/:image', sendDownload) } export default loadDownloadRoutes diff --git a/api/src/routes/images.js b/api/src/routes/images.js index 49896998..ad1f2e3f 100644 --- a/api/src/routes/images.js +++ b/api/src/routes/images.js @@ -177,9 +177,9 @@ async function verifyShareToken({ shareToken, id, driver }) { } } -function loadImageRoutes(app) { - app.use('/images/:id/:image', getImageFromRequest) - app.use('/images/:id/:image', sendImage) +function loadImageRoutes(router) { + router.use('/images/:id/:image', getImageFromRequest) + router.use('/images/:id/:image', sendImage) } export default loadImageRoutes diff --git a/docker-compose.proxy-example.yml b/docker-compose.proxy-example.yml new file mode 100644 index 00000000..ae3f002b --- /dev/null +++ b/docker-compose.proxy-example.yml @@ -0,0 +1,57 @@ +version: '3' + +services: + neo4j: + build: ./docker/neo4j + expose: + - 7474 + - 7687 + environment: + - NEO4J_dbms_security_procedures_unrestricted=apoc.* + - NEO4J_apoc_import_file_enabled=true + - NEO4J_apoc_export_file_enabled=true + - NEO4J_dbms_shell_enabled=true + volumes: + - db_data:/data + + api: + build: ./api + expose: + - 80 + depends_on: + - neo4j + environment: + - NEO4J_URI=bolt://neo4j:7687 + - PHOTO_CACHE=/app/cache + # Change This: The publicly exposed url for the api + - API_ENDPOINT=http://localhost:8080/api + - GRAPHQL_LISTEN_PORT=80 + volumes: + # Change This: Link photo paths from the host machine + - ./photos_path:/photos + - api_cache:/app/cache + + ui: + build: + context: ./ui + args: + # Change This: The publicly exposed url for the graphql api + GRAPHQL_ENDPOINT: http://localhost:8080/api/graphql + expose: + - 80 + depends_on: + - api + + proxy: + image: nginx + volumes: + - ./docker/nginx-proxy/default.conf:/etc/nginx/conf.d/default.conf + ports: + - 8080:80 + depends_on: + - api + - ui + +volumes: + db_data: + api_cache: \ No newline at end of file diff --git a/docker/nginx-proxy/default.conf b/docker/nginx-proxy/default.conf new file mode 100644 index 00000000..37b35712 --- /dev/null +++ b/docker/nginx-proxy/default.conf @@ -0,0 +1,36 @@ +server { + listen 80 default_server; + + location /api { + proxy_pass http://api; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # Required for Websocket + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + #add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"; + client_max_body_size 0; + + access_log /var/log/nginx/photoview-api.access.log; + error_log /var/log/nginx/photoview-api.error.log; + } + + location / { + proxy_pass http://ui; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + #add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"; + client_max_body_size 0; + + access_log /var/log/nginx/photoview-ui.access.log; + error_log /var/log/nginx/photoview-ui.error.log; + } + +} \ No newline at end of file