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
|
|