A lightweight, tag-driven web platform to create and run business tools in Go. Define a struct with tags, auto-generate the web form, bind submitted data back to the struct, run your logic, and return results to the page. Supports organizing tools by business spaces.
Looking for Chinese docs? See: 中文说明 / README_zh.md
- Requirements: Go 1.22+
- Run:
cd kfunc
go run .
# Open http://localhost:8080- Tag-driven form generation from Go structs.
- Bind
url.Values→ struct with type conversion (string/int/float/bool/[]string). - Tool interface to plug in custom logic; results rendered on a result page.
- Business spaces to group tools; see tools per space.
- Clean templates; minimal dependencies.
/— Home, list spaces/spaces/{space}— Tools in the space/tools/{toolID}— GET form, POST execute and show result
.
├── internal/platform # platform core
│ ├── form.go # tags → fields, values → struct
│ └── registry.go # tool registry, space index
├── templates # HTML templates
│ ├── layout.html # base layout & styles
│ ├── index.html # spaces list
│ ├── space.html # tools list in a space
│ ├── form.html # dynamic form
│ └── result.html # result display
├── tools/examples # example tools
│ ├── register.go
│ ├── text.go
│ └── calc.go
└── main.go # HTTP routes & boot
A tool must implement:
// internal/platform/registry.go
type FormTool interface {
ID() string
Name() string
Description() string
Space() string
FormStruct() any
Run(ctx context.Context, form any) (any, error)
}Tags are key=value pairs separated by commas on exported fields. Supported keys:
type:text | number | textarea | select | multiselect | radio | checkboxlabel: label text shown in the formrequired:true | falseoptions: option list, separated by|(forselect,multiselect,radio)placeholder: placeholder textdefault: default valuename: field name in the form (override struct field name)
Example:
// tools/examples/text.go
type TextForm struct {
Content string `form:"type=textarea,label=Text,placeholder=Type here,required=true"`
Action string `form:"type=select,label=Action,options=Upper|Lower|Title,required=true"`
}// tools/examples/text.go
type TextTool struct{}
func (t *TextTool) ID() string { return "text_tool" }
func (t *TextTool) Name() string { return "Text Processor" }
func (t *TextTool) Description() string { return "String case transforms" }
func (t *TextTool) Space() string { return "content" }
func (t *TextTool) FormStruct() any { return &TextForm{} }
func (t *TextTool) Run(ctx context.Context, form any) (any, error) {
f := form.(*TextForm)
switch f.Action {
case "Upper":
return strings.ToUpper(f.Content), nil
case "Lower":
return strings.ToLower(f.Content), nil
case "Title":
return strings.Title(f.Content), nil
default:
return nil, fmt.Errorf("unknown action: %s", f.Action)
}
}Register tools (centralized):
// tools/examples/register.go
func Register() {
platform.RegisterTool(&TextTool{})
platform.RegisterTool(&CalcTool{})
}
// main.go
examples.Register()- Create a new package under
tools/yourpkg, define form struct withformtags and a type implementingFormTool. - Add a
Register()function in your package to callplatform.RegisterTool(...)for each tool. - Import and call your
yourpkg.Register()frommain.go. - Visit
/spaces/{yourSpace}to find your tool.
- Only exported struct fields are read and bound.
multiselectbinds to[]string.numberbinds tofloat64or any numeric kinds; parsing errors return 400.- Validation can be enhanced in
Run()or by extendingform.go.
- Routing is implemented with Gin (v1.9.1); HTML is rendered via
html/templateusing embedded templates (embed.FS). - Port: default is
8080. You can override viaPORTenv:PORT=8081 go run .. - Mainland China users: set Go module proxy if needed:
go env -w GOPROXY=https://goproxy.cn,direct. - Trusted proxies: in production, configure explicitly to avoid trusting all proxies:
r := gin.Default()
r.SetTrustedProxies(nil) // or set specific CIDR ranges