diff --git a/package-lock.json b/package-lock.json index 4cf810c..65ad597 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "packages": { "": { "dependencies": { + "ace-builds": "^1.43.3", "bootstrap": "^5.3.8" } }, @@ -18,6 +19,11 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/ace-builds": { + "version": "1.43.3", + "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.43.3.tgz", + "integrity": "sha512-MCl9rALmXwIty/4Qboijo/yNysx1r6hBTzG+6n/TiOm5LFhZpEvEIcIITPFiEOEFDfgBOEmxu+a4f54LEFM6Sg==" + }, "node_modules/bootstrap": { "version": "5.3.8", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz", diff --git a/package.json b/package.json index 719e5df..5374149 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "dependencies": { + "ace-builds": "^1.43.3", "bootstrap": "^5.3.8" } } diff --git a/src/cmd/collerd/README.md b/src/cmd/collerd/README.md index ca8674d..5431de8 100644 --- a/src/cmd/collerd/README.md +++ b/src/cmd/collerd/README.md @@ -31,12 +31,14 @@ The file format is **JSON**: * **prometheus_route** (string): Route to expose Prometheus metrics (default "/metrics") * **prometheus_notes_metric** (string): Name of the notes count metric (default "collerd_notes") * **observation_internal** (int): Number of seconds to wait between two observations (default 60) -* **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/ajaxorg/ace/tree/master/src/mode) * **language** (string): Default language (default "text") * **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 +* **ace_directory** (string): Serve [Ace](hhttps://ace.c9.io/) assets from this local directory (ex: "./node_modules/ace-builds"). See **Dependencies** for details. * **bootstrap_directory** (string): Serve [Bootstrap](https://getbootstrap.com/) assets from this local directory (ex: "./node_modules/bootstrap/dist"). See **Dependencies** for details. +* **disable_editor** (bool): Disable Ace editor. The configuration file is not required but the service might not be exposed to the public. @@ -85,12 +87,12 @@ Errors return **500 Server Internal Error** with the **JSON** payload: The web interface depends on: +- [Ace](https://ace.c9.io/) - [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: +If you would like to download and serve them locally: ``` npm install @@ -106,8 +108,7 @@ Then configure the local directories: ```json { + "ace_directory": "./node_modules/ace-builds", "bootstrap_directory": "./node_modules/bootstrap/dist" } -``` - -Downloading Monaco Editor is not supported yet. +``` \ No newline at end of file diff --git a/src/server/config.go b/src/server/config.go index f2a6a6b..ca614ee 100644 --- a/src/server/config.go +++ b/src/server/config.go @@ -28,7 +28,9 @@ type Config struct { EnableUploadFileButton bool `json:"enable_upload_file_button"` TLSCertFile string `json:"tls_cert_file"` TLSKeyFile string `json:"tls_key_file"` + AceDirectory string `json:"ace_directory"` BootstrapDirectory string `json:"bootstrap_directory"` + DisableEditor bool `json:"disable_editor"` } func NewConfig() *Config { @@ -56,22 +58,21 @@ func NewConfig() *Config { PrometheusNotesMetric: "collerd_notes", ObservationInterval: 60, Languages: []string{ - "Text", - "CSS", - "Dockerfile", - "Go", - "HCL", - "HTML", - "Javascript", - "JSON", - "Markdown", - "Perl", - "Python", - "Ruby", - "Rust", - "Shell", - "SQL", - "YAML", + "css", + "dockerfile", + "golang", + "html", + "javascript", + "json", + "markdown", + "perl", + "python", + "ruby", + "rust", + "sh", + "sql", + "text", + "yaml", }, Language: "text", EnableUploadFileButton: true, diff --git a/src/server/handlers_api.go b/src/server/handlers_api.go index feab64e..47bc512 100644 --- a/src/server/handlers_api.go +++ b/src/server/handlers_api.go @@ -89,12 +89,12 @@ func (h *GetNoteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } -type GetProtectedNoteHandler struct { +type GetEncryptedNoteHandler struct { logger *slog.Logger db *Database } -func (h *GetProtectedNoteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { +func (h *GetEncryptedNoteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain; charset=utf-8") vars := mux.Vars(r) diff --git a/src/server/handlers_web.go b/src/server/handlers_web.go index d5e92ad..b71fceb 100644 --- a/src/server/handlers_web.go +++ b/src/server/handlers_web.go @@ -21,12 +21,15 @@ type PageData struct { Version string Expirations []int Expiration int + Language string Languages []string Err error URL string Note *Note EnableUploadFileButton bool + AceDirectory string BootstrapDirectory string + DisableEditor bool } type HomeHandler struct { @@ -191,7 +194,7 @@ func (h *GetWebNoteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.Templates.ExecuteTemplate(w, "note", h.PageData) } -func (h *GetProtectedWebNoteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { +func (h *GetEncryptedWebNoteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.PageData.Err = nil templateName := "note" @@ -224,7 +227,7 @@ func (h *GetProtectedWebNoteHandler) ServeHTTP(w http.ResponseWriter, r *http.Re h.PageData.Note = note - h.logger.Debug("rendering protected note web page") + h.logger.Debug("rendering encrypted note web page") h.Templates.ExecuteTemplate(w, "note", h.PageData) } diff --git a/src/server/server.go b/src/server/server.go index b8b3778..104bfcc 100644 --- a/src/server/server.go +++ b/src/server/server.go @@ -71,7 +71,7 @@ func WriteError(w http.ResponseWriter, message string, err error) { }.ToJSON()) } -type GetProtectedWebNoteHandler struct { +type GetEncryptedWebNoteHandler struct { Templates *template.Template PageData PageData logger *slog.Logger @@ -100,7 +100,7 @@ func (s *Server) Start() error { // API r.Path("/api/note").Handler(&CreateNoteHandler{logger: s.logger, db: s.db, maxUploadSize: s.config.MaxUploadSize}).Methods("POST") - r.Path("/{id:[a-zA-Z0-9]+}/{encryptionKey:[a-zA-Z0-9]+}").Handler(&GetProtectedNoteHandler{logger: s.logger, db: s.db}).Methods("GET") + r.Path("/{id:[a-zA-Z0-9]+}/{encryptionKey:[a-zA-Z0-9]+}").Handler(&GetEncryptedNoteHandler{logger: s.logger, db: s.db}).Methods("GET") r.Path("/{id:[a-zA-Z0-9]+}").Handler(&GetNoteHandler{logger: s.logger, db: s.db}).Methods("GET") // Web pages @@ -114,8 +114,11 @@ func (s *Server) Start() error { Title: s.config.Title, Expirations: s.config.Expirations, Expiration: s.config.Expiration, + Language: s.config.Language, Languages: s.config.Languages, + AceDirectory: s.config.AceDirectory, BootstrapDirectory: s.config.BootstrapDirectory, + DisableEditor: s.config.DisableEditor, } if s.config.ShowVersion { @@ -144,13 +147,13 @@ func (s *Server) Start() error { r.Path("/clients.html").Handler(clientsHandler).Methods("GET") r.Path("/clients/{os:[a-z]+}-{arch:[a-z0-9]+}/{clientName:[a-z]+}").Handler(&ClientHandler{logger: s.logger, version: p.Version}).Methods("GET") - protectedWebNoteHandler := &GetProtectedWebNoteHandler{ + encryptedWebNoteHandler := &GetEncryptedWebNoteHandler{ Templates: templates, PageData: p, logger: s.logger, db: s.db, } - r.Path("/{id:[a-zA-Z0-9]+}/{encryptionKey:[a-zA-Z0-9]+}.html").Handler(protectedWebNoteHandler).Methods("GET") + r.Path("/{id:[a-zA-Z0-9]+}/{encryptionKey:[a-zA-Z0-9]+}.html").Handler(encryptedWebNoteHandler).Methods("GET") webNoteHandler := &GetWebNoteHandler{ Templates: templates, @@ -160,6 +163,10 @@ func (s *Server) Start() error { } r.Path("/{id:[a-zA-Z0-9]+}.html").Handler(webNoteHandler).Methods("GET") + if s.config.AceDirectory != "" { + r.PathPrefix("/static/ace-builds/").Handler(http.StripPrefix("/static/ace-builds/", http.FileServer(http.Dir(s.config.AceDirectory)))) + } + if s.config.BootstrapDirectory != "" { r.PathPrefix("/static/bootstrap/").Handler(http.StripPrefix("/static/bootstrap/", http.FileServer(http.Dir(s.config.BootstrapDirectory)))) } @@ -169,10 +176,10 @@ func (s *Server) Start() error { addr := fmt.Sprintf("%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)) + s.logger.Info(fmt.Sprintf("listening to %s:%d", s.config.ListenAddress, s.config.ListenPort), slog.Any("scheme", "http")) 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)) + s.logger.Info(fmt.Sprintf("listening to %s:%d", s.config.ListenAddress, s.config.ListenPort), slog.Any("scheme", "https")) return http.ListenAndServe(addr, r) } } diff --git a/src/server/templates/head.html b/src/server/templates/head.html index e5f3544..ab2edfd 100644 --- a/src/server/templates/head.html +++ b/src/server/templates/head.html @@ -4,6 +4,8 @@
+{{string .Note.Content}} ++