10 |
{{.DataSet.Name}}
11 |
{{.DataSet.BlockCount}} blocks {{.Block.HumanSize}}/{{.DataSet.HumanSize}}
12 |
13 |
Prev Block |
Next Block
14 |
15 | {{.Content}}
16 |
17 |
Prev Record |
Next Record
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/static/errors.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
{{.Title}}
10 |
11 | {{if .DataSet.BadBlocks}}
12 |
Bad Blocks
13 |
14 | {{range $key, $value := .DataSet.BadBlocks}}
15 | - {{ $value }}
16 | {{end}}
17 |
18 | {{end}} {{if .DataSet.BadRecords}}
19 |
Bad Records
20 |
21 | {{range $key, $value := .DataSet.BadRecords}}
22 | - {{ $value }}
23 | {{end}}
24 |
25 | {{end}}
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/tools/fix_corrupt_missing_objects.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | #https://stackoverflow.com/questions/33012869/broken-branch-in-git-fatal-your-current-branch-appears-to-be-broken
4 |
5 | git fsck --full >& fsck_report.txt
6 | cat fsck_report.txt | grep "missing" | awk '{print $7}' > fsck_report.txt
7 |
8 |
9 | Did it report a corrupted file?
10 | If so delete the file, go back to step #1.
11 |
12 | for item in `cat fsck_report.txt`
13 | do
14 | echo "deleting $item"
15 | rm -f $item
16 | done
17 |
18 | Do del .git/index
19 | Do git reset
20 |
21 | rm -f fsck_report
22 |
23 |
24 | #remove invalid reflog references
25 | git reflog expire --stale-fix --all
26 |
27 | clone down repo to a separate dir
28 | and run
29 | cat ../fresh/.git/objects/pack/pack-*.pack | git unpack-objects
30 | within the broken repo
--------------------------------------------------------------------------------
/internal/digital/size.go:
--------------------------------------------------------------------------------
1 | package digital
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | )
7 |
8 | const (
9 | sizeByte = 1.0 << (10 * iota)
10 | sizeKb
11 | sizeMb
12 | sizeGb
13 | sizeTb
14 | )
15 |
16 | func FormatBytes(bytes uint64) string {
17 | unit := ""
18 | value := float32(bytes)
19 |
20 | switch {
21 | case bytes >= sizeTb:
22 | unit = "TB"
23 | value = value / sizeTb
24 | case bytes >= sizeGb:
25 | unit = "GB"
26 | value = value / sizeGb
27 | case bytes >= sizeMb:
28 | unit = "MB"
29 | value = value / sizeMb
30 | case bytes >= sizeKb:
31 | unit = "KB"
32 | value = value / sizeKb
33 | case bytes >= sizeByte:
34 | unit = "B"
35 | case bytes == 0:
36 | return "0"
37 | }
38 |
39 | stringValue := fmt.Sprintf("%.1f", value)
40 | stringValue = strings.TrimSuffix(stringValue, ".0")
41 | return fmt.Sprintf("%s%s", stringValue, unit)
42 | }
43 |
--------------------------------------------------------------------------------
/errors.go:
--------------------------------------------------------------------------------
1 | package gitdb
2 |
3 | import "github.com/gogitdb/gitdb/v2/internal/errors"
4 |
5 | var (
6 | ErrNoRecords = errors.ErrNoRecords
7 | ErrRecordNotFound = errors.ErrRecordNotFound
8 | ErrInvalidRecordID = errors.ErrInvalidRecordID
9 | ErrDBSyncFailed = errors.ErrDBSyncFailed
10 | ErrLowBattery = errors.ErrLowBattery
11 | ErrNoOnlineRemote = errors.ErrNoOnlineRemote
12 | ErrAccessDenied = errors.ErrAccessDenied
13 | ErrInvalidDataset = errors.ErrInvalidDataset
14 | )
15 |
16 | type ResolvableError interface {
17 | Resolution() string
18 | }
19 |
20 | type Error struct {
21 | err error
22 | resolution string
23 | }
24 |
25 | func (e Error) Error() string {
26 | return e.err.Error()
27 | }
28 |
29 | func (e Error) Resolution() string {
30 | return e.resolution
31 | }
32 |
33 | func ErrorWithResolution(e error, resolution string) Error {
34 | return Error{err: e, resolution: resolution}
35 | }
36 |
--------------------------------------------------------------------------------
/driver_local.go:
--------------------------------------------------------------------------------
1 | package gitdb
2 |
3 | import (
4 | "errors"
5 | "os"
6 | "time"
7 |
8 | "github.com/bouggo/log"
9 | )
10 |
11 | type localDriver struct {
12 | config Config
13 | absDBPath string
14 | }
15 |
16 | func (d *localDriver) name() string {
17 | return "local"
18 | }
19 |
20 | func (d *localDriver) setup(db *gitdb) error {
21 | d.config = db.config
22 | d.absDBPath = db.dbDir()
23 | // create db directory
24 | if err := os.MkdirAll(db.dbDir(), 0755); err != nil {
25 | return err
26 | }
27 | return nil
28 | }
29 |
30 | func (d *localDriver) sync() error {
31 | return nil
32 | }
33 |
34 | func (d *localDriver) commit(filePath string, msg string, user *User) error {
35 | log.Info("new changes committed")
36 | return nil
37 | }
38 |
39 | func (d *localDriver) undo() error {
40 | log.Info("changes reverted")
41 | return nil
42 | }
43 |
44 | func (d *localDriver) changedFiles() []string {
45 | var files []string
46 | return files
47 | }
48 |
49 | func (d *localDriver) lastCommitTime() (time.Time, error) {
50 | return time.Now(), errors.New("no commit history in repo")
51 | }
52 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020 Okechukwu Ugwu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/internal/errors/errors.go:
--------------------------------------------------------------------------------
1 | package errors
2 |
3 | import "errors"
4 |
5 | var (
6 | //internal errors
7 | errDB = errors.New("gitDB: database error")
8 | errBadBlock = errors.New("gitDB: Bad block error - invalid json")
9 | errBadRecord = errors.New("gitDB: Bad record error")
10 | errConnectionClosed = errors.New("gitDB: connection is closed")
11 | errConnectionInvalid = errors.New("gitDB: connection is not valid. use gitdb.Start to construct a valid connection")
12 |
13 | //external errors
14 | ErrNoRecords = errors.New("gitDB: no records found")
15 | ErrRecordNotFound = errors.New("gitDB: record not found")
16 | ErrInvalidRecordID = errors.New("gitDB: invalid record id")
17 | ErrDBSyncFailed = errors.New("gitDB: Database sync failed")
18 | ErrLowBattery = errors.New("gitDB: Insufficient battery power. Syncing disabled")
19 | ErrNoOnlineRemote = errors.New("gitDB: Online remote is not set. Syncing disabled")
20 | ErrAccessDenied = errors.New("gitDB: Access was denied to online repository")
21 | ErrInvalidDataset = errors.New("gitDB: invalid dataset. Dataset not in registry")
22 | )
23 |
--------------------------------------------------------------------------------
/static/list.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
{{.DataSet.Name}}
11 |
{{.DataSet.BlockCount}} blocks {{.DataSet.HumanSize}}
12 |
13 |
14 |
15 |
16 | {{range $key, $value := .Table.Headers}}
17 | | {{ $value }} |
18 | {{end}}
19 |
20 | {{range $key, $value := .Table.Rows}}
21 |
22 | {{range $k, $v := $value}} {{if eq $k 0}}
23 | | {{ $v }} |
24 | {{else}}
25 | {{ $v }} |
26 | {{end}} {{end}}
27 |
28 | {{end}}
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/sync.go:
--------------------------------------------------------------------------------
1 | package gitdb
2 |
3 | import (
4 | "fmt"
5 | "github.com/bouggo/log"
6 | "time"
7 | )
8 |
9 | func (g *gitdb) Sync() error {
10 | g.syncMu.Lock()
11 | defer g.syncMu.Unlock()
12 |
13 | if len(g.config.OnlineRemote) == 0 {
14 | return ErrNoOnlineRemote
15 | }
16 |
17 | // if client PC has at least 20% battery life
18 | if !hasSufficientBatteryPower(20) {
19 | return ErrLowBattery
20 | }
21 |
22 | log.Info("Syncing database...")
23 | changedFiles := g.driver.changedFiles()
24 | if err := g.driver.sync(); err != nil {
25 | log.Error(err.Error())
26 | return ErrDBSyncFailed
27 | }
28 |
29 | // reset loaded blocks
30 | g.loadedBlocks = nil
31 |
32 | g.buildIndexSmart(changedFiles)
33 | return nil
34 | }
35 |
36 | func (g *gitdb) startSyncClock() {
37 | go func(g *gitdb) {
38 | log.Test(fmt.Sprintf("starting sync clock @ interval %s", g.config.SyncInterval))
39 | ticker := time.NewTicker(g.config.SyncInterval)
40 | for {
41 | select {
42 | case <-g.shutdown:
43 | log.Test("shutting down sync clock")
44 | return
45 | case <-ticker.C:
46 | g.writeMu.Lock()
47 | if err := g.Sync(); err != nil {
48 | log.Error(err.Error())
49 | }
50 | g.writeMu.Unlock()
51 | }
52 | }
53 | }(g)
54 | }
55 |
--------------------------------------------------------------------------------
/transaction.go:
--------------------------------------------------------------------------------
1 | package gitdb
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/bouggo/log"
7 | )
8 |
9 | type operation func() error
10 |
11 | // Transaction represents a db transaction
12 | type Transaction interface {
13 | Commit() error
14 | AddOperation(o operation)
15 | }
16 |
17 | type transaction struct {
18 | name string
19 | operations []operation
20 | db *gitdb
21 | }
22 |
23 | func (t *transaction) Commit() error {
24 | t.db.autoCommit = false
25 | for _, o := range t.operations {
26 | if err := o(); err != nil {
27 | log.Info("Reverting transaction: " + err.Error())
28 | err2 := t.db.driver.undo()
29 | t.db.autoCommit = true
30 | if err2 != nil {
31 | err = fmt.Errorf("%s - %s", err.Error(), err2.Error())
32 | }
33 |
34 | return err
35 | }
36 | }
37 |
38 | t.db.autoCommit = true
39 | commitMsg := "Committing transaction: " + t.name
40 | t.db.commit.Add(1)
41 | t.db.events <- newWriteEvent(commitMsg, ".", t.db.autoCommit)
42 | t.db.waitForCommit()
43 | return nil
44 | }
45 |
46 | func (t *transaction) AddOperation(o operation) {
47 | t.operations = append(t.operations, o)
48 | }
49 |
50 | func (g *gitdb) StartTransaction(name string) Transaction {
51 | return &transaction{name: name, db: g}
52 | }
53 |
--------------------------------------------------------------------------------
/example/booking/collection.go:
--------------------------------------------------------------------------------
1 | package booking
2 |
3 | //implement sort interface to allow us sort an array of files i.e []os.FileInfo
4 | // type collection []*Model
5 |
6 | // func (c collection) Len() int {
7 | // return len(c)
8 | // }
9 | // func (c collection) Less(i, j int) bool {
10 | // return c[i].CreatedAt.Before(c[j].CreatedAt)
11 | // }
12 | // func (c collection) Swap(i, j int) {
13 | // c[i], c[j] = c[j], c[i]
14 | // }
15 |
16 | // type Collection struct {
17 | // SortFunc func(i int, j int) bool
18 | // Data []*db.Model
19 | // }
20 |
21 | // func NewCollection(data []*db.Model) *Collection {
22 | // return &Collection{Data: data}
23 | // }
24 |
25 | // func (c *Collection) Len() int {
26 | // return len(c.Data)
27 | // }
28 | // func (c *Collection) Less(i, j int) bool {
29 | // return c.SortFunc(i, j)
30 | // }
31 | // func (c *Collection) Swap(i, j int) {
32 | // c.Data[i], c.Data[j] = c.Data[j], c.Data[i]
33 | // }
34 |
35 | // func (c *Collection) SortByDate(i, j int) bool {
36 | // return c.Data[i].CreatedAt.Before(c.Data[j].CreatedAt)
37 | // }
38 |
39 | // func (c *Collection) SortByName(i, j int) bool {
40 | // return c.Data[i].Name.Before(c.Data[j].CreatedAt)
41 | // }
42 |
43 | // c := NewCollection(a)
44 | // c.SortFunc = c.SortByDate
45 |
46 | // sort(c)
47 |
--------------------------------------------------------------------------------
/ui_test.go:
--------------------------------------------------------------------------------
1 | package gitdb_test
2 |
3 | import (
4 | "net/http"
5 | "testing"
6 | )
7 |
8 | func TestServer(t *testing.T) {
9 | cfg := getConfig()
10 | cfg.EnableUI = true
11 | teardown := setup(t, cfg)
12 |
13 | insert(getTestMessageWithId(1), false)
14 |
15 | //fire off some requests
16 | client := http.DefaultClient
17 | requests := []*http.Request{
18 | request(http.MethodGet, "http://localhost:4120/css/app.css"),
19 | request(http.MethodGet, "http://localhost:4120/js/app.js"),
20 | request(http.MethodGet, "http://localhost:4120/"),
21 | request(http.MethodGet, "http://localhost:4120/errors/Message"),
22 | request(http.MethodGet, "http://localhost:4120/list/Message"),
23 | request(http.MethodGet, "http://localhost:4120/view/Message"),
24 | request(http.MethodGet, "http://localhost:4120/view/Message/b0/r0"),
25 | }
26 |
27 | for _, req := range requests {
28 | t.Logf("Testing %s", req.URL.String())
29 | resp, err := client.Do(req)
30 | if err != nil {
31 | t.Errorf("GitDB UI Server request failed: %s", err)
32 | }
33 |
34 | //todo use golden files to check response
35 | // b, _ := ioutil.ReadAll(resp.Body)
36 | resp.Body.Close()
37 | // t.Log(string(b))
38 | }
39 |
40 | teardown(t)
41 | }
42 |
43 | func request(method, url string) *http.Request {
44 | req, _ := http.NewRequest(method, url, nil)
45 | return req
46 | }
47 |
--------------------------------------------------------------------------------
/paths.go:
--------------------------------------------------------------------------------
1 | package gitdb
2 |
3 | import (
4 | "path/filepath"
5 | )
6 |
7 | func (g *gitdb) absDbPath() string {
8 | absDbPath, err := filepath.Abs(g.config.DBPath)
9 | if err != nil {
10 | panic(err)
11 | }
12 |
13 | return absDbPath
14 | }
15 |
16 | func (g *gitdb) dbDir() string {
17 | return filepath.Join(g.absDbPath(), "data")
18 | }
19 |
20 | func (g *gitdb) fullPath(m Model) string {
21 | return filepath.Join(g.dbDir(), m.GetSchema().name())
22 | }
23 |
24 | func (g *gitdb) blockFilePath(dataset, block string) string {
25 | return filepath.Join(g.dbDir(), dataset, block+".json")
26 | }
27 |
28 | func (g *gitdb) lockDir(m Model) string {
29 | return filepath.Join(g.fullPath(m), "Lock")
30 | }
31 |
32 | //index path
33 | func (g *gitdb) indexDir() string {
34 | return filepath.Join(g.absDbPath(), g.internalDirName(), "index")
35 | }
36 |
37 | func (g *gitdb) indexPath(dataset string) string {
38 | return filepath.Join(g.indexDir(), dataset)
39 | }
40 |
41 | //ssh paths
42 | func (g *gitdb) sshDir() string {
43 | return filepath.Join(g.absDbPath(), g.internalDirName(), "ssh")
44 | }
45 |
46 | //ssh paths
47 | func (g *gitdb) publicKeyFilePath() string {
48 | return filepath.Join(g.sshDir(), "gitdb.pub")
49 | }
50 |
51 | func (g *gitdb) privateKeyFilePath() string {
52 | return filepath.Join(g.sshDir(), "gitdb")
53 | }
54 |
55 | func (g *gitdb) internalDirName() string {
56 | return ".gitdb" //todo rename
57 | }
58 |
--------------------------------------------------------------------------------
/static/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |