github.com/code-to-go/safepool.lib@v0.0.0-20221205180519-ee25e63c226e/pool/pool.go (about) 1 package pool 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io" 9 "path" 10 "time" 11 12 "github.com/code-to-go/safepool.lib/core" 13 "github.com/code-to-go/safepool.lib/security" 14 "github.com/code-to-go/safepool.lib/transport" 15 16 "github.com/godruoyi/go-snowflake" 17 "github.com/sirupsen/logrus" 18 ) 19 20 const SafeConfigFile = ".safepool-pool.json" 21 22 var ErrNoExchange = errors.New("no Exchange available") 23 var ErrInvalidSignature = errors.New("signature is invalid") 24 var ErrNotTrusted = errors.New("the author is not a trusted user") 25 var ErrNotAuthorized = errors.New("no authorization for this file") 26 var ErrAlreadyExist = errors.New("pool already exists") 27 var ErrInvalidToken = errors.New("provided token is invalid: missing name or configs") 28 var ErrInvalidConfig = errors.New("provided config is invalid: missing name or configs") 29 30 // type SafeConfig struct { 31 // Version float32 32 // Id uint64 33 // } 34 35 type Consumer interface { 36 TimeOffset(s *Pool) time.Time 37 Accept(s *Pool, h Head) bool 38 } 39 40 type Pool struct { 41 Name string 42 Self security.Identity 43 Consumers []Consumer 44 45 e transport.Exchanger 46 exchangers []transport.Exchanger 47 masterKeyId uint64 48 masterKey []byte 49 lastReplica time.Time 50 accessHash []byte 51 } 52 53 type Identity struct { 54 security.Identity 55 //Since is the keyId used when the identity was added to the Pool access 56 Since uint64 57 //AddedOn is the timestamp when the identity is stored on the local DB 58 AddedOn time.Time 59 } 60 61 type Head struct { 62 Id uint64 63 Name string 64 Size int64 65 Hash []byte 66 ModTime time.Time 67 Author security.Identity 68 Signature []byte 69 TimeStamp time.Time `json:"-"` 70 Meta []byte 71 } 72 73 const ( 74 ID_CREATE = 0x0 75 ID_FORCE_CREATE = 0x1 76 ) 77 78 var ForceCreation = false 79 var ReplicaPeriod = time.Hour 80 81 type Config struct { 82 Name string 83 Configs []transport.Config 84 } 85 86 func List() []string { 87 names, _ := sqlList() 88 return names 89 } 90 91 func Create(self security.Identity, name string) (*Pool, error) { 92 configs, err := sqlLoad(name) 93 if core.IsErr(err, "unknown pool %s: %v", name) { 94 return nil, err 95 } 96 97 s := &Pool{ 98 Name: name, 99 Self: self, 100 lastReplica: time.Now(), 101 } 102 err = s.connectSafe(name, configs) 103 if err != nil { 104 return nil, err 105 } 106 107 s.masterKeyId = snowflake.ID() 108 s.masterKey = security.GenerateBytesKey(32) 109 err = s.sqlSetKey(s.masterKeyId, s.masterKey) 110 if core.IsErr(err, "çannot store master encryption key to db: %v") { 111 return nil, err 112 } 113 err = security.SetIdentity(self) 114 if core.IsErr(err, "çannot save identity to db: %v") { 115 return nil, err 116 } 117 118 err = s.sqlSetIdentity(self, s.masterKeyId) 119 if core.IsErr(err, "cannot link identity to save: %v") { 120 return nil, err 121 } 122 123 if !ForceCreation { 124 _, err = s.e.Stat(path.Join(s.Name, ".access")) 125 if err == nil { 126 return nil, ErrAlreadyExist 127 } 128 } 129 130 err = s.ExportAccessFile(s.e) 131 if core.IsErr(err, "cannot export access file: %v") { 132 return nil, err 133 } 134 135 return s, err 136 } 137 138 // Init initialized a domain on the specified exchangers 139 func Open(self security.Identity, name string) (*Pool, error) { 140 configs, err := sqlLoad(name) 141 if core.IsErr(err, "unknown pool %s: %v", name) { 142 return nil, err 143 } 144 s := &Pool{ 145 Name: name, 146 Self: self, 147 } 148 err = s.connectSafe(name, configs) 149 if err != nil { 150 return nil, err 151 } 152 153 _, err = s.ImportAccess(s.e) 154 return s, err 155 } 156 157 func (p *Pool) List(afterId uint64, afterTs time.Time) []Head { 158 hs, _ := p.list(afterId, afterTs) 159 return hs 160 } 161 162 func (p *Pool) Post(name string, r io.Reader, meta []byte) (Head, error) { 163 id := snowflake.ID() 164 n := path.Join(p.Name, fmt.Sprintf("%d.body", id)) 165 hr, err := p.writeFile(n, r) 166 if core.IsErr(err, "cannot post file %s to %s: %v", name, p.e) { 167 return Head{}, err 168 } 169 170 hash := hr.Hash() 171 signature, err := security.Sign(p.Self, hash) 172 if core.IsErr(err, "cannot sign file %s.body in %s: %v", name, p.e) { 173 return Head{}, err 174 } 175 h := Head{ 176 Id: id, 177 Name: name, 178 Size: hr.Size(), 179 Hash: hash, 180 ModTime: time.Now(), 181 Author: p.Self.Public(), 182 Signature: signature, 183 Meta: meta, 184 } 185 data, err := json.Marshal(h) 186 if core.IsErr(err, "cannot marshal header to json: %v") { 187 return Head{}, err 188 } 189 190 n = path.Join(p.Name, fmt.Sprintf("%d.head", id)) 191 _, err = p.writeFile(n, bytes.NewBuffer(data)) 192 core.IsErr(err, "cannot write header %s.head in %s: %v", name, p.e) 193 194 return h, nil 195 } 196 197 func (p *Pool) Get(id uint64, rang *transport.Range, w io.Writer) error { 198 headName := path.Join(p.Name, fmt.Sprintf("%d.head", id)) 199 bodyName := path.Join(p.Name, fmt.Sprintf("%d.body", id)) 200 201 h, err := p.readHead(headName) 202 if core.IsErr(err, "cannot read header '%s': %v") { 203 return err 204 } 205 206 hr, err := p.readFile(bodyName, rang, w) 207 if core.IsErr(err, "cannot read body '%s': %v", bodyName) { 208 return err 209 } 210 hash := hr.Hash() 211 if !bytes.Equal(hash, h.Hash) { 212 return ErrInvalidSignature 213 } 214 return nil 215 } 216 217 func (p *Pool) Close() { 218 for _, e := range p.exchangers { 219 _ = e.Close() 220 } 221 } 222 223 func (p *Pool) Delete() error { 224 for _, e := range p.exchangers { 225 err := e.Delete(p.Name) 226 if err != nil { 227 return err 228 } 229 } 230 return nil 231 } 232 233 func (p *Pool) Identities() ([]Identity, error) { 234 identities, err := p.sqlGetIdentities(false) 235 return identities, err 236 } 237 238 func (p *Pool) Sync() { 239 logrus.Infof("poll request on %s", p.Name) 240 241 if !p.e.Touched(p.Name + "/") { 242 return 243 } 244 245 timeOffset := time.Now() 246 offsets := map[Consumer]time.Time{} 247 for _, c := range p.Consumers { 248 o := c.TimeOffset(p) 249 offsets[c] = o 250 if timeOffset.After(o) { 251 timeOffset = o 252 } 253 } 254 255 for _, h := range p.List(0, timeOffset) { 256 for _, c := range p.Consumers { 257 if c.Accept(p, h) { 258 break 259 } 260 } 261 } 262 263 if time.Since(p.lastReplica) > ReplicaPeriod { 264 p.replica() 265 } 266 }