Skip to content
This repository was archived by the owner on Nov 20, 2025. It is now read-only.

Commit 9d39225

Browse files
committed
backup
1 parent 1cc3489 commit 9d39225

File tree

7 files changed

+194
-13
lines changed

7 files changed

+194
-13
lines changed

.env.example

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
# ALIENOS_WORK_DIR/blossom (stores blobs)
33
# ALIENOS_WORK_DIR/db (stores badger db file)
44
# ALIENOS_WORK_DIR/search_db (stores bulge db file)
5-
# ALIENOS_WORK_DIR/plugins (stores installed plugins)
5+
# ALIENOS_WORK_DIR/management.json (stores management information)
66
# ALIENOS_WORK_DIR/nip05.json (stores nip05 document)
77
ALIENOS_WORK_DIR="alienos_wd/"
88

99
# Relay information document (NIP-11)
10-
ALIENOS_RELAY_NAME="alienos"
11-
ALIENOS_RELAY_ICON="https://raw.githubusercontent.com/dezh-tech/alienos/refs/heads/main/.image/logo.png?token=GHSAT0AAAAAACYQ42AVUYV5HPNY2PCL4PR6Z5HXGAA"
12-
ALIENOS_RELAY_BANNER="https://raw.githubusercontent.com/dezh-tech/alienos/d11be85135ce5dddcc4350d8a779396761642d76/.image/banner.png?token=GHSAT0AAAAAACYQ42AVV3LOPA6HWE5JKL2MZ5HXFRQ"
10+
ALIENOS_RELAY_NAME="Alienos"
11+
ALIENOS_RELAY_ICON=""
12+
ALIENOS_RELAY_BANNER=""
1313
ALIENOS_RELAY_DESCRIPTION="A self-hosting Nostr stack!"
1414
ALIENOS_RELAY_PUBKEY="badbdda507572b397852048ea74f2ef3ad92b1aac07c3d4e1dec174e8cdc962a"
1515
ALIENOS_RELAY_CONTACT="hi@dezh.tech"
@@ -23,13 +23,20 @@ ALIENOS_RELAY_PORT=":7771"
2323
ALIENOS_RELAY_BIND="0.0.0.0"
2424
ALIENOS_RELAY_URL="alienos.jellyfish.land"
2525

26+
# Backup Info
27+
ALIENOS_BACKUP_ENABLE="false"
28+
ALIENOS_BACKUP_INTERVAL_HOURS=24
29+
ALIENOS_S3_ACCESS_KEY_ID=""
30+
ALIENOS_S3_SECRET_KEY=""
31+
ALIENOS_S3_ENDPOINT=""
32+
ALIENOS_S3_REGION=""
33+
ALIENOS_S3_BUCKET_NAME="alienos-relay-backups"
34+
2635
# Access Control
2736

2837
# If set to true, accept notes only with white listed pubkeys/kinds.
29-
3038
ALIENOS_PUBKEY_WHITE_LISTED="false"
3139
ALIENOS_KIND_WHITE_LISTED="false"
3240

3341
# List of keys with access to NIP-86 moderation APIs
34-
3542
ALIENOS_ADMINS="badbdda507572b397852048ea74f2ef3ad92b1aac07c3d4e1dec174e8cdc962a"

README.md

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Alienos
99
<br/>
1010

1111

12-
The Alienos is a Nostr stack (relay/blossom mediaserver/nip-05 server) which is manageable (using nip-86) and [plugin-able](./docs/plugins.md). We designed it for self-hosting and backups.
12+
The Alienos is a Nostr stack (relay/blossom mediaserver/nip-05 server) which is manageable (using nip-86) and [plugin-able](wip). We designed it for self-hosting and backups.
1313

1414
This project is based on [Khatru](https://github.com/fiatjaf/khatru), [EventStore](https://github.com/fiatjaf/eventstore), [BlobStore](github.com/kehiy/blobstore) and [go-nostr](github.com/nbd-wtf/go-nostr).
1515

@@ -19,19 +19,37 @@ This project is based on [Khatru](https://github.com/fiatjaf/khatru), [EventStor
1919
- [X] Support BUDs: 1, 2, 4, 6, 9.
2020
- [X] NIP-05 server.
2121
- [X] Manageable using NIP-86.
22-
- [X] Landing Page with NIP-11 document.
23-
- [ ] S3 backups (relay dbs/blobs/nip05 data/management info).
24-
- [ ] Full install document.
22+
- [X] Landing page with NIP-11 document.
23+
- [X] S3 backups (relay dbs/blobs/nip05 data/management info).
24+
- [ ] Full setup document.
2525
- [ ] Moderator notifications.
2626
- [ ] StartOS support.
2727
- [ ] Umbrel support.
2828
- [ ] Support plugins.
2929

30-
## How to run?
30+
## How to set it up?
31+
32+
### VPS
33+
34+
> TODO.
35+
36+
#### Docker
37+
38+
> TODO.
39+
40+
#### OS
41+
42+
> TODO.
43+
44+
### Umbrel
45+
46+
> TODO.
47+
48+
### StartOS
3149

3250
> TODO.
3351
34-
## Use cases
52+
## Limitations
3553

3654
This project is highly suitable for personal, community, team and backup usage since its light-weight, feature-full and easy to setup/manage.
3755

backup.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package main
2+
3+
import (
4+
"archive/zip"
5+
"context"
6+
"fmt"
7+
"io"
8+
"log"
9+
"os"
10+
"path/filepath"
11+
"time"
12+
13+
"github.com/minio/minio-go/v7"
14+
"github.com/minio/minio-go/v7/pkg/credentials"
15+
)
16+
17+
func backupWorker() {
18+
ticker := time.NewTicker(time.Duration(config.BackupInterval) * time.Minute)
19+
defer ticker.Stop()
20+
21+
for range ticker.C {
22+
path, err := CreateZIPBackup()
23+
if err != nil {
24+
log.Printf("can't create backup file: %s\n", err.Error())
25+
continue
26+
}
27+
28+
if err := S3Upload(path); err != nil {
29+
log.Printf("can't upload backup file: %s\n", err.Error())
30+
}
31+
}
32+
}
33+
34+
func S3Upload(backupPath string) error {
35+
client, err := minio.New(config.S3Endpoint, &minio.Options{
36+
Creds: credentials.NewStaticV4(config.S3AccessKeyID, config.S3SecretKey, ""),
37+
Region: config.S3Region,
38+
Secure: true,
39+
})
40+
if err != nil {
41+
return err
42+
}
43+
44+
file, err := os.Open(backupPath)
45+
if err != nil {
46+
return err
47+
}
48+
defer file.Close()
49+
50+
fileInfo, err := file.Stat()
51+
if err != nil {
52+
return err
53+
}
54+
55+
_, err = client.PutObject(
56+
context.Background(),
57+
config.S3BucketName,
58+
backupPath,
59+
file,
60+
fileInfo.Size(),
61+
minio.PutObjectOptions{
62+
ContentType: "application/octet-stream",
63+
},
64+
)
65+
if err != nil {
66+
return err
67+
68+
}
69+
70+
if err := os.Remove(backupPath); err != nil {
71+
return err
72+
}
73+
74+
return nil
75+
}
76+
77+
func CreateZIPBackup() (string, error) {
78+
backupPath := fmt.Sprintf("alienos-backup-%s.zip", time.Now().Format("2006-January-02"))
79+
file, err := os.Create(backupPath)
80+
if err != nil {
81+
return "", err
82+
}
83+
defer file.Close()
84+
85+
w := zip.NewWriter(file)
86+
defer w.Close()
87+
88+
walker := func(path string, info os.FileInfo, err error) error {
89+
if err != nil {
90+
return err
91+
}
92+
if info.IsDir() {
93+
return nil
94+
}
95+
file, err := os.Open(path)
96+
if err != nil {
97+
return err
98+
}
99+
defer file.Close()
100+
101+
f, err := w.Create(path)
102+
if err != nil {
103+
return err
104+
}
105+
106+
_, err = io.Copy(f, file)
107+
if err != nil {
108+
return err
109+
}
110+
111+
return nil
112+
}
113+
114+
if err := filepath.Walk(config.WorkingDirectory, walker); err != nil {
115+
return "", err
116+
}
117+
118+
return backupPath, nil
119+
}

config.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ type Config struct {
2020
RelayURL string `mapstructure:"ALIENOS_RELAY_URL"`
2121
WhiteListedPubkey bool `mapstructure:"ALIENOS_PUBKEY_WHITE_LISTED"`
2222
WhiteListedKind bool `mapstructure:"ALIENOS_KIND_WHITE_LISTED"`
23+
BackupEnabled bool `mapstructure:"ALIENOS_BACKUP_ENABLE"`
24+
BackupInterval int `mapstructure:"ALIENOS_BACKUP_INTERVAL_HOURS"`
25+
S3AccessKeyID string `mapstructure:"ALIENOS_S3_ACCESS_KEY_ID"`
26+
S3SecretKey string `mapstructure:"ALIENOS_S3_SECRET_KEY"`
27+
S3Endpoint string `mapstructure:"ALIENOS_S3_ENDPOINT"`
28+
S3Region string `mapstructure:"ALIENOS_S3_REGION"`
29+
S3BucketName string `mapstructure:"ALIENOS_S3_BUCKET_NAME"`
2330
Admins []string `mapstructure:"ALIENOS_ADMINS"`
2431
}
2532

@@ -42,6 +49,7 @@ func LoadConfig() {
4249
viper.SetDefault("ALIENOS_PUBKEY_WHITE_LISTED", false)
4350
viper.SetDefault("ALIENOS_KIND_WHITE_LISTED", false)
4451
viper.SetDefault("ALIENOS_ADMINS", []string{"badbdda507572b397852048ea74f2ef3ad92b1aac07c3d4e1dec174e8cdc962a"})
52+
viper.SetDefault("BackupEnabled", false)
4553

4654
viper.AutomaticEnv()
4755

go.mod

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/fiatjaf/eventstore v0.15.0
77
github.com/fiatjaf/khatru v0.15.2
88
github.com/kehiy/blobstore v0.1.0
9+
github.com/minio/minio-go/v7 v7.0.84
910
github.com/nbd-wtf/go-nostr v0.49.3
1011
github.com/spf13/viper v1.19.0
1112
)
@@ -39,16 +40,21 @@ require (
3940
github.com/dustin/go-humanize v1.0.1 // indirect
4041
github.com/fasthttp/websocket v1.5.7 // indirect
4142
github.com/fsnotify/fsnotify v1.7.0 // indirect
43+
github.com/go-ini/ini v1.67.0 // indirect
44+
github.com/goccy/go-json v0.10.4 // indirect
4245
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
4346
github.com/golang/snappy v0.0.4 // indirect
4447
github.com/google/flatbuffers v24.12.23+incompatible // indirect
48+
github.com/google/uuid v1.6.0 // indirect
4549
github.com/hashicorp/hcl v1.0.0 // indirect
4650
github.com/josharian/intern v1.0.0 // indirect
4751
github.com/json-iterator/go v1.1.12 // indirect
4852
github.com/klauspost/compress v1.17.11 // indirect
53+
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
4954
github.com/liamg/magic v0.0.1 // indirect
5055
github.com/magiconair/properties v1.8.7 // indirect
5156
github.com/mailru/easyjson v0.7.7 // indirect
57+
github.com/minio/md5-simd v1.1.2 // indirect
5258
github.com/mitchellh/mapstructure v1.5.0 // indirect
5359
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
5460
github.com/modern-go/reflect2 v1.0.2 // indirect
@@ -57,6 +63,7 @@ require (
5763
github.com/pkg/errors v0.9.1 // indirect
5864
github.com/puzpuzpuz/xsync/v3 v3.4.0 // indirect
5965
github.com/rs/cors v1.11.1 // indirect
66+
github.com/rs/xid v1.6.0 // indirect
6067
github.com/sagikazarmark/locafero v0.4.0 // indirect
6168
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
6269
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
@@ -73,6 +80,7 @@ require (
7380
go.opencensus.io v0.24.0 // indirect
7481
go.uber.org/atomic v1.9.0 // indirect
7582
go.uber.org/multierr v1.9.0 // indirect
83+
golang.org/x/crypto v0.32.0 // indirect
7684
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d // indirect
7785
golang.org/x/net v0.34.0 // indirect
7886
golang.org/x/sys v0.29.0 // indirect

go.sum

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z
9898
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
9999
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
100100
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
101+
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
102+
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
103+
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
104+
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
101105
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
102106
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
103107
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -128,6 +132,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
128132
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
129133
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
130134
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
135+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
136+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
131137
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
132138
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
133139
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
@@ -142,6 +148,9 @@ github.com/kehiy/blobstore v0.1.0/go.mod h1:gT1BsTg5Mzqd0nawV7qqaqcW3KFR56R4R4wo
142148
github.com/klauspost/compress v1.15.2/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
143149
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
144150
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
151+
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
152+
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
153+
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
145154
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
146155
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
147156
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -155,6 +164,10 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V
155164
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
156165
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
157166
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
167+
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
168+
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
169+
github.com/minio/minio-go/v7 v7.0.84 h1:D1HVmAF8JF8Bpi6IU4V9vIEj+8pc+xU88EWMs2yed0E=
170+
github.com/minio/minio-go/v7 v7.0.84/go.mod h1:57YXpvc5l3rjPdhqNrDsvVlY0qPI6UTk1bflAe+9doY=
158171
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
159172
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
160173
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
@@ -183,6 +196,8 @@ github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU
183196
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
184197
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
185198
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
199+
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
200+
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
186201
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
187202
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
188203
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
@@ -246,6 +261,8 @@ go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTV
246261
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
247262
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
248263
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
264+
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
265+
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
249266
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
250267
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
251268
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=

main.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package main
22

33
import (
4-
_ "embed"
54
"context"
5+
_ "embed"
66
"fmt"
77
"html/template"
88
"log"
@@ -117,6 +117,10 @@ func main() {
117117
mux.HandleFunc("GET /{$}", StaticViewHandler)
118118
mux.HandleFunc("/.well-known/nostr.json", NIP05Handler)
119119

120+
if config.BackupEnabled {
121+
go backupWorker()
122+
}
123+
120124
log.Printf("Serving on ws://%s\n", config.RelayBind+config.RelayPort)
121125
http.ListenAndServe(config.RelayBind+config.RelayPort, relay)
122126
}

0 commit comments

Comments
 (0)