1
+ package provider
2
+
3
+ import (
4
+ "context"
5
+ "encoding/json"
6
+ "fmt"
7
+ "net/http"
8
+ "strings"
9
+
10
+ "github.com/cli/browser"
11
+ )
12
+
13
+ func init () {
14
+ Register ("forgejo" , & ForgejoProvider {})
15
+ Register ("codeberg" , & ForgejoProvider {host : "codeberg.org" })
16
+ }
17
+
18
+ type ForgejoProvider struct {
19
+ host string
20
+ }
21
+
22
+ func (f * ForgejoProvider ) SetHost (host string ) {
23
+ f .host = host
24
+ }
25
+
26
+ func (f * ForgejoProvider ) SetClientID (clientID string ) {
27
+ }
28
+
29
+ func (f * ForgejoProvider ) getBaseURL () string {
30
+ if f .host != "" {
31
+ return fmt .Sprintf ("https://%s" , f .host )
32
+ }
33
+ // This should not happen as we validate in Authenticate()
34
+ return ""
35
+ }
36
+
37
+ func (f * ForgejoProvider ) getAPIURL () string {
38
+ return fmt .Sprintf ("%s/api/v1" , f .getBaseURL ())
39
+ }
40
+
41
+ func (f * ForgejoProvider ) makeForgejoAPIRequest (ctx context.Context , token string , endpoint string ) (* http.Response , error ) {
42
+ headers := map [string ]string {
43
+ "Accept" : "application/json" ,
44
+ }
45
+ return makeAuthenticatedRequest (ctx , "GET" , endpoint , "token " + token , headers )
46
+ }
47
+
48
+ func (f * ForgejoProvider ) Name () string {
49
+ return "forgejo"
50
+ }
51
+
52
+ func (f * ForgejoProvider ) Host () string {
53
+ return f .host
54
+ }
55
+
56
+ func (f * ForgejoProvider ) GetScopes () []string {
57
+ return []string {"read:repository" , "read:user" }
58
+ }
59
+
60
+ func (f * ForgejoProvider ) Authenticate (ctx context.Context ) (string , error ) {
61
+ // Validate that we have a host
62
+ if f .host == "" {
63
+ return "" , fmt .Errorf ("--host flag is required for forgejo provider (e.g., --host git.company.com)" )
64
+ }
65
+
66
+ fmt .Println ()
67
+ fmt .Println ("Forgejo does not support OAuth device flow. You'll need to create a Personal Access Token." )
68
+ fmt .Println ()
69
+ fmt .Println ("Instructions:" )
70
+ fmt .Printf ("1. Go to %s/user/settings/applications\n " , f .getBaseURL ())
71
+ fmt .Println ("2. In the 'Generate New Token' section, enter a token name (e.g., 'nix-auth')" )
72
+ fmt .Println ("3. Select the following access and permissions:" )
73
+ fmt .Println (" - Repository and Organization Access: All (public, private, and limited)" )
74
+ fmt .Println (" - Permissions: read:repository, read:user" )
75
+ fmt .Println ("4. Click 'Generate Token'" )
76
+ fmt .Println ("5. Copy the generated token" )
77
+ fmt .Println ()
78
+
79
+ tokenURL := fmt .Sprintf ("%s/user/settings/applications" , f .getBaseURL ())
80
+ fmt .Printf ("Opening %s in your browser...\n " , tokenURL )
81
+
82
+ if err := browser .OpenURL (tokenURL ); err != nil {
83
+ fmt .Println ("Could not open browser automatically." )
84
+ fmt .Printf ("Please manually visit: %s\n " , tokenURL )
85
+ }
86
+
87
+ fmt .Println ()
88
+ var token string
89
+ fmt .Print ("Enter your Personal Access Token: " )
90
+ if _ , err := fmt .Scanln (& token ); err != nil {
91
+ return "" , fmt .Errorf ("failed to read token: %w" , err )
92
+ }
93
+
94
+ token = strings .TrimSpace (token )
95
+ if token == "" {
96
+ return "" , fmt .Errorf ("token cannot be empty" )
97
+ }
98
+
99
+ if err := f .ValidateToken (ctx , token ); err != nil {
100
+ return "" , fmt .Errorf ("invalid token: %w" , err )
101
+ }
102
+
103
+ return token , nil
104
+ }
105
+
106
+ func (f * ForgejoProvider ) ValidateToken (ctx context.Context , token string ) error {
107
+ userURL := fmt .Sprintf ("%s/user" , f .getAPIURL ())
108
+ resp , err := f .makeForgejoAPIRequest (ctx , token , userURL )
109
+ if err != nil {
110
+ return fmt .Errorf ("failed to validate token: %w" , err )
111
+ }
112
+ defer resp .Body .Close ()
113
+
114
+ return nil
115
+ }
116
+
117
+ func (f * ForgejoProvider ) GetUserInfo (ctx context.Context , token string ) (username , fullName string , err error ) {
118
+ userURL := fmt .Sprintf ("%s/user" , f .getAPIURL ())
119
+ resp , err := f .makeForgejoAPIRequest (ctx , token , userURL )
120
+ if err != nil {
121
+ return "" , "" , fmt .Errorf ("failed to get user info: %w" , err )
122
+ }
123
+ defer resp .Body .Close ()
124
+
125
+ var user struct {
126
+ Login string `json:"login"`
127
+ Username string `json:"username"`
128
+ FullName string `json:"full_name"`
129
+ }
130
+
131
+ if err := json .NewDecoder (resp .Body ).Decode (& user ); err != nil {
132
+ return "" , "" , fmt .Errorf ("failed to decode response: %w" , err )
133
+ }
134
+
135
+ username = user .Username
136
+ if username == "" {
137
+ username = user .Login
138
+ }
139
+
140
+ return username , user .FullName , nil
141
+ }
142
+
143
+ func (f * ForgejoProvider ) GetTokenScopes (ctx context.Context , token string ) ([]string , error ) {
144
+ return f .GetScopes (), nil
145
+ }
0 commit comments