create a golang project there will be multiple - modules - one is for installers - one is for a fiber web server with a web ui, swagger UI and opeapi rest interface (v3.1.0 swagger) - a generic redis server - on the fiber webserver create multiple endpoints nicely structures as separate directories underneith the module - executor (for executing commands, results in jobs) - package manager (on basis of apt, brew, scoop) - create an openapi interface for each of those v3.1.0 - integrate in generic way the goswagger interface so people can use the rest interface from web - create a main server which connects to all the modules ### code for the redis server ```go package main import ( "fmt" "log" "strconv" "strings" "sync" "time" "github.com/tidwall/redcon" ) // entry represents a stored value. For strings, value is stored as a string. // For hashes, value is stored as a map[string]string. type entry struct { value interface{} expiration time.Time // zero means no expiration } // Server holds the in-memory datastore and provides thread-safe access. type Server struct { mu sync.RWMutex data map[string]*entry } // NewServer creates a new server instance and starts a cleanup goroutine. func NewServer() *Server { s := &Server{ data: make(map[string]*entry), } go s.cleanupExpiredKeys() return s } // cleanupExpiredKeys periodically removes expired keys. func (s *Server) cleanupExpiredKeys() { ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() for range ticker.C { now := time.Now() s.mu.Lock() for k, ent := range s.data { if !ent.expiration.IsZero() && now.After(ent.expiration) { delete(s.data, k) } } s.mu.Unlock() } } // set stores a key with a value and an optional expiration duration. func (s *Server) set(key string, value interface{}, duration time.Duration) { s.mu.Lock() defer s.mu.Unlock() var exp time.Time if duration > 0 { exp = time.Now().Add(duration) } s.data[key] = &entry{ value: value, expiration: exp, } } // get retrieves the value for a key if it exists and is not expired. func (s *Server) get(key string) (interface{}, bool) { s.mu.RLock() ent, ok := s.data[key] s.mu.RUnlock() if !ok { return nil, false } if !ent.expiration.IsZero() && time.Now().After(ent.expiration) { // Key has expired; remove it. s.mu.Lock() delete(s.data, key) s.mu.Unlock() return nil, false } return ent.value, true } // del deletes a key and returns 1 if the key was present. func (s *Server) del(key string) int { s.mu.Lock() defer s.mu.Unlock() if _, ok := s.data[key]; ok { delete(s.data, key) return 1 } return 0 } // keys returns all keys matching the given pattern. // For simplicity, only "*" is fully supported. func (s *Server) keys(pattern string) []string { s.mu.RLock() defer s.mu.RUnlock() var result []string // Simple pattern matching: if pattern is "*", return all nonexpired keys. if pattern == "*" { for k, ent := range s.data { if !ent.expiration.IsZero() && time.Now().After(ent.expiration) { continue } result = append(result, k) } } else { // For any other pattern, do a simple substring match. for k, ent := range s.data { if !ent.expiration.IsZero() && time.Now().After(ent.expiration) { continue } if strings.Contains(k, pattern) { result = append(result, k) } } } return result } // getHash retrieves the hash map stored at key. func (s *Server) getHash(key string) (map[string]string, bool) { v, ok := s.get(key) if !ok { return nil, false } hash, ok := v.(map[string]string) return hash, ok } // hset sets a field in the hash stored at key. It returns 1 if the field is new. func (s *Server) hset(key, field, value string) int { s.mu.Lock() defer s.mu.Unlock() var hash map[string]string ent, exists := s.data[key] if exists { if !ent.expiration.IsZero() && time.Now().After(ent.expiration) { // expired; recreate a new hash. hash = make(map[string]string) s.data[key] = &entry{value: hash} } else { var ok bool hash, ok = ent.value.(map[string]string) if !ok { // Overwrite if the key holds a non-hash value. hash = make(map[string]string) s.data[key] = &entry{value: hash} } } } else { hash = make(map[string]string) s.data[key] = &entry{value: hash} } _, fieldExists := hash[field] hash[field] = value if fieldExists { return 0 } return 1 } // hget retrieves the value of a field in the hash stored at key. func (s *Server) hget(key, field string) (string, bool) { hash, ok := s.getHash(key) if !ok { return "", false } val, exists := hash[field] return val, exists } // hdel deletes one or more fields from the hash stored at key. // Returns the number of fields that were removed. func (s *Server) hdel(key string, fields []string) int { hash, ok := s.getHash(key) if !ok { return 0 } count := 0 for _, field := range fields { if _, exists := hash[field]; exists { delete(hash, field) count++ } } return count } // hkeys returns all field names in the hash stored at key. func (s *Server) hkeys(key string) []string { hash, ok := s.getHash(key) if !ok { return nil } var keys []string for field := range hash { keys = append(keys, field) } return keys } // hlen returns the number of fields in the hash stored at key. func (s *Server) hlen(key string) int { hash, ok := s.getHash(key) if !ok { return 0 } return len(hash) } // incr increments the integer value stored at key by one. // If the key does not exist, it is set to 0 before performing the operation. func (s *Server) incr(key string) (int64, error) { s.mu.Lock() defer s.mu.Unlock() var current int64 ent, exists := s.data[key] if exists { if !ent.expiration.IsZero() && time.Now().After(ent.expiration) { current = 0 } else { switch v := ent.value.(type) { case string: var err error current, err = strconv.ParseInt(v, 10, 64) if err != nil { return 0, err } case int: current = int64(v) case int64: current = v default: return 0, fmt.Errorf("value is not an integer") } } } current++ // Store the new value as a string. s.data[key] = &entry{ value: strconv.FormatInt(current, 10), } return current, nil } func main() { server := NewServer() log.Println("Starting Redis-like server on :6379") err := redcon.ListenAndServe(":6379", func(conn redcon.Conn, cmd redcon.Command) { // Every command is expected to have at least one argument (the command name). if len(cmd.Args) == 0 { conn.WriteError("ERR empty command") return } command := strings.ToLower(string(cmd.Args[0])) switch command { case "ping": conn.WriteString("PONG") case "set": // Usage: SET key value [EX seconds] if len(cmd.Args) < 3 { conn.WriteError("ERR wrong number of arguments for 'set' command") return } key := string(cmd.Args[1]) value := string(cmd.Args[2]) duration := time.Duration(0) // Check for an expiration option (only EX is supported here). if len(cmd.Args) > 3 { if strings.ToLower(string(cmd.Args[3])) == "ex" && len(cmd.Args) > 4 { seconds, err := strconv.Atoi(string(cmd.Args[4])) if err != nil { conn.WriteError("ERR invalid expire time") return } duration = time.Duration(seconds) * time.Second } } server.set(key, value, duration) conn.WriteString("OK") case "get": if len(cmd.Args) < 2 { conn.WriteError("ERR wrong number of arguments for 'get' command") return } key := string(cmd.Args[1]) v, ok := server.get(key) if !ok { conn.WriteNull() return } // Only string type is returned by GET. switch val := v.(type) { case string: conn.WriteBulkString(val) default: conn.WriteError("WRONGTYPE Operation against a key holding the wrong kind of value") } case "del": if len(cmd.Args) < 2 { conn.WriteError("ERR wrong number of arguments for 'del' command") return } count := 0 for i := 1; i < len(cmd.Args); i++ { key := string(cmd.Args[i]) count += server.del(key) } conn.WriteInt(count) case "keys": if len(cmd.Args) < 2 { conn.WriteError("ERR wrong number of arguments for 'keys' command") return } pattern := string(cmd.Args[1]) keys := server.keys(pattern) res := make([][]byte, len(keys)) for i, k := range keys { res[i] = []byte(k) } conn.WriteArray(res) case "hset": // Usage: HSET key field value if len(cmd.Args) < 4 { conn.WriteError("ERR wrong number of arguments for 'hset' command") return } key := string(cmd.Args[1]) field := string(cmd.Args[2]) value := string(cmd.Args[3]) added := server.hset(key, field, value) conn.WriteInt(added) case "hget": // Usage: HGET key field if len(cmd.Args) < 3 { conn.WriteError("ERR wrong number of arguments for 'hget' command") return } key := string(cmd.Args[1]) field := string(cmd.Args[2]) v, ok := server.hget(key, field) if !ok { conn.WriteNull() return } conn.WriteBulkString(v) case "hdel": // Usage: HDEL key field [field ...] if len(cmd.Args) < 3 { conn.WriteError("ERR wrong number of arguments for 'hdel' command") return } key := string(cmd.Args[1]) fields := make([]string, 0, len(cmd.Args)-2) for i := 2; i < len(cmd.Args); i++ { fields = append(fields, string(cmd.Args[i])) } removed := server.hdel(key, fields) conn.WriteInt(removed) case "hkeys": // Usage: HKEYS key if len(cmd.Args) < 2 { conn.WriteError("ERR wrong number of arguments for 'hkeys' command") return } key := string(cmd.Args[1]) fields := server.hkeys(key) res := make([][]byte, len(fields)) for i, field := range fields { res[i] = []byte(field) } conn.WriteArray(res) case "hlen": // Usage: HLEN key if len(cmd.Args) < 2 { conn.WriteError("ERR wrong number of arguments for 'hlen' command") return } key := string(cmd.Args[1]) length := server.hlen(key) conn.WriteInt(length) case "incr": if len(cmd.Args) < 2 { conn.WriteError("ERR wrong number of arguments for 'incr' command") return } key := string(cmd.Args[1]) newVal, err := server.incr(key) if err != nil { conn.WriteError("ERR " + err.Error()) return } conn.WriteInt64(newVal) default: conn.WriteError("ERR unknown command '" + command + "'") } }, // Accept connection: always allow. func(conn redcon.Conn) bool { return true }, // On connection close. func(conn redcon.Conn, err error) {}, ) if err != nil { log.Fatal(err) } } ``` test above code, test with a redis client it works