ikuaiapi

package module
v1.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 1, 2026 License: MIT Imports: 18 Imported by: 0

README ΒΆ

ikuai-api (v4)

A focused Go SDK for the iKuai v4.0 REST API used by iKuai routers (running iKuai OS 4.x). The SDK targets the v4 surface only β€” the v3 /Action/call protocol has been removed in this major version.

Go Report Card GoDoc License

Features

  • πŸš€ Zero third-party deps β€” uses only the Go standard library (net/http, encoding/json, context).
  • 🧰 151 typed methods across 13 functional groups, generated directly from the v4 endpoint catalog.
  • πŸ” Retry with exponential back-off for transient failures; per request and total timeout controlled by the same context.
  • πŸ§ͺ Dry-run mode β€” every call returns a JSON preview of the request it would have made, no router traffic.
  • πŸ” Catalog discovery β€” iterate all 151 endpoints at runtime with service.Endpoints() or service.Path<Group>().
  • πŸ›‘οΈ iKuai-specific quirks handled β€” the firmware emits bare nil instead of null; the SDK normalizes that before parsing. Envelope fields data, results, and rowid are normalized across endpoints.

Installation

go get github.com/zy84338719/ikuai-api

Quick start

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"
    "time"

    ikuaiapi "github.com/zy84338719/ikuai-api"
    "github.com/zy84338719/ikuai-api/service"
)

func main() {
    client, err := ikuaiapi.NewClient("https://192.168.1.1",
        ikuaiapi.WithToken("deadbeefcafebabe1234567890abcdef"),
        ikuaiapi.WithTimeout(15*time.Second),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    api := service.NewAPIClient(client)
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()

    raw, err := api.Monitoring().GetMonitoringSystem(ctx)
    if err != nil {
        log.Fatal(err)
    }
    var overview map[string]any
    if err := json.Unmarshal(raw, &overview); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("hostname: %v\n", overview["hostname"])
}

How to get a token

iKuai v4 uses Bearer tokens. Generate one in the router web UI:

  1. Sign in to the router as admin.
  2. Go to System β†’ Auth β†’ API Token.
  3. Click Generate and copy the 32-character hex string.

The SDK exposes ikuaiapi.ValidateToken(token) for early validation.

Service groups

Group Service Endpoints
advanced FTP / Samba / SNMP / HTTP 6
auth users, packages, web services, online users 4
interfaces LAN / WAN / physical / VLAN 4
log arp, auth, dhcp, ddns, notice, pppoe, system, web, wireless 9
monitoring system, cpu, memory, disk, network, clients, traffic, … 37
network dhcp, dmz, dnat, dns, nat, pppoe, qos, vlan, ac 25
objects domain / ip / ipv6 / mac / port / protocol / time 7
routing static / policy / 5-tuple / load-balance / app-protocols 6
security acl / mac / url / domain / peerconn / terminals 13
system basic / alg / ntp / cpufreq / kernel / reboot / backup / upgrade / … 28
vpn pptp / l2tp / openvpn / ikev2 / ipsec / wireguard 10
wireless access-control, vlan 2

Access any group via api.<Group>() (CamelCased). For example:

api.Network().ListNetworkDhcpServices(ctx, &service.NetworkDhcpServicesListOptions{
    Page: 1, PageSize: 50, Order: "desc", OrderBy: "id",
})

api.System().GetSystemBasicConfig(ctx)

api.Monitoring().GetMonitoringClientsOnline(ctx)

Method shapes

For each catalog entry, the generator emits one of two shapes:

  1. Single GET β€” <Group>Service.Get<Name>(ctx) (json.RawMessage, error)
  2. CRUD β€” a <Name>ListOptions struct plus List<Name> / Get<Name> / Create<Name> / Update<Name> / Patch<Name> (if PATCH is supported) / Delete<Name>. Create returns the rowid parsed out of the synthetic envelope the router emits on create responses.

The full path and supported verbs are documented above each generated method β€” for example:

// NetworkDhcpServices wraps network dhcp-services.
//
// Methods: GET, POST, PUT, PATCH
//
// Path: /network/dhcp/services
//
// Use ListNetworkDhcpServices(ctx, opts...) for paginated reads.

Escape hatch: catalog-driven calls

If a generated helper does not exist yet, or you need to call a method that is not in the catalog, use APIClient.Call:

raw, err := api.Call(ctx, "interfaces", "wan-config", "GET", nil, nil)

Call resolves the (group, name) pair from the catalog and dispatches the request with the supplied method, body and query params.

Options

client, _ := ikuaiapi.NewClient("https://192.168.1.1",
    ikuaiapi.WithToken("..."),                     // Bearer token
    ikuaiapi.WithTimeout(15*time.Second),          // per-request timeout
    ikuaiapi.WithInsecureSkipVerify(true),         // trust self-signed cert
    ikuaiapi.WithHTTPClient(customHTTP),           // bring your own http.Client
    ikuaiapi.WithAPIBase("/api/v4.0"),             // override the path prefix
    ikuaiapi.WithRawMode(true),                    // return the full envelope
    ikuaiapi.WithDryRun(true),                     // print requests, do not send
    ikuaiapi.WithRetry(3),                         // total attempt count
    ikuaiapi.WithRetryDelay(200*time.Millisecond, 5*time.Second),
    ikuaiapi.WithLogger(func(format string, args ...any) { log.Printf(format, args...) }),
    ikuaiapi.WithMetrics(ikuaiapi.NewMetrics()),           // count requests / latency (v1.1.0+)
    ikuaiapi.WithStructuredLogger(ikuaiapi.NewDefaultLogger(ikuaiapi.LogLevelInfo)), // leveled logger (v1.1.0+)
)

Observability (v1.1.0+)

  • WithMetrics(*Metrics) wires a request counter/latency collector into every call. Read snapshot stats with client.Metrics().GetStats() β†’ (requestCount, errorCount, avgDuration); handy for a /metrics endpoint or a health check. Use Metrics.Reset() between scrapes.
  • WithStructuredLogger(Logger) routes retry / timeout / debug events through a leveled Logger interface (Debug/Info/Warn/Error) instead of the printf-style WithLogger callback. NewDefaultLogger(level) returns a stdlib-backed one; adapt zap/zerolog by implementing the interface.
  • SDKVersion is the semantic version string ("1.1.0") for runtime introspection.

Error model

Two typed errors come back from every call:

  • *ikuaiapi.APIError β€” protocol-level failure: non-zero code, HTTP 4xx/5xx, or unparseable JSON. Fields: HTTPStatus, Code, Message, Details (per-field validation errors), and RetryAfter (parsed from the HTTP Retry-After header on 429/503, v1.1.0+).
  • *ikuaiapi.NetworkError β€” transport failure: DNS, refused, TLS, timeout. Wraps the original net / tls / http error.

Both error types implement IsRetryable() bool (v1.1.0+), exposing the SDK's own retryability judgement so applications can reuse it for custom retry loops or circuit breakers: APIError is retryable on HTTP 429 / 5xx; NetworkError is retryable. Note the SDK only auto-retries network errors for idempotent verbs (GET/HEAD/OPTIONS/DELETE) β€” writes are never auto-retried, since the request may have reached the router.

_, err := api.Network().GetNetworkDnsConfig(ctx)
var apiErr *ikuaiapi.APIError
switch {
case errors.As(err, &apiErr):
    fmt.Printf("router said no: code=%d status=%d msg=%q\n",
        apiErr.Code, apiErr.HTTPStatus, apiErr.Message)
case errors.As(err, &netErr):
    fmt.Printf("transport: %v\n", netErr)
}

Regenerating the service layer

The service/ package is generated from v4_catalog.go. After editing the catalog, regenerate:

go run ./codegen

The generator parses the catalog literal with a regex (no parent-package import needed, so no import cycle), then writes one file per group plus service/root.go. The output is deterministic and checked in.

Project layout

.
β”œβ”€β”€ README.md             β€” this file
β”œβ”€β”€ go.mod                β€” zero external dependencies
β”œβ”€β”€ auth.go               β€” token validation helpers
β”œβ”€β”€ client.go             β€” net/http + retry + envelope + sanitization
β”œβ”€β”€ errors.go             β€” APIError, NetworkError, error hints
β”œβ”€β”€ version.go            β€” Version enum (V4 only)
β”œβ”€β”€ v4_catalog.go         β€” the source of truth for all 151 endpoints
β”œβ”€β”€ logger.go             β€” optional structured logging
β”œβ”€β”€ codegen/              β€” service-layer generator (run with `go run ./codegen`)
β”œβ”€β”€ service/              β€” generated, 13 files, one per group
β”‚   β”œβ”€β”€ root.go           β€” APIClient entry point + Call()
β”‚   β”œβ”€β”€ advanced.go
β”‚   β”œβ”€β”€ auth.go
β”‚   β”œβ”€β”€ interfaces.go
β”‚   β”œβ”€β”€ log.go
β”‚   β”œβ”€β”€ monitoring.go
β”‚   β”œβ”€β”€ network.go
β”‚   β”œβ”€β”€ objects.go
β”‚   β”œβ”€β”€ routing.go
β”‚   β”œβ”€β”€ security.go
β”‚   β”œβ”€β”€ system.go
β”‚   β”œβ”€β”€ vpn.go
β”‚   └── wireless.go
β”œβ”€β”€ internal/             β€” small helpers shared by core + generated
β”œβ”€β”€ example/              β€” runnable demo (env-driven)
└── *_test.go             β€” unit tests for every layer

Testing

go test ./...

The tests use httptest.Server to stand in for a real router. They cover:

  • SanitizeNil edge cases (CRLF, escaped quotes, identifier boundaries).
  • Token validation.
  • Envelope handling for data, results, rowid and bare nil.
  • Retry on 5xx.
  • Dry-run mode.
  • Every endpoint in the catalog (round-trip via api.Call).

License

MIT. See LICENSE.

Documentation ΒΆ

Overview ΒΆ

Package ikuaiapi provides a Go SDK for interacting with iKuai routers using the local v4.0 REST API.

The SDK uses the Go standard library net/http with a small custom layer (retry + timeout + sanitization) and exposes a typed service layer per functional area (system, network, firewall, monitor, ...).

Authentication uses a Bearer token obtained from the router web UI (System β†’ Auth β†’ API Token). iKuai OS v4.x exposes all router configuration under /api/v4.0/*.

Basic usage:

client, err := ikuaiapi.NewClient("https://192.168.1.1",
    ikuaiapi.WithToken("<router-api-token>"),
)
if err != nil {
    log.Fatal(err)
}
defer client.Close()

api := service.NewAPIClient(client)
iface, err := api.Network().GetInterfaces(ctx)

Package version: the iKuai API only supports the v4 REST surface.

Index ΒΆ

Constants ΒΆ

View Source
const SDKVersion = "1.1.1"

SDKVersion is the semantic version of this SDK.

View Source
const TokenHelp = "obtain a token from the router web UI (System β†’ Auth β†’ API Token)"

TokenHelp is a short, copy-pasteable instruction for obtaining a token from an iKuai router web UI. The SDK does not log in on behalf of the caller: tokens are generated manually in System β†’ Auth β†’ API Token.

View Source
const V4APIBase = "/api/v4.0"

V4APIBase is the canonical root for all iKuai v4 REST endpoints.

Variables ΒΆ

View Source
var V4EndpointCatalog = []V4Endpoint{}/* 151 elements not displayed */

Functions ΒΆ

func SanitizeNil ΒΆ

func SanitizeNil(body []byte) []byte

SanitizeNil replaces bare `nil` tokens in JSON value positions with `null`. Some iKuai firmware emits `nil` instead of `null`; the function tracks string state to avoid corrupting legitimate string content.

func ValidateToken ΒΆ

func ValidateToken(token string) error

ValidateToken returns an error if the token looks malformed. iKuai router tokens are 32-character lowercase hex strings.

Types ΒΆ

type APIError ΒΆ

type APIError struct {
	HTTPStatus int
	Code       int
	Message    string
	Details    []APIErrorDetail
	// RetryAfter, when non-zero, carries a server-advised back-off hint
	// (parsed from the HTTP Retry-After header, typically on 429/503).
	RetryAfter time.Duration
}

APIError is returned when the router replies with a non-success envelope or an HTTP 4xx/5xx status. It is the only typed error the SDK raises for protocol-level failures; transport failures come back as *NetworkError.

func (*APIError) Error ΒΆ

func (e *APIError) Error() string

func (*APIError) IsRetryable ΒΆ added in v1.1.0

func (e *APIError) IsRetryable() bool

IsRetryable on an *APIError reflects server-side retryability.

type APIErrorDetail ΒΆ

type APIErrorDetail struct {
	Field string `json:"field"`
	Type  string `json:"type"`
	Msg   string `json:"msg"`
}

type Client ΒΆ

type Client struct {
	BaseURL    string
	Token      string
	APIBase    string
	HTTPClient *http.Client
	UserAgent  string

	// RawMode returns the full JSON envelope (data/results/rowid/code/message)
	// instead of just the data field. Useful for debugging.
	RawMode bool
	// DryRun reports the request it would have made without contacting the
	// router. Read methods return the preview as a JSON object, write
	// methods return without executing.
	DryRun bool
	// Logger, if set, receives short human-readable status lines.
	Logger func(format string, args ...any)
	// contains filtered or unexported fields
}

Client is the iKuai HTTP API client.

func NewClient ΒΆ

func NewClient(baseURL string, opts ...ClientOption) (*Client, error)

NewClient creates a Client targeting the given router. baseURL should be of the form "http://192.168.1.1" or "https://router.lan:443".

func (*Client) Close ΒΆ

func (c *Client) Close()

Close releases the underlying transport. Safe to call multiple times.

func (*Client) Delete ΒΆ

func (c *Client) Delete(ctx context.Context, p string, body any) (json.RawMessage, error)

Delete issues a DELETE request. The optional body is sent as JSON.

func (*Client) Do ΒΆ

func (c *Client) Do(ctx context.Context, method, p string, body, out any) error

Do executes a typed REST call and decodes the result into out (which may be nil for requests that only return a rowid/message).

func (*Client) FormatQuery ΒΆ added in v1.0.1

func (c *Client) FormatQuery(q map[string]string) string

FormatQuery is exported for callers that need to assemble a query string from a map (e.g. the Call escape hatch appends ?key=value to the path for DELETE requests that iKuai drives with query params).

func (*Client) Get ΒΆ

func (c *Client) Get(ctx context.Context, p string, params map[string]string) (json.RawMessage, error)

Get issues a GET request. params is optional and added to the query string.

func (*Client) Metrics ΒΆ added in v1.1.0

func (c *Client) Metrics() *Metrics

Metrics returns the attached Metrics collector, or nil if none was set.

func (*Client) Patch ΒΆ

func (c *Client) Patch(ctx context.Context, p string, body any) (json.RawMessage, error)

Patch issues a PATCH request with a JSON body.

func (*Client) Post ΒΆ

func (c *Client) Post(ctx context.Context, p string, body any) (json.RawMessage, error)

Post issues a POST request with a JSON body.

func (*Client) Put ΒΆ

func (c *Client) Put(ctx context.Context, p string, body any) (json.RawMessage, error)

Put issues a PUT request with a JSON body.

type ClientOption ΒΆ

type ClientOption func(*Client)

ClientOption configures a Client at construction time.

func WithAPIBase ΒΆ

func WithAPIBase(base string) ClientOption

WithAPIBase overrides the default /api/v4.0 prefix.

func WithDryRun ΒΆ

func WithDryRun(dry bool) ClientOption

WithDryRun reports the request it would have made without contacting the router.

func WithHTTPClient ΒΆ

func WithHTTPClient(h *http.Client) ClientOption

WithHTTPClient replaces the underlying *http.Client. Callers that need proxy, custom CA, or tracing support can pass their own.

func WithInsecureSkipVerify ΒΆ

func WithInsecureSkipVerify(skip bool) ClientOption

WithInsecureSkipVerify disables TLS certificate verification. iKuai routers use self-signed certificates by default, so this is normally the desired behaviour. Use only on trusted networks.

func WithLogger ΒΆ

func WithLogger(fn func(format string, args ...any)) ClientOption

WithLogger sets a logging callback. The callback is invoked once per request with a short status line. Prefer WithStructuredLogger for new code.

func WithMetrics ΒΆ added in v1.1.0

func WithMetrics(m *Metrics) ClientOption

WithMetrics attaches a Metrics collector. When set, every request records its duration and outcome (see Metrics.RecordRequest). Use GetStats to read counters, e.g. for a /metrics endpoint or health check.

func WithRawMode ΒΆ

func WithRawMode(raw bool) ClientOption

WithRawMode enables envelope-level responses (see Client.RawMode).

func WithRetry ΒΆ

func WithRetry(retryMax int) ClientOption

WithRetry configures exponential-back-off retries. retryMax is the total attempt count (initial + retries). The default is 3.

func WithRetryDelay ΒΆ

func WithRetryDelay(base, max time.Duration) ClientOption

WithRetryDelay sets the base delay and maximum delay for retries.

func WithStructuredLogger ΒΆ added in v1.1.0

func WithStructuredLogger(l Logger) ClientOption

WithStructuredLogger attaches a leveled, structured Logger (see logger.go). When set, retry / timeout / token-failure events are emitted through it instead of the printf-style Logger callback.

func WithTimeout ΒΆ

func WithTimeout(d time.Duration) ClientOption

WithTimeout sets the per-request timeout. The same value is also used as the overall upper bound for retried requests.

func WithToken ΒΆ

func WithToken(token string) ClientOption

WithToken sets the Bearer token used on every request.

type LogLevel ΒΆ

type LogLevel int
const (
	LogLevelDebug LogLevel = iota
	LogLevelInfo
	LogLevelWarn
	LogLevelError
	LogLevelNone
)

type Logger ΒΆ

type Logger interface {
	Debug(msg string, args ...interface{})
	Info(msg string, args ...interface{})
	Warn(msg string, args ...interface{})
	Error(msg string, args ...interface{})
}

func NewDefaultLogger ΒΆ

func NewDefaultLogger(level LogLevel) Logger

type Metrics ΒΆ

type Metrics struct {
	// contains filtered or unexported fields
}

func NewMetrics ΒΆ

func NewMetrics() *Metrics

func (*Metrics) GetStats ΒΆ

func (m *Metrics) GetStats() (count int64, errors int64, avgDuration time.Duration)

func (*Metrics) RecordRequest ΒΆ

func (m *Metrics) RecordRequest(duration time.Duration, hasError bool)

func (*Metrics) Reset ΒΆ

func (m *Metrics) Reset()

type NetworkError ΒΆ

type NetworkError struct {
	Message string
	Cause   error
}

NetworkError wraps connection-level failures (DNS, refused, TLS, timeout).

func (*NetworkError) Error ΒΆ

func (e *NetworkError) Error() string

func (*NetworkError) IsRetryable ΒΆ added in v1.1.0

func (e *NetworkError) IsRetryable() bool

IsRetryable reports whether a caller may safely retry the request that produced this error. It encodes the SDK's own retry policy so applications can reuse it for custom retry loops or circuit-breaker decisions.

  • *NetworkError: retryable (transport hiccups usually clear up), but only for idempotent verbs β€” the SDK never auto-retries a write on a network error because the request may have reached the router.
  • *APIError: retryable on HTTP 429 (rate limited), 5xx, and gateway errors. 4xx (other than 429) are not retryable.

Pass the HTTP method to qualify network errors: IsRetryable on a *NetworkError returns false for POST/PUT/PATCH to avoid duplicate writes.

func (*NetworkError) Unwrap ΒΆ

func (e *NetworkError) Unwrap() error

type V4Endpoint ΒΆ

type V4Endpoint struct {
	Group   string
	Name    string
	Path    string
	Methods []string
	// Load marks monitoring load-style endpoints. Such endpoints accept
	// datetype/start_time/end_time/math query params rather than the
	// usual page/page_size/filter/order/order_by. The codegen emits a
	// typed <Name>LoadOptions struct plus enum validation for these.
	Load bool
	// Action is the verb suffix for action-style endpoints (those whose
	// path ends with ":start", ":stop", ":restart", ":sync", ":restore",
	// ":check"). The codegen uses this to emit semantically named
	// helpers (Start<Name>, Stop<Name>, Restore<Name>, etc.) instead of
	// a generic Do<Name>. Empty means the endpoint follows the standard
	// CRUD shape.
	Action string
}

func V4EndpointByGroupName ΒΆ

func V4EndpointByGroupName(group, name string) (V4Endpoint, bool)

V4EndpointByGroupName resolves an endpoint by its (group, name) pair. Use this when the same Name appears under multiple Groups (e.g. "system" exists in both "log" and "monitoring").

func V4EndpointByName ΒΆ

func V4EndpointByName(name string) (V4Endpoint, bool)

type Version ΒΆ

type Version int

Version enumerates the iKuai API generations the SDK recognises.

const (
	// VersionUnknown is the zero value; iKuai API v4 is the only supported
	// version, so new clients should pass VersionV4 explicitly.
	VersionUnknown Version = iota
	// VersionV4 is the current iKuai REST API (/api/v4.0).
	VersionV4
)

func (Version) String ΒΆ

func (v Version) String() string

Directories ΒΆ

Path Synopsis
codegen generates the v4 service layer from v4_catalog.go.
codegen generates the v4 service layer from v4_catalog.go.
Example program demonstrating the v4 SDK.
Example program demonstrating the v4 SDK.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL