forked from
hailey.at/cocoon
fork
Configure Feed
Select the types of activity you want to include in your feed.
An atproto PDS written in Go
fork
Configure Feed
Select the types of activity you want to include in your feed.
1package main
2
3import (
4 "crypto/ecdsa"
5 "crypto/elliptic"
6 "crypto/rand"
7 "encoding/json"
8 "fmt"
9 "os"
10 "time"
11
12 "github.com/bluesky-social/indigo/atproto/crypto"
13 "github.com/bluesky-social/indigo/atproto/syntax"
14 "github.com/haileyok/cocoon/internal/helpers"
15 "github.com/lestrrat-go/jwx/v2/jwk"
16 "github.com/urfave/cli/v2"
17 "golang.org/x/crypto/bcrypt"
18 "gorm.io/driver/sqlite"
19 "gorm.io/gorm"
20)
21
22func main() {
23 app := cli.App{
24 Name: "admin",
25 Commands: cli.Commands{
26 runCreateRotationKey,
27 runCreatePrivateJwk,
28 runCreateInviteCode,
29 runResetPassword,
30 },
31 ErrWriter: os.Stdout,
32 }
33
34 app.Run(os.Args)
35}
36
37var runCreateRotationKey = &cli.Command{
38 Name: "create-rotation-key",
39 Usage: "creates a rotation key for your pds",
40 Flags: []cli.Flag{
41 &cli.StringFlag{
42 Name: "out",
43 Required: true,
44 Usage: "output file for your rotation key",
45 },
46 },
47 Action: func(cmd *cli.Context) error {
48 key, err := crypto.GeneratePrivateKeyK256()
49 if err != nil {
50 return err
51 }
52
53 bytes := key.Bytes()
54
55 if err := os.WriteFile(cmd.String("out"), bytes, 0644); err != nil {
56 return err
57 }
58
59 return nil
60 },
61}
62
63var runCreatePrivateJwk = &cli.Command{
64 Name: "create-private-jwk",
65 Usage: "creates a private jwk for your pds",
66 Flags: []cli.Flag{
67 &cli.StringFlag{
68 Name: "out",
69 Required: true,
70 Usage: "output file for your jwk",
71 },
72 },
73 Action: func(cmd *cli.Context) error {
74 privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
75 if err != nil {
76 return err
77 }
78
79 key, err := jwk.FromRaw(privKey)
80 if err != nil {
81 return err
82 }
83
84 kid := fmt.Sprintf("%d", time.Now().Unix())
85
86 if err := key.Set(jwk.KeyIDKey, kid); err != nil {
87 return err
88 }
89
90 b, err := json.Marshal(key)
91 if err != nil {
92 return err
93 }
94
95 if err := os.WriteFile(cmd.String("out"), b, 0644); err != nil {
96 return err
97 }
98
99 return nil
100 },
101}
102
103var runCreateInviteCode = &cli.Command{
104 Name: "create-invite-code",
105 Usage: "creates an invite code",
106 Flags: []cli.Flag{
107 &cli.StringFlag{
108 Name: "for",
109 Usage: "optional did to assign the invite code to",
110 },
111 &cli.IntFlag{
112 Name: "uses",
113 Usage: "number of times the invite code can be used",
114 Value: 1,
115 },
116 },
117 Action: func(cmd *cli.Context) error {
118 db, err := newDb()
119 if err != nil {
120 return err
121 }
122
123 forDid := "did:plc:123"
124 if cmd.String("for") != "" {
125 did, err := syntax.ParseDID(cmd.String("for"))
126 if err != nil {
127 return err
128 }
129
130 forDid = did.String()
131 }
132
133 uses := cmd.Int("uses")
134
135 code := fmt.Sprintf("%s-%s", helpers.RandomVarchar(8), helpers.RandomVarchar(8))
136
137 if err := db.Exec("INSERT INTO invite_codes (did, code, remaining_use_count) VALUES (?, ?, ?)", forDid, code, uses).Error; err != nil {
138 return err
139 }
140
141 fmt.Printf("New invite code created with %d uses: %s\n", uses, code)
142
143 return nil
144 },
145}
146
147var runResetPassword = &cli.Command{
148 Name: "reset-password",
149 Usage: "resets a password",
150 Flags: []cli.Flag{
151 &cli.StringFlag{
152 Name: "did",
153 Usage: "did of the user who's password you want to reset",
154 },
155 },
156 Action: func(cmd *cli.Context) error {
157 db, err := newDb()
158 if err != nil {
159 return err
160 }
161
162 didStr := cmd.String("did")
163 did, err := syntax.ParseDID(didStr)
164 if err != nil {
165 return err
166 }
167
168 newPass := fmt.Sprintf("%s-%s", helpers.RandomVarchar(12), helpers.RandomVarchar(12))
169 hashed, err := bcrypt.GenerateFromPassword([]byte(newPass), 10)
170 if err != nil {
171 return err
172 }
173
174 if err := db.Exec("UPDATE repos SET password = ? WHERE did = ?", hashed, did.String()).Error; err != nil {
175 return err
176 }
177
178 fmt.Printf("Password for %s has been reset to: %s", did.String(), newPass)
179
180 return nil
181 },
182}
183
184func newDb() (*gorm.DB, error) {
185 return gorm.Open(sqlite.Open("cocoon.db"), &gorm.Config{})
186}