Skip to content

Commit

Permalink
feat(): simplify self-hosting setup
Browse files Browse the repository at this point in the history
- serve UI
- UI runtime configuration
- defaults set to self hosting values
  • Loading branch information
ncarlier committed May 27, 2023
1 parent befb51d commit ca1adbe
Show file tree
Hide file tree
Showing 32 changed files with 176 additions and 208 deletions.
2 changes: 1 addition & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
release/
ui/
node_modules
docs/
landing/
.env
Expand Down
32 changes: 28 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,26 @@
#########################################
# Build stage
# Build frontend stage
#########################################
FROM golang:1.19 AS builder
FROM node:lts-alpine AS frontend-builder

# Setup env
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
ENV PATH /usr/src/app/node_modules/.bin:$PATH

# Install dependencies
COPY ui/package.json /usr/src/app/package.json
COPY ui/package-lock.json /usr/src/app/package-lock.json
RUN npm install --silent --legacy-peer-deps

# Build website
COPY ./ui /usr/src/app
RUN npm run build

#########################################
# Build backend stage
#########################################
FROM golang:1.19 AS backend-builder

# Repository location
ARG REPOSITORY=github.com/ncarlier
Expand Down Expand Up @@ -29,15 +48,20 @@ ARG REPOSITORY=github.com/ncarlier
# Artifact name
ARG ARTIFACT=readflow

# Install binary
COPY --from=builder /go/src/$REPOSITORY/$ARTIFACT/release/$ARTIFACT /usr/local/bin/$ARTIFACT
# Install backend binary
COPY --from=backend-builder /go/src/$REPOSITORY/$ARTIFACT/release/$ARTIFACT /usr/local/bin/$ARTIFACT
# Install frontend assets
COPY --from=frontend-builder /usr/src/app/build /var/local/html

# Add configuration file
ADD ./pkg/config/readflow.toml /etc/readflow.toml

# Set configuration file
ENV READFLOW_CONFIG /etc/readflow.toml

# Serve UI
ENV READFLOW_UI /var/local/html

# Exposed ports
EXPOSE 8080 9090

Expand Down
51 changes: 6 additions & 45 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,26 +1,6 @@
version: "3"

services:
#######################################
# Reverse proxy (Traefik)
#######################################
traefik:
image: traefik:2.1
command: >
--providers.docker=true
--api.dashboard=true
--api.insecure=true
--log.level=INFO
--entryPoints.web.address=:80
restart: always
ports:
- "${PORT:-3000}:80"
- "${RPROXY_PORT:-8080}:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
labels:
- traefik.enable=false

########################################
# PostgreSQL
########################################
Expand All @@ -33,8 +13,6 @@ services:
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-secret}
volumes:
- db-data:/var/lib/postgresql/data
labels:
- "traefik.enable=false"

#######################################
# Imaginary: Image proxy
Expand All @@ -49,40 +27,23 @@ services:
options:
max-size: "1m"
restart: always
labels:
- "traefik.enable=false"

########################################
# API
# readflow
########################################
api:
build: .
readflow:
#build: .
image: "ncarlier/readflow:latest"
restart: always
depends_on:
- db
ports:
- "${PORT:-8080}:8080"
environment:
- READFLOW_DB=postgres://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-secret}@db/${POSTGRES_DB:-readflow}?sslmode=disable
- READFLOW_LISTEN_METRICS=:9090
- READFLOW_IMAGE_PROXY_URL=http://imaginary:9000
labels:
- "traefik.http.services.api.loadbalancer.server.port=8080"
- "traefik.http.routers.api.priority=2"
- "traefik.http.routers.api.rule=PathPrefix(`/api`)"
- "traefik.http.routers.api.middlewares=api-stripprefix@docker"
- "traefik.http.middlewares.api-stripprefix.stripprefix.prefixes=/api"

########################################
# Webapp
########################################
app:
build: ./ui/
image: "ncarlier/readflow-app:latest"
restart: always
labels:
- "traefik.http.services.webapp.loadbalancer.server.port=80"
- "traefik.http.routers.webapp.priority=1"
- "traefik.http.routers.webapp.rule=PathPrefix(`/`)"
- READFLOW_AUTHN=mock

networks:
default:
Expand Down
33 changes: 5 additions & 28 deletions pkg/api/index.go
Original file line number Diff line number Diff line change
@@ -1,40 +1,17 @@
package api

import (
"encoding/json"
"net/http"

"github.com/ncarlier/readflow/pkg/config"
"github.com/ncarlier/readflow/pkg/service"
"github.com/ncarlier/readflow/pkg/version"
"github.com/rs/zerolog/log"
)

// Info API informations model structure.
type Info struct {
Version string `json:"version"`
Authority string `json:"authority"`
VAPID string `json:"vapid"`
}

// index is the handler to show API details.
func index(conf *config.Config) http.Handler {
v := Info{
Version: version.Version,
Authority: conf.Global.AuthN,
VAPID: service.Lookup().GetProperties().VAPIDPublicKey,
if conf.Global.UILocation != "" {
log.Debug().Str("location", conf.Global.UILocation).Msg("serving UI")
return http.FileServer(http.Dir(conf.Global.UILocation))
}

return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.NotFound(w, r)
return
}
data, err := json.Marshal(v)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(data)
})
return http.RedirectHandler("/info", http.StatusSeeOther)
}
36 changes: 36 additions & 0 deletions pkg/api/info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package api

import (
"encoding/json"
"net/http"

"github.com/ncarlier/readflow/pkg/config"
"github.com/ncarlier/readflow/pkg/service"
"github.com/ncarlier/readflow/pkg/version"
)

// Info API informations model structure.
type Info struct {
Version string `json:"version"`
Authority string `json:"authority"`
VAPID string `json:"vapid"`
}

// info is the handler to show API details.
func info(conf *config.Config) http.Handler {
v := Info{
Version: version.Version,
Authority: conf.Global.AuthN,
VAPID: service.Lookup().GetProperties().VAPIDPublicKey,
}

return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
data, err := json.Marshal(v)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(data)
})
}
28 changes: 17 additions & 11 deletions pkg/api/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,77 +35,83 @@ func routes(conf *config.Config) Routes {
route(
"/",
index(conf),
middleware.Methods("GET"),
middleware.Methods(http.MethodGet),
middleware.Cors("*"),
),
route(
"/info",
info(conf),
middleware.Methods(http.MethodGet),
middleware.Cors(origin),
),
route(
"/articles",
articles(),
middleware.RateLimiting("webhook", conf.RateLimiting.Webhook),
middleware.IncomingWebhookAuth,
middleware.Methods("POST"),
middleware.Methods(http.MethodPost),
middleware.Cors("*"),
),
route(
"/articles/",
download(),
authnMiddleware,
middleware.Methods("GET"),
middleware.Methods(http.MethodGet),
middleware.Cors(origin),
),
route(
"/graphql",
graphqlHandler(),
authnMiddleware,
middleware.Methods("GET", "POST"),
middleware.Methods(http.MethodGet, http.MethodPost),
middleware.Cors(origin),
),
route(
"/linking/",
linking(conf),
authnMiddleware,
middleware.Methods("GET"),
middleware.Methods(http.MethodGet),
middleware.Cors(origin),
),
route(
"/admin",
adminHandler(),
middleware.IsAdmin,
authnMiddleware,
middleware.Methods("GET", "POST"),
middleware.Methods(http.MethodGet, http.MethodPost),
middleware.Cors(origin),
),
route(
"/img",
imgProxyHandler(conf),
middleware.Methods("GET"),
middleware.Methods(http.MethodGet),
middleware.Cors(origin),
),
route(
"/qr",
qrcodeHandler(conf),
authnMiddleware,
middleware.Methods("GET"),
middleware.Methods(http.MethodGet),
middleware.Cors(origin),
),
route(
"/avatar/",
avatarHandler(conf),
middleware.Methods("GET"),
middleware.Methods(http.MethodGet),
middleware.Cors(origin),
),
route(
"/healthz",
healthz(),
middleware.Methods("GET"),
middleware.Methods(http.MethodGet, http.MethodHead),
middleware.Cors("*"),
),
route(
"/varz",
varz(),
middleware.IsAdmin,
authnMiddleware,
middleware.Methods("GET"),
middleware.Methods(http.MethodGet),
middleware.Cors(origin),
),
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/config/readflow.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ metrics_listen_addr = "${READFLOW_LISTEN_METRICS}"
# Default: "https://login.nunux.org/auth/realms/readflow"
authn = "${READFLOW_AUTHN}"

## UI location, deactivated if empty
# Example: "/var/local/html"
ui = "${READFLOW_UI}"

## Public URL
# Default: "https://readflow.app"
public_url = "${READFLOW_PUBLIC_URL}"
Expand Down
1 change: 1 addition & 0 deletions pkg/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type GlobalConfig struct {
ListenAddr string `toml:"listen_addr"`
MetricsListenAddr string `toml:"metrics_listen_addr"`
PublicURL string `toml:"public_url"`
UILocation string `toml:"ui"`
SecretSalt string `toml:"secret_salt"`
BlockList string `toml:"block_list"`
}
Expand Down
38 changes: 0 additions & 38 deletions ui/Dockerfile

This file was deleted.

Loading

0 comments on commit ca1adbe

Please sign in to comment.