micro--go-micro
69fc228c73
* feat: expose framework primitives via API gateway and MCP
Add registry, store, and broker as both HTTP routes and MCP tools
so AI agents and HTTP clients can inspect and operate the framework.
API gateway (/micro/* namespace):
GET /micro/registry List registered services
GET /micro/registry/{name} Describe a service
GET /micro/store List store keys
GET /micro/store/{key} Read a record
POST /micro/store/{key} Write a record
POST /micro/broker/{topic} Publish a message
MCP gateway (micro_* tool prefix):
micro_registry_list List services
micro_registry_get Describe a service
micro_store_list List keys
micro_store_read Read a record
micro_store_write Write a record
micro_broker_publish Publish a message
Framework tools use a Handler field on the MCP Tool struct for
direct dispatch (no RPC). Service tools continue to use RPC.
Rate limiters and circuit breakers are applied to framework
tools the same as service tools.
* fix: make framework internals opt-in on API and MCP gateways
Framework primitives (registry, broker, store) are now only
exposed when explicitly enabled:
API gateway: micro api --internal
MCP gateway: Options{Internal: true}
Off by default — user services are always exposed, framework
internals require the flag. Banner output only shows framework
routes when enabled.
* fix: always expose framework internals, gate by auth in production
Revert the --internal flag approach. Framework primitives (registry,
broker, store) are now always exposed:
- micro api: /micro/* routes always available (dev tool)
- MCP gateway: micro_* tools always registered. When Auth is
configured (production), they require micro:admin scope.
Without Auth (dev), they're open — same as all other tools.
This follows the existing pattern: micro run/api = dev (open),
micro server = production (auth + scopes). Framework internals
follow the same security model as user services.
Remove the Internal option from MCP Options. Remove --internal
flag from micro api.
Note: scope persistence depends on the store backend. The default
in-memory store does not survive restarts. Use MICRO_STORE=file
for persistent scopes in production.
* fix: correct DefaultStore comment — it's file-backed, not memory
* fix(server): don't recreate deleted admin user on restart
When the default admin account is deleted via the dashboard, set
a marker key (auth/.admin-deleted) in the store. On startup, skip
admin creation if the marker exists. This prevents the default
admin/micro credentials from reappearing after restart when the
user has intentionally removed them.
---------
Co-authored-by: Claude <noreply@anthropic.com>
98 行
2.8 KiB
Go
98 行
2.8 KiB
Go
// Package store is an interface for distributed data storage.
|
|
// The design document is located at https://github.com/micro/development/blob/master/design/store.md
|
|
package store
|
|
|
|
import (
|
|
"errors"
|
|
"time"
|
|
|
|
"encoding/json"
|
|
)
|
|
|
|
var (
|
|
// ErrNotFound is returned when a key doesn't exist.
|
|
ErrNotFound = errors.New("not found")
|
|
// DefaultStore is the file store (persists to ~/micro/store/).
|
|
DefaultStore Store = NewStore()
|
|
)
|
|
|
|
// Store is a data storage interface.
|
|
type Store interface {
|
|
// Init initializes the store. It must perform any required setup on the backing storage implementation and check that it is ready for use, returning any errors.
|
|
Init(...Option) error
|
|
// Options allows you to view the current options.
|
|
Options() Options
|
|
// Read takes a single key name and optional ReadOptions. It returns matching []*Record or an error.
|
|
Read(key string, opts ...ReadOption) ([]*Record, error)
|
|
// Write() writes a record to the store, and returns an error if the record was not written.
|
|
Write(r *Record, opts ...WriteOption) error
|
|
// Delete removes the record with the corresponding key from the store.
|
|
Delete(key string, opts ...DeleteOption) error
|
|
// List returns any keys that match, or an empty list with no error if none matched.
|
|
List(opts ...ListOption) ([]string, error)
|
|
// Close the store
|
|
Close() error
|
|
// String returns the name of the implementation.
|
|
String() string
|
|
}
|
|
|
|
// Record is an item stored or retrieved from a Store.
|
|
type Record struct {
|
|
// Any associated metadata for indexing
|
|
Metadata map[string]interface{} `json:"metadata"`
|
|
// The key to store the record
|
|
Key string `json:"key"`
|
|
// The value within the record
|
|
Value []byte `json:"value"`
|
|
// Time to expire a record: TODO: change to timestamp
|
|
Expiry time.Duration `json:"expiry,omitempty"`
|
|
}
|
|
|
|
func NewStore(opts ...Option) Store {
|
|
return NewFileStore(opts...)
|
|
}
|
|
|
|
func NewRecord(key string, val interface{}) *Record {
|
|
b, _ := json.Marshal(val)
|
|
return &Record{
|
|
Key: key,
|
|
Value: b,
|
|
}
|
|
}
|
|
|
|
// Encode will marshal any type into the byte Value field
|
|
func (r *Record) Encode(v interface{}) error {
|
|
b, err := json.Marshal(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r.Value = b
|
|
return nil
|
|
}
|
|
|
|
// Decode is a convenience helper for decoding records
|
|
func (r *Record) Decode(v interface{}) error {
|
|
return json.Unmarshal(r.Value, v)
|
|
}
|
|
|
|
// Read records
|
|
func Read(key string, opts ...ReadOption) ([]*Record, error) {
|
|
// execute the query
|
|
return DefaultStore.Read(key, opts...)
|
|
}
|
|
|
|
// Write a record to the store
|
|
func Write(r *Record) error {
|
|
return DefaultStore.Write(r)
|
|
}
|
|
|
|
// Delete removes the record with the corresponding key from the store.
|
|
func Delete(key string) error {
|
|
return DefaultStore.Delete(key)
|
|
}
|
|
|
|
// List returns any keys that match, or an empty list with no error if none matched.
|
|
func List(opts ...ListOption) ([]string, error) {
|
|
return DefaultStore.List(opts...)
|
|
}
|