-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstorage_file.go
More file actions
134 lines (109 loc) · 2.84 KB
/
storage_file.go
File metadata and controls
134 lines (109 loc) · 2.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Copyright 2017-2026 Allow2 Pty Ltd. All rights reserved.
// Use of this source code is governed by the Allow2 API and SDK Licence.
package allow2service
import (
"encoding/json"
"os"
"path/filepath"
"sync"
)
// FileTokenStorage is a file-based JSON token storage.
// Stores all tokens in a single JSON file. Suitable for development
// and single-server deployments.
type FileTokenStorage struct {
filePath string
mu sync.RWMutex
data map[string]map[string]interface{}
loaded bool
}
// NewFileTokenStorage creates a new FileTokenStorage with the given file path.
func NewFileTokenStorage(filePath string) *FileTokenStorage {
return &FileTokenStorage{
filePath: filePath,
}
}
// Store persists tokens for a user.
func (s *FileTokenStorage) Store(userID string, tokens *OAuthTokens) error {
s.mu.Lock()
defer s.mu.Unlock()
data, err := s.loadAll()
if err != nil {
return err
}
data[userID] = tokens.ToMap()
return s.saveAll(data)
}
// Retrieve returns tokens for a user, or nil if none stored.
func (s *FileTokenStorage) Retrieve(userID string) (*OAuthTokens, error) {
s.mu.RLock()
defer s.mu.RUnlock()
data, err := s.loadAll()
if err != nil {
return nil, err
}
tokenData, ok := data[userID]
if !ok {
return nil, nil
}
return OAuthTokensFromMap(tokenData), nil
}
// Delete removes tokens for a user.
func (s *FileTokenStorage) Delete(userID string) error {
s.mu.Lock()
defer s.mu.Unlock()
data, err := s.loadAll()
if err != nil {
return err
}
delete(data, userID)
return s.saveAll(data)
}
// Exists checks whether tokens exist for a user.
func (s *FileTokenStorage) Exists(userID string) (bool, error) {
s.mu.RLock()
defer s.mu.RUnlock()
data, err := s.loadAll()
if err != nil {
return false, err
}
_, ok := data[userID]
return ok, nil
}
// loadAll loads all tokens from disk. Must be called with lock held.
func (s *FileTokenStorage) loadAll() (map[string]map[string]interface{}, error) {
if s.loaded && s.data != nil {
return s.data, nil
}
contents, err := os.ReadFile(s.filePath)
if err != nil {
if os.IsNotExist(err) {
s.data = make(map[string]map[string]interface{})
s.loaded = true
return s.data, nil
}
return nil, err
}
var data map[string]map[string]interface{}
if err := json.Unmarshal(contents, &data); err != nil {
s.data = make(map[string]map[string]interface{})
s.loaded = true
return s.data, nil
}
s.data = data
s.loaded = true
return s.data, nil
}
// saveAll persists all tokens to disk. Must be called with lock held.
func (s *FileTokenStorage) saveAll(data map[string]map[string]interface{}) error {
s.data = data
s.loaded = true
dir := filepath.Dir(s.filePath)
if err := os.MkdirAll(dir, 0700); err != nil {
return err
}
jsonData, err := json.MarshalIndent(data, "", " ")
if err != nil {
return err
}
return os.WriteFile(s.filePath, jsonData, 0600)
}