micro--go-micro
1bb25d6e7f
* feat: add agent platform showcase and blog post Add a complete platform example (Users, Posts, Comments, Mail) that mirrors micro/blog, demonstrating how existing microservices become AI-accessible through MCP with zero code changes. Includes blog post "Your Microservices Are Already an AI Platform" walking through real agent workflows: signup, content creation, commenting, tagging, and cross-service messaging. https://claude.ai/code/session_01GkduEhcrqcG45rdfYh8dAc * refactor: rename handler types to drop redundant Service suffix UserService → Users, PostService → Posts, CommentService → Comments, MailService → Mail. Matches micro/blog naming convention. https://claude.ai/code/session_01GkduEhcrqcG45rdfYh8dAc * refactor: consolidate top-level directories, reduce framework bloat Move internal/non-public packages behind internal/ or into their parent packages where they belong: - deploy/ → gateway/mcp/deploy/ (Helm charts belong with the gateway) - profile/ → service/profile/ (preset plugin profiles are a service concern) - scripts/ → internal/scripts/ (install script is not public API) - test/ → internal/test/ (test harness is not public API) - util/ → internal/util/ (internal helpers shouldn't be imported externally) Also fixes CLAUDE.md merge conflict markers and updates project structure documentation. All import paths updated. Build and tests pass. https://claude.ai/code/session_01GkduEhcrqcG45rdfYh8dAc * refactor: redesign model package to match framework conventions Rename model.Database interface to model.Model (consistent with client.Client, server.Server, store.Store). Remove generics in favor of interface{}-based API with reflection. Key changes: - model.Model interface: Register once, CRUD infers table from type - DefaultModel + NewModel() + package-level convenience functions - Schema registered via Register(&User{}), no per-call schema passing - Memory implementation as default (in model package, like store) - memory/sqlite/postgres backends updated for new interface - protoc-gen-micro generates RegisterXModel() instead of generic factory - All docs, blog, and README updated https://claude.ai/code/session_01GkduEhcrqcG45rdfYh8dAc --------- Co-authored-by: Claude <noreply@anthropic.com>
166 行
3.2 KiB
Go
166 行
3.2 KiB
Go
// addr provides functions to retrieve local IP addresses from device interfaces.
|
|
package addr
|
|
|
|
import (
|
|
"net"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
var (
|
|
// ErrIPNotFound no IP address found, and explicit IP not provided.
|
|
ErrIPNotFound = errors.New("no IP address found, and explicit IP not provided")
|
|
)
|
|
|
|
// IsLocal checks whether an IP belongs to one of the device's interfaces.
|
|
func IsLocal(addr string) bool {
|
|
// Extract the host
|
|
host, _, err := net.SplitHostPort(addr)
|
|
if err == nil {
|
|
addr = host
|
|
}
|
|
|
|
if addr == "localhost" {
|
|
return true
|
|
}
|
|
|
|
// Check against all local ips
|
|
for _, ip := range IPs() {
|
|
if addr == ip {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// Extract returns a valid IP address. If the address provided is a valid
|
|
// address, it will be returned directly. Otherwise, the available interfaces
|
|
// will be iterated over to find an IP address, preferably private.
|
|
func Extract(addr string) (string, error) {
|
|
// if addr is already specified then it's directly returned
|
|
if len(addr) > 0 && (addr != "0.0.0.0" && addr != "[::]" && addr != "::") {
|
|
return addr, nil
|
|
}
|
|
|
|
var (
|
|
addrs []net.Addr
|
|
loAddrs []net.Addr
|
|
)
|
|
|
|
ifaces, err := net.Interfaces()
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "failed to get interfaces")
|
|
}
|
|
|
|
for _, iface := range ifaces {
|
|
ifaceAddrs, err := iface.Addrs()
|
|
if err != nil {
|
|
// ignore error, interface can disappear from system
|
|
continue
|
|
}
|
|
|
|
if iface.Flags&net.FlagLoopback != 0 {
|
|
loAddrs = append(loAddrs, ifaceAddrs...)
|
|
continue
|
|
}
|
|
|
|
addrs = append(addrs, ifaceAddrs...)
|
|
}
|
|
|
|
// Add loopback addresses to the end of the list
|
|
addrs = append(addrs, loAddrs...)
|
|
|
|
// Try to find private IP in list, public IP otherwise
|
|
ip, err := findIP(addrs)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return ip.String(), nil
|
|
}
|
|
|
|
// IPs returns all available interface IP addresses.
|
|
func IPs() []string {
|
|
ifaces, err := net.Interfaces()
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
var ipAddrs []string
|
|
|
|
for _, i := range ifaces {
|
|
addrs, err := i.Addrs()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
for _, addr := range addrs {
|
|
var ip net.IP
|
|
switch v := addr.(type) {
|
|
case *net.IPNet:
|
|
ip = v.IP
|
|
case *net.IPAddr:
|
|
ip = v.IP
|
|
}
|
|
|
|
if ip == nil {
|
|
continue
|
|
}
|
|
|
|
ipAddrs = append(ipAddrs, ip.String())
|
|
}
|
|
}
|
|
|
|
return ipAddrs
|
|
}
|
|
|
|
// findIP will return the first private IP available in the list.
|
|
// If no private IP is available it will return the first public IP, if present.
|
|
// If no public IP is available, it will return the first loopback IP, if present.
|
|
func findIP(addresses []net.Addr) (net.IP, error) {
|
|
var publicIP net.IP
|
|
var localIP net.IP
|
|
|
|
for _, rawAddr := range addresses {
|
|
var ip net.IP
|
|
switch addr := rawAddr.(type) {
|
|
case *net.IPAddr:
|
|
ip = addr.IP
|
|
case *net.IPNet:
|
|
ip = addr.IP
|
|
default:
|
|
continue
|
|
}
|
|
|
|
if ip.IsLoopback() {
|
|
if localIP == nil {
|
|
localIP = ip
|
|
}
|
|
continue
|
|
}
|
|
|
|
if !ip.IsPrivate() {
|
|
if publicIP == nil {
|
|
publicIP = ip
|
|
}
|
|
continue
|
|
}
|
|
|
|
// Return private IP if available
|
|
return ip, nil
|
|
}
|
|
|
|
// Return public or virtual IP
|
|
if len(publicIP) > 0 {
|
|
return publicIP, nil
|
|
}
|
|
|
|
// Return local IP
|
|
if len(localIP) > 0 {
|
|
return localIP, nil
|
|
}
|
|
|
|
return nil, ErrIPNotFound
|
|
}
|