From 4ee03451573321aced3ba04086ad921a2f274ce5 Mon Sep 17 00:00:00 2001 From: Julien Riou Date: Tue, 16 Sep 2025 09:36:10 +0200 Subject: [PATCH 1/3] feat: Option to serve Bootstrap from the filesystem Signed-off-by: Julien Riou --- .gitignore | 1 + Makefile | 3 +++ package-lock.json | 40 ++++++++++++++++++++++++++++++++++ package.json | 5 +++++ src/cmd/collerd/README.md | 34 ++++++++++++++++++++++++++++- src/server/config.go | 1 + src/server/server.go | 14 ++++++++---- src/server/templates/head.html | 2 +- 8 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.gitignore b/.gitignore index cf8b1ff..d2fe01d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ releases collerd.db !docker/collerd.json collerd.json +node_modules diff --git a/Makefile b/Makefile index d73d30f..712a245 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,9 @@ archive: release: build archive +dependencies: + npm install + test: (cd src \ && go test internal/*.go \ diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..4cf810c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,40 @@ +{ + "name": "coller", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "bootstrap": "^5.3.8" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/bootstrap": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz", + "integrity": "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..719e5df --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "bootstrap": "^5.3.8" + } +} diff --git a/src/cmd/collerd/README.md b/src/cmd/collerd/README.md index d99a273..59bf68e 100644 --- a/src/cmd/collerd/README.md +++ b/src/cmd/collerd/README.md @@ -34,6 +34,7 @@ The file format is **JSON**: * **languages** ([]string): List of supported [languages](https://github.com/microsoft/monaco-editor/tree/main/src/basic-languages) * **language** (string): Default language (default "text") * **enable_upload_file_button** (bool): Display the upload file button in the UI (default true) +* **bootstrap_directory** (string): Serve [Bootstrap](https://getbootstrap.com/) assets from this local directory (ex: "./node_modules/bootstrap/dist"). See **Dependencies** for details. The configuration file is not required but the service might not be exposed to the public. @@ -76,4 +77,35 @@ If the note is encrypted, the encrypted value is returned (application/octet-str Errors return **500 Server Internal Error** with the **JSON** payload: * **message** (string): context of the error -* **error** (string): error message \ No newline at end of file +* **error** (string): error message + +## Dependencies + +The web interface depends on: + +- [Bootstrap](https://getbootstrap.com/) +- [Monaco Editor](https://github.com/microsoft/monaco-editor/) + +By default, those dependencies are fetched from **remote CDN** services by the client. + +If you would like to download them to serve them locally: + +``` +npm install +``` + +or via `make`: + +``` +make dependencies +``` + +Then configure the local directories: + +```json +{ + "bootstrap_directory": "./node_modules/bootstrap/dist" +} +``` + +Downloading Monaco Editor is not supported yet. diff --git a/src/server/config.go b/src/server/config.go index 8ba51ca..e1ef5e7 100644 --- a/src/server/config.go +++ b/src/server/config.go @@ -26,6 +26,7 @@ type Config struct { Languages []string `json:"languages"` Language string `json:"language"` EnableUploadFileButton bool `json:"enable_upload_file_button"` + BootstrapDirectory string `json:"bootstrap_directory"` } func NewConfig() *Config { diff --git a/src/server/server.go b/src/server/server.go index 703714d..ab61605 100644 --- a/src/server/server.go +++ b/src/server/server.go @@ -195,6 +195,7 @@ type PageData struct { URL string Note *Note EnableUploadFileButton bool + BootstrapDirectory string } type HomeHandler struct { @@ -429,10 +430,11 @@ func (s *Server) Start() error { "string": func(b []byte) string { return string(b) }, } p := PageData{ - Title: s.config.Title, - Expirations: s.config.Expirations, - Expiration: s.config.Expiration, - Languages: s.config.Languages, + Title: s.config.Title, + Expirations: s.config.Expirations, + Expiration: s.config.Expiration, + Languages: s.config.Languages, + BootstrapDirectory: s.config.BootstrapDirectory, } if s.config.ShowVersion { @@ -469,6 +471,10 @@ func (s *Server) Start() error { } r.Path("/{id:[a-zA-Z0-9]+}.html").Handler(webNoteHandler).Methods("GET") + if s.config.BootstrapDirectory != "" { + r.PathPrefix("/static/bootstrap/").Handler(http.StripPrefix("/static/bootstrap/", http.FileServer(http.Dir(s.config.BootstrapDirectory)))) + } + r.Path("/").Handler(&HomeHandler{Templates: templates, PageData: p}).Methods("GET") addr := fmt.Sprintf("%s:%d", s.config.ListenAddress, s.config.ListenPort) diff --git a/src/server/templates/head.html b/src/server/templates/head.html index bd8edba..e5f3544 100644 --- a/src/server/templates/head.html +++ b/src/server/templates/head.html @@ -4,6 +4,6 @@ {{.Title}} - + {{end}} \ No newline at end of file From 5122000d4862745095fb149997ec2dad2ece1501 Mon Sep 17 00:00:00 2001 From: Julien Riou Date: Wed, 17 Sep 2025 10:24:35 +0200 Subject: [PATCH 2/3] feat: Create releases with make releases Fixes #22. Signed-off-by: Julien Riou --- Makefile | 61 ++++++++++++++++++++++++++++++++++++++----------- docker/build.sh | 20 ++++++++++++++++ 2 files changed, 68 insertions(+), 13 deletions(-) create mode 100755 docker/build.sh diff --git a/Makefile b/Makefile index d73d30f..cb3b93c 100644 --- a/Makefile +++ b/Makefile @@ -4,26 +4,61 @@ GITCOMMIT := $(shell git log -1 --oneline | awk '{print $$1}') OS := $(shell uname -s | tr [A-Z] [a-z]) ARCH := $(shell uname -m | tr [A-Z] [a-z]) LDFLAGS = -X main.AppVersion=${APPVERSION} -X main.GoVersion=${GOVERSION} -X main.GitCommit=${GITCOMMIT} +DOCKER_IMAGE = golang:1.24-trixie -.PHONY: clean test +.PHONY: clean clean_for_releases test build: - (cd src \ - && go build -ldflags "${LDFLAGS}" -o ../bin/collerd cmd/collerd/main.go \ - && go build -ldflags "${LDFLAGS}" -o ../bin/copier cmd/copier/main.go \ - && go build -ldflags "${LDFLAGS}" -o ../bin/coller cmd/coller/main.go \ - ) + cd src \ + && go build -ldflags "${LDFLAGS}" -o ../bin/collerd cmd/collerd/main.go \ + && go build -ldflags "${LDFLAGS}" -o ../bin/copier cmd/copier/main.go \ + && go build -ldflags "${LDFLAGS}" -o ../bin/coller cmd/coller/main.go -archive: - (mkdir -p releases && cd bin && tar cvzpf ../releases/coller-${APPVERSION}-${OS}-${ARCH}.tar.gz * && cd ../releases && sha256sum *.tar.gz) +build_linux_amd64: + cd src \ + && GOOS=linux GOARCH=amd64 go build -ldflags "${LDFLAGS}" -o ../bin/collerd-${APPVERSION}-linux-amd64 cmd/collerd/main.go \ + && GOOS=linux GOARCH=amd64 go build -ldflags "${LDFLAGS}" -o ../bin/coller-${APPVERSION}-linux-amd64 cmd/coller/main.go \ + && GOOS=linux GOARCH=amd64 go build -ldflags "${LDFLAGS}" -o ../bin/copier-${APPVERSION}-linux-amd64 cmd/copier/main.go -release: build archive +archive_linux_amd64: + mkdir -p releases/coller-${APPVERSION}-linux-amd64 \ + && cp bin/collerd-${APPVERSION}-linux-amd64 releases/coller-${APPVERSION}-linux-amd64/collerd \ + && cp bin/coller-${APPVERSION}-linux-amd64 releases/coller-${APPVERSION}-linux-amd64/coller \ + && cp bin/copier-${APPVERSION}-linux-amd64 releases/coller-${APPVERSION}-linux-amd64/copier \ + && cd releases/ \ + && tar cvpzf coller-${APPVERSION}-linux-amd64.tar.gz coller-${APPVERSION}-linux-amd64 + +build_darwin_arm64: + cd src \ + && GOOS=darwin GOARCH=arm64 go build -ldflags "${LDFLAGS}" -o ../bin/collerd-${APPVERSION}-darwin-arm64 cmd/collerd/main.go \ + && GOOS=darwin GOARCH=arm64 go build -ldflags "${LDFLAGS}" -o ../bin/coller-${APPVERSION}-darwin-arm64 cmd/coller/main.go \ + && GOOS=darwin GOARCH=arm64 go build -ldflags "${LDFLAGS}" -o ../bin/copier-${APPVERSION}-darwin-arm64 cmd/copier/main.go + +archive_darwin_arm64: + mkdir -p releases/coller-${APPVERSION}-darwin-arm64 \ + && cp bin/collerd-${APPVERSION}-darwin-arm64 releases/coller-${APPVERSION}-darwin-arm64/collerd \ + && cp bin/coller-${APPVERSION}-darwin-arm64 releases/coller-${APPVERSION}-darwin-arm64/coller \ + && cp bin/copier-${APPVERSION}-darwin-arm64 releases/coller-${APPVERSION}-darwin-arm64/copier \ + && cd releases/ \ + && tar cvpzf coller-${APPVERSION}-darwin-arm64.tar.gz coller-${APPVERSION}-darwin-arm64 + +checksum: + cd releases \ + && sha256sum *.tar.gz > checksums.txt + +clean_for_releases: + rm -rf releases/coller-${APPVERSION}-linux-amd64 \ + && rm -rf releases/coller-${APPVERSION}-darwin-arm64 + +releases: build_linux_amd64 build_darwin_arm64 archive_linux_amd64 archive_darwin_arm64 checksum clean_for_releases + +releases_with_docker: + docker run -it -v $(shell pwd):/mnt -w /mnt -e "UID=$(shell id -u)" -e "GID=$(shell id -g)" ${DOCKER_IMAGE} ./docker/build.sh test: - (cd src \ - && go test internal/*.go \ - && go test server/*.go \ - ) + cd src \ + && go test internal/*.go \ + && go test server/*.go clean: rm -rf bin releases diff --git a/docker/build.sh b/docker/build.sh new file mode 100755 index 0000000..783ba44 --- /dev/null +++ b/docker/build.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# Script to create releases with Docker +# Used by `make releases_with_docker` +set -e + +if [ -z "UID" ] ; then + echo "UID not defined" + exit 1 +fi +if [ -z "GID" ] ; then + echo "GID not defined" + exit 1 +fi + +apt-get update +apt-get install -y libx11-dev + +make releases + +chown ${UID}:${GID} -R releases From 2a37725b2eeaac073a3c2f84928c1ed5b97e06ed Mon Sep 17 00:00:00 2001 From: Julien Riou Date: Tue, 16 Sep 2025 09:36:10 +0200 Subject: [PATCH 3/3] feat: Option to serve Bootstrap from the filesystem Signed-off-by: Julien Riou --- .gitignore | 1 + Makefile | 3 +++ package-lock.json | 40 ++++++++++++++++++++++++++++++++++ package.json | 5 +++++ src/cmd/collerd/README.md | 34 ++++++++++++++++++++++++++++- src/server/config.go | 1 + src/server/server.go | 14 ++++++++---- src/server/templates/head.html | 2 +- 8 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.gitignore b/.gitignore index cf8b1ff..d2fe01d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ releases collerd.db !docker/collerd.json collerd.json +node_modules diff --git a/Makefile b/Makefile index cb3b93c..547f06e 100644 --- a/Makefile +++ b/Makefile @@ -55,6 +55,9 @@ releases: build_linux_amd64 build_darwin_arm64 archive_linux_amd64 archive_darwi releases_with_docker: docker run -it -v $(shell pwd):/mnt -w /mnt -e "UID=$(shell id -u)" -e "GID=$(shell id -g)" ${DOCKER_IMAGE} ./docker/build.sh +dependencies: + npm install + test: cd src \ && go test internal/*.go \ diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..4cf810c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,40 @@ +{ + "name": "coller", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "bootstrap": "^5.3.8" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/bootstrap": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz", + "integrity": "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..719e5df --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "bootstrap": "^5.3.8" + } +} diff --git a/src/cmd/collerd/README.md b/src/cmd/collerd/README.md index d99a273..59bf68e 100644 --- a/src/cmd/collerd/README.md +++ b/src/cmd/collerd/README.md @@ -34,6 +34,7 @@ The file format is **JSON**: * **languages** ([]string): List of supported [languages](https://github.com/microsoft/monaco-editor/tree/main/src/basic-languages) * **language** (string): Default language (default "text") * **enable_upload_file_button** (bool): Display the upload file button in the UI (default true) +* **bootstrap_directory** (string): Serve [Bootstrap](https://getbootstrap.com/) assets from this local directory (ex: "./node_modules/bootstrap/dist"). See **Dependencies** for details. The configuration file is not required but the service might not be exposed to the public. @@ -76,4 +77,35 @@ If the note is encrypted, the encrypted value is returned (application/octet-str Errors return **500 Server Internal Error** with the **JSON** payload: * **message** (string): context of the error -* **error** (string): error message \ No newline at end of file +* **error** (string): error message + +## Dependencies + +The web interface depends on: + +- [Bootstrap](https://getbootstrap.com/) +- [Monaco Editor](https://github.com/microsoft/monaco-editor/) + +By default, those dependencies are fetched from **remote CDN** services by the client. + +If you would like to download them to serve them locally: + +``` +npm install +``` + +or via `make`: + +``` +make dependencies +``` + +Then configure the local directories: + +```json +{ + "bootstrap_directory": "./node_modules/bootstrap/dist" +} +``` + +Downloading Monaco Editor is not supported yet. diff --git a/src/server/config.go b/src/server/config.go index 8ba51ca..e1ef5e7 100644 --- a/src/server/config.go +++ b/src/server/config.go @@ -26,6 +26,7 @@ type Config struct { Languages []string `json:"languages"` Language string `json:"language"` EnableUploadFileButton bool `json:"enable_upload_file_button"` + BootstrapDirectory string `json:"bootstrap_directory"` } func NewConfig() *Config { diff --git a/src/server/server.go b/src/server/server.go index 703714d..ab61605 100644 --- a/src/server/server.go +++ b/src/server/server.go @@ -195,6 +195,7 @@ type PageData struct { URL string Note *Note EnableUploadFileButton bool + BootstrapDirectory string } type HomeHandler struct { @@ -429,10 +430,11 @@ func (s *Server) Start() error { "string": func(b []byte) string { return string(b) }, } p := PageData{ - Title: s.config.Title, - Expirations: s.config.Expirations, - Expiration: s.config.Expiration, - Languages: s.config.Languages, + Title: s.config.Title, + Expirations: s.config.Expirations, + Expiration: s.config.Expiration, + Languages: s.config.Languages, + BootstrapDirectory: s.config.BootstrapDirectory, } if s.config.ShowVersion { @@ -469,6 +471,10 @@ func (s *Server) Start() error { } r.Path("/{id:[a-zA-Z0-9]+}.html").Handler(webNoteHandler).Methods("GET") + if s.config.BootstrapDirectory != "" { + r.PathPrefix("/static/bootstrap/").Handler(http.StripPrefix("/static/bootstrap/", http.FileServer(http.Dir(s.config.BootstrapDirectory)))) + } + r.Path("/").Handler(&HomeHandler{Templates: templates, PageData: p}).Methods("GET") addr := fmt.Sprintf("%s:%d", s.config.ListenAddress, s.config.ListenPort) diff --git a/src/server/templates/head.html b/src/server/templates/head.html index bd8edba..e5f3544 100644 --- a/src/server/templates/head.html +++ b/src/server/templates/head.html @@ -4,6 +4,6 @@ {{.Title}} - + {{end}} \ No newline at end of file