Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
8ca9ece
build(deps): bump github.com/blevesearch/bleve_index_api
dependabot[bot] Apr 24, 2026
cdeb3a8
build(deps-dev): bump eslint from 10.2.0 to 10.2.1 in /webui
dependabot[bot] Apr 24, 2026
552094b
Merge pull request #943 from marle3003/dependabot/npm_and_yarn/webui/…
github-actions[bot] Apr 24, 2026
83b55df
build(deps): bump vue-router from 5.0.4 to 5.0.6 in /webui
dependabot[bot] Apr 24, 2026
c369d2e
Merge pull request #947 from marle3003/dependabot/npm_and_yarn/webui/…
github-actions[bot] Apr 24, 2026
bfdb1d6
build(deps): bump vue from 3.5.32 to 3.5.33 in /webui
dependabot[bot] Apr 24, 2026
e0dcdd4
Merge pull request #945 from marle3003/dependabot/npm_and_yarn/webui/…
github-actions[bot] Apr 24, 2026
e83d90c
build(deps-dev): bump vue-tsc from 3.2.6 to 3.2.7 in /webui
dependabot[bot] Apr 24, 2026
c44176b
Merge pull request #946 from marle3003/dependabot/npm_and_yarn/webui/…
github-actions[bot] Apr 24, 2026
2f386ec
build(deps-dev): bump vite from 8.0.8 to 8.0.10 in /webui
dependabot[bot] Apr 24, 2026
0e66dcb
Merge pull request #944 from marle3003/dependabot/npm_and_yarn/webui/…
github-actions[bot] Apr 24, 2026
3552ed2
feat(search): improve search feature and displaying results in dashboard
marle3003 Apr 26, 2026
b13e712
Merge remote-tracking branch 'origin/develop' into develop
marle3003 Apr 26, 2026
64cd556
fix: add missing files
marle3003 Apr 26, 2026
e3f488d
feat(search): improve search for events
marle3003 Apr 28, 2026
ecf59ae
fix(webui) add missing file
marle3003 Apr 28, 2026
ab289d8
test(search): improve test stability
marle3003 Apr 28, 2026
4a53464
fix(imap): fix parsing of optional date in APPEND command
marle3003 Apr 28, 2026
4651465
test: replace require with assert
marle3003 Apr 28, 2026
6979354
Merge branch 'develop' into dependabot/go_modules/develop/github.com/…
marle3003 Apr 28, 2026
ebf9788
Merge pull request #942 from marle3003/dependabot/go_modules/develop/…
github-actions[bot] Apr 28, 2026
62091e4
fix(webui): fix table width if long URLs are present
marle3003 Apr 29, 2026
f4bad78
fix(npm): fix concurrent map reads
marle3003 Apr 29, 2026
f5672ed
fix(webui): display Kafka key convert from base64 using UTF-8
marle3003 Apr 29, 2026
61c201b
fix(webui): display Kafka key convert from base64 using UTF-8
marle3003 Apr 29, 2026
5911fb4
fix(webui): improve order of Kafka message column
marle3003 Apr 29, 2026
7e6a026
test: fix Kafka message column order
marle3003 Apr 29, 2026
aa74d10
build(deps): bump github.com/blevesearch/bleve/v2 from 2.5.7 to 2.6.0
dependabot[bot] May 1, 2026
e6aa78b
build(deps): bump github.com/fsnotify/fsnotify from 1.9.0 to 1.10.0
dependabot[bot] May 1, 2026
65b93e4
build(deps): bump nodemailer from 8.0.5 to 8.0.7 in /webui
dependabot[bot] May 1, 2026
6b056f4
wip(mqtt): add ping and disconnect
marle3003 May 1, 2026
3c2312c
Merge remote-tracking branch 'origin/develop' into develop
marle3003 May 1, 2026
9ea547b
test(mqtt): fix compile error
marle3003 May 1, 2026
718d677
test(webui): fix e2e test
marle3003 May 1, 2026
dcf57e1
Merge branch 'develop' into dependabot/npm_and_yarn/webui/develop/nod…
marle3003 May 1, 2026
677fe43
Merge pull request #953 from marle3003/dependabot/npm_and_yarn/webui/…
github-actions[bot] May 1, 2026
bbc57f8
build(deps-dev): bump eslint-plugin-vue from 10.8.0 to 10.9.0 in /webui
dependabot[bot] May 1, 2026
598e04e
Merge pull request #952 from marle3003/dependabot/npm_and_yarn/webui/…
github-actions[bot] May 1, 2026
fa06677
wip(mqtt): improve separation between kafka and mqtt
marle3003 May 1, 2026
09eff5d
Merge remote-tracking branch 'origin/develop' into develop
marle3003 May 1, 2026
b877d10
wip(mqtt): add MQTT dashboard
marle3003 May 3, 2026
ca15827
wip(mqtt): fix demo-dashboard
marle3003 May 3, 2026
9c85c9e
Merge branch 'develop' into dependabot/go_modules/develop/github.com/…
marle3003 May 3, 2026
0232865
test(e2e): fix date issue on GitHub
marle3003 May 3, 2026
3b31ee7
test(e2e): fix date issue on GitHub
marle3003 May 3, 2026
8af5eec
chore(webui): update promo config
marle3003 May 3, 2026
8b73469
fix(webui): fix mqtt port issue
marle3003 May 4, 2026
313d9e9
test: remove IP address from test expectation
marle3003 May 4, 2026
0b24a12
perf(search): improve search index build performance
marle3003 May 7, 2026
8f30774
fix: add missing files
marle3003 May 7, 2026
de61831
fix(webui): build error
marle3003 May 7, 2026
62f618a
fix(api): HTTP operation method was not set correctly
marle3003 May 7, 2026
b1b5bcb
Merge branch 'develop' into dependabot/go_modules/develop/github.com/…
marle3003 May 7, 2026
bc5413b
Merge pull request #951 from marle3003/dependabot/go_modules/develop/…
github-actions[bot] May 7, 2026
9261185
Merge branch 'develop' into dependabot/go_modules/develop/github.com/…
marle3003 May 7, 2026
91adf02
Merge pull request #950 from marle3003/dependabot/go_modules/develop/…
github-actions[bot] May 7, 2026
a9c264c
build(deps): bump github.com/modelcontextprotocol/go-sdk
dependabot[bot] May 7, 2026
5be2998
Merge pull request #949 from marle3003/dependabot/go_modules/develop/…
github-actions[bot] May 7, 2026
6feb18b
build(deps-dev): bump @types/node from 25.6.0 to 25.6.2 in /webui
dependabot[bot] May 8, 2026
87b93eb
Merge pull request #956 from marle3003/dependabot/npm_and_yarn/webui/…
github-actions[bot] May 8, 2026
ebf8afb
build(deps-dev): bump vite from 8.0.10 to 8.0.11 in /webui
dependabot[bot] May 8, 2026
fa6ba69
Merge pull request #957 from marle3003/dependabot/npm_and_yarn/webui/…
github-actions[bot] May 8, 2026
1ca5c6c
build(deps): bump vue from 3.5.33 to 3.5.34 in /webui
dependabot[bot] May 8, 2026
5302401
Merge pull request #958 from marle3003/dependabot/npm_and_yarn/webui/…
github-actions[bot] May 8, 2026
c1d2941
build(deps-dev): bump vue-tsc from 3.2.7 to 3.2.8 in /webui
dependabot[bot] May 8, 2026
fbfdac2
Merge pull request #960 from marle3003/dependabot/npm_and_yarn/webui/…
github-actions[bot] May 8, 2026
4c44dcc
build(deps-dev): bump eslint-plugin-vue from 10.9.0 to 10.9.1 in /webui
dependabot[bot] May 8, 2026
3fddc72
Merge pull request #959 from marle3003/dependabot/npm_and_yarn/webui/…
github-actions[bot] May 8, 2026
5714c93
performance(webui): improve API to reduce payload
marle3003 May 10, 2026
91703b4
Merge remote-tracking branch 'origin/develop' into develop
marle3003 May 10, 2026
0785973
build(deps): bump github.com/fsnotify/fsnotify from 1.10.0 to 1.10.1
dependabot[bot] May 10, 2026
c270e4d
Merge pull request #955 from marle3003/dependabot/go_modules/develop/…
github-actions[bot] May 10, 2026
62f450d
build(deps): bump github.com/go-git/go-git/v5 from 5.18.0 to 5.19.0
dependabot[bot] May 10, 2026
7493f86
Merge pull request #954 from marle3003/dependabot/go_modules/develop/…
github-actions[bot] May 10, 2026
3590d27
fix(webui): fix kafka client views
marle3003 May 10, 2026
6831091
Merge remote-tracking branch 'origin/develop' into develop
marle3003 May 10, 2026
2b704e6
fix(mcp): fix produce Kafka message validating message
marle3003 May 10, 2026
e56968a
Merge branch 'main' into develop
marle3003 May 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/publish-website/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ runs:
run: docker load --input /tmp/mokapi.tar
shell: bash
- name: Run mokapi image
run: docker run --name mokapi --rm -d -p 80:80 -p 8080:8080 -p 9092:9092 -p 8389:8389 -p 8025:8025 --mount type=bind,source=$(pwd)/webui/scripts/dashboard-demo/demo-configs,target=/data --env MOKAPI_Providers_File_Directory=/data ${{ inputs.image-name }}
run: docker run --name mokapi --rm -d -p 80:80 -p 1883:1883 -p 8080:8080 -p 9092:9092 -p 8389:8389 -p 8025:8025 --mount type=bind,source=$(pwd)/webui/scripts/dashboard-demo/demo-configs,target=/data --env MOKAPI_Providers_File_Directory=/data ${{ inputs.image-name }}
shell: bash
- name: Build Demo Dashboard
working-directory: ./webui/scripts/dashboard-demo
Expand Down
10 changes: 6 additions & 4 deletions acceptance/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,12 @@ func Start(cfg *static.Config) (*Cmd, error) {
})

apiHandler := api.New(app, cfg.Api)
if u, err := api.BuildUrl(cfg.Api); err == nil {
err = http.AddInternalService("api", u, apiHandler)
if err != nil {
return nil, err
if urls, err := api.BuildUrl(cfg.Api); err == nil {
for _, u := range urls {
err = http.AddInternalService("api", u, apiHandler)
if err != nil {
return nil, err
}
}
} else {
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion acceptance/ldap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (suite *LdapSuite) TestMetric() {
}
_, err := suite.Client.Search(search)
require.NoError(suite.T(), err)
require.Equal(suite.T(), float64(1), suite.cmd.App.Monitor.Ldap.RequestCounter.Sum())
require.Equal(suite.T(), float64(1), suite.cmd.App.Monitor.Ldap.RequestCounter.Sum(metrics.NewQuery()))
q := metrics.NewQuery(metrics.ByNamespace("ldap"), metrics.ByName("request_timestamp"))
require.Greater(suite.T(), suite.cmd.App.Monitor.Ldap.LastRequest.Value(q), float64(1))
}
Expand Down
3 changes: 3 additions & 0 deletions acceptance/petstore/asyncapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ servers:
broker:
url: 127.0.0.1:19092
protocol: kafka
mqtt:
url: 127.0.0.1:11883
protocol: mqtt
channels:
petstore.order-event:
subscribe:
Expand Down
88 changes: 20 additions & 68 deletions acceptance/petstore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func (suite *PetStoreSuite) SetupSuite() {
cfg.Providers.File.Directories = []static.FileConfig{{Path: "./petstore"}}
cfg.Api.Search.Enabled = true
cfg.Api.Search.InMemory = true
cfg.Api.Search.NumIndexWorker = 1
suite.initCmd(cfg)
}

Expand All @@ -54,78 +55,21 @@ func (suite *PetStoreSuite) TestApi() {

suite.T().Run("get AsyncAPI service", func(t *testing.T) {
expected := map[string]interface{}{
"version": "1.0.0",
"name": "A sample AsyncApi Kafka streaming api",
"description": "",
"version": "1.0.0",
"name": "A sample AsyncApi Kafka streaming api",
"servers": []interface{}{
map[string]interface{}{
"host": "127.0.0.1:19092",
"name": "broker",
"protocol": "kafka",
"title": "",
"summary": "",
"description": "",
"host": "127.0.0.1:19092",
"name": "broker",
"protocol": "kafka",
},
},

"topics": []interface{}{map[string]interface{}{
"bindings": map[string]interface{}{"partitions": float64(2), "segmentMs": float64(30000), "valueSchemaValidation": true},
"description": "",
"messages": map[string]interface{}{
"order": map[string]interface{}{
"name": "order",
"contentType": "application/json",
"header": map[string]interface{}{
"schema": map[string]interface{}{
"properties": map[string]interface{}{
"number": map[string]interface{}{
"type": "number",
}, "test": map[string]interface{}{
"type": "string",
},
}, "type": "object",
},
},
"key": map[string]interface{}{
"schema": map[string]interface{}{
"type": "string",
},
},
"payload": map[string]interface{}{
"schema": map[string]interface{}{
"properties": map[string]interface{}{
"accepted": map[string]interface{}{
"properties": map[string]interface{}{
"timestamp": map[string]interface{}{"format": "date-time", "type": "string"},
},
"type": "object",
},
"completed": map[string]interface{}{
"properties": map[string]interface{}{
"timestamp": map[string]interface{}{"format": "date-time", "type": "string"},
},
"type": "object",
},
"id": map[string]interface{}{"type": "integer"},
"placed": map[string]interface{}{
"properties": map[string]interface{}{
"petid": map[string]interface{}{"type": "integer"},
"quantity": map[string]interface{}{"format": "int32", "type": "integer"},
"ship-date": map[string]interface{}{"format": "date-time", "type": "string"},
},
"type": "object",
},
},
"required": []interface{}{"id"},
"type": "object",
},
},
},
},
"name": "petstore.order-event",
"partitions": []interface{}{
map[string]interface{}{"id": float64(0), "offset": float64(1), "segments": float64(1), "startOffset": float64(0)},
map[string]interface{}{"id": float64(1), "offset": float64(0), "segments": float64(0), "startOffset": float64(0)},
"metrics": map[string]interface{}{
// skip timestamp check "kafka_message_timestamp"
"kafka_messages_total": float64(1),
},
}},
}
Expand Down Expand Up @@ -220,7 +164,7 @@ func (suite *PetStoreSuite) TestKafka_TopicConfig() {
require.Equal(suite.T(), "petstore.order-event", r.Topics[0].Name)
require.Len(suite.T(), r.Topics[0].Partitions, 2)

require.Len(suite.T(), suite.cmd.App.ListHttp(), 1)
require.Equal(suite.T(), 1, suite.cmd.App.Http.Len())
}

func (suite *PetStoreSuite) TestKafka_Produce_InvalidFormat() {
Expand Down Expand Up @@ -303,8 +247,16 @@ func (suite *PetStoreSuite) TestEvents() {
assert.True(t, ok, "event should be a map[string]any")
assert.NotNil(t, evt)
assert.Equal(t, "Event", evt["type"])
assert.Equal(t, "GET http://127.0.0.1:18080/user/bob", evt["title"])
assert.Equal(t, "http://127.0.0.1:18080/user/bob", evt["title"])
assert.Equal(t, "Swagger Petstore", evt["domain"])
params := evt["params"].(map[string]any)
assert.Len(t, params, 6)
assert.Equal(t, "event", params["type"])
assert.Equal(t, "http", params["traits.namespace"])
assert.Equal(t, "Swagger Petstore", params["traits.name"])
assert.Equal(t, "/user/{username}", params["traits.path"])
assert.Equal(t, "GET", params["traits.method"])
assert.Equal(t, "Swagger Petstore", params["traits.name"])
}),
)
}
Expand Down Expand Up @@ -389,7 +341,7 @@ func (suite *PetStoreSuite) TestSearch_Paging() {
assert.Len(t, items, 10)
evt := items[0].(map[string]interface{})
assert.Equal(t, "HTTP", evt["type"])
assert.Equal(t, "GET /pet/{petId}", evt["title"])
assert.Equal(t, "/pet/{petId}", evt["title"])
assert.Equal(t, "Swagger Petstore", evt["domain"])
}),
)
Expand Down
108 changes: 64 additions & 44 deletions api/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"io/fs"
"mokapi/config/static"
"mokapi/runtime"
"mokapi/runtime/metrics"
"mokapi/webui"
"net/http"
"net/url"
Expand All @@ -16,6 +15,7 @@ import (
"strconv"
"strings"

"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
)

Expand All @@ -36,6 +36,7 @@ type handler struct {
healthHandler http.Handler
mcpPath string
mcpHandler http.Handler
router *mux.Router
}

type info struct {
Expand All @@ -56,15 +57,17 @@ var (
ServiceKafka serviceType = "kafka"
ServiceMail serviceType = "mail"
ServiceLdap serviceType = "ldap"
ServiceMqtt serviceType = "mqtt"
)

type service struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Contact *contact `json:"contact,omitempty"`
Version string `json:"version,omitempty"`
Type serviceType `json:"type"`
Metrics []metrics.Metric `json:"metrics,omitempty"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Contact *contact `json:"contact,omitempty"`
Version string `json:"version,omitempty"`
Type serviceType `json:"type"`
Status string `json:"status,omitempty"`
Metrics any `json:"metrics,omitempty"`
}

type contact struct {
Expand All @@ -83,6 +86,7 @@ func New(app *runtime.App, config static.Api) Handler {
path: config.Path,
base: config.Base,
app: app,
router: mux.NewRouter(),
}

if config.Dashboard {
Expand All @@ -101,12 +105,26 @@ func New(app *runtime.App, config static.Api) Handler {
h.fileServer = http.FileServer(http.FS(dist))
}

h.setupHttp()
h.setupKafka()
h.setupMqtt()

return h
}

func BuildUrl(cfg static.Api) (*url.URL, error) {
s := fmt.Sprintf("http://:%v%v", cfg.Port, cfg.Path)
return url.Parse(s)
func BuildUrl(cfg static.Api) ([]*url.URL, error) {
var urls []*url.URL
u, err := url.Parse(fmt.Sprintf("http://:%v%v", cfg.Port, cfg.Path))
if err != nil {
return urls, err
}
urls = append(urls, u)
u, err = url.Parse(fmt.Sprintf("http://localhost:%v%v", cfg.Port, cfg.Path))
if err != nil {
return urls, err
}
urls = append(urls, u)
return urls, nil
}

func (h *handler) RegisterHealthHandler(path string, handler http.Handler) {
Expand Down Expand Up @@ -134,10 +152,6 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.getInfo(w, r)
case p == "/api/services":
h.getServices(w, r)
case strings.HasPrefix(p, "/api/services/http/"):
h.getHttpService(w, r, h.app.Monitor)
case strings.HasPrefix(p, "/api/services/kafka"):
h.handleKafka(w, r)
case strings.HasPrefix(p, "/api/services/mail/"):
h.handleMailService(w, r)
case strings.HasPrefix(p, "/api/services/ldap/"):
Expand All @@ -164,6 +178,8 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.healthHandler.ServeHTTP(w, r)
case strings.HasPrefix(p, h.mcpPath) && h.mcpHandler != nil:
h.mcpHandler.ServeHTTP(w, r)
case strings.HasPrefix(p, "/api/"):
h.router.ServeHTTP(w, r)
case h.fileServer != nil:
if r.Method != "GET" {
http.Error(w, fmt.Sprintf("method %v is not allowed", r.Method), http.StatusMethodNotAllowed)
Expand Down Expand Up @@ -198,14 +214,28 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}

func (h *handler) getServices(w http.ResponseWriter, _ *http.Request) {
services := make([]interface{}, 0)
services = append(services, getHttpServices(h.app.ListHttp(), h.app.Monitor)...)
services = append(services, getKafkaServices(h.app.Kafka, h.app.Monitor)...)
services = append(services, getMailServices(h.app.Mail, h.app.Monitor)...)
services = append(services, getLdapServices(h.app.Ldap, h.app.Monitor)...)
slices.SortFunc(services, func(a interface{}, b interface{}) int {
return compareService(a, b)
func (h *handler) getServices(w http.ResponseWriter, r *http.Request) {
services := make([]service, 0)

typ := r.URL.Query().Get("type")

if typ == "" || typ == "http" {
services = append(services, getHttpServices(h.app.Http, h.app.Monitor)...)
}
if typ == "" || typ == "kafka" {
services = append(services, getKafkaServices(h.app.Kafka, h.app.Monitor)...)
}
if typ == "" || typ == "mail" {
services = append(services, getMailServices(h.app.Mail, h.app.Monitor)...)
}
if typ == "" || typ == "ldap" {
services = append(services, getLdapServices(h.app.Ldap, h.app.Monitor)...)
}
if typ == "" || typ == "mqtt" {
services = append(services, getMqttServices(h.app.Mqtt, h.app.Monitor)...)
}
slices.SortFunc(services, func(a service, b service) int {
return strings.Compare(a.Name, b.Name)
})
w.Header().Set("Content-Type", "application/json")
writeJsonBody(w, services)
Expand All @@ -230,22 +260,30 @@ func (h *handler) getInfo(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")

i := info{Version: h.app.Version, BuildTime: h.app.BuildTime, Search: searchInfo{Enabled: h.config.Search.Enabled}}
if len(h.app.ListHttp()) > 0 {
if h.app.Http.Len() > 0 {
i.ActiveServices = append(i.ActiveServices, "http")
}
if len(h.app.Kafka.List()) > 0 {
if h.app.Kafka.Len() > 0 {
i.ActiveServices = append(i.ActiveServices, "kafka")
}
if len(h.app.Mail.List()) > 0 {
if h.app.Mail.Len() > 0 {
i.ActiveServices = append(i.ActiveServices, "mail")
}
if len(h.app.Ldap.List()) > 0 {
if h.app.Ldap.Len() > 0 {
i.ActiveServices = append(i.ActiveServices, "ldap")
}
if h.app.Mqtt.Len() > 0 {
i.ActiveServices = append(i.ActiveServices, "mqtt")
}

writeJsonBody(w, i)
}

func write(w http.ResponseWriter, data any) {
w.Header().Set("Content-Type", "application/json")
writeJsonBody(w, data)
}

func writeJsonBody(w http.ResponseWriter, v interface{}) {
var buf bytes.Buffer
enc := json.NewEncoder(&buf)
Expand Down Expand Up @@ -278,24 +316,6 @@ func isImage(path string) bool {
}
}

func compareService(a, b interface{}) int {
return strings.Compare(getServiceName(a), getServiceName(b))
}

func getServiceName(a interface{}) string {
switch v := a.(type) {
case *httpSummary:
return v.Name
case *kafkaSummary:
return v.Name
case *ldapSummary:
return v.Name
case *mailSummary:
return v.Name
}
return ""
}

func getPageInfo(r *http.Request) (index int, limit int, err error) {
limit = 10

Expand Down
Loading
Loading