package storage import ( "encoding/json" "fmt" "os" "sync" "github.com/play-with-docker/play-with-docker/pwd/types" ) type storage struct { rw sync.Mutex path string db *DB } type DB struct { Sessions map[string]*types.Session `json:"sessions"` Instances map[string]*types.Instance `json:"instances"` Clients map[string]*types.Client `json:"clients"` WindowsInstances map[string]*types.WindowsInstance `json:"windows_instances"` LoginRequests map[string]*types.LoginRequest `json:"login_requests"` Users map[string]*types.User `json:"user"` Playgrounds map[string]*types.Playground `json:"playgrounds"` WindowsInstancesBySessionId map[string][]string `json:"windows_instances_by_session_id"` InstancesBySessionId map[string][]string `json:"instances_by_session_id"` ClientsBySessionId map[string][]string `json:"clients_by_session_id"` UsersByProvider map[string]string `json:"users_by_providers"` } func (store *storage) SessionGet(id string) (*types.Session, error) { store.rw.Lock() defer store.rw.Unlock() s, found := store.db.Sessions[id] if !found { return nil, NotFoundError } return s, nil } func (store *storage) SessionGetAll() ([]*types.Session, error) { store.rw.Lock() defer store.rw.Unlock() sessions := make([]*types.Session, len(store.db.Sessions)) i := 0 for _, s := range store.db.Sessions { sessions[i] = s i++ } return sessions, nil } func (store *storage) SessionPut(session *types.Session) error { store.rw.Lock() defer store.rw.Unlock() store.db.Sessions[session.Id] = session return store.save() } func (store *storage) SessionDelete(id string) error { store.rw.Lock() defer store.rw.Unlock() _, found := store.db.Sessions[id] if !found { return nil } for _, i := range store.db.WindowsInstancesBySessionId[id] { delete(store.db.WindowsInstances, i) } store.db.WindowsInstancesBySessionId[id] = []string{} for _, i := range store.db.InstancesBySessionId[id] { delete(store.db.Instances, i) } store.db.InstancesBySessionId[id] = []string{} for _, i := range store.db.ClientsBySessionId[id] { delete(store.db.Clients, i) } store.db.ClientsBySessionId[id] = []string{} delete(store.db.Sessions, id) return store.save() } func (store *storage) SessionCount() (int, error) { store.rw.Lock() defer store.rw.Unlock() return len(store.db.Sessions), nil } func (store *storage) InstanceGet(name string) (*types.Instance, error) { store.rw.Lock() defer store.rw.Unlock() i := store.db.Instances[name] if i == nil { return nil, NotFoundError } return i, nil } func (store *storage) InstancePut(instance *types.Instance) error { store.rw.Lock() defer store.rw.Unlock() _, found := store.db.Sessions[string(instance.SessionId)] if !found { return NotFoundError } store.db.Instances[instance.Name] = instance found = false for _, i := range store.db.InstancesBySessionId[string(instance.SessionId)] { if i == instance.Name { found = true break } } if !found { store.db.InstancesBySessionId[string(instance.SessionId)] = append(store.db.InstancesBySessionId[string(instance.SessionId)], instance.Name) } return store.save() } func (store *storage) InstanceDelete(name string) error { store.rw.Lock() defer store.rw.Unlock() instance, found := store.db.Instances[name] if !found { return nil } instances := store.db.InstancesBySessionId[string(instance.SessionId)] for n, i := range instances { if i == name { instances = append(instances[:n], instances[n+1:]...) break } } store.db.InstancesBySessionId[string(instance.SessionId)] = instances delete(store.db.Instances, name) return store.save() } func (store *storage) InstanceCount() (int, error) { store.rw.Lock() defer store.rw.Unlock() return len(store.db.Instances), nil } func (store *storage) InstanceFindBySessionId(sessionId string) ([]*types.Instance, error) { store.rw.Lock() defer store.rw.Unlock() instanceIds := store.db.InstancesBySessionId[sessionId] instances := make([]*types.Instance, len(instanceIds)) for i, id := range instanceIds { instances[i] = store.db.Instances[id] } return instances, nil } func (store *storage) WindowsInstanceGetAll() ([]*types.WindowsInstance, error) { store.rw.Lock() defer store.rw.Unlock() instances := []*types.WindowsInstance{} for _, s := range store.db.WindowsInstances { instances = append(instances, s) } return instances, nil } func (store *storage) WindowsInstancePut(instance *types.WindowsInstance) error { store.rw.Lock() defer store.rw.Unlock() _, found := store.db.Sessions[string(instance.SessionId)] if !found { return NotFoundError } store.db.WindowsInstances[instance.Id] = instance found = false for _, i := range store.db.WindowsInstancesBySessionId[string(instance.SessionId)] { if i == instance.Id { found = true break } } if !found { store.db.WindowsInstancesBySessionId[string(instance.SessionId)] = append(store.db.WindowsInstancesBySessionId[string(instance.SessionId)], instance.Id) } return store.save() } func (store *storage) WindowsInstanceDelete(id string) error { store.rw.Lock() defer store.rw.Unlock() instance, found := store.db.WindowsInstances[id] if !found { return nil } instances := store.db.WindowsInstancesBySessionId[string(instance.SessionId)] for n, i := range instances { if i == id { instances = append(instances[:n], instances[n+1:]...) break } } store.db.WindowsInstancesBySessionId[string(instance.SessionId)] = instances delete(store.db.WindowsInstances, id) return store.save() } func (store *storage) ClientGet(id string) (*types.Client, error) { store.rw.Lock() defer store.rw.Unlock() i := store.db.Clients[id] if i == nil { return nil, NotFoundError } return i, nil } func (store *storage) ClientPut(client *types.Client) error { store.rw.Lock() defer store.rw.Unlock() _, found := store.db.Sessions[string(client.SessionId)] if !found { return NotFoundError } store.db.Clients[client.Id] = client found = false for _, i := range store.db.ClientsBySessionId[string(client.SessionId)] { if i == client.Id { found = true break } } if !found { store.db.ClientsBySessionId[string(client.SessionId)] = append(store.db.ClientsBySessionId[string(client.SessionId)], client.Id) } return store.save() } func (store *storage) ClientDelete(id string) error { store.rw.Lock() defer store.rw.Unlock() client, found := store.db.Clients[id] if !found { return nil } clients := store.db.ClientsBySessionId[string(client.SessionId)] for n, i := range clients { if i == client.Id { clients = append(clients[:n], clients[n+1:]...) break } } store.db.ClientsBySessionId[string(client.SessionId)] = clients delete(store.db.Clients, id) return store.save() } func (store *storage) ClientCount() (int, error) { store.rw.Lock() defer store.rw.Unlock() return len(store.db.Clients), nil } func (store *storage) ClientFindBySessionId(sessionId string) ([]*types.Client, error) { store.rw.Lock() defer store.rw.Unlock() clientIds := store.db.ClientsBySessionId[sessionId] clients := make([]*types.Client, len(clientIds)) for i, id := range clientIds { clients[i] = store.db.Clients[id] } return clients, nil } func (store *storage) LoginRequestPut(loginRequest *types.LoginRequest) error { store.rw.Lock() defer store.rw.Unlock() store.db.LoginRequests[loginRequest.Id] = loginRequest return nil } func (store *storage) LoginRequestGet(id string) (*types.LoginRequest, error) { store.rw.Lock() defer store.rw.Unlock() if lr, found := store.db.LoginRequests[id]; !found { return nil, NotFoundError } else { return lr, nil } } func (store *storage) LoginRequestDelete(id string) error { store.rw.Lock() defer store.rw.Unlock() delete(store.db.LoginRequests, id) return nil } func (store *storage) UserFindByProvider(providerName, providerUserId string) (*types.User, error) { store.rw.Lock() defer store.rw.Unlock() if userId, found := store.db.UsersByProvider[fmt.Sprintf("%s_%s", providerName, providerUserId)]; !found { return nil, NotFoundError } else { if user, found := store.db.Users[userId]; !found { return nil, NotFoundError } else { return user, nil } } } func (store *storage) UserPut(user *types.User) error { store.rw.Lock() defer store.rw.Unlock() store.db.UsersByProvider[fmt.Sprintf("%s_%s", user.Provider, user.ProviderUserId)] = user.Id store.db.Users[user.Id] = user return store.save() } func (store *storage) UserGet(id string) (*types.User, error) { store.rw.Lock() defer store.rw.Unlock() if user, found := store.db.Users[id]; !found { return nil, NotFoundError } else { return user, nil } } func (store *storage) PlaygroundPut(playground *types.Playground) error { store.rw.Lock() defer store.rw.Unlock() store.db.Playgrounds[playground.Id] = playground return store.save() } func (store *storage) PlaygroundGet(id string) (*types.Playground, error) { store.rw.Lock() defer store.rw.Unlock() playground, found := store.db.Playgrounds[id] if !found { return nil, NotFoundError } return playground, nil } func (store *storage) load() error { file, err := os.Open(store.path) if err == nil { decoder := json.NewDecoder(file) err = decoder.Decode(&store.db) if err != nil { return err } } else { store.db = &DB{ Sessions: map[string]*types.Session{}, Instances: map[string]*types.Instance{}, Clients: map[string]*types.Client{}, WindowsInstances: map[string]*types.WindowsInstance{}, LoginRequests: map[string]*types.LoginRequest{}, Users: map[string]*types.User{}, Playgrounds: map[string]*types.Playground{}, WindowsInstancesBySessionId: map[string][]string{}, InstancesBySessionId: map[string][]string{}, ClientsBySessionId: map[string][]string{}, UsersByProvider: map[string]string{}, } } file.Close() return nil } func (store *storage) PlaygroundGetAll() ([]*types.Playground, error) { store.rw.Lock() defer store.rw.Unlock() playgrounds := make([]*types.Playground, len(store.db.Playgrounds)) i := 0 for _, p := range store.db.Playgrounds { playgrounds[i] = p i++ } return playgrounds, nil } func (store *storage) save() error { file, err := os.Create(store.path) if err != nil { return err } defer file.Close() encoder := json.NewEncoder(file) err = encoder.Encode(&store.db) return err } func NewFileStorage(path string) (StorageApi, error) { s := &storage{path: path} err := s.load() if err != nil { return nil, err } return s, nil }