1
Fork 0

Rewrite environment variable configurations

This commit is contained in:
viktorstrate 2020-04-06 22:16:25 +02:00
parent 48520a7d2d
commit b4676af3c1
13 changed files with 156 additions and 75 deletions

View File

@ -2,6 +2,8 @@
.gitignore
.prettierrc
.vscode
.env
example.env
photos_path
screenshots

View File

@ -1,8 +1,8 @@
# Build UI
FROM node:10 as ui
ARG GRAPHQL_ENDPOINT
ENV GRAPHQL_ENDPOINT=${GRAPHQL_ENDPOINT}
ARG API_ENDPOINT
ENV API_ENDPOINT=${API_ENDPOINT}
RUN mkdir -p /app
WORKDIR /app

View File

@ -2,9 +2,17 @@
MYSQL_URL=user:password@tcp(localhost)/dbname
API_ENDPOINT=http://localhost:4001/
API_LISTEN_IP=localhost
API_LISTEN_PORT=4001
PUBLIC_ENDPOINT=http://localhost:1234/
# The url from which the server can be accessed publicly
API_ENDPOINT=http://localhost:4001/
UI_ENDPOINT=http://localhost:1234/
# Set to 1 for the server to also serve the built static ui files
SERVE_UI=0
# When SERVE_UI is 1, PUBLIC_ENDPOINT is used instead of API_ENDPOINT and UI_ENDPOINT
#PUBLIC_ENDPOINT=http://localhost:4001/
# Set to 1 to set server in development mode, this enables graphql playground
# Remove this if running in production

View File

@ -2,10 +2,9 @@ package models
import (
"database/sql"
"log"
"net/url"
"os"
"path"
"github.com/viktorstrate/photoview/api/utils"
)
type Photo struct {
@ -66,18 +65,9 @@ func NewPhotosFromRows(rows *sql.Rows) ([]*Photo, error) {
func (p *PhotoURL) URL() string {
publicUrl := os.Getenv("PUBLIC_ENDPOINT")
if publicUrl == "" {
publicUrl = os.Getenv("API_ENDPOINT")
}
imageUrl := utils.ApiEndpointUrl()
imageUrl.Path = path.Join(imageUrl.Path, "photo", p.PhotoName)
imageUrl, err := url.Parse(publicUrl)
if err != nil {
log.Println("Endpoint url is not properly configured, make sure the PUBLIC_ENDPOINT AND API_ENDPOINT environment variables are set correctly")
return p.PhotoName
}
imageUrl.Path = path.Join(imageUrl.Path, "api", "photo", p.PhotoName)
return imageUrl.String()
}

View File

@ -3,7 +3,6 @@ package main
import (
"log"
"net/http"
"net/url"
"os"
"path"
"path/filepath"
@ -16,14 +15,13 @@ import (
"github.com/viktorstrate/photoview/api/graphql/auth"
"github.com/viktorstrate/photoview/api/routes"
"github.com/viktorstrate/photoview/api/server"
"github.com/viktorstrate/photoview/api/utils"
"github.com/99designs/gqlgen/handler"
photoview_graphql "github.com/viktorstrate/photoview/api/graphql"
"github.com/viktorstrate/photoview/api/graphql/resolvers"
)
const defaultPort = "4001"
// spaHandler implements the http.Handler interface, so we can use it
// to respond to HTTP requests. The path to the static directory and
// path to the index file within that static directory are used to
@ -98,23 +96,19 @@ func main() {
Directives: graphqlDirective,
}
endpointURL, err := url.Parse(os.Getenv("API_ENDPOINT"))
if err != nil {
log.Println("WARN: Environment variable API_ENDPOINT not specified")
endpointURL, _ = url.Parse("/")
}
apiListenUrl := utils.ApiListenUrl()
endpointRouter := rootRouter.PathPrefix(endpointURL.Path).Subrouter()
endpointRouter := rootRouter.PathPrefix(apiListenUrl.Path).Subrouter()
if devMode {
endpointRouter.Handle("/api", handler.Playground("GraphQL playground", path.Join(endpointURL.Path, "/graphql")))
endpointRouter.Handle("/", handler.Playground("GraphQL playground", path.Join(apiListenUrl.Path, "/graphql")))
} else {
endpointRouter.HandleFunc("/api", func(w http.ResponseWriter, req *http.Request) {
endpointRouter.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("photoview api endpoint"))
})
}
endpointRouter.Handle("/api/graphql",
endpointRouter.Handle("/graphql",
handler.GraphQL(photoview_graphql.NewExecutableSchema(graphqlConfig),
handler.IntrospectionEnabled(devMode),
handler.WebsocketUpgrader(server.WebsocketUpgrader(devMode)),
@ -122,22 +116,28 @@ func main() {
),
)
photoRouter := endpointRouter.PathPrefix("/api/photo").Subrouter()
photoRouter := endpointRouter.PathPrefix("/photo").Subrouter()
routes.RegisterPhotoRoutes(db, photoRouter)
spa := spaHandler{staticPath: "/ui", indexPath: "index.html"}
endpointRouter.PathPrefix("/").Handler(spa)
shouldServeUI := os.Getenv("SERVE_UI") == "1"
if devMode {
log.Printf("🚀 Graphql playground ready at %s\n", endpointURL.String())
} else {
log.Printf("Photoview API endpoint listening at %s\n", endpointURL.String())
publicEndpoint := os.Getenv("PUBLIC_ENDPOINT")
if publicEndpoint != "" && publicEndpoint != endpointURL.String() {
log.Printf("Photoview API public endpoint ready at %s\n", publicEndpoint)
}
if shouldServeUI {
spa := spaHandler{staticPath: "/ui", indexPath: "index.html"}
rootRouter.PathPrefix("/").Handler(spa)
}
log.Fatal(http.ListenAndServe(":"+endpointURL.Port(), rootRouter))
if devMode {
log.Printf("🚀 Graphql playground ready at %s\n", apiListenUrl.String())
} else {
log.Printf("Photoview API endpoint listening at %s\n", apiListenUrl.String())
uiEndpoint := utils.UiEndpointUrl()
apiEndpoint := utils.ApiEndpointUrl()
log.Printf("Photoview API public endpoint ready at %s\n", apiEndpoint.String())
log.Printf("Photoview UI public endpoint ready at %s\n", uiEndpoint.String())
}
log.Fatal(http.ListenAndServe(":"+apiListenUrl.Port(), rootRouter))
}

View File

@ -1,14 +1,12 @@
package server
import (
"log"
"net/http"
"net/url"
"os"
"path"
"strings"
"github.com/gorilla/mux"
"github.com/viktorstrate/photoview/api/utils"
)
func CORSMiddleware(devMode bool) mux.MiddlewareFunc {
@ -22,10 +20,7 @@ func CORSMiddleware(devMode bool) mux.MiddlewareFunc {
w.Header().Set("Access-Control-Allow-Headers", strings.Join(headers, ","))
w.Header().Set("Access-Control-Expose-Headers", "content-length")
endpoint, err := url.Parse(os.Getenv("API_ENDPOINT"))
if err != nil {
log.Fatalln("Could not parse API_ENDPOINT environment variable as url")
}
endpoint := utils.ApiEndpointUrl()
endpoint.Path = path.Join(endpoint.Path, "graphql")
if devMode {
@ -34,12 +29,8 @@ func CORSMiddleware(devMode bool) mux.MiddlewareFunc {
w.Header().Set("Vary", "Origin")
} else {
// Production environment
publicEndpoint, err := url.Parse(os.Getenv("PUBLIC_ENDPOINT"))
if err != nil {
log.Printf("Error parsing environment variable PUBLIC_ENDPOINT as url: %s", err)
} else {
w.Header().Set("Access-Control-Allow-Origin", publicEndpoint.Scheme+"://"+publicEndpoint.Host)
}
uiEndpoint := utils.UiEndpointUrl()
w.Header().Set("Access-Control-Allow-Origin", uiEndpoint.Scheme+"://"+uiEndpoint.Host)
}
if req.Method != http.MethodOptions {

View File

@ -4,9 +4,9 @@ import (
"log"
"net/http"
"net/url"
"os"
"github.com/gorilla/websocket"
"github.com/viktorstrate/photoview/api/utils"
)
func WebsocketUpgrader(devMode bool) websocket.Upgrader {
@ -15,11 +15,7 @@ func WebsocketUpgrader(devMode bool) websocket.Upgrader {
if devMode {
return true
} else {
pubEndpoint, err := url.Parse(os.Getenv("PUBLIC_ENDPOINT"))
if err != nil {
log.Printf("Could not parse API_ENDPOINT environment variable as url: %s", err)
return false
}
uiEndpoint := utils.UiEndpointUrl()
if r.Header.Get("origin") == "" {
return true
@ -31,10 +27,10 @@ func WebsocketUpgrader(devMode bool) websocket.Upgrader {
return false
}
if pubEndpoint.Host == originURL.Host {
if uiEndpoint.Host == originURL.Host {
return true
} else {
log.Printf("Not allowing websocket request from %s because it doesn't match PUBLIC_ENDPOINT %s", originURL.Host, pubEndpoint.Host)
log.Printf("Not allowing websocket request from %s because it doesn't match UI_ENDPOINT %s", originURL.Host, uiEndpoint.Host)
return false
}
}

82
api/utils/Endpoints.go Normal file
View File

@ -0,0 +1,82 @@
package utils
import (
"fmt"
"log"
"net/url"
"os"
"path"
"strconv"
)
func ApiListenUrl() *url.URL {
const defaultPort = "4001"
shouldServeUI := os.Getenv("SERVE_UI") == "1"
apiPrefix := "/"
if shouldServeUI {
apiPrefix = "/api"
}
var listenAddr string
listenAddr = os.Getenv("API_LISTEN_IP")
if listenAddr == "" {
listenAddr = "127.0.0.1"
}
listenPortStr := os.Getenv("API_LISTEN_PORT")
if listenPortStr == "" {
listenPortStr = defaultPort
}
listenPort, err := strconv.Atoi(listenPortStr)
if err != nil {
log.Fatalf("API_LISTEN_PORT must be a number: '%s'\n%s", listenPortStr, err)
}
apiUrl, err := url.Parse(fmt.Sprintf("http://%s:%d", listenAddr, listenPort))
if err != nil {
log.Fatalf("Could not format api url: %s", err)
}
apiUrl.Path = apiPrefix
return apiUrl
}
func ApiEndpointUrl() *url.URL {
apiEndpointStr := os.Getenv("API_ENDPOINT")
shouldServeUI := os.Getenv("SERVE_UI") == "1"
if shouldServeUI {
apiEndpointStr = os.Getenv("PUBLIC_ENDPOINT")
}
apiEndpointUrl, err := url.Parse(apiEndpointStr)
if err != nil {
log.Fatalf("ERROR: Environment variable API_ENDPOINT is not a proper url")
}
if shouldServeUI {
apiEndpointUrl.Path = path.Join(apiEndpointUrl.Path, "/api")
}
return apiEndpointUrl
}
func UiEndpointUrl() *url.URL {
uiEndpointStr := os.Getenv("UI_ENDPOINT")
shouldServeUI := os.Getenv("SERVE_UI") == "1"
if shouldServeUI {
uiEndpointStr = os.Getenv("PUBLIC_ENDPOINT")
}
uiEndpointUrl, err := url.Parse(uiEndpointStr)
if err != nil {
log.Fatalf("ERROR: Environment variable UI_ENDPOINT is not a proper url")
}
return uiEndpointUrl
}

View File

@ -16,7 +16,7 @@ services:
build:
context: "."
args:
- GRAPHQL_ENDPOINT=http://localhost:8000/api/graphql
- API_ENDPOINT=http://localhost:8000/api/
restart: always
ports:
@ -26,21 +26,24 @@ services:
environment:
- MYSQL_URL=photoview:photo-secret@tcp(db)/photoview
- API_LISTEN_IP=photoview
- API_LISTEN_PORT=80
- PHOTO_CACHE=/app/cache
- SERVE_UI=1
# Change This: The publicly exposed url for the api
# For example if the server is available from the domain example.com,
# change this value to http://example.com/api
- API_ENDPOINT=http://localhost:80/
# change this value to http://example.com/
- PUBLIC_ENDPOINT=http://localhost:8000/
volumes:
# Change This: Link photo paths from the host machine
- api_cache:/app/cache
# Change this to the directory where your photos are located on your server.
# If the photos are located at `/home/user/photos`, then change this value
# to the following: `/home/user/photos:/photos:ro`.
# You can mount multiple paths, if your photos are spread across multiple directories.
- ./photos_path:/photos:ro
- api_cache:/app/cache
volumes:
db_data:

View File

@ -1 +1 @@
GRAPHQL_ENDPOINT=http://localhost:4001/graphql
API_ENDPOINT=http://localhost:4001/

5
ui/package-lock.json generated
View File

@ -10996,6 +10996,11 @@
}
}
},
"url-join": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
"integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA=="
},
"use": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",

View File

@ -30,7 +30,8 @@
"semantic-ui-css": "^2.4.1",
"semantic-ui-react": "^0.88.0",
"styled-components": "^5.0.1",
"subscriptions-transport-ws": "^0.9.16"
"subscriptions-transport-ws": "^0.9.16",
"url-join": "^4.0.1"
},
"scripts": {
"start": "parcel start src/index.html",

View File

@ -7,17 +7,20 @@ import { setContext } from 'apollo-link-context'
import { ApolloLink, split } from 'apollo-link'
import { getMainDefinition } from 'apollo-utilities'
import { MessageState } from './components/messages/Messages'
import urlJoin from 'url-join'
const GRAPHQL_ENDPOINT = urlJoin(process.env.API_ENDPOINT, '/graphql')
const httpLink = new HttpLink({
uri: process.env.GRAPHQL_ENDPOINT,
uri: GRAPHQL_ENDPOINT,
credentials: 'same-origin',
})
console.log('GRAPHQL ENDPOINT', process.env.GRAPHQL_ENDPOINT)
console.log('API ENDPOINT', process.env.API_ENDPOINT)
const apiProtocol = new URL(process.env.GRAPHQL_ENDPOINT).protocol
const apiProtocol = new URL(process.env.API_ENDPOINT).protocol
let websocketUri = new URL(process.env.GRAPHQL_ENDPOINT)
let websocketUri = new URL(GRAPHQL_ENDPOINT)
websocketUri.protocol = apiProtocol === 'https:' ? 'wss:' : 'ws:'
const wsLink = new WebSocketLink({