mirror of
https://github.com/anotherhadi/iknowyou.git
synced 2026-04-12 00:47:26 +02:00
init
This commit is contained in:
155
back/cmd/gendocs/main.go
Normal file
155
back/cmd/gendocs/main.go
Normal file
@@ -0,0 +1,155 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/anotherhadi/iknowyou/internal/registry"
|
||||
"github.com/anotherhadi/iknowyou/internal/tools"
|
||||
)
|
||||
|
||||
func main() {
|
||||
out := flag.String("out", "../.github/docs", "output directory for generated docs")
|
||||
flag.Parse()
|
||||
|
||||
toolsDir := filepath.Join(*out, "tools")
|
||||
if _, err := os.Stat(toolsDir); err == nil {
|
||||
if err := os.RemoveAll(toolsDir); err != nil {
|
||||
fatalf("removing tools dir: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(toolsDir, 0o755); err != nil {
|
||||
fatalf("mkdir: %v", err)
|
||||
}
|
||||
|
||||
runners := make([]tools.ToolRunner, len(registry.Factories))
|
||||
for i, f := range registry.Factories {
|
||||
runners[i] = f()
|
||||
}
|
||||
|
||||
if err := writeIndex(*out, runners); err != nil {
|
||||
fatalf("index: %v", err)
|
||||
}
|
||||
for _, r := range runners {
|
||||
if err := writeTool(*out, r); err != nil {
|
||||
fatalf("tool %s: %v", r.Name(), err)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("✓ generated docs for %d tools → %s\n", len(runners), *out)
|
||||
}
|
||||
|
||||
// writeIndex writes the tools.md index table.
|
||||
func writeIndex(outDir string, runners []tools.ToolRunner) error {
|
||||
var b strings.Builder
|
||||
|
||||
b.WriteString("# Tools\n\n")
|
||||
fmt.Fprintf(&b, "_%d tools registered._\n\n", len(runners))
|
||||
|
||||
b.WriteString("| Tool | Input types | Description | Link |\n")
|
||||
b.WriteString("|------|-------------|-------------|------|\n")
|
||||
|
||||
for _, r := range runners {
|
||||
types := make([]string, len(r.InputTypes()))
|
||||
for i, t := range r.InputTypes() {
|
||||
types[i] = fmt.Sprintf("`%s`", t)
|
||||
}
|
||||
link := fmt.Sprintf("[`%s`](tools/%s.md)", r.Name(), r.Name())
|
||||
projectLink := ""
|
||||
if r.Link() != "" {
|
||||
projectLink = fmt.Sprintf("[Link](%s)", r.Link())
|
||||
}
|
||||
fmt.Fprintf(&b, "| %s | %s | %s | %s |\n",
|
||||
link,
|
||||
strings.Join(types, ", "),
|
||||
r.Description(),
|
||||
projectLink,
|
||||
)
|
||||
}
|
||||
|
||||
return writeFile(filepath.Join(outDir, "tools.md"), b.String())
|
||||
}
|
||||
|
||||
// writeTool writes the per-tool detail page.
|
||||
func writeTool(outDir string, r tools.ToolRunner) error {
|
||||
var b strings.Builder
|
||||
|
||||
fmt.Fprintf(&b, "# `%s`\n\n", r.Name())
|
||||
fmt.Fprintf(&b, "%s\n\n", r.Description())
|
||||
|
||||
if r.Link() != "" {
|
||||
fmt.Fprintf(&b, "**Source / documentation:** [%s](%s)\n\n", r.Link(), r.Link())
|
||||
}
|
||||
|
||||
// Input types
|
||||
b.WriteString("## Input types\n\n")
|
||||
for _, t := range r.InputTypes() {
|
||||
fmt.Fprintf(&b, "- `%s`\n", t)
|
||||
}
|
||||
b.WriteString("\n")
|
||||
|
||||
// External binary dependencies
|
||||
if lister, ok := r.(tools.DependencyLister); ok {
|
||||
if deps := lister.Dependencies(); len(deps) > 0 {
|
||||
b.WriteString("## External dependencies\n\n")
|
||||
b.WriteString("The following binaries must be installed and available in `$PATH`:\n\n")
|
||||
for _, dep := range deps {
|
||||
fmt.Fprintf(&b, "- `%s`\n", dep)
|
||||
}
|
||||
b.WriteString("\n")
|
||||
}
|
||||
}
|
||||
|
||||
// Configuration
|
||||
if d, ok := r.(tools.ConfigDescriber); ok {
|
||||
fields := d.ConfigFields()
|
||||
if len(fields) > 0 {
|
||||
b.WriteString("## Configuration\n\n")
|
||||
b.WriteString("Configure globally via the Tools page or override per profile.\n\n")
|
||||
b.WriteString("| Field | Type | Required | Default | Description |\n")
|
||||
b.WriteString("|-------|------|:--------:|---------|-------------|\n")
|
||||
for _, f := range fields {
|
||||
req := "-"
|
||||
if f.Required {
|
||||
req = "**yes**"
|
||||
}
|
||||
def := "-"
|
||||
if f.Default != nil && fmt.Sprintf("%v", f.Default) != "" {
|
||||
def = fmt.Sprintf("`%v`", f.Default)
|
||||
}
|
||||
desc := f.Description
|
||||
if desc == "" {
|
||||
desc = "-"
|
||||
}
|
||||
fmt.Fprintf(&b, "| `%s` | `%s` | %s | %s | %s |\n",
|
||||
f.Name, f.Type, req, def, desc,
|
||||
)
|
||||
}
|
||||
b.WriteString("\n")
|
||||
} else {
|
||||
b.WriteString("## Configuration\n\nThis tool has no configuration fields.\n\n")
|
||||
}
|
||||
} else if _, ok := r.(tools.Configurable); ok {
|
||||
b.WriteString("## Configuration\n\nThis tool is configurable but does not expose field metadata.\n\n")
|
||||
} else {
|
||||
b.WriteString("## Configuration\n\nThis tool requires no configuration.\n\n")
|
||||
}
|
||||
|
||||
b.WriteString("---\n\n")
|
||||
b.WriteString("[← Back to tools index](../tools.md)\n")
|
||||
|
||||
return writeFile(filepath.Join(outDir, "tools", r.Name()+".md"), b.String())
|
||||
}
|
||||
|
||||
func writeFile(path, content string) error {
|
||||
return os.WriteFile(path, []byte(content), 0o644)
|
||||
}
|
||||
|
||||
func fatalf(format string, args ...any) {
|
||||
fmt.Fprintf(os.Stderr, "gendocs: "+format+"\n", args...)
|
||||
os.Exit(1)
|
||||
}
|
||||
62
back/cmd/server/main.go
Normal file
62
back/cmd/server/main.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/anotherhadi/iknowyou/config/env"
|
||||
internalapi "github.com/anotherhadi/iknowyou/internal/api"
|
||||
"github.com/anotherhadi/iknowyou/internal/registry"
|
||||
"github.com/anotherhadi/iknowyou/internal/search"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfg, err := env.Load()
|
||||
if err != nil {
|
||||
log.Fatalf("env: %v", err)
|
||||
}
|
||||
|
||||
manager := search.NewManager(cfg.ConfigPath, registry.Factories, cfg.SearchTTL, cfg.CleanupInterval)
|
||||
defer manager.Stop()
|
||||
|
||||
if cfg.Demo {
|
||||
manager.InjectDemoSearches()
|
||||
log.Println("demo mode enabled")
|
||||
}
|
||||
|
||||
router := internalapi.NewRouter(manager, registry.Factories, cfg.ConfigPath, cfg.FrontDir, cfg.Demo)
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", cfg.Port),
|
||||
Handler: router,
|
||||
ReadTimeout: 10 * time.Second,
|
||||
WriteTimeout: 30 * time.Second,
|
||||
IdleTimeout: 60 * time.Second,
|
||||
}
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
go func() {
|
||||
log.Printf("listening on :%d (config: %s)", cfg.Port, cfg.ConfigPath)
|
||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatalf("server error: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
<-ctx.Done()
|
||||
log.Println("shutting down...")
|
||||
|
||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
if err := srv.Shutdown(shutdownCtx); err != nil {
|
||||
log.Fatalf("graceful shutdown failed: %v", err)
|
||||
}
|
||||
|
||||
log.Println("stopped")
|
||||
}
|
||||
Reference in New Issue
Block a user