Goit

Simple and lightweight Git web server
git clone http://git.omkov.net/Goit
Log | Tree | Refs | README | Download

Goit/src/goit/goit.go (313 lines, 6.8 KiB) -rw-r--r-- file download

ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
0
// Copyright (C) 2023, Jakob Wakeling
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
1
// All rights reserved.
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
2
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
3
package goit
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
4
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
5
import (
08dff9a Jakob Wakeling 2023-11-07 21:03:04
6
	"archive/zip"
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
7
	"database/sql"
1828e5f Jakob Wakeling 2023-07-19 19:53:59
8
	"encoding/json"
1828e5f Jakob Wakeling 2023-07-19 19:53:59
9
	"errors"
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
10
	"fmt"
08dff9a Jakob Wakeling 2023-11-07 21:03:04
11
	"io"
08dff9a Jakob Wakeling 2023-11-07 21:03:04
12
	"io/fs"
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
13
	"log"
1828e5f Jakob Wakeling 2023-07-19 19:53:59
14
	"os"
08dff9a Jakob Wakeling 2023-11-07 21:03:04
15
	"path/filepath"
efb68c3 Jakob Wakeling 2023-12-06 20:37:06
16
	"slices"
08dff9a Jakob Wakeling 2023-11-07 21:03:04
17
	"strings"
08dff9a Jakob Wakeling 2023-11-07 21:03:04
18
	"time"
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
19
c768e5c Jakob Wakeling 2023-11-27 21:58:27
20
	"github.com/Jamozed/Goit/res"
570144e Jakob Wakeling 2023-12-15 23:28:06
21
	"github.com/Jamozed/Goit/src/cron"
08dff9a Jakob Wakeling 2023-11-07 21:03:04
22
	"github.com/Jamozed/Goit/src/util"
08dff9a Jakob Wakeling 2023-11-07 21:03:04
23
	"github.com/go-git/go-git/v5"
08dff9a Jakob Wakeling 2023-11-07 21:03:04
24
	"github.com/go-git/go-git/v5/plumbing/transport"
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
25
	_ "github.com/mattn/go-sqlite3"
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
26
)
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
27
2ac8bbb Jakob Wakeling 2024-01-29 22:10:46
28
var Conf config
1828e5f Jakob Wakeling 2023-07-19 19:53:59
29
var db *sql.DB
17c34f4 Jakob Wakeling 2023-07-19 22:07:36
30
var Favicon []byte
570144e Jakob Wakeling 2023-12-15 23:28:06
31
var Cron *cron.Cron
1828e5f Jakob Wakeling 2023-07-19 19:53:59
32
7974d70 Jakob Wakeling 2023-11-03 22:19:52
33
var Reserved []string = []string{"admin", "repo", "static", "user"}
7974d70 Jakob Wakeling 2023-11-03 22:19:52
34
83800a4 Jakob Wakeling 2023-12-25 23:04:58
35
var StartTime = time.Now()
83800a4 Jakob Wakeling 2023-12-25 23:04:58
36
2ac8bbb Jakob Wakeling 2024-01-29 22:10:46
37
func Goit() error {
2ac8bbb Jakob Wakeling 2024-01-29 22:10:46
38
	if conf, err := loadConfig(); err != nil {
2ac8bbb Jakob Wakeling 2024-01-29 22:10:46
39
		return err
2ac8bbb Jakob Wakeling 2024-01-29 22:10:46
40
	} else {
2ac8bbb Jakob Wakeling 2024-01-29 22:10:46
41
		Conf = conf
17c34f4 Jakob Wakeling 2023-07-19 22:07:36
42
	}
c768e5c Jakob Wakeling 2023-11-27 21:58:27
43
2ac8bbb Jakob Wakeling 2024-01-29 22:10:46
44
	if err := os.MkdirAll(Conf.LogsPath, 0o777); err != nil {
2ac8bbb Jakob Wakeling 2024-01-29 22:10:46
45
		return fmt.Errorf("[config] %w", err)
c768e5c Jakob Wakeling 2023-11-27 21:58:27
46
	}
c768e5c Jakob Wakeling 2023-11-27 21:58:27
47
2ac8bbb Jakob Wakeling 2024-01-29 22:10:46
48
	logFile, err := os.Create(filepath.Join(Conf.LogsPath, fmt.Sprint("goit_", time.Now().Unix(), ".log")))
c768e5c Jakob Wakeling 2023-11-27 21:58:27
49
	if err != nil {
c768e5c Jakob Wakeling 2023-11-27 21:58:27
50
		log.Fatalln("[log]", err.Error())
c768e5c Jakob Wakeling 2023-11-27 21:58:27
51
	}
c768e5c Jakob Wakeling 2023-11-27 21:58:27
52
c768e5c Jakob Wakeling 2023-11-27 21:58:27
53
	log.SetOutput(io.MultiWriter(os.Stderr, logFile))
c768e5c Jakob Wakeling 2023-11-27 21:58:27
54
	log.Println("Starting Goit", res.Version)
17c34f4 Jakob Wakeling 2023-07-19 22:07:36
55
d631c5e Jakob Wakeling 2023-07-20 23:13:39
56
	log.Println("[Config] using data path:", Conf.DataPath)
d631c5e Jakob Wakeling 2023-07-20 23:13:39
57
	if err := os.MkdirAll(Conf.DataPath, 0o777); err != nil {
2ac8bbb Jakob Wakeling 2024-01-29 22:10:46
58
		return fmt.Errorf("[config] %w", err)
d631c5e Jakob Wakeling 2023-07-20 23:13:39
59
	}
d631c5e Jakob Wakeling 2023-07-20 23:13:39
60
08dff9a Jakob Wakeling 2023-11-07 21:03:04
61
	if dat, err := os.ReadFile(filepath.Join(Conf.DataPath, "favicon.png")); err != nil {
2ac8bbb Jakob Wakeling 2024-01-29 22:10:46
62
		log.Println("[favicon]", err.Error())
17c34f4 Jakob Wakeling 2023-07-19 22:07:36
63
	} else {
17c34f4 Jakob Wakeling 2023-07-19 22:07:36
64
		Favicon = dat
1828e5f Jakob Wakeling 2023-07-19 19:53:59
65
	}
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
66
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
67
	if db, err = sql.Open("sqlite3", filepath.Join(Conf.DataPath, "goit.db?_timeout=5000")); err != nil {
2ac8bbb Jakob Wakeling 2024-01-29 22:10:46
68
		return fmt.Errorf("[database] %w", err)
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
69
	}
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
70
570144e Jakob Wakeling 2023-12-15 23:28:06
71
	/* Update the database if necessary */
2954edd Jakob Wakeling 2024-06-16 22:57:19
72
	if err := updateDatabase(db); err != nil {
2ac8bbb Jakob Wakeling 2024-01-29 22:10:46
73
		return fmt.Errorf("[database] %w", err)
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
74
	}
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
75
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
76
	/* Create an admin user if one does not exist */
1828e5f Jakob Wakeling 2023-07-19 19:53:59
77
	if exists, err := UserExists("admin"); err != nil {
2ac8bbb Jakob Wakeling 2024-01-29 22:10:46
78
		log.Println("[admin:exists]", err.Error())
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
79
		err = nil /* ignored */
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
80
	} else if !exists {
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
81
		if salt, err := Salt(); err != nil {
2ac8bbb Jakob Wakeling 2024-01-29 22:10:46
82
			log.Println("[admin:salt]", err.Error())
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
83
			err = nil /* ignored */
1828e5f Jakob Wakeling 2023-07-19 19:53:59
84
		} else if _, err = db.Exec(
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
85
			"INSERT INTO users (id, name, name_full, pass, pass_algo, salt, is_admin) VALUES (?, ?, ?, ?, ?, ?, ?)",
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
86
			0, "admin", "Administrator", Hash("admin", salt), "argon2", salt, true,
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
87
		); err != nil {
2ac8bbb Jakob Wakeling 2024-01-29 22:10:46
88
			log.Println("[admin:INSERT]", err.Error())
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
89
			err = nil /* ignored */
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
90
		}
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
91
	}
570144e Jakob Wakeling 2023-12-15 23:28:06
92
570144e Jakob Wakeling 2023-12-15 23:28:06
93
	/* Initialise and start the cron service */
570144e Jakob Wakeling 2023-12-15 23:28:06
94
	Cron = cron.New()
570144e Jakob Wakeling 2023-12-15 23:28:06
95
	Cron.Start()
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
96
2d1243e Jakob Wakeling 2023-12-16 00:03:03
97
	/* Periodically clean up expired sessions */
1547a71 Jakob Wakeling 2023-12-16 15:24:55
98
	Cron.Add(-1, cron.Hourly, CleanupSessions)
2d1243e Jakob Wakeling 2023-12-16 00:03:03
99
2d1243e Jakob Wakeling 2023-12-16 00:03:03
100
	/* Add cron jobs for mirror repositories */
2d1243e Jakob Wakeling 2023-12-16 00:03:03
101
	repos, err := GetRepos()
2d1243e Jakob Wakeling 2023-12-16 00:03:03
102
	if err != nil {
2d1243e Jakob Wakeling 2023-12-16 00:03:03
103
		return err
2d1243e Jakob Wakeling 2023-12-16 00:03:03
104
	}
2d1243e Jakob Wakeling 2023-12-16 00:03:03
105
2d1243e Jakob Wakeling 2023-12-16 00:03:03
106
	for _, r := range repos {
2d1243e Jakob Wakeling 2023-12-16 00:03:03
107
		if r.IsMirror {
1547a71 Jakob Wakeling 2023-12-16 15:24:55
108
			util.Debugln("Adding mirror cron job for", r.Name)
b6c2a7b Jakob Wakeling 2024-03-15 21:11:22
109
			rid, name := r.Id, r.Name
1547a71 Jakob Wakeling 2023-12-16 15:24:55
110
			Cron.Add(r.Id, cron.Daily, func() {
b6c2a7b Jakob Wakeling 2024-03-15 21:11:22
111
				if err := Pull(rid); err != nil {
b6c2a7b Jakob Wakeling 2024-03-15 21:11:22
112
					log.Println("[cron:mirror]", rid, name, err.Error())
b6c2a7b Jakob Wakeling 2024-03-15 21:11:22
113
				} else {
b6c2a7b Jakob Wakeling 2024-03-15 21:11:22
114
					log.Println("[cron:mirror] updated", rid, name)
2d1243e Jakob Wakeling 2023-12-16 00:03:03
115
				}
2d1243e Jakob Wakeling 2023-12-16 00:03:03
116
			})
2d1243e Jakob Wakeling 2023-12-16 00:03:03
117
		}
2d1243e Jakob Wakeling 2023-12-16 00:03:03
118
	}
2d1243e Jakob Wakeling 2023-12-16 00:03:03
119
2d1243e Jakob Wakeling 2023-12-16 00:03:03
120
	Cron.Update()
2d1243e Jakob Wakeling 2023-12-16 00:03:03
121
1828e5f Jakob Wakeling 2023-07-19 19:53:59
122
	return nil
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
123
}
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
124
08dff9a Jakob Wakeling 2023-11-07 21:03:04
125
func RepoPath(name string, abs bool) string {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
126
	return util.If(abs, filepath.Join(Conf.DataPath, "repos", name+".git"), filepath.Join(name+".git"))
efb68c3 Jakob Wakeling 2023-12-06 20:37:06
127
}
efb68c3 Jakob Wakeling 2023-12-06 20:37:06
128
efb68c3 Jakob Wakeling 2023-12-06 20:37:06
129
func IsLegal(s string) bool {
efb68c3 Jakob Wakeling 2023-12-06 20:37:06
130
	for i := 0; i < len(s); i += 1 {
efb68c3 Jakob Wakeling 2023-12-06 20:37:06
131
		if !slices.Contains([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~/"), s[i]) {
efb68c3 Jakob Wakeling 2023-12-06 20:37:06
132
			return false
efb68c3 Jakob Wakeling 2023-12-06 20:37:06
133
		}
efb68c3 Jakob Wakeling 2023-12-06 20:37:06
134
	}
efb68c3 Jakob Wakeling 2023-12-06 20:37:06
135
efb68c3 Jakob Wakeling 2023-12-06 20:37:06
136
	return true
08dff9a Jakob Wakeling 2023-11-07 21:03:04
137
}
08dff9a Jakob Wakeling 2023-11-07 21:03:04
138
08dff9a Jakob Wakeling 2023-11-07 21:03:04
139
func Backup() error {
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
140
	if conf, err := loadConfig(); err != nil {
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
141
		return err
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
142
	} else {
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
143
		Conf = conf
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
144
	}
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
145
08dff9a Jakob Wakeling 2023-11-07 21:03:04
146
	data := struct {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
147
		Users []User `json:"users"`
08dff9a Jakob Wakeling 2023-11-07 21:03:04
148
		Repos []Repo `json:"repos"`
08dff9a Jakob Wakeling 2023-11-07 21:03:04
149
	}{}
08dff9a Jakob Wakeling 2023-11-07 21:03:04
150
08dff9a Jakob Wakeling 2023-11-07 21:03:04
151
	bdir := filepath.Join(Conf.DataPath, "backup")
08dff9a Jakob Wakeling 2023-11-07 21:03:04
152
	if err := os.MkdirAll(bdir, 0o777); err != nil {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
153
		return err
08dff9a Jakob Wakeling 2023-11-07 21:03:04
154
	}
08dff9a Jakob Wakeling 2023-11-07 21:03:04
155
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
156
	db, err := sql.Open("sqlite3", filepath.Join(Conf.DataPath, "goit.db?_timeout=5000&_txlock=immediate"))
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
157
	if err != nil {
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
158
		return err
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
159
	}
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
160
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
161
	tx, err := db.Begin()
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
162
	if err != nil {
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
163
		return err
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
164
	}
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
165
	defer tx.Rollback()
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
166
08dff9a Jakob Wakeling 2023-11-07 21:03:04
167
	/* Dump users */
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
168
	rows, err := tx.Query("SELECT id, name, name_full, pass, pass_algo, salt, is_admin FROM users")
08dff9a Jakob Wakeling 2023-11-07 21:03:04
169
	if err != nil {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
170
		return err
08dff9a Jakob Wakeling 2023-11-07 21:03:04
171
	}
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
172
	defer rows.Close()
08dff9a Jakob Wakeling 2023-11-07 21:03:04
173
08dff9a Jakob Wakeling 2023-11-07 21:03:04
174
	for rows.Next() {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
175
		u := User{}
08dff9a Jakob Wakeling 2023-11-07 21:03:04
176
		if err := rows.Scan(&u.Id, &u.Name, &u.FullName, &u.Pass, &u.PassAlgo, &u.Salt, &u.IsAdmin); err != nil {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
177
			return err
08dff9a Jakob Wakeling 2023-11-07 21:03:04
178
		}
08dff9a Jakob Wakeling 2023-11-07 21:03:04
179
08dff9a Jakob Wakeling 2023-11-07 21:03:04
180
		data.Users = append(data.Users, u)
08dff9a Jakob Wakeling 2023-11-07 21:03:04
181
	}
08dff9a Jakob Wakeling 2023-11-07 21:03:04
182
08dff9a Jakob Wakeling 2023-11-07 21:03:04
183
	/* Dump repositories */
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
184
	rows, err = tx.Query(
778d467 Jakob Wakeling 2024-01-17 18:29:55
185
		"SELECT id, owner_id, name, description, default_branch, upstream, visibility, is_mirror FROM repos",
778d467 Jakob Wakeling 2024-01-17 18:29:55
186
	)
08dff9a Jakob Wakeling 2023-11-07 21:03:04
187
	if err != nil {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
188
		return err
08dff9a Jakob Wakeling 2023-11-07 21:03:04
189
	}
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
190
	defer rows.Close()
08dff9a Jakob Wakeling 2023-11-07 21:03:04
191
08dff9a Jakob Wakeling 2023-11-07 21:03:04
192
	for rows.Next() {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
193
		r := Repo{}
778d467 Jakob Wakeling 2024-01-17 18:29:55
194
		if err := rows.Scan(
778d467 Jakob Wakeling 2024-01-17 18:29:55
195
			&r.Id, &r.OwnerId, &r.Name, &r.Description, &r.DefaultBranch, &r.Upstream, &r.Visibility, &r.IsMirror,
778d467 Jakob Wakeling 2024-01-17 18:29:55
196
		); err != nil {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
197
			return err
08dff9a Jakob Wakeling 2023-11-07 21:03:04
198
		}
08dff9a Jakob Wakeling 2023-11-07 21:03:04
199
08dff9a Jakob Wakeling 2023-11-07 21:03:04
200
		data.Repos = append(data.Repos, r)
08dff9a Jakob Wakeling 2023-11-07 21:03:04
201
	}
08dff9a Jakob Wakeling 2023-11-07 21:03:04
202
08dff9a Jakob Wakeling 2023-11-07 21:03:04
203
	/* Open an output ZIP file */
08dff9a Jakob Wakeling 2023-11-07 21:03:04
204
	ts := "goit_" + time.Now().UTC().Format("20060102T150405Z")
08dff9a Jakob Wakeling 2023-11-07 21:03:04
205
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
206
	log.Println("Backing up to", filepath.Join(bdir, ts+".zip"))
08dff9a Jakob Wakeling 2023-11-07 21:03:04
207
	zf, err := os.Create(filepath.Join(bdir, ts+".zip"))
08dff9a Jakob Wakeling 2023-11-07 21:03:04
208
	if err != nil {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
209
		return err
08dff9a Jakob Wakeling 2023-11-07 21:03:04
210
	}
08dff9a Jakob Wakeling 2023-11-07 21:03:04
211
	defer zf.Close()
08dff9a Jakob Wakeling 2023-11-07 21:03:04
212
08dff9a Jakob Wakeling 2023-11-07 21:03:04
213
	zw := zip.NewWriter(zf)
08dff9a Jakob Wakeling 2023-11-07 21:03:04
214
	defer zw.Close()
08dff9a Jakob Wakeling 2023-11-07 21:03:04
215
08dff9a Jakob Wakeling 2023-11-07 21:03:04
216
	/* Copy repositories to ZIP */
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
217
	tempdir, err := os.MkdirTemp(os.TempDir(), "goit-")
08dff9a Jakob Wakeling 2023-11-07 21:03:04
218
	if err != nil {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
219
		return err
08dff9a Jakob Wakeling 2023-11-07 21:03:04
220
	}
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
221
	defer os.RemoveAll(tempdir)
08dff9a Jakob Wakeling 2023-11-07 21:03:04
222
08dff9a Jakob Wakeling 2023-11-07 21:03:04
223
	for _, r := range data.Repos {
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
224
		t0 := time.Now()
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
225
		cd := filepath.Join(tempdir, RepoPath(r.Name, false))
08dff9a Jakob Wakeling 2023-11-07 21:03:04
226
08dff9a Jakob Wakeling 2023-11-07 21:03:04
227
		gr, err := git.PlainClone(cd, true, &git.CloneOptions{
08dff9a Jakob Wakeling 2023-11-07 21:03:04
228
			URL: RepoPath(r.Name, true), Mirror: true,
08dff9a Jakob Wakeling 2023-11-07 21:03:04
229
		})
08dff9a Jakob Wakeling 2023-11-07 21:03:04
230
		if err != nil {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
231
			if errors.Is(err, transport.ErrRepositoryNotFound) {
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
232
				log.Println("Skipping", r.Name, "as it does not exist")
08dff9a Jakob Wakeling 2023-11-07 21:03:04
233
				continue
08dff9a Jakob Wakeling 2023-11-07 21:03:04
234
			}
08dff9a Jakob Wakeling 2023-11-07 21:03:04
235
08dff9a Jakob Wakeling 2023-11-07 21:03:04
236
			if errors.Is(err, transport.ErrEmptyRemoteRepository) {
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
237
				log.Println("Skipping", r.Name, "as it is empty")
08dff9a Jakob Wakeling 2023-11-07 21:03:04
238
				continue
08dff9a Jakob Wakeling 2023-11-07 21:03:04
239
			}
08dff9a Jakob Wakeling 2023-11-07 21:03:04
240
08dff9a Jakob Wakeling 2023-11-07 21:03:04
241
			return err
08dff9a Jakob Wakeling 2023-11-07 21:03:04
242
		}
08dff9a Jakob Wakeling 2023-11-07 21:03:04
243
08dff9a Jakob Wakeling 2023-11-07 21:03:04
244
		if err := gr.DeleteRemote("origin"); err != nil {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
245
			return fmt.Errorf("%s %w", cd, err)
08dff9a Jakob Wakeling 2023-11-07 21:03:04
246
		}
08dff9a Jakob Wakeling 2023-11-07 21:03:04
247
08dff9a Jakob Wakeling 2023-11-07 21:03:04
248
		/* Walk duplicated repository and add it to the ZIP */
08dff9a Jakob Wakeling 2023-11-07 21:03:04
249
		if err = filepath.WalkDir(cd, func(path string, d fs.DirEntry, err error) error {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
250
			if err != nil {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
251
				return err
08dff9a Jakob Wakeling 2023-11-07 21:03:04
252
			}
08dff9a Jakob Wakeling 2023-11-07 21:03:04
253
08dff9a Jakob Wakeling 2023-11-07 21:03:04
254
			info, err := d.Info()
08dff9a Jakob Wakeling 2023-11-07 21:03:04
255
			if err != nil {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
256
				return err
08dff9a Jakob Wakeling 2023-11-07 21:03:04
257
			}
08dff9a Jakob Wakeling 2023-11-07 21:03:04
258
08dff9a Jakob Wakeling 2023-11-07 21:03:04
259
			head, err := zip.FileInfoHeader(info)
08dff9a Jakob Wakeling 2023-11-07 21:03:04
260
			if err != nil {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
261
				return err
08dff9a Jakob Wakeling 2023-11-07 21:03:04
262
			}
08dff9a Jakob Wakeling 2023-11-07 21:03:04
263
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
264
			head.Name = filepath.Join(ts, "repos", strings.TrimPrefix(path, tempdir))
08dff9a Jakob Wakeling 2023-11-07 21:03:04
265
08dff9a Jakob Wakeling 2023-11-07 21:03:04
266
			if d.IsDir() {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
267
				head.Name += "/"
08dff9a Jakob Wakeling 2023-11-07 21:03:04
268
			} else {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
269
				head.Method = zip.Store
08dff9a Jakob Wakeling 2023-11-07 21:03:04
270
			}
08dff9a Jakob Wakeling 2023-11-07 21:03:04
271
08dff9a Jakob Wakeling 2023-11-07 21:03:04
272
			w, err := zw.CreateHeader(head)
08dff9a Jakob Wakeling 2023-11-07 21:03:04
273
			if err != nil {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
274
				return err
08dff9a Jakob Wakeling 2023-11-07 21:03:04
275
			}
08dff9a Jakob Wakeling 2023-11-07 21:03:04
276
08dff9a Jakob Wakeling 2023-11-07 21:03:04
277
			if !d.IsDir() {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
278
				fi, err := os.Open(path)
08dff9a Jakob Wakeling 2023-11-07 21:03:04
279
				if err != nil {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
280
					return err
08dff9a Jakob Wakeling 2023-11-07 21:03:04
281
				}
08dff9a Jakob Wakeling 2023-11-07 21:03:04
282
08dff9a Jakob Wakeling 2023-11-07 21:03:04
283
				if _, err := io.Copy(w, fi); err != nil {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
284
					return err
08dff9a Jakob Wakeling 2023-11-07 21:03:04
285
				}
08dff9a Jakob Wakeling 2023-11-07 21:03:04
286
			}
08dff9a Jakob Wakeling 2023-11-07 21:03:04
287
08dff9a Jakob Wakeling 2023-11-07 21:03:04
288
			return nil
08dff9a Jakob Wakeling 2023-11-07 21:03:04
289
		}); err != nil {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
290
			return err
08dff9a Jakob Wakeling 2023-11-07 21:03:04
291
		}
08dff9a Jakob Wakeling 2023-11-07 21:03:04
292
08dff9a Jakob Wakeling 2023-11-07 21:03:04
293
		os.RemoveAll(cd)
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
294
		log.Println("Backed up", r.Name, "in", time.Since(t0))
08dff9a Jakob Wakeling 2023-11-07 21:03:04
295
	}
08dff9a Jakob Wakeling 2023-11-07 21:03:04
296
08dff9a Jakob Wakeling 2023-11-07 21:03:04
297
	/* Write database as JSON to ZIP */
08dff9a Jakob Wakeling 2023-11-07 21:03:04
298
	if b, err := json.MarshalIndent(data, "", "\t"); err != nil {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
299
		return err
08dff9a Jakob Wakeling 2023-11-07 21:03:04
300
	} else if w, err := zw.Create(filepath.Join(ts, "goit.json")); err != nil {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
301
		return err
08dff9a Jakob Wakeling 2023-11-07 21:03:04
302
	} else if _, err := w.Write(b); err != nil {
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
303
		return err
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
304
	}
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
305
32e8b4d Jakob Wakeling 2024-07-07 17:50:40
306
	if err := tx.Commit(); err != nil {
08dff9a Jakob Wakeling 2023-11-07 21:03:04
307
		return err
08dff9a Jakob Wakeling 2023-11-07 21:03:04
308
	}
08dff9a Jakob Wakeling 2023-11-07 21:03:04
309
08dff9a Jakob Wakeling 2023-11-07 21:03:04
310
	return nil
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
311
}
312