diff --git a/options/locale/locale_en-US.json b/options/locale/locale_en-US.json index c4324bd51a..eba5b039e9 100644 --- a/options/locale/locale_en-US.json +++ b/options/locale/locale_en-US.json @@ -4466,6 +4466,8 @@ "repo.settings.pages.secondary_cta": "Secondary Call to Action", "repo.settings.pages.cta_label": "Button Label", "repo.settings.pages.cta_url": "Button URL", + "repo.settings.pages.public_releases": "Public Releases", + "repo.settings.pages.public_releases_desc": "Allow unauthenticated users to download releases. Useful for landing pages on private repositories.", "repo.settings.pages.stats": "Stats", "repo.settings.pages.value_props": "Value Propositions", "repo.settings.pages.features": "Features", diff --git a/routers/web/pages/pages.go b/routers/web/pages/pages.go index ab8e38ee07..f5d3cfdaa8 100644 --- a/routers/web/pages/pages.go +++ b/routers/web/pages/pages.go @@ -118,11 +118,13 @@ func renderLandingPage(ctx *context.Context, repo *repo_model.Repository, config ctx.Data["NumForks"] = repo.NumForks ctx.Data["Year"] = time.Now().Year() - // Load latest release + // Load latest release with attachments release, err := repo_model.GetLatestReleaseByRepoID(ctx, repo.ID) if err == nil && release != nil { + _ = repo_model.GetReleaseAttachments(ctx, release) ctx.Data["LatestRelease"] = release } + ctx.Data["PublicReleases"] = config.Advanced.PublicReleases // Select template based on config tpl := selectTemplate(config.Template) diff --git a/routers/web/repo/setting/pages.go b/routers/web/repo/setting/pages.go index 5b33f70bf7..8d0dcda993 100644 --- a/routers/web/repo/setting/pages.go +++ b/routers/web/repo/setting/pages.go @@ -260,6 +260,7 @@ func PagesContent(ctx *context.Context) { func PagesContentPost(ctx *context.Context) { config := getPagesLandingConfig(ctx) + config.Advanced.PublicReleases = ctx.FormBool("public_releases") config.Stats = nil for i := range 10 { value := ctx.FormString(fmt.Sprintf("stat_value_%d", i)) diff --git a/services/context/repo.go b/services/context/repo.go index b0d03174c5..068fa456b9 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -37,6 +37,7 @@ import ( "code.gitcaddy.com/server/v3/modules/setting" "code.gitcaddy.com/server/v3/modules/util" asymkey_service "code.gitcaddy.com/server/v3/services/asymkey" + pages_service "code.gitcaddy.com/server/v3/services/pages" "github.com/editorconfig/editorconfig-core-go/v2" ) @@ -402,8 +403,13 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) { EarlyResponseForGoGetMeta(ctx) return } - ctx.NotFound(nil) - return + // Allow release downloads for private repos with public_releases enabled + reqPath := ctx.Req.URL.Path + isReleaseDownload := strings.Contains(reqPath, "/releases/download/") || strings.Contains(reqPath, "/releases/attachments/") + if !isReleaseDownload || !pages_service.HasPublicReleases(ctx, repo) { + ctx.NotFound(nil) + return + } } ctx.Data["Permission"] = &ctx.Repo.Permission diff --git a/templates/pages/bold-marketing.tmpl b/templates/pages/bold-marketing.tmpl index 81a82aa947..ea9b892faa 100644 --- a/templates/pages/bold-marketing.tmpl +++ b/templates/pages/bold-marketing.tmpl @@ -429,6 +429,76 @@ font-weight: 700; } + /* --- Downloads --- */ + .nb-downloads { + position: relative; + z-index: 1; + padding: 80px 48px; + border-top: 1px solid var(--nb-border-hard); + border-bottom: 1px solid var(--nb-border-hard); + } + + .nb-downloads-inner { + max-width: 900px; + margin: 0 auto; + } + + .nb-downloads-header { + margin-bottom: 28px; + text-align: center; + } + + .nb-downloads-header h2 { + font-size: 32px; + font-weight: 800; + text-transform: uppercase; + letter-spacing: -0.02em; + margin-bottom: 8px; + } + + .nb-downloads-header p { + font-family: 'Space Mono', monospace; + font-size: 12px; + color: var(--nb-subtle); + text-transform: uppercase; + letter-spacing: 0.15em; + font-weight: 700; + } + + .nb-downloads-grid { + display: flex; + flex-direction: column; + gap: 2px; + } + + .nb-download-item { + display: flex; + align-items: center; + gap: 16px; + padding: 18px 24px; + background: var(--nb-surface); + color: var(--nb-text); + text-decoration: none; + font-family: 'Space Mono', monospace; + font-size: 13px; + font-weight: 700; + transition: all 0.3s ease; + border-left: 3px solid transparent; + } + + .nb-download-item:hover { + background: #161616; + border-left-color: var(--nb-accent); + transform: translateX(4px); + } + + .nb-download-size { + margin-left: auto; + font-size: 11px; + color: var(--nb-subtle); + letter-spacing: 0.04em; + } + /* --- Trust bar --- */ .nb-trust-bar { position: relative; @@ -1024,6 +1094,7 @@ .nb-cta-banner { padding: 56px 24px; } .nb-cta-banner h2 { font-size: 32px; } .nb-cta-banner p { font-size: 16px; } + .nb-downloads { padding: 48px 24px; } .nb-footer { flex-direction: column; text-align: center; padding: 32px 24px; } .nb-section-header h2 { font-size: 32px; } } @@ -1112,6 +1183,27 @@ {{end}} + {{if and .PublicReleases .LatestRelease .LatestRelease.Attachments}} + + + + Download + v{{.LatestRelease.TagName}} + Get the latest release + + + {{range .LatestRelease.Attachments}} + + {{svg "octicon-download" 16}} + {{.Name}} + {{FileSize .Size}} + + {{end}} + + + + {{end}} + {{if .Config.SocialProof.Logos}} diff --git a/templates/pages/minimalist-docs.tmpl b/templates/pages/minimalist-docs.tmpl index af183f4e92..068ada407a 100644 --- a/templates/pages/minimalist-docs.tmpl +++ b/templates/pages/minimalist-docs.tmpl @@ -365,6 +365,45 @@ text-transform: uppercase; } + /* --- Downloads --- */ + .ea-downloads { + max-width: 780px; + margin: 0 auto; + padding: 0 32px 72px; + } + + .ea-downloads-list { + display: flex; + flex-direction: column; + } + + .ea-download-item { + display: flex; + align-items: center; + gap: 14px; + padding: 16px 0; + border-bottom: 1px solid var(--ea-border); + color: var(--ea-text); + text-decoration: none; + font-family: 'IBM Plex Mono', monospace; + font-size: 14px; + transition: color 0.3s ease; + } + + .ea-download-item:first-child { + border-top: 1px solid var(--ea-ink); + } + + .ea-download-item:hover { + color: var(--ea-accent); + } + + .ea-download-size { + margin-left: auto; + color: var(--ea-light); + font-size: 12px; + } + /* --- Sections --- */ .ea-section { position: relative; @@ -907,6 +946,9 @@ .ea-rule { padding: 0 24px; } + .ea-downloads { + padding: 0 24px 48px; + } .ea-used-by { padding: 48px 24px 64px; } @@ -1031,6 +1073,24 @@ {{end}} + {{if and .PublicReleases .LatestRelease .LatestRelease.Attachments}} + + Downloads + v{{.LatestRelease.TagName}} + Get the latest release + + {{range .LatestRelease.Attachments}} + + {{svg "octicon-download" 16}} + {{.Name}} + {{FileSize .Size}} + + {{end}} + + + + {{end}} + {{if .Config.ValueProps}} diff --git a/templates/pages/open-source-hero.tmpl b/templates/pages/open-source-hero.tmpl index 4a96b3fd9e..be5a99d465 100644 --- a/templates/pages/open-source-hero.tmpl +++ b/templates/pages/open-source-hero.tmpl @@ -431,6 +431,62 @@ font-weight: 500; } + /* Downloads section */ + .osh-downloads { + padding: 80px 40px; + position: relative; + } + + .osh-downloads-inner { + max-width: 800px; + margin: 0 auto; + text-align: center; + } + + .osh-downloads h2 { + font-size: clamp(24px, 3vw, 32px); + font-weight: 600; + margin-bottom: 8px; + letter-spacing: -0.025em; + } + + .osh-downloads p { + color: var(--osh-muted); + margin-bottom: 24px; + font-size: 15px; + } + + .osh-downloads-grid { + display: flex; + flex-wrap: wrap; + gap: 12px; + justify-content: center; + } + + .osh-download-item { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 10px 20px; + background: rgba(255,255,255,0.04); + border: 1px solid rgba(255,255,255,0.08); + border-radius: 8px; + color: var(--osh-text); + text-decoration: none; + font-size: 14px; + transition: all 0.2s; + } + + .osh-download-item:hover { + background: rgba(255,255,255,0.08); + border-color: var(--osh-accent); + } + + .osh-download-size { + font-size: 12px; + color: var(--osh-muted); + } + /* Feature cards */ .osh-features { padding: 120px 40px; @@ -1014,6 +1070,25 @@ {{end}} + + {{if and .PublicReleases .LatestRelease .LatestRelease.Attachments}} + + + Download v{{.LatestRelease.TagName}} + Get the latest release + + {{range .LatestRelease.Attachments}} + + {{svg "octicon-download" 16}} + {{.Name}} + {{FileSize .Size}} + + {{end}} + + + + {{end}} + {{if .Config.ValueProps}} diff --git a/templates/pages/saas-conversion.tmpl b/templates/pages/saas-conversion.tmpl index f8e9393fc7..c63ad6afab 100644 --- a/templates/pages/saas-conversion.tmpl +++ b/templates/pages/saas-conversion.tmpl @@ -501,6 +501,70 @@ font-weight: 500; } + /* --- Downloads --- */ + .gm-downloads { + position: relative; + z-index: 2; + padding: 60px 48px; + } + + .gm-downloads-inner { + max-width: 900px; + margin: 0 auto; + } + + .gm-downloads-header { + text-align: center; + margin-bottom: 28px; + } + + .gm-downloads-header h2 { + font-size: 28px; + font-weight: 700; + margin-bottom: 8px; + color: var(--gm-text); + } + + .gm-downloads-header p { + font-size: 15px; + color: var(--gm-muted); + } + + .gm-downloads-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 12px; + } + + .gm-download-item { + display: flex; + align-items: center; + gap: 14px; + padding: 20px 24px; + background: var(--gm-glass); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + border: 1px solid var(--gm-glass-border); + border-radius: 14px; + color: var(--gm-text); + text-decoration: none; + font-size: 14px; + font-weight: 500; + transition: all 0.3s ease; + } + + .gm-download-item:hover { + background: rgba(255, 255, 255, 0.1); + border-color: rgba(125, 211, 252, 0.3); + transform: translateY(-2px); + } + + .gm-download-size { + margin-left: auto; + font-size: 12px; + color: var(--gm-light); + } + /* --- Trust bar --- */ .gm-trust { position: relative; @@ -988,6 +1052,7 @@ .gm-stats { padding: 40px 24px; } .gm-trust { padding: 32px 24px; } .gm-cta-gradient { padding: 48px 24px; border-radius: 20px; } + .gm-downloads { padding: 40px 24px; } .gm-footer { flex-direction: column; text-align: center; padding: 32px 24px; } .gm-value-grid { grid-template-columns: 1fr; } .gm-pricing-grid { grid-template-columns: 1fr; } @@ -1120,6 +1185,26 @@ {{end}} + {{if and .PublicReleases .LatestRelease .LatestRelease.Attachments}} + + + + Download v{{.LatestRelease.TagName}} + Get the latest release + + + {{range .LatestRelease.Attachments}} + + {{svg "octicon-download" 16}} + {{.Name}} + {{FileSize .Size}} + + {{end}} + + + + {{end}} + {{if .Config.SocialProof.Logos}} diff --git a/templates/repo/settings/pages_content.tmpl b/templates/repo/settings/pages_content.tmpl index c0053a2ff5..82e495f0ce 100644 --- a/templates/repo/settings/pages_content.tmpl +++ b/templates/repo/settings/pages_content.tmpl @@ -4,6 +4,14 @@ {{.CsrfTokenHtml}} + {{ctx.Locale.Tr "repo.settings.pages.public_releases"}} + + + + {{ctx.Locale.Tr "repo.settings.pages.public_releases_desc"}} + + + {{ctx.Locale.Tr "repo.settings.pages.stats"}} {{range $i, $stat := .Config.Stats}}
Get the latest release