forked from davecheney/pub
-
Notifications
You must be signed in to change notification settings - Fork 0
/
instance.go
130 lines (115 loc) · 3.52 KB
/
instance.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package models
import (
"fmt"
"time"
"github.com/bardic/pub/internal/crypto"
"github.com/bardic/pub/internal/snowflake"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
)
// An Instance is an ActivityPub domain managed by this server.
// An Instance has many InstanceRules.
// An Instance has one Admin Account.
type Instance struct {
snowflake.ID `gorm:"primarykey;autoIncrement:false"`
UpdatedAt time.Time
Domain string `gorm:"size:64;uniqueIndex"`
AdminID *snowflake.ID
Admin *Account `gorm:"constraint:OnDelete:CASCADE;<-:create;"` // the admin account for this instance
SourceURL string
Title string `gorm:"size:64"`
ShortDescription string
Description string
Thumbnail string `gorm:"size:64"`
AccountsCount int `gorm:"default:0;not null"`
StatusesCount int `gorm:"default:0;not null"`
DomainsCount int32 `gorm:"default:0;not null"`
Rules []InstanceRule `gorm:"constraint:OnDelete:CASCADE;"`
}
type InstanceRule struct {
ID uint32 `gorm:"primarykey"`
InstanceID uint64
Text string
}
type Instances struct {
db *gorm.DB
}
func NewInstances(db *gorm.DB) *Instances {
return &Instances{db: db}
}
// Create creates a new instance, complete with an admin account.
func (i *Instances) Create(domain, title, description, adminEmail string) (*Instance, error) {
var instance Instance
err := i.db.Transaction(func(tx *gorm.DB) error {
kp, err := crypto.GenerateRSAKeypair()
if err != nil {
return err
}
// use the first 72 bytes of the private key as the bcrypt password for the admin account
passwd := trim(kp.PrivateKey, 72)
encrypted, err := bcrypt.GenerateFromPassword(passwd, bcrypt.DefaultCost)
if err != nil {
return err
}
instance = Instance{
ID: snowflake.Now(),
Domain: domain,
SourceURL: "https://github.com/bardic/pub",
Title: title,
ShortDescription: description,
Description: description,
Thumbnail: "https://avatars.githubusercontent.com/u/1024?v=4",
Rules: []InstanceRule{{
Text: "No loafing",
}},
}
if err := tx.Create(&instance).Error; err != nil {
return err
}
var adminRole AccountRole
if err := tx.Where("name = ?", "admin").FirstOrCreate(&adminRole, AccountRole{
Name: "admin",
Position: 1,
Permissions: 0xFFFFFFFF,
Highlighted: true,
}).Error; err != nil {
return err
}
adminAccount := Account{
ID: snowflake.Now(),
Instance: &instance,
Actor: &Actor{
ID: snowflake.Now(),
Type: "LocalService",
URI: fmt.Sprintf("https://%s/u/%s", domain, "admin"),
Name: "admin",
Domain: instance.Domain,
DisplayName: "admin",
Locked: false,
Note: "The admin account for " + domain,
Avatar: "https://avatars.githubusercontent.com/u/1024?v=4",
Header: "https://avatars.githubusercontent.com/u/1024?v=4",
PublicKey: kp.PublicKey,
},
Email: adminEmail,
EncryptedPassword: encrypted,
PrivateKey: kp.PrivateKey,
RoleID: adminRole.ID,
}
if err := tx.Create(&adminAccount).Error; err != nil {
return err
}
return tx.Model(&instance).Update("admin_id", adminAccount.ID).Error
})
return &instance, err
}
// trim trims the first n bytes from the given byte slice
func trim[S []T, T any](s S, n int) S {
return s[:min(len(s), n)]
}
func min(a, b int) int {
if a < b {
return a
}
return b
}