Skip to content
This repository has been archived by the owner on Oct 3, 2022. It is now read-only.

Commit

Permalink
Merge branch 'dev/graceful-restarts'
Browse files Browse the repository at this point in the history
close #992
  • Loading branch information
bakape committed May 4, 2019
2 parents c57a37a + 3360821 commit 80c1b9a
Show file tree
Hide file tree
Showing 30 changed files with 521 additions and 643 deletions.
29 changes: 23 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,18 @@ Platforms: Linux, OSX, Win64
License: GNU AGPL

## Runtime dependencies

* [PostgresSQL](https://www.postgresql.org/download/) >= 10.0

## Building from source


### Native installation.

For installing meguca directly onto a server follow the steps bellow.
A reference list of commands can be found in `./docs/installation.md`

#### Build dependencies

* [Go](https://golang.org/doc/install) >=1.11 (for building server)
* [Node.js](https://nodejs.org) >=5.0 (for building client)
* C11 compiler
Expand All @@ -39,9 +40,11 @@ compiled with:
* git

#### Linux and OSX

* Run `make`

#### Windows

* Install [MSYS2](https://sourceforge.net/projects/msys2/)
* Open MSYS2 shell
* Install dependencies listed above with the `mingw-w64-x86_64-` prefix with
Expand All @@ -59,20 +62,34 @@ docker run -t -p 8000:8000 meguca
```

## Setup
* See `./meguca help` for server operation

### Deployment

meguca can be started in debug mode simply with `./meguca`.
Configurations are split between meguca instance configurations
and server instance configurations, which are required to start
the server and connect to the database.
The meguca instance configurations are stored in the database, but
server instance configurations are optionally loaded from a `config.json`
file on server start.
A sample configuration file can be found under `docs/config.json`.
Documentation for this file is available under `docs/config.jsonc`.

It is recommended to serve meguca behind a reverse proxy like NGINX or Apache
with properly configured TLS settings. A sample NGINX configuration file can be
found in `docs/`.

### Initial instance configuration

* Login into the "admin" account via the infinity symbol in the top banner with
the password "password"
* Change the default password
* Create a board from the administration panel
* Configure server from the administration panel
* To enable country flags on posts download and place `GeoLite2-Country.mmdb`
into the root directory
* To avoid having to always type in CLI flags on server start you can specify them in `config.json` file in the project root. A sample file with all the default settings can be found in `docs/`.

## Development

* See `./docs` for more documentation
* `./meguca` or `./meguca debug` run the server in development mode
* `make server` and `make client` build the server and client separately
* `make watch` watches the file system for changes and incrementally rebuilds
the client
Expand Down
48 changes: 17 additions & 31 deletions auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,13 @@ import (
"crypto/rand"
"encoding/base64"
"fmt"
"github.com/bakape/meguca/config"
"net"
"net/http"
"strings"

"golang.org/x/crypto/bcrypt"
)

var (
// IsReverseProxied specifies, if the server is deployed behind a reverse
// proxy.
IsReverseProxied bool
"github.com/bakape/meguca/config"

// ReverseProxyIP specifies the IP of a non-localhost reverse proxy. Used
// for filtering in XFF IP determination.
ReverseProxyIP string
"golang.org/x/crypto/bcrypt"
)

// IsBoard confirms the string is a valid board
Expand All @@ -45,31 +36,26 @@ func GetIP(r *http.Request) (string, error) {
}

func getIP(req *http.Request) string {
if IsReverseProxied {
for _, h := range [...]string{"X-Forwarded-For", "X-Real-Ip"} {
addresses := strings.Split(req.Header.Get(h), ",")

// March from right to left until we get a public address.
// That will be the address right before our reverse proxy.
for i := len(addresses) - 1; i >= 0; i-- {
// Header can contain padding spaces
ip := strings.TrimSpace(addresses[i])

// Filter the reverse proxy IPs
switch {
case ip == ReverseProxyIP:
case !net.ParseIP(ip).IsGlobalUnicast():
default:
return ip
}
var ip string
if config.Server.Server.ReverseProxied {
h := req.Header.Get("X-Forwarded-For")
if h != "" {
if i := strings.LastIndexByte(h, ','); i != -1 {
h = h[i+1:]
}

ip = strings.TrimSpace(h) // Header can contain padding spaces
}
}
ip, _, err := net.SplitHostPort(req.RemoteAddr)
if ip == "" {
ip = req.RemoteAddr
}

split, _, err := net.SplitHostPort(ip)
if err != nil {
return req.RemoteAddr // No port in address
return ip // No port in address
}
return ip
return split
}

// RandomID generates a randomID of base64 characters of desired byte length
Expand Down
26 changes: 12 additions & 14 deletions auth/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,40 +48,38 @@ func TestGetIP(t *testing.T) {
ip = "207.178.71.93"
reverseProxyIP = "162.30.251.246"
)
IsReverseProxied = true
ReverseProxyIP = reverseProxyIP
config.Server.Server.ReverseProxied = true

cases := [...]struct {
name, xff, out string
}{
{"valid XFF", "10.121.169.19", "10.121.169.19"},
{"no XFF", "", ip},
{"invalid XFF", "notip, nope", ip},
{
"hosted on localhost",
"105.124.243.122, 10.168.239.157, 127.0.0.1, ::1",
"10.168.239.157",
name: "valid XFF",
xff: "10.121.169.19",
out: "10.121.169.19",
},
{
"behind reverse proxy",
"105.124.243.122," + reverseProxyIP,
"105.124.243.122",
name: "no XFF",
out: ip,
},
}

for i := range cases {
c := cases[i]
t.Run(c.name, func(t *testing.T) {
t.Parallel()
// t.Parallel()

req := httptest.NewRequest("GET", "/", nil)
req.RemoteAddr = ip
if c.xff != "" {
req.Header.Set("X-Forwarded-For", c.xff)
}
if i, _ := GetIP(req); i != c.out {
LogUnexpected(t, c.out, ip)

res, err := GetIP(req)
if err != nil {
t.Fatal(err)
}
AssertEquals(t, res, c.out)
})
}
}
Expand Down
7 changes: 3 additions & 4 deletions auth/captcha.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ import (
"net/http/httptest"
"sync"

"github.com/bakape/meguca/common"
"github.com/bakape/meguca/config"

"github.com/bakape/captchouli"
captchouli_common "github.com/bakape/captchouli/common"
"github.com/bakape/meguca/common"
"github.com/bakape/meguca/config"
)

var (
Expand Down Expand Up @@ -112,7 +111,7 @@ func CaptchaService(board string) *captchouli.Service {
// This function blocks until all services are initialized.
func LoadCaptchaServices() (err error) {
conf := config.Get()
if !conf.Captcha || config.ImagerMode == config.NoImager {
if !conf.Captcha || config.Server.ImagerMode == config.NoImager {
return
}

Expand Down
2 changes: 2 additions & 0 deletions cache/getters_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"
"time"

"github.com/bakape/meguca/config"
. "github.com/bakape/meguca/test"
)

Expand Down Expand Up @@ -94,6 +95,7 @@ func TestGetHTML(t *testing.T) {

func TestCounterExpiry(t *testing.T) {
Clear()
config.Server.CacheSize = 128 * (1 << 20)

var counterChecks, fetches int
f := FrontEnd{
Expand Down
7 changes: 3 additions & 4 deletions cache/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"container/list"
"sync"
"time"

"github.com/bakape/meguca/config"
)

// Time for the cache to expire and need counter comparison
Expand All @@ -16,9 +18,6 @@ var (
ll = list.New()
totalUsed int
mu sync.Mutex

// Size sets the maximum size of the cache before evicting unread data in MB
Size float64 = 1 << 7
)

// Key stores the ID of either a thread or board page
Expand Down Expand Up @@ -82,7 +81,7 @@ func updateUsedSize(k Key, delta int) {
}
totalUsed += delta

for totalUsed > int(Size)*(1<<20) {
for totalUsed > int(config.Server.CacheSize)*(1<<20) {
if last := ll.Back(); last != nil {
removeEntry(last)
}
Expand Down
9 changes: 5 additions & 4 deletions cache/main_test.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
package cache

import (
"github.com/bakape/meguca/test"
"sync"
"testing"

"time"

"github.com/bakape/meguca/config"
"github.com/bakape/meguca/test"
)

// Basic test for deadlocks
func TestConcurrency(t *testing.T) {
Clear()
Size = 1
config.Server.CacheSize = 1

f := FrontEnd{
GetCounter: func(k Key) (uint64, error) {
Expand Down Expand Up @@ -43,7 +44,7 @@ func TestConcurrency(t *testing.T) {
func TestCacheEviction(t *testing.T) {
Clear()

Size = 0.005
config.Server.CacheSize = 0.005
f := FrontEnd{
GetCounter: func(k Key) (uint64, error) {
return 1, nil
Expand Down
17 changes: 0 additions & 17 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,6 @@ import (
"github.com/bakape/meguca/util"
)

// ImagerModeType is the imager functionality setting for this meguca process
type ImagerModeType int

const (
// IntegratedImager is regular and imager functionality both handled by this process
IntegratedImager ImagerModeType = iota

// NoImager is imager functionality not handled by this process
NoImager

// ImagerOnly is only imager functionality handled by this process
ImagerOnly
)

// ImagerMode is imager functionality setting for this meguca process
var ImagerMode ImagerModeType

var (
// Ensures no reads happen, while the configuration is reloading
globalMu, boardMu sync.RWMutex
Expand Down
Loading

0 comments on commit 80c1b9a

Please sign in to comment.