Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,12 @@ jobs:

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5.5.1
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
# set latest tag for master branch
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'master') }}


- name: Build and push Docker image
uses: docker/build-push-action@v6.0.0
uses: docker/build-push-action@v6
with:
context: .
push: true
Expand Down
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ RUN go mod download

COPY *.go ./
COPY internal ./internal/
COPY handlers ./handlers/

RUN go build -o /goboard

Expand Down
52 changes: 20 additions & 32 deletions goboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@ import (

"github.com/gorilla/handlers"
"github.com/gorilla/mux"
bolt "go.etcd.io/bbolt"
"go.etcd.io/bbolt"
"gopkg.in/yaml.v2"

admin "github.com/dguihal/goboard/handlers/admin"
backend "github.com/dguihal/goboard/handlers/backend"
template "github.com/dguihal/goboard/handlers/template"
user "github.com/dguihal/goboard/handlers/user"
)

const goBoardVer = 0.03
const goBoardVer = "0.0.4"

// Config holds the configuration of the process
type Config struct {
Expand All @@ -35,23 +40,6 @@ type Config struct {
AdminToken string `yaml:"AdminToken"`
}

// RESTEndpointHandler defines a handler function for a REST Endpoint
type RESTEndpointHandler func(http.ResponseWriter, *http.Request)

// SupportedOp Defines a REST endpoint with its path, method and endpoint
type SupportedOp struct {
PathBase string
RestPath string
Method string
handler RESTEndpointHandler
}

// GoBoardHandler Base Class for endpoint handlers
type GoBoardHandler struct {
Db *bolt.DB
supportedOps []SupportedOp
}

// Command line arguments management
var configFilePath string
var showHelp bool
Expand Down Expand Up @@ -120,7 +108,7 @@ func main() {
}

// Open database
db, err := bolt.Open(config.GoBoardDBFile, config.GoBoardDBFileMode, &bolt.Options{Timeout: 1 * time.Second})
db, err := bbolt.Open(config.GoBoardDBFile, config.GoBoardDBFileMode, &bbolt.Options{Timeout: 1 * time.Second})
if err != nil {
log.Fatalf("error: %v", err)
}
Expand All @@ -130,27 +118,27 @@ func main() {
r := mainRouter

// Backend operations
backendHandler := NewBackendHandler(config.MaxHistorySize, config.BackendTimeZone)
backendHandler := backend.NewBackendHandler(config.MaxHistorySize, config.BackendTimeZone)
backendHandler.Db = db
for _, op := range backendHandler.supportedOps {
for _, op := range backendHandler.SupportedOps {
r.Handle(op.RestPath, backendHandler).Methods(op.Method)
}

// User operations
userHandler := NewUserHandler(config.CookieDuration)
userHandler := user.NewUserHandler(config.CookieDuration)
userHandler.Db = db
for _, op := range userHandler.supportedOps {
for _, op := range userHandler.SupportedOps {
r.Handle(op.RestPath, userHandler).Methods(op.Method)
}

// Admin operations
adminHandler := NewAdminHandler(config.AdminToken)
adminHandler := admin.NewAdminHandler(config.AdminToken)
adminHandler.Db = db
for _, op := range adminHandler.supportedOps {
for _, op := range adminHandler.SupportedOps {
r.Handle(op.RestPath, adminHandler).Methods(op.Method)
}

templateHandler := NewTemplateHandler()
templateHandler := template.NewTemplateHandler()

// Swagger operations
if len(config.SwaggerPath) > 0 {
Expand All @@ -164,7 +152,7 @@ func main() {
} else {
templateHandler.SetSwaggerBaseDir(realPath)
swaggerOp := templateHandler.GetSwaggerOp()
r.HandleFunc(swaggerOp.RestPath, swaggerOp.handler).Methods(swaggerOp.Method)
r.HandleFunc(swaggerOp.RestPath, swaggerOp.Handler).Methods(swaggerOp.Method)
r.PathPrefix("/swagger/").Handler(http.StripPrefix("/swagger/", http.FileServer(http.Dir(realPath))))
r.Handle("/swagger", http.RedirectHandler("/swagger/", http.StatusMovedPermanently))

Expand All @@ -181,14 +169,14 @@ func main() {
} else if _, err := os.Stat(strings.Join([]string{realPath, "/index.html"}, "")); os.IsNotExist(err) {
log.Println(strings.Join([]string{realPath, "/index.html"}, ""), "Not found: Disabling webui capabilities")
} else {
templateHandler.setWebUIBaseDir(realPath)
templateHandler.SetWebUIBaseDir(realPath)
r.PathPrefix("/webui/").Handler(http.StripPrefix("/webui/", http.FileServer(http.Dir(realPath))))
r.Handle("/webui", http.RedirectHandler("/webui/", http.StatusMovedPermanently))
r.Handle("/", http.RedirectHandler("/webui/", http.StatusMovedPermanently))
}
}

fmt.Println("GoBoard version ", goBoardVer, " starting on port", config.ListenPort)
fmt.Println("GoBoard version", goBoardVer, "starting on port", config.ListenPort)

var handler http.Handler
if fiAccessLog != nil {
Expand Down Expand Up @@ -228,7 +216,7 @@ func main() {
//https://github.com/golang/go/wiki/LearnServerProgramming
//https://astaxie.gitbooks.io/build-web-application-with-golang/

//https://github.com/boltdb/bolt : Backend
//https://github.com/skyec/boltdb-server/blob/master/server.go
//https://github.com/etcd-io/bbolt : Backend
//https://github.com/skyec/boltdb-server : Example

//https://blog.golang.org/error-handling-and-go
17 changes: 9 additions & 8 deletions adminhandler.go → handlers/admin/adminhandler.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package main
package admin

import (
"encoding/json"
Expand All @@ -8,6 +8,7 @@ import (
"strconv"
"strings"

"github.com/dguihal/goboard/handlers"
goboardbackend "github.com/dguihal/goboard/internal/backend"
goboardcookie "github.com/dguihal/goboard/internal/cookie"
goboarduser "github.com/dguihal/goboard/internal/user"
Expand All @@ -19,7 +20,7 @@ const tokenWarnLen int = 12

// AdminHandler represents the handler of admin URLs
type AdminHandler struct {
GoBoardHandler
handlers.GoBoardHandler

adminToken string
}
Expand All @@ -28,10 +29,10 @@ type AdminHandler struct {
func NewAdminHandler(adminToken string) (a *AdminHandler) {
a = &AdminHandler{}

a.supportedOps = []SupportedOp{
{"/admin/user/", "/admin/user/{login}", "DELETE", a.deleteUser}, // Delete a user
{"/admin/user/", "/admin/user/{login}", "GET", a.getUser}, // Get a user info
{"/admin/post/", "/admin/post/{id}", "DELETE", a.deletePost}, // Delete a post
a.SupportedOps = []handlers.SupportedOp{
{PathBase: "/admin/user/", RestPath: "/admin/user/{login}", Method: "DELETE", Handler: a.deleteUser}, // Delete a user
{PathBase: "/admin/user/", RestPath: "/admin/user/{login}", Method: "GET", Handler: a.getUser}, // Get a user info
{PathBase: "/admin/post/", RestPath: "/admin/post/{id}", Method: "DELETE", Handler: a.deletePost}, // Delete a post
}

if len(adminToken) <= tokenMinLen {
Expand All @@ -51,10 +52,10 @@ func (a *AdminHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}

for _, op := range a.supportedOps {
for _, op := range a.SupportedOps {
if r.Method == op.Method && strings.HasPrefix(r.URL.Path, op.PathBase) {
// Call specific handling method
op.handler(w, r)
op.Handler(w, r)
return
}
}
Expand Down
32 changes: 20 additions & 12 deletions backendhandler.go → handlers/backend/backendhandler.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package main
package backend

import (
"bytes"
Expand All @@ -10,6 +10,7 @@ import (
"strings"
"time"

"github.com/dguihal/goboard/handlers"
goboardbackend "github.com/dguihal/goboard/internal/backend"
goboardcookie "github.com/dguihal/goboard/internal/cookie"
"github.com/gorilla/mux"
Expand All @@ -31,7 +32,7 @@ var knownHeaders = map[string]string{

// BackendHandler represents the handler of backend URLs
type BackendHandler struct {
GoBoardHandler
handlers.GoBoardHandler

historySize int
}
Expand All @@ -40,12 +41,12 @@ type BackendHandler struct {
func NewBackendHandler(historySize int, frontLocation string) (b *BackendHandler) {
b = &BackendHandler{}

b.supportedOps = []SupportedOp{
{"/backend", "/backend", "GET", b.getBackend}, // Get backend (in xml)
{"/backend", "/backend/{format}", "GET", b.getBackend}, // Get backend (in specific format)
{"/post", "/post", "POST", b.post}, // Post new message
{"/post/", "/post/{id}", "GET", b.getPost}, // Get a specific message (in xml)
{"/post/", "/post/{id}/{format}", "GET", b.getPost}, // Get a specific message (in specific format)
b.SupportedOps = []handlers.SupportedOp{
{PathBase: "/backend", RestPath: "/backend", Method: "GET", Handler: b.getBackend}, // Get backend (in xml)
{PathBase: "/backend", RestPath: "/backend/{format}", Method: "GET", Handler: b.getBackend}, // Get backend (in specific format)
{PathBase: "/post", RestPath: "/post", Method: "POST", Handler: b.post}, // Post new message
{PathBase: "/post/", RestPath: "/post/{id}", Method: "GET", Handler: b.getPost}, // Get a specific message (in xml)
{PathBase: "/post/", RestPath: "/post/{id}/{format}", Method: "GET", Handler: b.getPost}, // Get a specific message (in specific format)
}

if location, err := time.LoadLocation(frontLocation); err == nil {
Expand All @@ -61,10 +62,10 @@ func NewBackendHandler(historySize int, frontLocation string) (b *BackendHandler

func (b *BackendHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

for _, op := range b.supportedOps {
for _, op := range b.SupportedOps {
if r.Method == op.Method && strings.HasPrefix(r.URL.Path, op.PathBase) {
// Call specific handling method
op.handler(w, r)
op.Handler(w, r)
return
}
}
Expand Down Expand Up @@ -114,7 +115,8 @@ func (b *BackendHandler) getBackend(w http.ResponseWriter, r *http.Request) {
}

// TODO : Manage returning an original posted data for a specific id as text
// Maybe consider allowing this only for admins (not sure it is relevant)
//
// Maybe consider allowing this only for admins (not sure it is relevant)
func (b *BackendHandler) getPost(w http.ResponseWriter, r *http.Request) {

vars := mux.Vars(r)
Expand Down Expand Up @@ -258,6 +260,10 @@ func guessFormat(formatAttr string, acceptHeader string) (format string) {

func postsToXML(posts []goboardbackend.Post, backendLocation string) []byte {
var b = goboardbackend.Board{}
var outputBuffer bytes.Buffer

outputBuffer.WriteString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")

if (len(backendLocation)) > 0 {
b.Site = "http://" + backendLocation
} else {
Expand All @@ -279,7 +285,9 @@ func postsToXML(posts []goboardbackend.Post, backendLocation string) []byte {
if err != nil {
return []byte(err.Error())
}
return s
outputBuffer.Write(s)

return outputBuffer.Bytes()
}

func postsToJSON(posts []goboardbackend.Post) []byte {
Expand Down
24 changes: 24 additions & 0 deletions handlers/goboardhandler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package handlers

import (
"net/http"

"go.etcd.io/bbolt"
)

// RESTEndpointHandler defines a handler function for a REST Endpoint
type RESTEndpointHandler func(http.ResponseWriter, *http.Request)

// SupportedOp Defines a REST endpoint with its path, method and endpoint
type SupportedOp struct {
PathBase string
RestPath string
Method string
Handler RESTEndpointHandler
}

// GoBoardHandler Base Class for endpoint handlers
type GoBoardHandler struct {
Db *bbolt.DB
SupportedOps []SupportedOp
}
10 changes: 6 additions & 4 deletions templatehandler.go → handlers/template/templatehandler.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package main
package template

import (
"bytes"
"html/template"
"net/http"
"os"

"github.com/dguihal/goboard/handlers"
)

// TemplateHandler represents the handler of swagger URLs
Expand Down Expand Up @@ -33,12 +35,12 @@ func (s *TemplateHandler) SetSwaggerBaseDir(swaggerBaseDir string) {
}

// GetSwaggerOp GET swagger content
func (s *TemplateHandler) GetSwaggerOp() SupportedOp {
return SupportedOp{"/swagger/swagger.yaml", "/swagger/swagger.yaml", "GET", s.ServeSwagger}
func (s *TemplateHandler) GetSwaggerOp() handlers.SupportedOp {
return handlers.SupportedOp{PathBase: "/swagger/swagger.yaml", RestPath: "/swagger/swagger.yaml", Method: "GET", Handler: s.ServeSwagger}
}

// SetSwaggerBaseDir configures webui base dir
func (s *TemplateHandler) setWebUIBaseDir(webuiBaseDir string) {
func (s *TemplateHandler) SetWebUIBaseDir(webuiBaseDir string) {
s.webuiBaseDir = http.Dir(webuiBaseDir)

s.webuiBaseDirSet = true
Expand Down
Loading