First commit
This commit is contained in:
commit
b5d63d9ea6
29
.github/workflows/release.yaml
vendored
Normal file
29
.github/workflows/release.yaml
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [created]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
goreleaser:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
-
|
||||||
|
name: Set up Go
|
||||||
|
uses: actions/setup-go@v4
|
||||||
|
-
|
||||||
|
name: Run GoReleaser
|
||||||
|
uses: goreleaser/goreleaser-action@v5
|
||||||
|
with:
|
||||||
|
distribution: goreleaser
|
||||||
|
version: latest
|
||||||
|
args: release --clean
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
18
.github/workflows/test_golang.yaml
vendored
Normal file
18
.github/workflows/test_golang.yaml
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
name: Go package
|
||||||
|
|
||||||
|
on: [push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version: '1.21'
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: go build -v ./...
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
nginx-cache-purge
|
||||||
|
dist/
|
30
.goreleaser.yaml
Normal file
30
.goreleaser.yaml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# This is an example .goreleaser.yml file with some sensible defaults.
|
||||||
|
# Make sure to check the documentation at https://goreleaser.com
|
||||||
|
|
||||||
|
# The lines below are called `modelines`. See `:help modeline`
|
||||||
|
# Feel free to remove those if you don't want/need to use them.
|
||||||
|
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
|
||||||
|
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
|
||||||
|
|
||||||
|
version: 1
|
||||||
|
|
||||||
|
before:
|
||||||
|
hooks:
|
||||||
|
# You may remove this if you don't use go modules.
|
||||||
|
- go mod tidy
|
||||||
|
# you may remove this if you don't need go generate
|
||||||
|
- go generate ./...
|
||||||
|
|
||||||
|
builds:
|
||||||
|
- env:
|
||||||
|
- CGO_ENABLED=0
|
||||||
|
goos:
|
||||||
|
- linux
|
||||||
|
- darwin
|
||||||
|
|
||||||
|
archives:
|
||||||
|
- format: tar.gz
|
||||||
|
# this name template makes the OS and Arch compatible with the results of `uname`.
|
||||||
|
name_template: "{{ .ProjectName }}-{{ .Version }}.{{ .Os }}-{{ .Arch }}"
|
||||||
|
wrap_in_directory: true
|
||||||
|
strip_parent_binary_folder: false
|
19
LICENSE.txt
Normal file
19
LICENSE.txt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2023 Mr. Gecko's Media (James Coleman). http://mrgeckosmedia.com/
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
136
README.md
Normal file
136
README.md
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
# nginx-cache-purge
|
||||||
|
A tool to help purge Nginx cache. It can either run locally with the purge command, or run as a local unix service to allow for purging by Nginx http requests.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
You can install either by downloading the latest binary release, or by building.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
Building should be as simple as running:
|
||||||
|
```
|
||||||
|
go build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running as a service
|
||||||
|
If you want to run as a service to allow purge requests via http requests, you'll need to create a systemd service file and place it in `/etc/systemd/system/nginx-cache-purge.service`.
|
||||||
|
```
|
||||||
|
[Unit]
|
||||||
|
Description=Nginx Cache Purge
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
User=nginx
|
||||||
|
Group=nginx
|
||||||
|
RuntimeDirectory=nginx-cache-purge
|
||||||
|
PIDFile=/var/run/nginx-cache-purge/service.pid
|
||||||
|
ExecStart=/usr/local/bin/nginx-cache-purge server
|
||||||
|
Restart=always
|
||||||
|
RestartSec=3s
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then run the following to start the service:
|
||||||
|
```
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl start nginx-cache-purge.service
|
||||||
|
```
|
||||||
|
|
||||||
|
## Nginx config
|
||||||
|
If you want to purge via Nginx http requests, you'll need to add configuration to your Nginx config file.
|
||||||
|
|
||||||
|
### Map PURGE requests
|
||||||
|
```
|
||||||
|
http {
|
||||||
|
map $request_method $is_purge {
|
||||||
|
default 0;
|
||||||
|
PURGE 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy_cache_path /var/nginx/proxy_temp/cache levels=1:2 keys_zone=my_cache:10m;
|
||||||
|
proxy_cache_key $host$request_uri;
|
||||||
|
|
||||||
|
server {
|
||||||
|
location / {
|
||||||
|
if ($is_purge) {
|
||||||
|
proxy_pass http://unix:/var/run/nginx-cache-purge/http.sock;
|
||||||
|
rewrite ^ /?path=/var/nginx/proxy_temp/cache&key=$host$request_uri break;
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy_cache my_cache;
|
||||||
|
proxy_pass http://upstream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Auth via cookie
|
||||||
|
```
|
||||||
|
http {
|
||||||
|
map $cookie_purge_token $is_purge {
|
||||||
|
default 0;
|
||||||
|
nnCgKUx1p2bIABXR 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy_cache_path /var/nginx/proxy_temp/cache levels=1:2 keys_zone=my_cache:10m;
|
||||||
|
proxy_cache_key $host$request_uri;
|
||||||
|
|
||||||
|
server {
|
||||||
|
location / {
|
||||||
|
if ($is_purge) {
|
||||||
|
proxy_pass http://unix:/var/run/nginx-cache-purge/http.sock;
|
||||||
|
rewrite ^ /?path=/var/nginx/proxy_temp/cache&key=$host$request_uri break;
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy_cache my_cache;
|
||||||
|
proxy_pass http://upstream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Auth via header
|
||||||
|
```
|
||||||
|
http {
|
||||||
|
map $http_purge_token $is_purge {
|
||||||
|
default 0;
|
||||||
|
nnCgKUx1p2bIABXR 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy_cache_path /var/nginx/proxy_temp/cache levels=1:2 keys_zone=my_cache:10m;
|
||||||
|
proxy_cache_key $host$request_uri;
|
||||||
|
|
||||||
|
server {
|
||||||
|
location / {
|
||||||
|
if ($is_purge) {
|
||||||
|
proxy_pass http://unix:/var/run/nginx-cache-purge/http.sock;
|
||||||
|
rewrite ^ /?path=/var/nginx/proxy_temp/cache&key=$host$request_uri break;
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy_cache my_cache;
|
||||||
|
proxy_pass http://upstream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using IP whitelists
|
||||||
|
```
|
||||||
|
http {
|
||||||
|
proxy_cache_path /var/nginx/proxy_temp/cache levels=1:2 keys_zone=my_cache:10m;
|
||||||
|
proxy_cache_key $host$request_uri;
|
||||||
|
|
||||||
|
server {
|
||||||
|
location / {
|
||||||
|
proxy_cache my_cache;
|
||||||
|
proxy_pass http://upstream;
|
||||||
|
}
|
||||||
|
location ~ /purge(/.*) {
|
||||||
|
allow 127.0.0.1;
|
||||||
|
deny all;
|
||||||
|
proxy_pass http://unix:/var/run/nginx-cache-purge/http.sock;
|
||||||
|
rewrite ^ /?path=/var/nginx/proxy_temp/cache&key=$host$1 break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
41
flags.go
Normal file
41
flags.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/alecthomas/kong"
|
||||||
|
)
|
||||||
|
|
||||||
|
// When version is requested, print the version.
|
||||||
|
type VersionFlag bool
|
||||||
|
|
||||||
|
func (v VersionFlag) Decode(ctx *kong.DecodeContext) error { return nil }
|
||||||
|
func (v VersionFlag) IsBool() bool { return true }
|
||||||
|
func (v VersionFlag) BeforeApply(app *kong.Kong, vars kong.Vars) error {
|
||||||
|
fmt.Println(serviceName + ": " + serviceVersion)
|
||||||
|
app.Exit(0)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flags and or commands supplied to cli.
|
||||||
|
type Flags struct {
|
||||||
|
Version VersionFlag `name:"version" help:"Print version information and quit"`
|
||||||
|
|
||||||
|
Server ServerCmd `cmd:"" aliases:"s" default:"1" help:"Run the server"`
|
||||||
|
Purge PurgeCmd `cmd:"" aliases:"p" help:"Purge cache now"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the supplied flags and commands.
|
||||||
|
func (a *App) ParseFlags() *kong.Context {
|
||||||
|
app.flags = &Flags{}
|
||||||
|
|
||||||
|
ctx := kong.Parse(app.flags,
|
||||||
|
kong.Name(serviceName),
|
||||||
|
kong.Description(serviceDescription),
|
||||||
|
kong.UsageOnError(),
|
||||||
|
kong.ConfigureHelp(kong.HelpOptions{
|
||||||
|
Compact: true,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
return ctx
|
||||||
|
}
|
11
go.mod
Normal file
11
go.mod
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
module github.com/grmrgecko/nginx-cache-purge
|
||||||
|
|
||||||
|
go 1.22.5
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/alecthomas/kong v0.9.0
|
||||||
|
github.com/gobwas/glob v0.2.3
|
||||||
|
github.com/portmapping/go-reuse v0.0.3
|
||||||
|
)
|
||||||
|
|
||||||
|
require golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 // indirect
|
14
go.sum
Normal file
14
go.sum
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
github.com/alecthomas/assert/v2 v2.6.0 h1:o3WJwILtexrEUk3cUVal3oiQY2tfgr/FHWiz/v2n4FU=
|
||||||
|
github.com/alecthomas/assert/v2 v2.6.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||||
|
github.com/alecthomas/kong v0.9.0 h1:G5diXxc85KvoV2f0ZRVuMsi45IrBgx9zDNGNj165aPA=
|
||||||
|
github.com/alecthomas/kong v0.9.0/go.mod h1:Y47y5gKfHp1hDc7CH7OeXgLIpp+Q2m1Ni0L5s3bI8Os=
|
||||||
|
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
||||||
|
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||||
|
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||||
|
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||||
|
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||||
|
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||||
|
github.com/portmapping/go-reuse v0.0.3 h1:iY0JDxTTUaYopewHL0CLN5BqJ0BvDP48VzC2osPpkBQ=
|
||||||
|
github.com/portmapping/go-reuse v0.0.3/go.mod h1:xKeiOLrJpAUOineqiMEm1bpy6cq0vTdpoiebdRD45mo=
|
||||||
|
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 h1:5B6i6EAiSYyejWfvc5Rc9BbI3rzIsrrXfAQBWnYfn+w=
|
||||||
|
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
153
main.go
Normal file
153
main.go
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gobwas/glob"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Basic application info.
|
||||||
|
const (
|
||||||
|
serviceName = "nginx-cache-purge"
|
||||||
|
serviceDescription = "Tool to help purge Nginx cache "
|
||||||
|
serviceVersion = "0.1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// App structure to access global app variables.
|
||||||
|
type App struct {
|
||||||
|
flags *Flags
|
||||||
|
}
|
||||||
|
|
||||||
|
var app *App
|
||||||
|
|
||||||
|
// Function to purge nginx cache keys.
|
||||||
|
func (a *App) PurgeCache(CachePath string, Key string, ExcludeKeys []string) error {
|
||||||
|
// Key must be provided.
|
||||||
|
if len(Key) == 0 {
|
||||||
|
return fmt.Errorf("no key provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regex to determine if key is a glob pattern.
|
||||||
|
globRegex := regexp.MustCompile(`[\*?\[{]+`)
|
||||||
|
|
||||||
|
// Inline function to check if excludes contains a key.
|
||||||
|
keyIsExcluded := func(Key string) bool {
|
||||||
|
for _, exclude := range ExcludeKeys {
|
||||||
|
if globRegex.MatchString(exclude) {
|
||||||
|
g, err := glob.Compile(exclude)
|
||||||
|
if err != nil && g != nil && g.Match(Key) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if exclude == Key {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Confirm that the cache path exists.
|
||||||
|
if _, err := os.Stat(CachePath); err != nil {
|
||||||
|
return fmt.Errorf("cache directory error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the key is a wildcard. If its not, we should purge the key by hash.
|
||||||
|
if !globRegex.MatchString(Key) {
|
||||||
|
// If excluded, skip the key.
|
||||||
|
if keyIsExcluded(Key) {
|
||||||
|
fmt.Println("Key", Key, "is excluded, will not purge.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the hash of the key.
|
||||||
|
hash := md5.Sum([]byte(Key))
|
||||||
|
keyHash := hex.EncodeToString(hash[:])
|
||||||
|
|
||||||
|
// Find key in cache directory.
|
||||||
|
err := filepath.Walk(CachePath, func(filePath string, info os.FileInfo, err error) error {
|
||||||
|
// Do not tolerate errors.
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// We only care to look at files.
|
||||||
|
if info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// If this file matches our key hash then delete.
|
||||||
|
if info.Name() == keyHash {
|
||||||
|
fmt.Printf("Purging %s as it matches the key %s requested to be purged.\n", filePath, Key)
|
||||||
|
err := os.Remove(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// We're done, so lets stop the walk.
|
||||||
|
return filepath.SkipAll
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while scanning for file to purge: %s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// This is a wildcard, so we need to find all files that match it and delete them.
|
||||||
|
g, err := glob.Compile(Key)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while compiling glob: %s", err)
|
||||||
|
}
|
||||||
|
err = filepath.Walk(CachePath, func(filePath string, info os.FileInfo, err error) error {
|
||||||
|
// Do not tolerate errors.
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// We only care to look at files.
|
||||||
|
if info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the file to extract the key.
|
||||||
|
file, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
if strings.HasPrefix(line, "KEY: ") {
|
||||||
|
key := line[5:]
|
||||||
|
if g.Match(key) {
|
||||||
|
fmt.Printf("Purging %s as it matches the key %s requested to be purged.\n", filePath, Key)
|
||||||
|
err := os.Remove(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while scanning for file to purge: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main function to start the app.
|
||||||
|
func main() {
|
||||||
|
app = new(App)
|
||||||
|
ctx := app.ParseFlags()
|
||||||
|
|
||||||
|
// Run the command requested.
|
||||||
|
err := ctx.Run()
|
||||||
|
ctx.FatalIfErrorf(err)
|
||||||
|
}
|
13
purgeCmd.go
Normal file
13
purgeCmd.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// Purge command for CLI to purge cache keys.
|
||||||
|
type PurgeCmd struct {
|
||||||
|
CachePath string `arg:"" name:"cache-path" help:"Path to cache directory." type:"existingdir"`
|
||||||
|
Key string `arg:"" name:"key" help:"Cache key or wildcard match."`
|
||||||
|
ExcludeKeys []string `optional:"" name:"exclude-key" help:"Key to exclude, can be wild card and can add multiple excludes."`
|
||||||
|
}
|
||||||
|
|
||||||
|
// The purge command execution just runs the apps purge cache function.
|
||||||
|
func (a *PurgeCmd) Run() error {
|
||||||
|
return app.PurgeCache(a.CachePath, a.Key, a.ExcludeKeys)
|
||||||
|
}
|
74
serverCmd.go
Normal file
74
serverCmd.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The server command for the CLI to run the HTTP server.
|
||||||
|
type ServerCmd struct {
|
||||||
|
Socket string `help:"Socket path for HTTP communication." type:"path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle request.
|
||||||
|
func (a *ServerCmd) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
|
// Parse query parameters.
|
||||||
|
query := req.URL.Query()
|
||||||
|
cachePath := query.Get("path")
|
||||||
|
if cachePath == "" {
|
||||||
|
io.WriteString(w, "Need path parameter.")
|
||||||
|
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
key := query.Get("key")
|
||||||
|
if key == "" {
|
||||||
|
io.WriteString(w, "Need key parameter.")
|
||||||
|
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
excludes := query["exclude"]
|
||||||
|
|
||||||
|
// Purge cache.
|
||||||
|
err := app.PurgeCache(cachePath, key, excludes)
|
||||||
|
// If error, return error.
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error purging cache:", err)
|
||||||
|
io.WriteString(w, "Error occurred while processing purge.")
|
||||||
|
http.Error(w, http.StatusText(http.StatusBadGateway), http.StatusBadGateway)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Successful purge.
|
||||||
|
w.Write([]byte("PURGED"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the FastCGI server.
|
||||||
|
func (a *ServerCmd) Run() error {
|
||||||
|
// Determine UNIX socket path.
|
||||||
|
unixSocket := a.Socket
|
||||||
|
if unixSocket == "" {
|
||||||
|
unixSocket = "/var/run/nginx-cache-purge/http.sock"
|
||||||
|
}
|
||||||
|
|
||||||
|
// If socket exists, remove it.
|
||||||
|
if _, err := os.Stat(unixSocket); !os.IsNotExist(err) {
|
||||||
|
os.Remove(unixSocket)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the socket for FCGI communication.
|
||||||
|
listener, err := net.Listen("unix", unixSocket)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer listener.Close()
|
||||||
|
|
||||||
|
// Start the FastCGI server.
|
||||||
|
fmt.Println("Starting server at", unixSocket)
|
||||||
|
http.HandleFunc("/", a.ServeHTTP)
|
||||||
|
err = http.Serve(listener, nil)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user