2
0

feat(vault): Solo tier now includes limited versioning and CI/CD tokens
All checks were successful
Build and Release / Create Release (push) Successful in 0s
Build and Release / Unit Tests (push) Successful in 3m5s
Build and Release / Integration Tests (PostgreSQL) (push) Successful in 4m44s
Build and Release / Lint (push) Successful in 5m5s
Build and Release / Build Binaries (amd64, darwin, linux-latest) (push) Successful in 2m51s
Build and Release / Build Binaries (arm64, darwin, linux-latest) (push) Successful in 3m42s
Build and Release / Build Binaries (amd64, linux, linux-latest) (push) Successful in 4m1s
Build and Release / Build Binaries (arm64, linux, linux-latest) (push) Successful in 4m6s
Build and Release / Build Binaries (amd64, windows, windows-latest) (push) Successful in 9h4m37s

- Solo tier changes:
  - 2 versions history (limited rollback capability)
  - 1 CI/CD token (24h max TTL, read-only access)
  - SSO remains Pro+ only

- Added granular license limits:
  - MaxVersions, MaxTokens, MaxTokenTTLHours, TokensReadOnly

- Added token limit enforcement with CheckTokenLimit()

- Updated all 28 locale files with new translations

- Added templates:
  - feature_upgrade.tmpl (for SSO upgrade prompts)
  - not_installed.tmpl (when vault plugin missing)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-17 11:49:12 -05:00
parent e82aaf9a72
commit 71bf7c06e7
33 changed files with 63168 additions and 62740 deletions

View File

@@ -103,7 +103,11 @@ type LicenseLimits struct {
SecretsPerRepo int // Max secrets per repo (-1 = unlimited)
AuditRetentionDays int // Days to keep audit logs
VersioningEnabled bool // Can use secret versioning/rollback
MaxVersions int // Max versions to keep per secret (-1 = unlimited, 0 = disabled)
CICDTokensEnabled bool // Can create CI/CD tokens
MaxTokens int // Max CI/CD tokens per repo (-1 = unlimited, 0 = disabled)
MaxTokenTTLHours int // Max token TTL in hours (-1 = unlimited)
TokensReadOnly bool // If true, tokens can only read secrets (not write)
SSOEnabled bool // Can use SSO/SAML
}
@@ -126,8 +130,12 @@ func GetLimitsForTier(tier string) *LicenseLimits {
Users: 1,
SecretsPerRepo: 5,
AuditRetentionDays: 7,
VersioningEnabled: false,
CICDTokensEnabled: false,
VersioningEnabled: true, // Limited versioning
MaxVersions: 2, // Keep last 2 versions only
CICDTokensEnabled: true, // Limited CI/CD
MaxTokens: 1, // 1 token only
MaxTokenTTLHours: 24, // Max 24h TTL
TokensReadOnly: true, // Read-only access
SSOEnabled: false,
}
case TierPro:
@@ -136,7 +144,11 @@ func GetLimitsForTier(tier string) *LicenseLimits {
SecretsPerRepo: -1, // Unlimited
AuditRetentionDays: 90,
VersioningEnabled: true,
MaxVersions: -1, // Unlimited
CICDTokensEnabled: true,
MaxTokens: -1, // Unlimited
MaxTokenTTLHours: -1, // Unlimited
TokensReadOnly: false,
SSOEnabled: false,
}
case TierTeam:
@@ -145,7 +157,11 @@ func GetLimitsForTier(tier string) *LicenseLimits {
SecretsPerRepo: -1, // Unlimited
AuditRetentionDays: 365,
VersioningEnabled: true,
MaxVersions: -1, // Unlimited
CICDTokensEnabled: true,
MaxTokens: -1, // Unlimited
MaxTokenTTLHours: -1, // Unlimited
TokensReadOnly: false,
SSOEnabled: true,
}
case TierEnterprise:
@@ -154,7 +170,11 @@ func GetLimitsForTier(tier string) *LicenseLimits {
SecretsPerRepo: -1, // Unlimited
AuditRetentionDays: -1, // Custom/unlimited
VersioningEnabled: true,
MaxVersions: -1, // Unlimited
CICDTokensEnabled: true,
MaxTokens: -1, // Unlimited
MaxTokenTTLHours: -1, // Unlimited
TokensReadOnly: false,
SSOEnabled: true,
}
default:

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

@@ -4042,6 +4042,12 @@
"repo.vault.feature_unlimited_secrets_desc": "Store as many secrets as you need without limits.",
"repo.vault.feature_audit_extended": "Extended Audit Retention",
"repo.vault.feature_audit_extended_desc": "Keep audit logs for up to 1 year (Team) or custom retention (Enterprise).",
"repo.vault.token_limit_reached": "Token limit reached. Your current tier allows %d active token(s). Revoke an existing token or upgrade to Pro for unlimited tokens.",
"repo.vault.version_limit_info": "Solo tier keeps the last %d versions. Upgrade to Pro for unlimited version history.",
"repo.vault.token_limit_info": "Solo tier allows %d active token(s) with %dh max TTL (read-only). Upgrade to Pro for unlimited tokens.",
"repo.vault.tokens_read_only": "Read-only",
"repo.vault.tokens_read_only_desc": "Solo tier tokens can only read secrets. Upgrade to Pro for write access.",
"repo.vault.unlimited": "Unlimited",
"actions.runs.disk_usage": "Disk Usage",
"actions.runs.clear_cancelled": "Clear Cancelled",
"actions.runs.clear_failed": "Clear Failed",

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
{
{
"home_title": "Beranda",
"dashboard": "Dasbor",
"explore_title": "Jelajahi",
@@ -15,8 +15,8 @@
"template": "Contoh",
"language": "Bahasa",
"notifications": "Notifikasi",
"create_new": "Buat",
"user_profile_and_more": "Profil dan Pengaturan",
"create_new": "Buat…",
"user_profile_and_more": "Profil dan Pengaturan…",
"signed_in_as": "Masuk sebagai",
"toc": "Daftar Isi",
"username": "Nama Pengguna",
@@ -28,7 +28,7 @@
"passcode": "Kode Akses",
"webauthn_insert_key": "Masukkan kunci keamanan anda",
"webauthn_sign_in": "Tekan tombol pada kunci keamanan Anda. Jika kunci keamanan Anda tidak memiliki tombol, masukkan kembali.",
"webauthn_press_button": "Silakan tekan tombol pada kunci keamanan Anda",
"webauthn_press_button": "Silakan tekan tombol pada kunci keamanan Anda…",
"webauthn_use_twofa": "Gunakan kode dua faktor dari telepon Anda",
"webauthn_error": "Tidak dapat membaca kunci keamanan Anda.",
"webauthn_unsupported_browser": "Browser Anda saat ini tidak mendukung WebAuthn.",
@@ -90,7 +90,7 @@
"copy_type_unsupported": "Tipe berkas ini tidak dapat disalin",
"write": "Tulis",
"preview": "Pratinjau",
"loading": "Memuat",
"loading": "Memuat…",
"error_title": "Gangguan",
"error404": "Halaman yang akan kamu akses \u003cstrong\u003etidak dapat ditemukan\u003c/strong\u003e atau \u003cstrong\u003ekamu tidak memiliki akses \u003c/strong\u003e untuk melihatnya.",
"go_back": "Kembali",
@@ -175,7 +175,7 @@
"home.password_holder": "Kata Sandi",
"home.switch_dashboard_context": "Alihkan Dasbor Konteks",
"home.my_repos": "Repositori",
"home.show_more_repos": "Tampilkan repositori lainnya",
"home.show_more_repos": "Tampilkan repositori lainnya…",
"home.collaborative_repos": "Repositori Kolaboratif",
"home.my_orgs": "Organisasi Saya",
"home.my_mirrors": "Duplikat Saya",
@@ -293,7 +293,7 @@
"form.invalid_gpg_key": "Tidak dapat memverifikasi kunci GPG Anda: %s",
"form.auth_failed": "Otentikasi gagal: %v",
"form.target_branch_not_exist": "Target cabang tidak ada.",
"user.change_avatar": "Ganti avatar anda",
"user.change_avatar": "Ganti avatar anda…",
"user.repositories": "Repositori",
"user.activity": "Aktivitas Publik",
"user.followers": "Pengikut",
@@ -560,17 +560,17 @@
"repo.editor.fork_before_edit": "Anda harus mencabangkan repositori ini untuk membuat atau mengusulkan perubahan pada berkas ini.",
"repo.editor.delete_this_file": "Hapus Berkas",
"repo.editor.must_have_write_access": "Anda harus punya akses tulis untuk membuat atau mengusulkan perubahan pada berkas ini.",
"repo.editor.name_your_file": "Nama berkas",
"repo.editor.name_your_file": "Nama berkas…",
"repo.editor.filename_help": "Tambahkan direktori dengan mengetikkan nama direktori diikuti dengan garis miring (\u0027/\u0027). Hapus direktori dengan mengetikkan spasi balik pada awal bidang input.",
"repo.editor.or": "atau",
"repo.editor.cancel_lower": "Batalkan",
"repo.editor.commit_changes": "Perubahan komitmen",
"repo.editor.commit_message_desc": "Tambahkan deskripsi opsional yang panjang",
"repo.editor.commit_message_desc": "Tambahkan deskripsi opsional yang panjang…",
"repo.editor.commit_directly_to_this_branch": "Komitmen langsung ke \u003cstrong class=\"branch-name\"\u003e%s\u003c/strong\u003e cabang.",
"repo.editor.create_new_branch": "Membuat \u003cstrong\u003enew branch\u003c/strong\u003e untuk tarik komit ini mulai permintaan.",
"repo.editor.create_new_branch_np": "Buat \u003cstrong\u003ecabang baru\u003c/strong\u003e untuk komit ini.",
"repo.editor.propose_file_change": "Usul perubahan berkas",
"repo.editor.new_branch_name_desc": "Nama branch baru",
"repo.editor.new_branch_name_desc": "Nama branch baru…",
"repo.editor.cancel": "Membatalkan",
"repo.editor.no_changes_to_show": "Tidak ada perubahan untuk ditampilkan.",
"repo.commits.commits": "Melakukan",
@@ -810,7 +810,7 @@
"repo.settings.title": "Judul",
"repo.settings.deploy_key_content": "Konten",
"repo.settings.branches": "Cabang",
"repo.settings.choose_branch": "Pilih branch",
"repo.settings.choose_branch": "Pilih branch…",
"repo.settings.tags": "Tag",
"repo.diff.browse_source": "Telusuri Sumber",
"repo.diff.parent": "orang tua",
@@ -1268,5 +1268,11 @@
"repo.vault.rollback": "Rollback",
"repo.vault.scope_write_single": "Write only db.credentials",
"repo.vault.updated": "Updated",
"repo.vault.name": "Name"
}
"repo.vault.name": "Name",
"repo.vault.tokens_read_only_desc": "Solo tier tokens can only read secrets. Upgrade to Pro for write access.",
"repo.vault.version_limit_info": "Solo tier keeps the last %d versions. Upgrade to Pro for unlimited version history.",
"repo.vault.token_limit_info": "Solo tier allows %d active token(s) with %dh max TTL (read-only). Upgrade to Pro for unlimited tokens.",
"repo.vault.unlimited": "Unlimited",
"repo.vault.tokens_read_only": "Read-only",
"repo.vault.token_limit_reached": "Token limit reached. Your current tier allows %d active token(s). Revoke an existing token or upgrade to Pro for unlimited tokens."
}

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
{
{
"home_title": "Beginscherm",
"dashboard": "Overzicht",
"explore_title": "Verkennen",
@@ -14,8 +14,8 @@
"language": "Taal",
"notifications": "Meldingen",
"active_stopwatch": "Actieve Tijd Tracker",
"create_new": "Maken",
"user_profile_and_more": "Profiel en instellingen",
"create_new": "Maken…",
"user_profile_and_more": "Profiel en instellingen…",
"signed_in_as": "Aangemeld als",
"toc": "Inhoudsopgave",
"licenses": "Licenties",
@@ -30,7 +30,7 @@
"passcode": "PIN",
"webauthn_insert_key": "Voer uw beveiligingssleutel in",
"webauthn_sign_in": "Druk op de knop van uw beveiligingssleutel. Als uw beveiligingssleutel geen knop heeft, voeg deze dan opnieuw in.",
"webauthn_press_button": "Druk alstublieft op de knop van uw beveiligingssleutel",
"webauthn_press_button": "Druk alstublieft op de knop van uw beveiligingssleutel…",
"webauthn_use_twofa": "Gebruik een twee-factor code van uw telefoon",
"webauthn_error": "Kon uw beveiligingssleutel niet lezen.",
"webauthn_unsupported_browser": "Uw browser ondersteunt momenteel geen WebAuthn.",
@@ -77,10 +77,10 @@
"copy_url": "Kopieer URL",
"copy_branch": "Kopieer branchnaam",
"copy_success": "Gekopieerd!",
"copy_error": "Kopiëren mislukt",
"copy_error": "Kopiëren mislukt",
"write": "Schrijf",
"preview": "Voorbeeld",
"loading": "Laden",
"loading": "Laden…",
"error_title": "Fout",
"error404": "De pagina die u probeert te bereiken \u003cstrong\u003ebestaat niet\u003c/strong\u003e of \u003cstrong\u003eu bent niet gemachtigd\u003c/strong\u003e om het te bekijken.",
"never": "Nooit",
@@ -96,12 +96,12 @@
"error.occurred": "Er is een fout opgetreden",
"error.not_found": "Het doel kon niet worden gevonden.",
"error.network_error": "Netwerk fout",
"startpage.app_desc": "Geïntegreerd in je workflow",
"startpage.app_desc": "Geïntegreerd in je workflow",
"startpage.install": "Overal Implementeren",
"startpage.lightweight": "Razendsnel",
"startpage.lightweight_desc": "Minimale voetafdruk, maximale prestaties. GitCaddy draait efficiënt op alles, van Raspberry Pi tot enterprise servers.",
"startpage.lightweight_desc": "Minimale voetafdruk, maximale prestaties. GitCaddy draait efficiënt op alles, van Raspberry Pi tot enterprise servers.",
"install.install": "Installatie",
"install.title": "Initiële configuratie",
"install.title": "Initiële configuratie",
"install.docker_helper": "Als je gitea draait in Docker, Lees eerst de \u003ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\"\u003edocumentatie\u003c/a\u003e voordat je een instelling aanpast.",
"install.require_db_desc": "Gitea vereist MySQL, PostgreSQL, MSSQL, SQLite3 of TiDB (MySQL protocol).",
"install.db_title": "Database-instellingen",
@@ -162,7 +162,7 @@
"install.enable_captcha": "Registratie CAPTCHA inschakelen",
"install.enable_captcha_popup": "Vereis captcha validatie voor zelf-registratie van gebruiker.",
"install.require_sign_in_view": "Vereis inloggen om pagina\u0027s te kunnen bekijken",
"install.admin_setting_desc": "Het creëren van een administrator-account is optioneel. De eerste geregistreerde gebruiker wordt automatisch de beheerder.",
"install.admin_setting_desc": "Het creëren van een administrator-account is optioneel. De eerste geregistreerde gebruiker wordt automatisch de beheerder.",
"install.admin_title": "Instellingen beheerdersaccount",
"install.admin_name": "Admin gebruikersnaam",
"install.admin_password": "Wachtwoord",
@@ -170,7 +170,7 @@
"install.admin_email": "E-mailadres",
"install.install_btn_confirm": "Installeer Gitea",
"install.test_git_failed": "Git test niet gelukt: \u0027git\u0027 commando %v",
"install.sqlite3_not_available": "Deze Gitea-versie biedt geen ondersteuning voor SQLite3. Download de officiële build van %s (niet de versie van de \u0027gobuild\u0027).",
"install.sqlite3_not_available": "Deze Gitea-versie biedt geen ondersteuning voor SQLite3. Download de officiële build van %s (niet de versie van de \u0027gobuild\u0027).",
"install.invalid_db_setting": "De database instelling zijn niet correct: %v",
"install.invalid_repo_path": "Het pad van de hoofdmap van de repository is ongeldig: %v",
"install.invalid_app_data_path": "Ongeldig app-gegevenspad: %v",
@@ -192,7 +192,7 @@
"home.uname_holder": "Gebruikersnaam of e-mailadres",
"home.password_holder": "Wachtwoord",
"home.switch_dashboard_context": "Wissel voorpaginacontext",
"home.show_more_repos": "Toon meer repositories",
"home.show_more_repos": "Toon meer repositories…",
"home.collaborative_repos": "Gedeelde repositories",
"home.my_orgs": "Mijn organisaties",
"home.my_mirrors": "Mijn spiegels",
@@ -204,14 +204,14 @@
"home.show_both_archived_unarchived": "Toont zowel gearchiveerd als niet-gearchiveerd",
"home.show_only_archived": "Toon alleen gearchiveerd",
"home.show_only_unarchived": "Toon alleen niet gearchiveerd",
"home.show_private": "Privé",
"home.show_both_private_public": "Toon zowel openbaar als privé",
"home.show_only_private": "Toon alleen privé",
"home.show_private": "Privé",
"home.show_both_private_public": "Toon zowel openbaar als privé",
"home.show_only_private": "Toon alleen privé",
"home.show_only_public": "Toon alleen opbenbaar",
"home.issues.in_your_repos": "In uw repositories",
"explore.users": "Gebruikers",
"explore.organizations": "Organisaties",
"explore.code_last_indexed_at": "Laatst geïndexeerd %s",
"explore.code_last_indexed_at": "Laatst geïndexeerd %s",
"auth.create_new_account": "Account registreren",
"auth.disable_register_prompt": "Registratie is uitgeschakeld. Neem alstublieft contact op met de pagina beheerder.",
"auth.disable_register_mail": "E-mailbevestiging voor registratie is uitgeschakeld.",
@@ -234,7 +234,7 @@
"auth.reset_password_helper": "Account herstellen",
"auth.password_too_short": "De lengte van uw wachtwoord moet tenminste %d karakters zijn.",
"auth.non_local_account": "Non-lokale gebruikers mogen hun wachtwoord niet wijzigen via de webinterface.",
"auth.verify": "Verifiëren",
"auth.verify": "Verifiëren",
"auth.scratch_code": "Eenmalige code",
"auth.use_scratch_code": "Gebruik een eenmalige code",
"auth.twofa_scratch_used": "Je hebt je eenmalige code gebruikt. Je wordt omgeleid naar de tweeledige-authenticatie instellingen pagina zodat je de inschrijving van het apparaat kan verwijderen of een nieuwe eenmalige code kan genereren.",
@@ -263,7 +263,7 @@
"auth.sspi_auth_failed": "SSPI-authenticatie mislukt",
"auth.password_pwned_err": "Kan het verzoek om HaveIBeenPwned niet voltooien",
"mail.view_it_on": "Bekijk het op %s",
"mail.link_not_working_do_paste": "Werkt dit niet? Probeer het te kopiëren en te plakken naar uw browser.",
"mail.link_not_working_do_paste": "Werkt dit niet? Probeer het te kopiëren en te plakken naar uw browser.",
"mail.hi_user_x": "Hoi \u003cb\u003e%s\u003c/b\u003e,",
"mail.activate_account": "Activeer uw account",
"mail.activate_account.title": "%s, activeer alstublieft uw account",
@@ -342,36 +342,36 @@
"form.username_been_taken": "Deze naam is al in gebruik.",
"form.username_change_not_local_user": "Niet-lokale gebruikers mogen hun gebruikersnaam niet wijzigen.",
"form.repo_name_been_taken": "De repository-naam wordt al gebruikt.",
"form.repository_force_private": "Forceer privé is ingeschakeld: privé repositories kunnen niet openbaar worden gemaakt.",
"form.repository_force_private": "Forceer privé is ingeschakeld: privé repositories kunnen niet openbaar worden gemaakt.",
"form.repository_files_already_exist": "Er bestaan al bestanden voor deze repository. Neem contact op met de systeembeheerder.",
"form.repository_files_already_exist.delete": "Er bestaan al bestanden voor deze repository. U moet deze verwijderen.",
"form.repository_files_already_exist.adopt_or_delete": "Er bestaan al bestanden voor deze repository. Adopteer of verwijder deze.",
"form.visit_rate_limit": "Bezoeklimiet op afstand gerichter.",
"form.org_name_been_taken": "Naam van de organisatie wordt al gebruikt.",
"form.team_name_been_taken": "De teamnaam is al in gebruik.",
"form.team_no_units_error": "Toegang verlenen tot ten minste één repository sectie.",
"form.team_no_units_error": "Toegang verlenen tot ten minste één repository sectie.",
"form.email_been_used": "Het emailadres is al in gebruik.",
"form.email_invalid": "Het e-mailadres is ongeldig.",
"form.username_password_incorrect": "Gebruikersnaam of wachtwoord is onjuist.",
"form.password_complexity": "Wachtwoord voldoet niet aan complexiteit eisen:",
"form.password_lowercase_one": "Minstens één kleine letter",
"form.password_uppercase_one": "Minstens één hoofdletter",
"form.password_digit_one": "Minstens één cijfer",
"form.password_special_one": "Minstens één speciaal teken (interpunctie, haakjes, aanhalingstekens, etc.)",
"form.password_lowercase_one": "Minstens één kleine letter",
"form.password_uppercase_one": "Minstens één hoofdletter",
"form.password_digit_one": "Minstens één cijfer",
"form.password_special_one": "Minstens één speciaal teken (interpunctie, haakjes, aanhalingstekens, etc.)",
"form.enterred_invalid_repo_name": "De repository-naam die u hebt ingevoerd is niet correct.",
"form.enterred_invalid_org_name": "De organizatienaam die u hebt ingevoerd is niet correct.",
"form.enterred_invalid_owner_name": "De nieuwe eigenaarnaam is niet geldig.",
"form.enterred_invalid_password": "Het ingevoerde wachtwoord is onjuist.",
"form.user_not_exist": "De gebruiker bestaat niet.",
"form.team_not_exist": "Dit team bestaat niet.",
"form.last_org_owner": "Je kunt de laatste eigenaar van een organisatie niet verwijderen. Er moet er minimaal één eigenaar in een organisatie zitten.",
"form.last_org_owner": "Je kunt de laatste eigenaar van een organisatie niet verwijderen. Er moet er minimaal één eigenaar in een organisatie zitten.",
"form.cannot_add_org_to_team": "Een organisatie kan niet worden toegevoegd als een teamlid.",
"form.invalid_ssh_key": "Kan de SSH-sleutel niet verifiëren: %s",
"form.invalid_gpg_key": "Kan de GPG-sleutel niet verifiëren: %s",
"form.invalid_ssh_key": "Kan de SSH-sleutel niet verifiëren: %s",
"form.invalid_gpg_key": "Kan de GPG-sleutel niet verifiëren: %s",
"form.invalid_ssh_principal": "Ongeldige verantwoordelijke: %s",
"form.auth_failed": "Verificatie mislukt: %v",
"form.target_branch_not_exist": "Doel branch bestaat niet",
"user.change_avatar": "Wijzig je profielfoto",
"user.change_avatar": "Wijzig je profielfoto…",
"user.repositories": "repositories",
"user.activity": "Openbare activiteit",
"user.followers": "Volgers",
@@ -428,7 +428,7 @@
"settings.enable_custom_avatar": "Aangepaste avatar inschakelen",
"settings.choose_new_avatar": "Kies een nieuwe avatar",
"settings.delete_current_avatar": "Verwijder huidige avatar",
"settings.uploaded_avatar_not_a_image": "Het geüploade bestand is geen afbeelding.",
"settings.uploaded_avatar_not_a_image": "Het geüploade bestand is geen afbeelding.",
"settings.update_avatar_success": "Je avatar is bijgewerkt.",
"settings.update_user_avatar_success": "De avatar van de gebruiker is bijgewerkt.",
"settings.change_password": "Wachtwoord bijwerken",
@@ -471,7 +471,7 @@
"settings.add_key": "Sleutel toevoegen",
"settings.ssh_desc": "Deze publieke SSH sleutels worden geassocieerd met uw account. De bijbehorende private sleutels geven volledige toegang toe tot je repositories.",
"settings.principal_desc": "Deze SSH-certificaatverantwoordelijken zijn gekoppeld aan uw account en geven volledige toegang tot uw repositories.",
"settings.gpg_desc": "Deze publieke GPG-sleutels zijn verbonden met je account. Houd je privé-sleutels veilig, omdat hiermee commits kunnen worden ondertekend.",
"settings.gpg_desc": "Deze publieke GPG-sleutels zijn verbonden met je account. Houd je privé-sleutels veilig, omdat hiermee commits kunnen worden ondertekend.",
"settings.ssh_helper": "\u003cstrong\u003eWeet u niet hoe?\u003c/strong\u003e Lees dan onze handleiding voor het \u003ca href=\"%s\"\u003e genereren van SSH sleutels\u003c/a\u003e of voor \u003ca href=\"%s\"\u003e algemene SSH\u003c/a\u003e problemen.",
"settings.gpg_helper": "\u003cstrong\u003eHulp nodig?\u003c/strong\u003e Neem een kijkje op de GitHub handleiding \u003ca href=\"%s\"\u003eover GPG\u003c/a\u003e.",
"settings.add_new_key": "SSH sleutel toevoegen",
@@ -487,15 +487,15 @@
"settings.gpg_key_matched_identities": "Overeenkomende identiteiten:",
"settings.gpg_key_matched_identities_long": "De ingesloten identiteiten in deze sleutel komen overeen met de geactiveerde e-mailadressen voor deze gebruiker. Commits die overeenkomen met deze e-mailadressen kunnen worden geverifieerd met deze sleutel.",
"settings.gpg_key_verified": "Geverifieerde sleutel",
"settings.gpg_key_verified_long": "Sleutel is geverifieerd met een token en kan worden gebruikt om commits te verifiëren die overeenkomen met alle geactiveerde e-mailadressen voor deze gebruiker naast de bijbehorende identiteiten voor deze sleutel.",
"settings.gpg_key_verify": "Verifiëren",
"settings.gpg_key_verified_long": "Sleutel is geverifieerd met een token en kan worden gebruikt om commits te verifiëren die overeenkomen met alle geactiveerde e-mailadressen voor deze gebruiker naast de bijbehorende identiteiten voor deze sleutel.",
"settings.gpg_key_verify": "Verifiëren",
"settings.gpg_token_required": "U moet een handtekening opgeven voor de onderstaande token",
"settings.gpg_token_help": "U kunt een handtekening genereren met:",
"settings.gpg_token_signature": "Gepantserde GPG-handtekening",
"settings.key_signature_gpg_placeholder": "Begint met \u0027-----BEGIN PGP SIGNATURE-----\u0027",
"settings.ssh_key_verified": "Geverifieerde sleutel",
"settings.ssh_key_verified_long": "Sleutel is geverifieerd met een token en kan worden gebruikt om commits te verifiëren die overeenkomen met alle geactiveerde e-mailadressen voor deze gebruiker.",
"settings.ssh_key_verify": "Verifiëren",
"settings.ssh_key_verified_long": "Sleutel is geverifieerd met een token en kan worden gebruikt om commits te verifiëren die overeenkomen met alle geactiveerde e-mailadressen voor deze gebruiker.",
"settings.ssh_key_verify": "Verifiëren",
"settings.ssh_token_required": "U moet een handtekening opgeven voor het onderstaande token",
"settings.ssh_token_help": "U kunt een handtekening genereren door het volgende:",
"settings.ssh_token_signature": "Gepantserde SSH handtekening",
@@ -595,7 +595,7 @@
"settings.visibility": "Gebruiker zichtbaarheid",
"settings.visibility.public": "Openbaar",
"settings.visibility.limited": "Beperkt",
"settings.visibility.private": "Privé",
"settings.visibility.private": "Privé",
"repo.owner": "Eigenaar",
"repo.owner_helper": "Sommige organisaties kunnen niet worden weergegeven in de dropdown vanwege een limiet op het maximale aantal repositories.",
"repo.repo_name": "Naam van repository",
@@ -606,7 +606,7 @@
"repo.template_description": "Sjabloon repositories laten gebruikers nieuwe repositories genereren met dezelfde directory structuur, bestanden en optionele instellingen.",
"repo.visibility": "Zichtbaarheid",
"repo.visibility_description": "Alleen de eigenaar of de organisatielid kan het zien als ze rechten hebben.",
"repo.visibility_helper_forced": "De sitebeheerder verplicht alle repositories om privé te zijn.",
"repo.visibility_helper_forced": "De sitebeheerder verplicht alle repositories om privé te zijn.",
"repo.visibility_fork_helper": "(Verandering van deze waarde zal van invloed zijn op alle forks)",
"repo.clone_helper": "Heb je hulp nodig om te clonen? Bekijk dan de \u003ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\"\u003ehandleiding\u003c/a\u003e.",
"repo.fork_repo": "Repository forken",
@@ -669,18 +669,18 @@
"repo.blame_prior": "Bekijk de schuld voorafgaand aan deze verandering",
"repo.transfer.accept": "Accepteer overdracht",
"repo.transfer.reject": "Overdracht afwijzen",
"repo.desc.private": "Privé",
"repo.desc.private": "Privé",
"repo.desc.public": "Openbaar",
"repo.desc.template": "Sjabloon",
"repo.desc.internal": "Interne",
"repo.desc.archived": "Gearchiveerd",
"repo.template.items": "Sjabloon items",
"repo.template.git_content": "Git inhoud (standaard Branch)",
"repo.template.git_hooks_tooltip": "Je bent momenteel niet in staat om Git Hooks één keer te wijzigen of te verwijderen. Selecteer deze optie alleen als je de sjabloonrepository vertrouwt.",
"repo.template.git_hooks_tooltip": "Je bent momenteel niet in staat om Git Hooks één keer te wijzigen of te verwijderen. Selecteer deze optie alleen als je de sjabloonrepository vertrouwt.",
"repo.template.topics": "Onderwerpen",
"repo.template.avatar": "Profielfoto",
"repo.template.issue_labels": "Issuelabels",
"repo.template.one_item": "Moet ten minste één sjabloon selecteren",
"repo.template.one_item": "Moet ten minste één sjabloon selecteren",
"repo.template.invalid": "Moet een sjabloon repository selecteren",
"repo.archive.issue.nocomment": "Deze repo is gearchiveerd. U kunt niet reageren op problemen.",
"repo.archive.pull.nocomment": "Deze repo is gearchiveerd. U kunt niet reageren op pull requests.",
@@ -769,7 +769,7 @@
"repo.invisible_runes_line": "Deze lijn heeft onzichtbare unicode karakters",
"repo.ambiguous_runes_line": "Deze lijn heeft dubbelzinnige unicode karakters",
"repo.unescape_control_characters": "Onescape",
"repo.file_copy_permalink": "Permalink kopiëren",
"repo.file_copy_permalink": "Permalink kopiëren",
"repo.view_git_blame": "Bekijk Git Blame",
"repo.video_not_supported_in_browser": "Je browser ondersteunt de HTML5 \u0027video\u0027-tag niet.",
"repo.audio_not_supported_in_browser": "Je browser ondersteunt de HTML5 \u0027audio\u0027-tag niet.",
@@ -795,7 +795,7 @@
"repo.editor.fork_before_edit": "Je moet deze repository forken om veranderingen te maken of voor te stellen.",
"repo.editor.delete_this_file": "Verwijder bestand",
"repo.editor.must_have_write_access": "U moet schrijftoegang hebben om aanpassingen te maken of voor te stellen in dit bestand.",
"repo.editor.name_your_file": "Bestandsnaam",
"repo.editor.name_your_file": "Bestandsnaam…",
"repo.editor.filename_help": "Voeg een map toe door zijn naam te typen, gevolgd door een slash (\u0027/\u0027). Verwijder een map door op backspace te drukken aan het begin van het tekstveld.",
"repo.editor.or": "of",
"repo.editor.cancel_lower": "Annuleer",
@@ -804,13 +804,13 @@
"repo.editor.patch": "Patch toepassen",
"repo.editor.patching": "Patchen:",
"repo.editor.new_patch": "Nieuwe Patch",
"repo.editor.commit_message_desc": "Voeg een optionele uitgebreide omschrijving toe",
"repo.editor.commit_message_desc": "Voeg een optionele uitgebreide omschrijving toe…",
"repo.editor.signoff_desc": "Voeg een Signed-off-by toe aan het einde van het commit logbericht.",
"repo.editor.commit_directly_to_this_branch": "Commit direct naar de branch \u0027\u003cstrong class=\"branch-name\"\u003e%s\u003c/strong\u003e\u0027.",
"repo.editor.create_new_branch": "Maak een \u003cstrong\u003enieuwe branch\u003c/strong\u003e voor deze commit en start van een pull-aanvraag.",
"repo.editor.create_new_branch_np": "Maak een \u003cstrong\u003enieuwe branch\u003c/strong\u003e voor deze commit.",
"repo.editor.propose_file_change": "Stel bestandswijziging voor",
"repo.editor.new_branch_name_desc": "Nieuwe branch naam",
"repo.editor.new_branch_name_desc": "Nieuwe branch naam…",
"repo.editor.cancel": "Annuleer",
"repo.editor.filename_cannot_be_empty": "Bestandsnaam mag niet leeg zijn.",
"repo.editor.file_changed_while_editing": "De bestandsinhoud is veranderd sinds je bent begonnen met bewerken. \u003ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\"\u003eKlik hier\u003c/a\u003e om ze te zien, of \u003cstrong\u003ecommit de veranderingen opnieuw\u003c/strong\u003e om ze te overschrijven.",
@@ -820,7 +820,7 @@
"repo.editor.push_rejected_no_message": "De wijziging is afgewezen door de server zonder bericht. Controleer de Git Hooks alsjeblieft.",
"repo.editor.push_rejected": "De wijziging is afgewezen door de server. Controleer Controleer de Git Hooks alsjeblieft.",
"repo.editor.push_rejected_summary": "Volledig afwijzingsbericht:",
"repo.editor.add_subdir": "Een map toevoegen",
"repo.editor.add_subdir": "Een map toevoegen…",
"repo.editor.no_commit_to_branch": "Kan niet rechtstreeks naar branch committen omdat:",
"repo.editor.user_no_push_to_branch": "Gebruiker kan niet pushen naar branch",
"repo.editor.require_signed_commit": "Branch vereist een ondertekende commit",
@@ -852,7 +852,7 @@
"repo.projects.create": "Project aanmaken",
"repo.projects.title": "Titel",
"repo.projects.new": "Nieuw project",
"repo.projects.new_subheader": "Coördineer, track en update uw werk op één plek, dus projecten blijven transparant en op schema.",
"repo.projects.new_subheader": "Coördineer, track en update uw werk op één plek, dus projecten blijven transparant en op schema.",
"repo.projects.deletion": "Project verwijderen",
"repo.projects.deletion_desc": "Als een project wordt verwijdert, wordt deze van alle gerelateerde kwesties verwijderd. Doorgaan?",
"repo.projects.deletion_success": "Het project is verwijderd.",
@@ -965,7 +965,7 @@
"repo.issues.num_comments": "%d opmerkingen",
"repo.issues.commented_at": "reageerde \u003ca href=\"#%s\"\u003e%s\u003c/a\u003e",
"repo.issues.delete_comment_confirm": "Weet u zeker dat u deze reactie wilt verwijderen?",
"repo.issues.context.copy_link": "Link kopiëren",
"repo.issues.context.copy_link": "Link kopiëren",
"repo.issues.context.quote_reply": "Citeer antwoord",
"repo.issues.context.reference_issue": "Verwijs in nieuw issue",
"repo.issues.context.edit": "Bewerken",
@@ -1065,7 +1065,7 @@
"repo.issues.dependency.title": "Afhankelijkheden",
"repo.issues.dependency.issue_no_dependencies": "Geen afhankelijkheden ingesteld.",
"repo.issues.dependency.pr_no_dependencies": "Geen afhankelijkheden ingesteld.",
"repo.issues.dependency.add": "Voeg afhankelijkheid toe",
"repo.issues.dependency.add": "Voeg afhankelijkheid toe…",
"repo.issues.dependency.cancel": "Annuleer",
"repo.issues.dependency.remove": "Verwijder",
"repo.issues.dependency.remove_info": "Verwijder afhankelijkheid",
@@ -1109,7 +1109,7 @@
"repo.issues.reference_issue.body": "Inhoud",
"repo.issues.content_history.deleted": "verwijderd",
"repo.issues.content_history.edited": "bewerkt",
"repo.issues.content_history.created": "gecreëerd",
"repo.issues.content_history.created": "gecreëerd",
"repo.issues.content_history.delete_from_history": "Uit geschiedenis verwijderen",
"repo.issues.content_history.delete_from_history_confirm": "Uit geschiedenis verwijderen?",
"repo.issues.content_history.options": "Opties",
@@ -1245,7 +1245,7 @@
"repo.wiki.back_to_wiki": "Terug naar wiki-pagina",
"repo.wiki.delete_page_button": "Verwijder pagina",
"repo.wiki.page_already_exists": "Er bestaat al een wiki-pagina met deze naam.",
"repo.wiki.pages": "Paginas",
"repo.wiki.pages": "Pagina’s",
"repo.wiki.last_updated": "Laatst bijgewerkt: %s",
"repo.wiki.page_name_desc": "Voer een naam in voor deze Wiki pagina. Sommige speciale namen zijn: \u0027Home\u0027, \u0027_Sidebar\u0027 en \u0027_Footer\u0027.",
"repo.activity": "Activiteit",
@@ -1405,9 +1405,9 @@
"repo.settings.discord_icon_url": "Icoon URL",
"repo.settings.event_desc": "Trigger op:",
"repo.settings.event_send_everything": "Alle gebeurtenissen",
"repo.settings.event_choose": "Aangepaste gebeurtenissen",
"repo.settings.event_choose": "Aangepaste gebeurtenissen…",
"repo.settings.event_header_repository": "Repository gebeurtenissen",
"repo.settings.event_create": "Creëer",
"repo.settings.event_create": "Creëer",
"repo.settings.event_create_desc": "Branch, of tag aangemaakt.",
"repo.settings.event_delete": "Verwijder",
"repo.settings.event_delete_desc": "Branch of tag verwijderd.",
@@ -1481,13 +1481,13 @@
"repo.settings.require_signed_commits_desc": "Weiger pushes naar deze branch als deze niet ondertekend of niet verifieerbaar is.",
"repo.settings.protected_branch_deletion_desc": "Branch bescherming uitschakelen zorgt ervoor dat gebruikers met schrijfrechten naar de branch kunnen pushen. Doorgaan?",
"repo.settings.block_rejected_reviews": "Samenvoegen van afgewezen beoordelingen blokkeren",
"repo.settings.block_rejected_reviews_desc": "Samenvoegen zal niet mogelijk zijn wanneer er wijzigingen worden aangevraagd door officiële beoordelaars, zelfs niet als er genoeg goedkeuringen zijn.",
"repo.settings.block_on_official_review_requests": "Blokkeer de samenvoeging van officiële beoordelingsverzoeken",
"repo.settings.block_on_official_review_requests_desc": "Samenvoegen is niet mogelijk wanneer het officiële herzieningsverzoeken heeft, ook al zijn er genoeg goedkeuringen.",
"repo.settings.block_rejected_reviews_desc": "Samenvoegen zal niet mogelijk zijn wanneer er wijzigingen worden aangevraagd door officiële beoordelaars, zelfs niet als er genoeg goedkeuringen zijn.",
"repo.settings.block_on_official_review_requests": "Blokkeer de samenvoeging van officiële beoordelingsverzoeken",
"repo.settings.block_on_official_review_requests_desc": "Samenvoegen is niet mogelijk wanneer het officiële herzieningsverzoeken heeft, ook al zijn er genoeg goedkeuringen.",
"repo.settings.block_outdated_branch": "Samenvoegen blokkeren als pull request verouderd is",
"repo.settings.block_outdated_branch_desc": "Samenvoegen is niet mogelijk als de hoofd branch achter loop op de basis branch.",
"repo.settings.default_branch_desc": "Selecteer een standaard repository branch voor pull requests en code commits:",
"repo.settings.choose_branch": "Kies een branch",
"repo.settings.choose_branch": "Kies een branch…",
"repo.settings.no_protected_branch": "Er zijn geen beschermde branches.",
"repo.settings.edit_protected_branch": "Bewerken",
"repo.settings.protected_branch_required_approvals_min": "Vereiste goedkeuringen kunnen niet negatief zijn.",
@@ -1566,7 +1566,7 @@
"repo.diff.load": "Laad Diff",
"repo.diff.generated": "gegenereerd",
"repo.diff.comment.placeholder": "Opmerking toevoegen",
"repo.diff.comment.add_single_comment": "Één reactie toevoegen",
"repo.diff.comment.add_single_comment": "Één reactie toevoegen",
"repo.diff.comment.add_review_comment": "Voeg commentaar toe",
"repo.diff.comment.start_review": "Review starten",
"repo.diff.comment.reply": "Reageer",
@@ -1593,7 +1593,7 @@
"repo.release.source_code": "Broncode",
"repo.release.tag_name": "Tagnaam",
"repo.release.target": "Doel",
"repo.release.tag_helper": "Kies een bestaande tag, of creëer een nieuwe tag bij publiceren.",
"repo.release.tag_helper": "Kies een bestaande tag, of creëer een nieuwe tag bij publiceren.",
"repo.release.prerelease_desc": "Markeren als voorlopige versie",
"repo.release.prerelease_helper": "Markeer deze release als ongeschikt voor productiedoeleinden.",
"repo.release.cancel": "Annuleren",
@@ -1644,8 +1644,8 @@
"org.settings.visibility": "Zichtbaarheid",
"org.settings.visibility.public": "Publiek",
"org.settings.visibility.limited_shortname": "Beperkt",
"org.settings.visibility.private": "Privé (alleen zichtbaar voor organisatieleden)",
"org.settings.visibility.private_shortname": "Privé",
"org.settings.visibility.private": "Privé (alleen zichtbaar voor organisatieleden)",
"org.settings.visibility.private_shortname": "Privé",
"org.settings.update_settings": "Instellingen bijwerken",
"org.settings.update_avatar_success": "De avatar van de organisatie is aangepast.",
"org.settings.delete": "Verwijder organisatie",
@@ -1799,7 +1799,7 @@
"admin.users.allow_create_organization": "Mag organisaties aanmaken",
"admin.users.update_profile": "Update gebruikers account",
"admin.users.delete_account": "Verwijder gebruikers account",
"admin.users.still_own_repo": "Deze gebruiker is nog steeds eigenaar van één of meerdere repositories. Verwijder of draag eerst deze repositories over.",
"admin.users.still_own_repo": "Deze gebruiker is nog steeds eigenaar van één of meerdere repositories. Verwijder of draag eerst deze repositories over.",
"admin.users.still_has_org": "Deze gebruiker is lid van een organisatie. Verwijder de gebruiker eerst uit alle organisaties.",
"admin.users.deletion_success": "De gebruiker is verwijderd.",
"admin.users.list_status_filter.is_active": "Actief",
@@ -2169,5 +2169,11 @@
"repo.vault.rollback": "Rollback",
"repo.vault.scope_write_single": "Write only db.credentials",
"repo.vault.updated": "Updated",
"repo.vault.name": "Name"
}
"repo.vault.name": "Name",
"repo.vault.tokens_read_only_desc": "Solo tier tokens can only read secrets. Upgrade to Pro for write access.",
"repo.vault.version_limit_info": "Solo tier keeps the last %d versions. Upgrade to Pro for unlimited version history.",
"repo.vault.token_limit_info": "Solo tier allows %d active token(s) with %dh max TTL (read-only). Upgrade to Pro for unlimited tokens.",
"repo.vault.unlimited": "Unlimited",
"repo.vault.tokens_read_only": "Read-only",
"repo.vault.token_limit_reached": "Token limit reached. Your current tier allows %d active token(s). Revoke an existing token or upgrade to Pro for unlimited tokens."
}

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

@@ -60,6 +60,14 @@ func setVaultLicenseData(ctx *context.Context) {
ctx.Data["CanUseSSO"] = vault_service.CanUseSSO()
ctx.Data["MaxSecretsPerRepo"] = vault_service.GetMaxSecretsPerRepo()
ctx.Data["AuditRetentionDays"] = vault_service.GetAuditRetentionDays()
// New granular limits for Solo tier
ctx.Data["MaxVersions"] = vault_service.GetMaxVersions()
ctx.Data["HasUnlimitedVersions"] = vault_service.HasUnlimitedVersions()
ctx.Data["MaxTokens"] = vault_service.GetMaxTokens()
ctx.Data["HasUnlimitedTokens"] = vault_service.HasUnlimitedTokens()
ctx.Data["MaxTokenTTLHours"] = vault_service.GetMaxTokenTTLHours()
ctx.Data["TokensReadOnly"] = vault_service.AreTokensReadOnly()
}
// List displays the vault secrets list
@@ -258,16 +266,6 @@ func Versions(ctx *context.Context) {
return
}
// Check if versioning is available in current tier
if !vault_service.CanUseVersioning() {
ctx.Data["PageIsVault"] = true
ctx.Data["FeatureNotAvailable"] = true
ctx.Data["FeatureName"] = "versioning"
setVaultLicenseData(ctx)
ctx.HTML(http.StatusOK, "repo/vault/feature_upgrade")
return
}
name := ctx.PathParam("name")
secret, err := vault_service.GetSecret(ctx, ctx.Repo.Repository.ID, name)
if err != nil {
@@ -299,13 +297,6 @@ func Rollback(ctx *context.Context) {
return
}
// Check if versioning is available in current tier
if !vault_service.CanUseVersioning() {
ctx.Flash.Error(ctx.Tr("repo.vault.feature_requires_upgrade", "versioning"))
ctx.Redirect(ctx.Repo.RepoLink + "/vault")
return
}
name := ctx.PathParam("name")
form := web.GetForm(ctx).(*RollbackForm)
@@ -358,25 +349,29 @@ func Tokens(ctx *context.Context) {
return
}
// Check if CI/CD tokens are available in current tier
if !vault_service.CanUseCICDTokens() {
ctx.Data["PageIsVault"] = true
ctx.Data["FeatureNotAvailable"] = true
ctx.Data["FeatureName"] = "cicd_tokens"
setVaultLicenseData(ctx)
ctx.HTML(http.StatusOK, "repo/vault/feature_upgrade")
return
}
tokens, err := vault_service.ListTokens(ctx, ctx.Repo.Repository.ID)
if err != nil {
ctx.ServerError("ListTokens", err)
return
}
// Count active tokens for limit display
activeCount := 0
for _, t := range tokens {
if !t.IsRevoked && !t.IsExpired {
activeCount++
}
}
ctx.Data["PageIsVault"] = true
ctx.Data["Tokens"] = tokens
ctx.Data["ActiveTokenCount"] = activeCount
setVaultLicenseData(ctx)
// Check if at token limit
maxTokens := vault_service.GetMaxTokens()
ctx.Data["AtTokenLimit"] = maxTokens != -1 && activeCount >= maxTokens
ctx.HTML(http.StatusOK, "repo/vault/tokens")
}
@@ -387,10 +382,14 @@ func CreateToken(ctx *context.Context) {
return
}
// Check if CI/CD tokens are available in current tier
if !vault_service.CanUseCICDTokens() {
ctx.Flash.Error(ctx.Tr("repo.vault.feature_requires_upgrade", "CI/CD tokens"))
ctx.Redirect(ctx.Repo.RepoLink + "/vault")
// Check token limit
if err := vault_service.CheckTokenLimit(ctx, ctx.Repo.Repository.ID); err != nil {
if err == vault_service.ErrTokenLimitReached {
ctx.Flash.Error(ctx.Tr("repo.vault.token_limit_reached", vault_service.GetMaxTokens()))
ctx.Redirect(ctx.Repo.RepoLink + "/vault/tokens")
return
}
ctx.ServerError("CheckTokenLimit", err)
return
}

View File

@@ -19,6 +19,8 @@ var (
ErrFeatureNotInTier = errors.New("feature not available in current license tier")
ErrTokenNotFound = errors.New("token not found")
ErrTokenExpired = errors.New("token expired")
ErrTokenLimitReached = errors.New("token limit reached for current license tier")
ErrTokenTTLExceeded = errors.New("token TTL exceeds maximum for current license tier")
ErrInvalidToken = errors.New("invalid token")
ErrInvalidScope = errors.New("invalid token scope")
ErrAccessDenied = errors.New("access denied")
@@ -208,10 +210,89 @@ func GetAuditRetentionDays() int {
return limits.AuditRetentionDays
}
// GetMaxVersions returns the max versions to keep per secret (-1 = unlimited)
func GetMaxVersions() int {
limits := GetLimits()
if limits == nil {
return 2 // Solo default
}
return limits.MaxVersions
}
// GetMaxTokens returns the max tokens allowed per repo (-1 = unlimited)
func GetMaxTokens() int {
limits := GetLimits()
if limits == nil {
return 1 // Solo default
}
return limits.MaxTokens
}
// GetMaxTokenTTLHours returns the max token TTL in hours (-1 = unlimited)
func GetMaxTokenTTLHours() int {
limits := GetLimits()
if limits == nil {
return 24 // Solo default
}
return limits.MaxTokenTTLHours
}
// AreTokensReadOnly returns true if tokens are read-only (Solo tier restriction)
func AreTokensReadOnly() bool {
limits := GetLimits()
if limits == nil {
return true // Solo default
}
return limits.TokensReadOnly
}
// HasUnlimitedVersions returns true if the tier has unlimited version history
func HasUnlimitedVersions() bool {
limits := GetLimits()
return limits != nil && limits.MaxVersions == -1
}
// HasUnlimitedTokens returns true if the tier has unlimited tokens
func HasUnlimitedTokens() bool {
limits := GetLimits()
return limits != nil && limits.MaxTokens == -1
}
// CheckTokenLimit checks if adding a new token would exceed the tier limit
func CheckTokenLimit(ctx context.Context, repoID int64) error {
maxTokens := GetMaxTokens()
if maxTokens == -1 {
return nil // Unlimited
}
vp := GetPlugin()
if vp == nil {
return ErrVaultNotAvailable
}
tokens, err := vp.ListTokens(ctx, repoID)
if err != nil {
return err
}
// Count active (non-revoked, non-expired) tokens
activeCount := 0
for _, t := range tokens {
if !t.IsRevoked && !t.IsExpired {
activeCount++
}
}
if activeCount >= maxTokens {
return ErrTokenLimitReached
}
return nil
}
// CheckSecretLimit checks if adding a new secret would exceed the tier limit
func CheckSecretLimit(ctx context.Context, repoID int64) error {
max := GetMaxSecretsPerRepo()
if max == -1 {
maxSecrets := GetMaxSecretsPerRepo()
if maxSecrets == -1 {
return nil // Unlimited
}
@@ -225,7 +306,7 @@ func CheckSecretLimit(ctx context.Context, repoID int64) error {
return err
}
if len(secrets) >= max {
if len(secrets) >= maxSecrets {
return ErrSecretLimitReached
}
return nil

View File

@@ -0,0 +1,127 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content repository vault">
{{template "repo/header" .}}
<div class="ui container">
{{template "repo/vault/navbar" .}}
<div class="twelve wide column content">
<div class="ui center aligned segment">
<div class="ui icon header">
{{if eq .FeatureName "sso"}}
{{svg "octicon-shield-lock" 48}}
{{else}}
{{svg "octicon-rocket" 48}}
{{end}}
<div class="content">
{{if eq .FeatureName "sso"}}
{{ctx.Locale.Tr "repo.vault.feature_sso"}}
{{else}}
{{ctx.Locale.Tr "repo.vault.upgrade_title"}}
{{end}}
</div>
</div>
<p class="ui large text">
{{if eq .FeatureName "sso"}}
{{ctx.Locale.Tr "repo.vault.feature_sso_desc"}}
{{else}}
{{ctx.Locale.Tr "repo.vault.upgrade_message"}}
{{end}}
</p>
<div class="ui divider"></div>
<div class="ui info message">
<div class="header">
{{ctx.Locale.Tr "repo.vault.current_tier"}}: {{ctx.Locale.Tr (printf "repo.vault.tier_%s" .VaultTier)}}
</div>
<p>{{ctx.Locale.Tr "repo.vault.feature_requires_upgrade" .FeatureName}}</p>
</div>
<div class="ui two column stackable grid" style="margin-top: 2em;">
{{if eq .FeatureName "sso"}}
{{/* SSO requires Team tier */}}
<div class="column">
<div class="ui blue segment">
<div class="ui blue ribbon label">{{ctx.Locale.Tr "repo.vault.upgrade_to_team"}}</div>
<h3>Team</h3>
<div class="ui large header">$29/mo</div>
<div class="ui list">
<div class="item">{{svg "octicon-check"}} 25 users</div>
<div class="item">{{svg "octicon-check"}} {{ctx.Locale.Tr "repo.vault.feature_unlimited_secrets"}}</div>
<div class="item">{{svg "octicon-check"}} {{ctx.Locale.Tr "repo.vault.unlimited"}} {{ctx.Locale.Tr "repo.vault.feature_versioning"}}</div>
<div class="item">{{svg "octicon-check"}} {{ctx.Locale.Tr "repo.vault.unlimited"}} {{ctx.Locale.Tr "repo.vault.feature_cicd_tokens"}}</div>
<div class="item">{{svg "octicon-check"}} {{ctx.Locale.Tr "repo.vault.feature_sso"}}</div>
<div class="item">{{svg "octicon-check"}} 1-year audit log</div>
</div>
<a class="ui primary button" href="https://gitcaddy.com/vault/team" target="_blank" style="margin-top: 1em;">
{{svg "octicon-link-external"}} {{ctx.Locale.Tr "repo.vault.upgrade_to_team"}}
</a>
</div>
</div>
<div class="column">
<div class="ui segment">
<h3>Enterprise</h3>
<div class="ui large header">Custom</div>
<div class="ui list">
<div class="item">{{svg "octicon-check"}} Unlimited users</div>
<div class="item">{{svg "octicon-check"}} {{ctx.Locale.Tr "repo.vault.feature_unlimited_secrets"}}</div>
<div class="item">{{svg "octicon-check"}} {{ctx.Locale.Tr "repo.vault.unlimited"}} {{ctx.Locale.Tr "repo.vault.feature_versioning"}}</div>
<div class="item">{{svg "octicon-check"}} {{ctx.Locale.Tr "repo.vault.unlimited"}} {{ctx.Locale.Tr "repo.vault.feature_cicd_tokens"}}</div>
<div class="item">{{svg "octicon-check"}} {{ctx.Locale.Tr "repo.vault.feature_sso"}}</div>
<div class="item">{{svg "octicon-check"}} Custom audit retention</div>
<div class="item">{{svg "octicon-check"}} Dedicated support</div>
</div>
<a class="ui button" href="https://gitcaddy.com/vault/enterprise" target="_blank" style="margin-top: 1em;">
{{svg "octicon-link-external"}} Contact Sales
</a>
</div>
</div>
{{else}}
{{/* Generic upgrade for other features */}}
<div class="column">
<div class="ui blue segment">
<div class="ui blue ribbon label">{{ctx.Locale.Tr "repo.vault.upgrade_to_pro"}}</div>
<h3>Pro</h3>
<div class="ui large header">$9/mo</div>
<div class="ui list">
<div class="item">{{svg "octicon-check"}} 5 users</div>
<div class="item">{{svg "octicon-check"}} {{ctx.Locale.Tr "repo.vault.feature_unlimited_secrets"}}</div>
<div class="item">{{svg "octicon-check"}} {{ctx.Locale.Tr "repo.vault.unlimited"}} {{ctx.Locale.Tr "repo.vault.feature_versioning"}}</div>
<div class="item">{{svg "octicon-check"}} {{ctx.Locale.Tr "repo.vault.unlimited"}} {{ctx.Locale.Tr "repo.vault.feature_cicd_tokens"}}</div>
<div class="item">{{svg "octicon-check"}} 90-day audit log</div>
</div>
<a class="ui primary button" href="https://gitcaddy.com/vault/pro" target="_blank" style="margin-top: 1em;">
{{svg "octicon-link-external"}} {{ctx.Locale.Tr "repo.vault.upgrade_to_pro"}}
</a>
</div>
</div>
<div class="column">
<div class="ui segment">
<h3>Team</h3>
<div class="ui large header">$29/mo</div>
<div class="ui list">
<div class="item">{{svg "octicon-check"}} 25 users</div>
<div class="item">{{svg "octicon-check"}} {{ctx.Locale.Tr "repo.vault.feature_unlimited_secrets"}}</div>
<div class="item">{{svg "octicon-check"}} {{ctx.Locale.Tr "repo.vault.unlimited"}} {{ctx.Locale.Tr "repo.vault.feature_versioning"}}</div>
<div class="item">{{svg "octicon-check"}} {{ctx.Locale.Tr "repo.vault.unlimited"}} {{ctx.Locale.Tr "repo.vault.feature_cicd_tokens"}}</div>
<div class="item">{{svg "octicon-check"}} {{ctx.Locale.Tr "repo.vault.feature_sso"}}</div>
<div class="item">{{svg "octicon-check"}} 1-year audit log</div>
</div>
<a class="ui button" href="https://gitcaddy.com/vault/team" target="_blank" style="margin-top: 1em;">
{{svg "octicon-link-external"}} {{ctx.Locale.Tr "repo.vault.upgrade_to_team"}}
</a>
</div>
</div>
{{end}}
</div>
<div class="ui divider"></div>
<a class="ui button" href="{{.RepoLink}}/vault">
{{svg "octicon-arrow-left"}} {{ctx.Locale.Tr "repo.vault.back_to_list"}}
</a>
</div>
</div>
</div>
</div>
{{template "base/footer" .}}

View File

@@ -0,0 +1,33 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content repository vault">
{{template "repo/header" .}}
<div class="ui container">
<div class="ui center aligned segment">
<div class="ui icon header">
{{svg "octicon-lock" 64}}
<div class="content">
{{ctx.Locale.Tr "repo.vault.plugin_not_installed"}}
<div class="sub header">GitCaddy Vault</div>
</div>
</div>
<p class="ui large text">
{{ctx.Locale.Tr "repo.vault.plugin_not_installed_desc"}}
</p>
<div class="ui divider"></div>
<div class="ui info message">
<div class="header">{{ctx.Locale.Tr "repo.vault"}}</div>
<p>{{ctx.Locale.Tr "repo.vault.upgrade_description"}}</p>
</div>
<div class="ui divider"></div>
<a class="ui large primary button" href="https://gitcaddy.com/vault" target="_blank">
{{svg "octicon-link-external"}} {{ctx.Locale.Tr "repo.vault.get_started"}}
</a>
</div>
</div>
</div>
{{template "base/footer" .}}