big juicy update
This commit is contained in:
parent
b58086263b
commit
1b6651c8e0
5 changed files with 101 additions and 11 deletions
|
|
@ -1,4 +1,8 @@
|
|||
PORT=8080
|
||||
LISTEN_NETWORK=tcp
|
||||
LISTEN_ADDRESS=:8080
|
||||
# Only used when LISTEN_NETWORK=unix
|
||||
UNIX_SOCKET_PERM=0660
|
||||
DATABASE_URL=postgres://postgres:postgres@localhost:5432/skills_market?sslmode=disable
|
||||
STORAGE_DIR=uploads
|
||||
DEV_MODE=true
|
||||
|
|
|
|||
16
README.md
16
README.md
|
|
@ -22,6 +22,22 @@ Go + Postgres marketplace where authenticated users can upload LLM skill files,
|
|||
```
|
||||
5. Open `http://localhost:8080`.
|
||||
|
||||
## Listener configuration
|
||||
- `LISTEN_NETWORK`: `tcp` (default) or `unix`
|
||||
- `LISTEN_ADDRESS`:
|
||||
- for `tcp`: address like `:8080` or `127.0.0.1:8080`
|
||||
- for `unix`: socket path like `/tmp/skills2.sock`
|
||||
- `UNIX_SOCKET_PERM`: octal file mode for unix socket path (default `0660`)
|
||||
|
||||
Examples:
|
||||
- TCP (default):
|
||||
- `LISTEN_NETWORK=tcp`
|
||||
- `LISTEN_ADDRESS=:8080`
|
||||
- Unix socket:
|
||||
- `LISTEN_NETWORK=unix`
|
||||
- `LISTEN_ADDRESS=/tmp/skills2.sock`
|
||||
- `UNIX_SOCKET_PERM=0660`
|
||||
|
||||
## Local dev mode (no auth setup)
|
||||
Set `DEV_MODE=true` in `.env`.
|
||||
In this mode, protected routes always authenticate as:
|
||||
|
|
|
|||
31
config.go
31
config.go
|
|
@ -3,6 +3,7 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
|
|
@ -10,6 +11,9 @@ import (
|
|||
|
||||
type Config struct {
|
||||
Port string
|
||||
ListenNetwork string
|
||||
ListenAddress string
|
||||
UnixSocketPerm os.FileMode
|
||||
DatabaseURL string
|
||||
StorageDir string
|
||||
DevMode bool
|
||||
|
|
@ -20,14 +24,32 @@ func LoadConfig() (Config, error) {
|
|||
|
||||
cfg := Config{
|
||||
Port: envOrDefault("PORT", "8080"),
|
||||
ListenNetwork: strings.ToLower(envOrDefault("LISTEN_NETWORK", "tcp")),
|
||||
ListenAddress: strings.TrimSpace(os.Getenv("LISTEN_ADDRESS")),
|
||||
DatabaseURL: os.Getenv("DATABASE_URL"),
|
||||
StorageDir: envOrDefault("STORAGE_DIR", "uploads"),
|
||||
DevMode: strings.EqualFold(envOrDefault("DEV_MODE", "false"), "true"),
|
||||
}
|
||||
if cfg.ListenAddress == "" {
|
||||
cfg.ListenAddress = ":" + cfg.Port
|
||||
}
|
||||
socketPerm, err := parseFileMode(envOrDefault("UNIX_SOCKET_PERM", "0660"))
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("invalid UNIX_SOCKET_PERM: %w", err)
|
||||
}
|
||||
cfg.UnixSocketPerm = socketPerm
|
||||
|
||||
if cfg.DatabaseURL == "" {
|
||||
return Config{}, fmt.Errorf("DATABASE_URL is required")
|
||||
}
|
||||
switch cfg.ListenNetwork {
|
||||
case "tcp", "unix":
|
||||
default:
|
||||
return Config{}, fmt.Errorf("LISTEN_NETWORK must be tcp or unix")
|
||||
}
|
||||
if cfg.ListenNetwork == "unix" && cfg.ListenAddress == "" {
|
||||
return Config{}, fmt.Errorf("LISTEN_ADDRESS is required when LISTEN_NETWORK=unix")
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
|
|
@ -38,3 +60,12 @@ func envOrDefault(key, fallback string) string {
|
|||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func parseFileMode(value string) (os.FileMode, error) {
|
||||
trimmed := strings.TrimSpace(value)
|
||||
perm, err := strconv.ParseUint(trimmed, 8, 32)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return os.FileMode(perm), nil
|
||||
}
|
||||
|
|
|
|||
45
main.go
45
main.go
|
|
@ -103,13 +103,52 @@ func main() {
|
|||
mux.Handle("/api/purchase", app.auth.RequireAuth(http.HandlerFunc(app.handlePurchaseAPI)))
|
||||
mux.Handle("/api/my-skills", app.auth.RequireAuth(http.HandlerFunc(app.handleMySkillsAPI)))
|
||||
|
||||
addr := ":" + cfg.Port
|
||||
log.Printf("server listening on %s", addr)
|
||||
if err := http.ListenAndServe(addr, loggingMiddleware(mux)); err != nil {
|
||||
listener, cleanup, err := createListener(cfg)
|
||||
if err != nil {
|
||||
log.Fatalf("create listener: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
log.Printf("server listening network=%s address=%s", cfg.ListenNetwork, cfg.ListenAddress)
|
||||
if err := http.Serve(listener, loggingMiddleware(mux)); err != nil {
|
||||
log.Fatalf("server failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func createListener(cfg Config) (net.Listener, func(), error) {
|
||||
if cfg.ListenNetwork == "unix" {
|
||||
if err := os.MkdirAll(filepath.Dir(cfg.ListenAddress), 0o755); err != nil {
|
||||
return nil, nil, fmt.Errorf("create socket dir: %w", err)
|
||||
}
|
||||
if _, err := os.Stat(cfg.ListenAddress); err == nil {
|
||||
if err := os.Remove(cfg.ListenAddress); err != nil {
|
||||
return nil, nil, fmt.Errorf("remove stale socket: %w", err)
|
||||
}
|
||||
} else if !os.IsNotExist(err) {
|
||||
return nil, nil, fmt.Errorf("check socket path: %w", err)
|
||||
}
|
||||
l, err := net.Listen("unix", cfg.ListenAddress)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := os.Chmod(cfg.ListenAddress, cfg.UnixSocketPerm); err != nil {
|
||||
_ = l.Close()
|
||||
return nil, nil, fmt.Errorf("chmod socket: %w", err)
|
||||
}
|
||||
cleanup := func() {
|
||||
_ = l.Close()
|
||||
_ = os.Remove(cfg.ListenAddress)
|
||||
}
|
||||
return l, cleanup, nil
|
||||
}
|
||||
|
||||
l, err := net.Listen("tcp", cfg.ListenAddress)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return l, func() { _ = l.Close() }, nil
|
||||
}
|
||||
|
||||
func runMigrations(db *sql.DB) error {
|
||||
schema, err := os.ReadFile("db/schema.sql")
|
||||
if err != nil {
|
||||
|
|
|
|||
BIN
skills2
BIN
skills2
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue