Any authenticated user can invite an e-mail address; the mailed single-use token lets exactly one signup through even while registration is closed. Open (pending, unexpired) invitations count against the new instance setting invitations.maxOpenPerUser (default 5, 0 disables inviting) — plus a 20/day per-user rate limit so a revoke-and-recreate loop cannot become a mail cannon. Only the SHA-256 token hash is stored (auth-tokens pattern); a failed signup (taken username) un-redeems the token so the invitee can retry. Surfaces: invitations section in the user settings (list, invite, revoke, quota line; wide table in a focusable .table-scroll region), signup page reads ?invitation=<token> (preview banner, e-mail prefill, closed-mode gate opens only for a previewed-valid token), admin general card gets the quota field (flat RHF name per #322; VS-NfD marked and hideable). Governance: audit actions invitation.created/revoked/accepted (catalogue 1.10), VS-NfD profile entry (compliant: 0) + hardening-guide row, i18n de+en including the invitation mail template. Tests: api e2e-db (mail link, closed-mode single-use signup with un-redeem on failure, quota + revoke frees slot, quota 0 = 403, auth matrix), new web e2e pack invitations.spec.ts (full UI loop through Mailpit, wired into ci.yml with its own rate-limit reset), a11y scan waits for the new section. Full api suite (107 files / 607 tests), auth/admin-settings/a11y packs green against a fresh local stack. Closes #332
143 lines
9.7 KiB
JSON
143 lines
9.7 KiB
JSON
{
|
|
"bad_request": "The request is invalid.",
|
|
"unauthorized": "Please sign in to continue.",
|
|
"forbidden": "You do not have permission for this action.",
|
|
"not_found": "The requested resource does not exist.",
|
|
"conflict": "The request conflicts with the current state.",
|
|
"gone": "This resource is no longer available.",
|
|
"payload_too_large": "The submitted data is too large.",
|
|
"rate_limited": "Too many requests — please try again later.",
|
|
"internal_error": "Internal server error.",
|
|
"registration_closed": "Registration is currently closed on this instance.",
|
|
"invitations_disabled": "Invitations are disabled on this instance.",
|
|
"invitation_quota_reached": "You have reached the maximum number of open invitations. Revoke an open invitation or wait until one is accepted or expires.",
|
|
"invitation_already_accepted": "This invitation has already been accepted and can no longer be revoked.",
|
|
"token_invalid": "This link is invalid or has expired.",
|
|
"login_failed": "Username/e-mail or password is incorrect.",
|
|
"login_backoff": "Too many failed attempts — please wait a few minutes.",
|
|
"email_unverified": "Please confirm your e-mail address first.",
|
|
"account_disabled": "This account has been disabled.",
|
|
"password_incorrect": "The current password is incorrect.",
|
|
"csrf_origin_mismatch": "The request came from an unexpected origin.",
|
|
"cannot_revoke_current_session": "Use sign-out to end your current session.",
|
|
"personal_pond_undeletable": "The personal pond cannot be deleted.",
|
|
"quota_exceeded": "The quota has been reached (limit: {{limit}}).",
|
|
"slug_taken": "This slug is already taken in this pond.",
|
|
"page_document_too_large": "The page is too large (limit: {{limitBytes}} bytes).",
|
|
"invalid_page_state": "The submitted page content is invalid.",
|
|
"page_trashed": "This page has been moved to the trash.",
|
|
"page_cycle": "A page cannot be moved into its own subtree.",
|
|
"page_depth_exceeded": "Pages can be nested at most {{max}} levels deep.",
|
|
"label_name_taken": "A label with this name already exists at this level.",
|
|
"label_cycle": "A label cannot be moved into its own subtree.",
|
|
"label_depth_exceeded": "Labels can be nested at most {{max}} levels deep.",
|
|
"label_has_pages": "This label still has pages assigned; confirm to detach them.",
|
|
"label_wrong_pond": "This label belongs to a different pond.",
|
|
"unsupported_file_type": "This file type is not supported.",
|
|
"upload_type_not_allowed": "This file type is not allowed on this instance.",
|
|
"file_too_large": "The file is too large (limit: {{limitBytes}} bytes).",
|
|
"converter_unavailable": "The document converter is currently unavailable. Please try again later.",
|
|
"converter_timeout": "The conversion took too long and was cancelled.",
|
|
"conversion_failed": "This document could not be converted.",
|
|
"renderer_unavailable": "The PDF renderer is currently unavailable. Please try again later.",
|
|
"render_failed": "This page could not be rendered as a PDF.",
|
|
"import_unsupported_format": "Only Word (.docx) and OpenDocument (.odt) documents can be imported.",
|
|
"import_vault_invalid_zip": "The archive is not a valid Obsidian vault (ZIP with Markdown notes).",
|
|
"import_vault_too_large": "The unpacked vault is too large for an import.",
|
|
"network": "The server could not be reached.",
|
|
"grant_exists": "This grant already exists.",
|
|
"plugin_invalid_zip": "The uploaded file is not a valid ZIP archive.",
|
|
"plugin_bad_structure": "The plugin package has an invalid structure.",
|
|
"plugin_invalid_manifest": "The plugin manifest is invalid.",
|
|
"plugin_api_incompatible": "This plugin targets an unsupported plugin API version.",
|
|
"plugin_too_large": "The plugin package is too large (limit: {{limitBytes}} bytes).",
|
|
"plugin_missing_bundle": "A code plugin must include a plugin.js bundle.",
|
|
"plugin_missing_styles": "A style plugin must include a styles.css file.",
|
|
"plugin_css_unsafe": "The plugin stylesheet uses constructs that are not allowed.",
|
|
"plugin_version_not_higher": "An update must have a higher version than the installed one.",
|
|
"plugin_required_cannot_uninstall": "A required plugin cannot be uninstalled.",
|
|
"plugin_not_found": "This plugin is not installed.",
|
|
"plugin_not_optional": "Only optional plugins can be activated per pond.",
|
|
"grant_pond_admin_scope": "A Pond Admin grant must apply to the whole pond and a specific user.",
|
|
"grant_pond_admin_personal_pond": "A personal pond's only administrator is its owner.",
|
|
"grant_subject_id_mismatch": "The grant's subject is inconsistent.",
|
|
"grant_scope_id_mismatch": "The grant's scope is inconsistent.",
|
|
"grant_scope_not_found": "The label or page this grant points to does not exist in this pond.",
|
|
"grant_subject_not_found": "This user does not exist.",
|
|
"grant_last_admin": "The last Pond Admin cannot be removed.",
|
|
"member_not_found": "No user with that username or e-mail exists.",
|
|
"member_exists": "This user is already a member of this pond.",
|
|
"member_not_a_member": "This user is not a member of this pond.",
|
|
"member_is_owner": "The pond owner's membership cannot be changed here.",
|
|
"cannot_modify_self": "You cannot perform this action on your own account.",
|
|
"last_site_admin": "The last Site Admin cannot be removed.",
|
|
"setup_required": "This instance is not set up yet. Please run the first-run setup first.",
|
|
"setup_locked": "First-run setup has already been completed.",
|
|
"setup_admin_exists": "A Site Admin account already exists.",
|
|
"setup_admin_missing": "Create the Site Admin account first.",
|
|
"smtp_test_failed": "The SMTP test failed. Please check the connection details.",
|
|
"validation": {
|
|
"required": "This field is required.",
|
|
"taken": "This value is already taken.",
|
|
"username": {
|
|
"tooShort": "The username needs at least 3 characters.",
|
|
"tooLong": "The username can have at most 32 characters.",
|
|
"charset": "Only letters, digits, and hyphens are allowed."
|
|
},
|
|
"password": {
|
|
"tooShort": "The password needs at least 10 characters.",
|
|
"tooLong": "The password can have at most 128 characters.",
|
|
"tooCommon": "This password is too common."
|
|
},
|
|
"email": {
|
|
"invalid": "Please enter a valid e-mail address."
|
|
},
|
|
"displayName": {
|
|
"required": "Please enter a display name."
|
|
},
|
|
"labelColor": "Please enter a colour like #a1b2c3.",
|
|
"tooLong": "The input is too long."
|
|
},
|
|
"comments_editors_only": "Comments on this pond are limited to editors.",
|
|
"comment_not_author": "Only the author can change this comment.",
|
|
"comment_has_replies": "This comment has replies — only a pond admin can delete the whole thread.",
|
|
"comment_parent_invalid": "Replies must reference a top-level comment on the same page.",
|
|
"comment_not_root": "Only top-level comments can be resolved.",
|
|
"maintenance_mode": "The instance is in maintenance mode while a backup is being restored.",
|
|
"backup_connection_failed": "The Nextcloud connection test failed.",
|
|
"backup_remote_disabled_by_policy": "Remote backup targets are disabled by deployment policy (BACKUP_ALLOWED_TARGETS is empty).",
|
|
"backup_target_not_allowed": "This destination host is not in the deployment policy (BACKUP_ALLOWED_TARGETS).",
|
|
"backup_restore_confirm_mismatch": "The confirmation text does not match the backup id.",
|
|
"backup_restore_running": "A restore is already running.",
|
|
"backup_remote_not_configured": "No Nextcloud backup target is configured.",
|
|
"backup_set_not_found": "The selected backup set was not found.",
|
|
"scope_required": "This API token does not have the required scope.",
|
|
"pond_not_found": "The pond does not exist.",
|
|
"classification_lower_forbidden": "You lack the permission to lower the classification (Pond Admin required).",
|
|
"classified_upload_blocked": "Uploads to classified pages are blocked on this instance.",
|
|
"vs_nfd_profile_violation": "This setting would deviate from the VS-NfD reference profile — the deployment enforces the profile (VS_NFD_MODE=enforced).",
|
|
"plugin_not_pinned": "This plugin is not on the allowlist (hash pinning active) — pin it first, then install.",
|
|
"plugin_hash_mismatch": "The bundle hash deviates from the pinned hash — the bundle is not the reviewed one (or a new version needs a re-pin).",
|
|
"font_family_reserved": "This font name belongs to the bundled catalog and cannot be overridden.",
|
|
"font_family_exists": "A custom font with this name already exists.",
|
|
"font_family_unusable": "No address name can be derived from this font name — please use Latin letters or digits.",
|
|
"font_file_empty": "The font file is empty.",
|
|
"font_file_too_large": "The font file is too large.",
|
|
"font_file_not_a_font": "This file is not a WOFF2/WOFF font file.",
|
|
"font_woff2_missing": "Every weight needs a WOFF2 file; a WOFF alone is not enough.",
|
|
"font_no_weights": "No font file was selected.",
|
|
"font_too_many_weights": "This font already has the maximum number of weights.",
|
|
"font_weight_exists": "This weight already exists for this font.",
|
|
"font_weight_invalid": "This weight is not allowed.",
|
|
"font_one_weight_expected": "Only one weight can be added at a time.",
|
|
"font_unexpected_field": "The request contains an unexpected field.",
|
|
"branding_file_empty": "The image file is empty.",
|
|
"branding_file_too_large": "The image file is too large.",
|
|
"branding_file_missing": "No image file was submitted.",
|
|
"branding_not_a_png": "No PNG could be produced — please choose a different image file.",
|
|
"branding_not_an_image": "This file is not a supported image (PNG, JPEG or WebP).",
|
|
"branding_svg_rejected": "SVG is not accepted: an SVG file can carry script. Please use PNG, JPEG or WebP.",
|
|
"branding_image_too_large": "The image is too large — please reduce the crop.",
|
|
"branding_favicon_not_square": "The favicon must be square."
|
|
}
|