feat(serve): add TLS support with --tls-cert and --tls-key flags
Adds make cert target for self-signed dev certs and development guide.
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
# Binary
|
# Binary
|
||||||
nib
|
nib
|
||||||
tmp/
|
tmp/
|
||||||
|
certs/
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
*.db
|
*.db
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ BINARY := nib
|
|||||||
MODULE := github.com/lerko/nib
|
MODULE := github.com/lerko/nib
|
||||||
GOFLAGS := -trimpath
|
GOFLAGS := -trimpath
|
||||||
|
|
||||||
.PHONY: build dev watch test lint fmt vet clean run help
|
.PHONY: build dev watch test lint fmt vet clean run cert help
|
||||||
|
|
||||||
## —— Build ——————————————————————————————————
|
## —— Build ——————————————————————————————————
|
||||||
|
|
||||||
@@ -41,6 +41,14 @@ fmt-check: ## Check formatting (fails if unformatted)
|
|||||||
|
|
||||||
## —— Utility ————————————————————————————————
|
## —— Utility ————————————————————————————————
|
||||||
|
|
||||||
|
cert: ## Generate self-signed dev TLS cert (certs/)
|
||||||
|
@mkdir -p certs
|
||||||
|
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \
|
||||||
|
-nodes -keyout certs/dev.key -out certs/dev.crt -days 365 \
|
||||||
|
-subj "/CN=localhost"
|
||||||
|
@echo " certs/dev.crt and certs/dev.key ready"
|
||||||
|
@echo " usage: make run ARGS=\"serve --tls-cert certs/dev.crt --tls-key certs/dev.key\""
|
||||||
|
|
||||||
clean: ## Remove build artifacts
|
clean: ## Remove build artifacts
|
||||||
rm -f $(BINARY) coverage.out
|
rm -f $(BINARY) coverage.out
|
||||||
|
|
||||||
|
|||||||
+26
-4
@@ -20,6 +20,8 @@ var WebFS fs.FS
|
|||||||
var (
|
var (
|
||||||
servePort int
|
servePort int
|
||||||
serveDev bool
|
serveDev bool
|
||||||
|
tlsCert string
|
||||||
|
tlsKey string
|
||||||
)
|
)
|
||||||
|
|
||||||
var serveCmd = &cobra.Command{
|
var serveCmd = &cobra.Command{
|
||||||
@@ -29,12 +31,19 @@ var serveCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
serveCmd.Flags().IntVar(&servePort, "port", 0, "port to listen on (default 4444)")
|
serveCmd.Flags().IntVar(&servePort, "port", 0, "port to listen on (default 4444, or 4443 with TLS)")
|
||||||
serveCmd.Flags().BoolVar(&serveDev, "dev", false, "enable CORS for development")
|
serveCmd.Flags().BoolVar(&serveDev, "dev", false, "enable CORS for development")
|
||||||
|
serveCmd.Flags().StringVar(&tlsCert, "tls-cert", "", "path to TLS certificate file")
|
||||||
|
serveCmd.Flags().StringVar(&tlsKey, "tls-key", "", "path to TLS private key file")
|
||||||
rootCmd.AddCommand(serveCmd)
|
rootCmd.AddCommand(serveCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runServe(_ *cobra.Command, _ []string) error {
|
func runServe(_ *cobra.Command, _ []string) error {
|
||||||
|
useTLS := tlsCert != "" && tlsKey != ""
|
||||||
|
if (tlsCert != "") != (tlsKey != "") {
|
||||||
|
return fmt.Errorf("both --tls-cert and --tls-key are required for TLS")
|
||||||
|
}
|
||||||
|
|
||||||
port := servePort
|
port := servePort
|
||||||
if port == 0 {
|
if port == 0 {
|
||||||
if envPort := os.Getenv("NIB_PORT"); envPort != "" {
|
if envPort := os.Getenv("NIB_PORT"); envPort != "" {
|
||||||
@@ -43,6 +52,8 @@ func runServe(_ *cobra.Command, _ []string) error {
|
|||||||
return fmt.Errorf("invalid NIB_PORT: %w", err)
|
return fmt.Errorf("invalid NIB_PORT: %w", err)
|
||||||
}
|
}
|
||||||
port = p
|
port = p
|
||||||
|
} else if useTLS {
|
||||||
|
port = 4443
|
||||||
} else {
|
} else {
|
||||||
port = 4444
|
port = 4444
|
||||||
}
|
}
|
||||||
@@ -69,12 +80,23 @@ func runServe(_ *cobra.Command, _ []string) error {
|
|||||||
defer stop()
|
defer stop()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
fmt.Printf("nib serving on %s\n", addr)
|
if useTLS {
|
||||||
|
fmt.Printf("nib serving on https://localhost%s\n", addr)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("nib serving on http://localhost%s\n", addr)
|
||||||
|
}
|
||||||
if serveDev {
|
if serveDev {
|
||||||
fmt.Println(" CORS enabled (dev mode)")
|
fmt.Println(" CORS enabled (dev mode)")
|
||||||
}
|
}
|
||||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
||||||
fmt.Fprintf(os.Stderr, "server error: %v\n", err)
|
var listenErr error
|
||||||
|
if useTLS {
|
||||||
|
listenErr = srv.ListenAndServeTLS(tlsCert, tlsKey)
|
||||||
|
} else {
|
||||||
|
listenErr = srv.ListenAndServe()
|
||||||
|
}
|
||||||
|
if listenErr != nil && listenErr != http.ErrServerClosed {
|
||||||
|
fmt.Fprintf(os.Stderr, "server error: %v\n", listenErr)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
# Development Guide
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Go 1.24+
|
||||||
|
- [air](https://github.com/air-verse/air) (live-reload, install: `go install github.com/air-verse/air@latest`)
|
||||||
|
- OpenSSL (for dev TLS certs)
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make cert # one-time: generate self-signed TLS cert
|
||||||
|
make watch # start dev server with live-reload (HTTP, port 4444)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Make Targets
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
| Target | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `make build` | Compile production binary (`./nib`) |
|
||||||
|
| `make dev` | Run dev server from source (HTTP, port 4444) |
|
||||||
|
| `make watch` | Live-reload dev server via air — auto-rebuilds on save |
|
||||||
|
|
||||||
|
### Quality
|
||||||
|
|
||||||
|
| Target | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `make test` | Run all tests |
|
||||||
|
| `make test-v` | Run all tests with verbose output |
|
||||||
|
| `make test-cover` | Run tests with per-function coverage report |
|
||||||
|
| `make lint` | Run vet + format check |
|
||||||
|
| `make vet` | Static analysis via `go vet` |
|
||||||
|
| `make fmt` | Auto-format all Go files |
|
||||||
|
| `make fmt-check` | Check formatting without modifying (CI-friendly) |
|
||||||
|
|
||||||
|
### Utility
|
||||||
|
|
||||||
|
| Target | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `make cert` | Generate self-signed dev TLS cert in `certs/` (valid 365 days) |
|
||||||
|
| `make clean` | Remove build artifacts |
|
||||||
|
| `make tidy` | Tidy and verify Go module dependencies |
|
||||||
|
| `make run ARGS="..."` | Build then run with custom args |
|
||||||
|
| `make help` | List all targets |
|
||||||
|
|
||||||
|
## TLS
|
||||||
|
|
||||||
|
nib supports TLS via `--tls-cert` and `--tls-key` flags on the `serve` command.
|
||||||
|
|
||||||
|
### Dev (self-signed)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make cert
|
||||||
|
make run ARGS="serve --tls-cert certs/dev.crt --tls-key certs/dev.key"
|
||||||
|
```
|
||||||
|
|
||||||
|
Serves HTTPS on port 4443. Browser will warn about the self-signed cert — accept once.
|
||||||
|
|
||||||
|
This is needed for features that require a secure context (e.g. clipboard API).
|
||||||
|
|
||||||
|
### Production
|
||||||
|
|
||||||
|
For production, use a reverse proxy (Caddy, nginx) with real certificates in front of nib's HTTP server. Alternatively, pass real cert/key paths directly:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./nib serve --tls-cert /path/to/cert.pem --tls-key /path/to/key.pem
|
||||||
|
```
|
||||||
|
|
||||||
|
### Port Defaults
|
||||||
|
|
||||||
|
| Mode | Default Port |
|
||||||
|
|------|-------------|
|
||||||
|
| HTTP | 4444 |
|
||||||
|
| HTTPS | 4443 |
|
||||||
|
|
||||||
|
Override with `--port` or the `NIB_PORT` environment variable.
|
||||||
|
|
||||||
|
## Typical Workflow
|
||||||
|
|
||||||
|
1. `make cert` — once, generates dev TLS cert
|
||||||
|
2. `make watch` — start coding, air rebuilds on save
|
||||||
|
3. Edit code, save, changes appear automatically
|
||||||
|
4. `make test` — verify nothing broke
|
||||||
|
5. `make lint` — check formatting and vet
|
||||||
|
6. Commit and push
|
||||||
Reference in New Issue
Block a user