Go · 4700 bytes Raw Blame History
1 // SPDX-License-Identifier: AGPL-3.0-or-later
2
3 package orgs
4
5 import (
6 "errors"
7 "net/http"
8 "net/url"
9 "strconv"
10 "strings"
11
12 "github.com/go-chi/chi/v5"
13 "github.com/jackc/pgx/v5"
14
15 orgdomain "github.com/tenseleyFlow/shithub/internal/orgs"
16 orgsdb "github.com/tenseleyFlow/shithub/internal/orgs/sqlc"
17 "github.com/tenseleyFlow/shithub/internal/web/middleware"
18 )
19
20 type importForm struct {
21 GitHubOrg string
22 GitHubToken string
23 }
24
25 func (h *Handlers) settingsImport(w http.ResponseWriter, r *http.Request) {
26 org, ok := h.loadOrgSettingsOwner(w, r)
27 if !ok {
28 return
29 }
30 h.renderSettingsImport(w, r, org, importForm{}, "", importNotice(r.URL.Query().Get("notice")))
31 }
32
33 func (h *Handlers) settingsImportSubmit(w http.ResponseWriter, r *http.Request) {
34 org, ok := h.loadOrgSettingsOwner(w, r)
35 if !ok {
36 return
37 }
38 if err := r.ParseForm(); err != nil {
39 h.d.Render.HTTPError(w, r, http.StatusBadRequest, "form parse")
40 return
41 }
42 form := importForm{
43 GitHubOrg: strings.TrimSpace(r.PostFormValue("github_org")),
44 GitHubToken: strings.TrimSpace(r.PostFormValue("github_token")),
45 }
46 viewer := middleware.CurrentUserFromContext(r.Context())
47 imp, err := orgdomain.StartGitHubImport(r.Context(), orgdomain.ImportDeps{
48 Pool: h.d.Pool, Box: h.d.SecretBox, Logger: h.d.Logger,
49 }, orgdomain.StartGitHubImportParams{
50 OrgID: org.ID, SourceOrg: form.GitHubOrg,
51 RequestedByUserID: viewer.ID, Token: form.GitHubToken,
52 })
53 if err != nil {
54 h.renderSettingsImport(w, r, org, form.withoutToken(), friendlyImportError(err), "")
55 return
56 }
57 http.Redirect(w, r, "/organizations/"+org.Slug+"/imports/"+strconv.FormatInt(imp.ID, 10), http.StatusSeeOther)
58 }
59
60 func (f importForm) withoutToken() importForm {
61 f.GitHubToken = ""
62 return f
63 }
64
65 func (h *Handlers) importProgress(w http.ResponseWriter, r *http.Request) {
66 org, ok := h.loadOrgSettingsOwner(w, r)
67 if !ok {
68 return
69 }
70 importID, err := strconv.ParseInt(chi.URLParam(r, "importID"), 10, 64)
71 if err != nil || importID <= 0 {
72 h.d.Render.HTTPError(w, r, http.StatusNotFound, "")
73 return
74 }
75 q := orgsdb.New()
76 progress, err := q.GetOrgGithubImportProgress(r.Context(), h.d.Pool, orgsdb.GetOrgGithubImportProgressParams{
77 ID: importID,
78 OrgID: org.ID,
79 })
80 if err != nil {
81 if errors.Is(err, pgx.ErrNoRows) {
82 h.d.Render.HTTPError(w, r, http.StatusNotFound, "")
83 return
84 }
85 h.d.Render.HTTPError(w, r, http.StatusInternalServerError, "")
86 return
87 }
88 items, err := q.ListOrgGithubImportRepos(r.Context(), h.d.Pool, importID)
89 if err != nil {
90 h.d.Render.HTTPError(w, r, http.StatusInternalServerError, "")
91 return
92 }
93 if err := h.d.Render.RenderPage(w, r, "orgs/import_progress", map[string]any{
94 "Title": org.Slug + " - GitHub import",
95 "CSRFToken": middleware.CSRFTokenForRequest(r),
96 "Org": org,
97 "AvatarURL": "/avatars/" + url.PathEscape(org.Slug),
98 "ActiveOrgNav": "settings",
99 "Progress": progress,
100 "Items": items,
101 "IsTerminal": orgdomain.IsTerminalImportStatus(progress.Status),
102 }); err != nil {
103 h.d.Logger.ErrorContext(r.Context(), "org import: render progress", "error", err)
104 }
105 }
106
107 func (h *Handlers) renderSettingsImport(w http.ResponseWriter, r *http.Request, org orgsdb.Org, form importForm, errMsg, notice string) {
108 imports, err := orgsdb.New().ListOrgGithubImportsForOrg(r.Context(), h.d.Pool, orgsdb.ListOrgGithubImportsForOrgParams{
109 OrgID: org.ID,
110 Limit: 10,
111 })
112 if err != nil {
113 h.d.Logger.WarnContext(r.Context(), "org import: list imports", "error", err, "org_id", org.ID)
114 }
115 _ = h.d.Render.RenderPage(w, r, "orgs/settings_import", map[string]any{
116 "Title": org.Slug + " - repository import",
117 "CSRFToken": middleware.CSRFTokenForRequest(r),
118 "Org": org,
119 "AvatarURL": "/avatars/" + url.PathEscape(org.Slug),
120 "ActiveOrgNav": "settings",
121 "OrgSettingsActive": "import",
122 "BillingEnabled": h.d.BillingEnabled,
123 "Form": form,
124 "Error": errMsg,
125 "Notice": notice,
126 "Imports": imports,
127 "SecretBoxOK": h.d.SecretBox != nil,
128 })
129 }
130
131 func friendlyImportError(err error) string {
132 switch {
133 case errors.Is(err, orgdomain.ErrInvalidGitHubOrg):
134 return "GitHub organization must be a valid organization name or github.com organization URL."
135 case errors.Is(err, orgdomain.ErrImportTokenKeyNeeded):
136 return "GitHub token imports require the server secret key to be configured."
137 default:
138 return "Could not start the GitHub organization import."
139 }
140 }
141
142 func importNotice(code string) string {
143 switch code {
144 case "start-failed":
145 return "The organization was created, but the import could not be started. Try again here."
146 default:
147 return ""
148 }
149 }
150