Compare commits

...

2 commits

Author SHA1 Message Date
ff92e30232
feat: Option to serve Bootstrap from the filesystem
All checks were successful
/ pre-commit (push) Successful in 1m7s
Signed-off-by: Julien Riou <julien@riou.xyz>
2025-09-19 07:14:33 +02:00
b62a807f89
feat: Add HTTPS support
All checks were successful
/ pre-commit (push) Successful in 1m6s
Fixes #19.

Signed-off-by: Julien Riou <julien@riou.xyz>
2025-09-19 07:12:21 +02:00
8 changed files with 112 additions and 9 deletions

1
.gitignore vendored
View file

@ -3,3 +3,4 @@ releases
collerd.db collerd.db
!docker/collerd.json !docker/collerd.json
collerd.json collerd.json
node_modules

View file

@ -55,6 +55,9 @@ releases: build_linux_amd64 build_darwin_arm64 archive_linux_amd64 archive_darwi
releases_with_docker: 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 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: test:
cd src \ cd src \
&& go test internal/*.go \ && go test internal/*.go \

40
package-lock.json generated Normal file
View file

@ -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"
}
}
}
}

5
package.json Normal file
View file

@ -0,0 +1,5 @@
{
"dependencies": {
"bootstrap": "^5.3.8"
}
}

View file

@ -34,6 +34,9 @@ The file format is **JSON**:
* **languages** ([]string): List of supported [languages](https://github.com/microsoft/monaco-editor/tree/main/src/basic-languages) * **languages** ([]string): List of supported [languages](https://github.com/microsoft/monaco-editor/tree/main/src/basic-languages)
* **language** (string): Default language (default "text") * **language** (string): Default language (default "text")
* **enable_upload_file_button** (bool): Display the upload file button in the UI (default true) * **enable_upload_file_button** (bool): Display the upload file button in the UI (default true)
* **tls_cert_file** (string): Path to TLS certificate file to enable HTTPS
* **tls_key_file** (string): Path to TLS key file to enable HTTPS
* **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. The configuration file is not required but the service might not be exposed to the public.
@ -77,3 +80,34 @@ If the note is encrypted, the encrypted value is returned (application/octet-str
Errors return **500 Server Internal Error** with the **JSON** payload: Errors return **500 Server Internal Error** with the **JSON** payload:
* **message** (string): context of the error * **message** (string): context of the error
* **error** (string): error message * **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.

View file

@ -26,6 +26,9 @@ type Config struct {
Languages []string `json:"languages"` Languages []string `json:"languages"`
Language string `json:"language"` Language string `json:"language"`
EnableUploadFileButton bool `json:"enable_upload_file_button"` EnableUploadFileButton bool `json:"enable_upload_file_button"`
TLSCertFile string `json:"tls_cert_file"`
TLSKeyFile string `json:"tls_key_file"`
BootstrapDirectory string `json:"bootstrap_directory"`
} }
func NewConfig() *Config { func NewConfig() *Config {
@ -94,3 +97,7 @@ func (c *Config) Check() error {
} }
return nil return nil
} }
func (c *Config) HasTLS() bool {
return c.TLSCertFile != "" && c.TLSKeyFile != ""
}

View file

@ -13,9 +13,10 @@ import (
"strconv" "strconv"
"strings" "strings"
"git.riou.xyz/jriou/coller/internal"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"git.riou.xyz/jriou/coller/internal"
) )
var passwordLength = internal.MIN_PASSWORD_LENGTH var passwordLength = internal.MIN_PASSWORD_LENGTH
@ -195,6 +196,7 @@ type PageData struct {
URL string URL string
Note *Note Note *Note
EnableUploadFileButton bool EnableUploadFileButton bool
BootstrapDirectory string
} }
type HomeHandler struct { type HomeHandler struct {
@ -433,6 +435,7 @@ func (s *Server) Start() error {
Expirations: s.config.Expirations, Expirations: s.config.Expirations,
Expiration: s.config.Expiration, Expiration: s.config.Expiration,
Languages: s.config.Languages, Languages: s.config.Languages,
BootstrapDirectory: s.config.BootstrapDirectory,
} }
if s.config.ShowVersion { if s.config.ShowVersion {
@ -469,9 +472,19 @@ func (s *Server) Start() error {
} }
r.Path("/{id:[a-zA-Z0-9]+}.html").Handler(webNoteHandler).Methods("GET") 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") r.Path("/").Handler(&HomeHandler{Templates: templates, PageData: p}).Methods("GET")
addr := fmt.Sprintf("%s:%d", s.config.ListenAddress, s.config.ListenPort) addr := fmt.Sprintf("%s:%d", s.config.ListenAddress, s.config.ListenPort)
s.logger.Info(fmt.Sprintf("listening to %s:%d", s.config.ListenAddress, s.config.ListenPort))
if s.config.HasTLS() {
s.logger.Info(fmt.Sprintf("listening to %s:%d (https)", s.config.ListenAddress, s.config.ListenPort))
return http.ListenAndServeTLS(addr, s.config.TLSCertFile, s.config.TLSKeyFile, r)
} else {
s.logger.Info(fmt.Sprintf("listening to %s:%d (http)", s.config.ListenAddress, s.config.ListenPort))
return http.ListenAndServe(addr, r) return http.ListenAndServe(addr, r)
} }
}

View file

@ -4,6 +4,6 @@
<title>{{.Title}}</title> <title>{{.Title}}</title>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="{{if ne .BootstrapDirectory ``}}/static/bootstrap/css/bootstrap.min.css{{else}}https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css{{end}}" rel="stylesheet">
</head> </head>
{{end}} {{end}}