Gab Social. All are welcome.

This commit is contained in:
robcolbert
2019-07-02 03:10:25 -04:00
commit bd0b5afc92
5366 changed files with 222812 additions and 0 deletions

118
config/application.rb Normal file
View File

@@ -0,0 +1,118 @@
require_relative 'boot'
require 'rails/all'
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
require_relative '../app/lib/exceptions'
require_relative '../lib/paperclip/lazy_thumbnail'
require_relative '../lib/paperclip/gif_transcoder'
require_relative '../lib/paperclip/video_transcoder'
require_relative '../lib/gabsocial/snowflake'
require_relative '../lib/gabsocial/version'
require_relative '../lib/devise/ldap_authenticatable'
Dotenv::Railtie.load
Bundler.require(:pam_authentication) if ENV['PAM_ENABLED'] == 'true'
require_relative '../lib/gabsocial/redis_config'
module GabSocial
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 5.2
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
# config.time_zone = 'Central Time (US & Canada)'
# All translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
config.i18n.available_locales = [
:en,
:ar,
:ast,
:bg,
:bn,
:ca,
:co,
:cs,
:cy,
:da,
:de,
:el,
:eo,
:es,
:eu,
:fa,
:fi,
:fr,
:ga,
:gl,
:he,
:hi,
:hr,
:hu,
:hy,
:id,
:io,
:it,
:ja,
:ka,
:kk,
:ko,
:lt,
:lv,
:ms,
:nl,
:no,
:oc,
:pl,
:pt,
:'pt-BR',
:ro,
:ru,
:sk,
:sl,
:sq,
:sr,
:'sr-Latn',
:sv,
:ta,
:te,
:th,
:tr,
:uk,
:'zh-CN',
:'zh-HK',
:'zh-TW',
]
config.i18n.default_locale = ENV['DEFAULT_LOCALE']&.to_sym
unless config.i18n.available_locales.include?(config.i18n.default_locale)
config.i18n.default_locale = :en
end
# config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
# config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
config.active_job.queue_adapter = :sidekiq
config.middleware.use Rack::Attack
config.middleware.use Rack::Deflater
config.to_prepare do
Doorkeeper::AuthorizationsController.layout 'modal'
Doorkeeper::AuthorizedApplicationsController.layout 'admin'
Doorkeeper::Application.send :include, ApplicationExtension
end
end
end

14
config/boot.rb Normal file
View File

@@ -0,0 +1,14 @@
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
require 'bundler/setup' # Set up gems listed in the Gemfile.
require 'bootsnap' # Speed up boot time by caching expensive operations.
Bootsnap.setup(
cache_dir: File.expand_path('../tmp/cache', __dir__),
development_mode: ENV.fetch('RAILS_ENV', 'development') == 'development',
load_path_cache: true,
autoload_paths_cache: true,
disable_trace: false,
compile_cache_iseq: false,
compile_cache_yaml: false
)

301
config/brakeman.ignore Normal file
View File

@@ -0,0 +1,301 @@
{
"ignored_warnings": [
{
"warning_type": "Mass Assignment",
"warning_code": 105,
"fingerprint": "0117d2be5947ea4e4fbed9c15f23c6615b12c6892973411820c83d079808819d",
"check_name": "PermitAttributes",
"message": "Potentially dangerous key allowed for mass assignment",
"file": "app/controllers/api/v1/search_controller.rb",
"line": 30,
"link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
"code": "params.permit(:type, :offset, :min_id, :max_id, :account_id)",
"render_path": null,
"location": {
"type": "method",
"class": "Api::V1::SearchController",
"method": "search_params"
},
"user_input": ":account_id",
"confidence": "High",
"note": ""
},
{
"warning_type": "SQL Injection",
"warning_code": 0,
"fingerprint": "04dbbc249b989db2e0119bbb0f59c9818e12889d2b97c529cdc0b1526002ba4b",
"check_name": "SQL",
"message": "Possible SQL injection",
"file": "app/models/report.rb",
"line": 90,
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
"code": "Admin::ActionLog.from(\"(#{[Admin::ActionLog.where(:target_type => \"Report\", :target_id => id, :created_at => ((created_at..updated_at))).unscope(:order), Admin::ActionLog.where(:target_type => \"Account\", :target_id => target_account_id, :created_at => ((created_at..updated_at))).unscope(:order), Admin::ActionLog.where(:target_type => \"Status\", :target_id => status_ids, :created_at => ((created_at..updated_at))).unscope(:order)].map do\n \"(#{query.to_sql})\"\n end.join(\" UNION ALL \")}) AS admin_action_logs\")",
"render_path": null,
"location": {
"type": "method",
"class": "Report",
"method": "history"
},
"user_input": "Admin::ActionLog.where(:target_type => \"Status\", :target_id => status_ids, :created_at => ((created_at..updated_at))).unscope(:order)",
"confidence": "High",
"note": ""
},
{
"warning_type": "SQL Injection",
"warning_code": 0,
"fingerprint": "19df3740b8d02a9fe0eb52c939b4b87d3a2a591162a6adfa8d64e9c26aeebe6d",
"check_name": "SQL",
"message": "Possible SQL injection",
"file": "app/models/status.rb",
"line": 87,
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
"code": "result.joins(\"INNER JOIN statuses_tags t#{id} ON t#{id}.status_id = statuses.id AND t#{id}.tag_id = #{id}\")",
"render_path": null,
"location": {
"type": "method",
"class": "Status",
"method": null
},
"user_input": "id",
"confidence": "Weak",
"note": ""
},
{
"warning_type": "Mass Assignment",
"warning_code": 105,
"fingerprint": "28d81cc22580ef76e912b077b245f353499aa27b3826476667224c00227af2a9",
"check_name": "PermitAttributes",
"message": "Potentially dangerous key allowed for mass assignment",
"file": "app/controllers/admin/reports_controller.rb",
"line": 56,
"link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
"code": "params.permit(:account_id, :resolved, :target_account_id)",
"render_path": null,
"location": {
"type": "method",
"class": "Admin::ReportsController",
"method": "filter_params"
},
"user_input": ":account_id",
"confidence": "High",
"note": ""
},
{
"warning_type": "Dynamic Render Path",
"warning_code": 15,
"fingerprint": "4b6a895e2805578d03ceedbe1d469cc75a0c759eba093722523edb4b8683c873",
"check_name": "Render",
"message": "Render path contains parameter value",
"file": "app/views/admin/action_logs/index.html.haml",
"line": 4,
"link": "https://brakemanscanner.org/docs/warning_types/dynamic_render_path/",
"code": "render(action => Admin::ActionLog.page(params[:page]), {})",
"render_path": [{"type":"controller","class":"Admin::ActionLogsController","method":"index","line":7,"file":"app/controllers/admin/action_logs_controller.rb","rendered":{"name":"admin/action_logs/index","file":"/home/eugr/Projects/gabsocial/app/views/admin/action_logs/index.html.haml"}}],
"location": {
"type": "template",
"template": "admin/action_logs/index"
},
"user_input": "params[:page]",
"confidence": "Weak",
"note": ""
},
{
"warning_type": "Redirect",
"warning_code": 18,
"fingerprint": "5fad11cd67f905fab9b1d5739d01384a1748ebe78c5af5ac31518201925265a7",
"check_name": "Redirect",
"message": "Possible unprotected redirect",
"file": "app/controllers/remote_interaction_controller.rb",
"line": 21,
"link": "https://brakemanscanner.org/docs/warning_types/redirect/",
"code": "redirect_to(RemoteFollow.new(resource_params).interact_address_for(Status.find(params[:id])))",
"render_path": null,
"location": {
"type": "method",
"class": "RemoteInteractionController",
"method": "create"
},
"user_input": "RemoteFollow.new(resource_params).interact_address_for(Status.find(params[:id]))",
"confidence": "High",
"note": ""
},
{
"warning_type": "Dynamic Render Path",
"warning_code": 15,
"fingerprint": "67afc0d5f7775fa5bd91d1912e1b5505aeedef61876347546fa20f92fd6915e6",
"check_name": "Render",
"message": "Render path contains parameter value",
"file": "app/views/stream_entries/embed.html.haml",
"line": 3,
"link": "https://brakemanscanner.org/docs/warning_types/dynamic_render_path/",
"code": "render(action => \"stream_entries/#{Account.find_local!(params[:account_username]).statuses.find(params[:id]).stream_entry.activity_type.downcase}\", { Account.find_local!(params[:account_username]).statuses.find(params[:id]).stream_entry.activity_type.downcase.to_sym => Account.find_local!(params[:account_username]).statuses.find(params[:id]).stream_entry.activity, :centered => true, :autoplay => ActiveModel::Type::Boolean.new.cast(params[:autoplay]) })",
"render_path": [{"type":"controller","class":"StatusesController","method":"embed","line":63,"file":"app/controllers/statuses_controller.rb","rendered":{"name":"stream_entries/embed","file":"/home/eugr/Projects/gabsocial/app/views/stream_entries/embed.html.haml"}}],
"location": {
"type": "template",
"template": "stream_entries/embed"
},
"user_input": "params[:id]",
"confidence": "Weak",
"note": ""
},
{
"warning_type": "SQL Injection",
"warning_code": 0,
"fingerprint": "6f075c1484908e3ec9bed21ab7cf3c7866be8da3881485d1c82e13093aefcbd7",
"check_name": "SQL",
"message": "Possible SQL injection",
"file": "app/models/status.rb",
"line": 92,
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
"code": "result.joins(\"LEFT OUTER JOIN statuses_tags t#{id} ON t#{id}.status_id = statuses.id AND t#{id}.tag_id = #{id}\")",
"render_path": null,
"location": {
"type": "method",
"class": "Status",
"method": null
},
"user_input": "id",
"confidence": "Weak",
"note": ""
},
{
"warning_type": "Dynamic Render Path",
"warning_code": 15,
"fingerprint": "8d843713d99e8403f7992f3e72251b633817cf9076ffcbbad5613859d2bbc127",
"check_name": "Render",
"message": "Render path contains parameter value",
"file": "app/views/admin/custom_emojis/index.html.haml",
"line": 45,
"link": "https://brakemanscanner.org/docs/warning_types/dynamic_render_path/",
"code": "render(action => filtered_custom_emojis.eager_load(:local_counterpart).page(params[:page]), {})",
"render_path": [{"type":"controller","class":"Admin::CustomEmojisController","method":"index","line":11,"file":"app/controllers/admin/custom_emojis_controller.rb","rendered":{"name":"admin/custom_emojis/index","file":"/home/eugr/Projects/gabsocial/app/views/admin/custom_emojis/index.html.haml"}}],
"location": {
"type": "template",
"template": "admin/custom_emojis/index"
},
"user_input": "params[:page]",
"confidence": "Weak",
"note": ""
},
{
"warning_type": "SQL Injection",
"warning_code": 0,
"fingerprint": "9ccb9ba6a6947400e187d515e0bf719d22993d37cfc123c824d7fafa6caa9ac3",
"check_name": "SQL",
"message": "Possible SQL injection",
"file": "lib/gabsocial/snowflake.rb",
"line": 87,
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
"code": "connection.execute(\" CREATE OR REPLACE FUNCTION timestamp_id(table_name text)\\n RETURNS bigint AS\\n $$\\n DECLARE\\n time_part bigint;\\n sequence_base bigint;\\n tail bigint;\\n BEGIN\\n time_part := (\\n -- Get the time in milliseconds\\n ((date_part('epoch', now()) * 1000))::bigint\\n -- And shift it over two bytes\\n << 16);\\n\\n sequence_base := (\\n 'x' ||\\n -- Take the first two bytes (four hex characters)\\n substr(\\n -- Of the MD5 hash of the data we documented\\n md5(table_name ||\\n '#{SecureRandom.hex(16)}' ||\\n time_part::text\\n ),\\n 1, 4\\n )\\n -- And turn it into a bigint\\n )::bit(16)::bigint;\\n\\n -- Finally, add our sequence number to our base, and chop\\n -- it to the last two bytes\\n tail := (\\n (sequence_base + nextval(table_name || '_id_seq'))\\n & 65535);\\n\\n -- Return the time part and the sequence part. OR appears\\n -- faster here than addition, but they're equivalent:\\n -- time_part has no trailing two bytes, and tail is only\\n -- the last two bytes.\\n RETURN time_part | tail;\\n END\\n $$ LANGUAGE plpgsql VOLATILE;\\n\")",
"render_path": null,
"location": {
"type": "method",
"class": "GabSocial::Snowflake",
"method": "define_timestamp_id"
},
"user_input": "SecureRandom.hex(16)",
"confidence": "Medium",
"note": ""
},
{
"warning_type": "Dynamic Render Path",
"warning_code": 15,
"fingerprint": "9f31d941f3910dba2e9bfcd81aef4513249bd24c02d0f98e13ad44fdeeccd0e8",
"check_name": "Render",
"message": "Render path contains parameter value",
"file": "app/views/admin/accounts/index.html.haml",
"line": 47,
"link": "https://brakemanscanner.org/docs/warning_types/dynamic_render_path/",
"code": "render(action => filtered_accounts.page(params[:page]), {})",
"render_path": [{"type":"controller","class":"Admin::AccountsController","method":"index","line":12,"file":"app/controllers/admin/accounts_controller.rb","rendered":{"name":"admin/accounts/index","file":"/home/eugr/Projects/gabsocial/app/views/admin/accounts/index.html.haml"}}],
"location": {
"type": "template",
"template": "admin/accounts/index"
},
"user_input": "params[:page]",
"confidence": "Weak",
"note": ""
},
{
"warning_type": "Redirect",
"warning_code": 18,
"fingerprint": "ba699ddcc6552c422c4ecd50d2cd217f616a2446659e185a50b05a0f2dad8d33",
"check_name": "Redirect",
"message": "Possible unprotected redirect",
"file": "app/controllers/media_controller.rb",
"line": 14,
"link": "https://brakemanscanner.org/docs/warning_types/redirect/",
"code": "redirect_to(MediaAttachment.attached.find_by!(:shortcode => ((params[:id] or params[:medium_id]))).file.url(:original))",
"render_path": null,
"location": {
"type": "method",
"class": "MediaController",
"method": "show"
},
"user_input": "MediaAttachment.attached.find_by!(:shortcode => ((params[:id] or params[:medium_id]))).file.url(:original)",
"confidence": "High",
"note": ""
},
{
"warning_type": "Redirect",
"warning_code": 18,
"fingerprint": "bb7e94e60af41decb811bb32171f1b27e9bf3f4d01e9e511127362e22510eb11",
"check_name": "Redirect",
"message": "Possible unprotected redirect",
"file": "app/controllers/remote_follow_controller.rb",
"line": 19,
"link": "https://brakemanscanner.org/docs/warning_types/redirect/",
"code": "redirect_to(RemoteFollow.new(resource_params).subscribe_address_for(Account.find_local!(params[:account_username])))",
"render_path": null,
"location": {
"type": "method",
"class": "RemoteFollowController",
"method": "create"
},
"user_input": "RemoteFollow.new(resource_params).subscribe_address_for(Account.find_local!(params[:account_username]))",
"confidence": "High",
"note": ""
},
{
"warning_type": "Mass Assignment",
"warning_code": 105,
"fingerprint": "e867661b2c9812bc8b75a5df12b28e2a53ab97015de0638b4e732fe442561b28",
"check_name": "PermitAttributes",
"message": "Potentially dangerous key allowed for mass assignment",
"file": "app/controllers/api/v1/reports_controller.rb",
"line": 36,
"link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
"code": "params.permit(:account_id, :comment, :forward, :status_ids => ([]))",
"render_path": null,
"location": {
"type": "method",
"class": "Api::V1::ReportsController",
"method": "report_params"
},
"user_input": ":account_id",
"confidence": "High",
"note": ""
},
{
"warning_type": "Dynamic Render Path",
"warning_code": 15,
"fingerprint": "fbd0fc59adb5c6d44b60e02debb31d3af11719f534c9881e21435bbff87404d6",
"check_name": "Render",
"message": "Render path contains parameter value",
"file": "app/views/stream_entries/show.html.haml",
"line": 23,
"link": "https://brakemanscanner.org/docs/warning_types/dynamic_render_path/",
"code": "render(partial => \"stream_entries/#{Account.find_local!(params[:account_username]).statuses.find(params[:id]).stream_entry.activity_type.downcase}\", { :locals => ({ Account.find_local!(params[:account_username]).statuses.find(params[:id]).stream_entry.activity_type.downcase.to_sym => Account.find_local!(params[:account_username]).statuses.find(params[:id]).stream_entry.activity, :include_threads => true }) })",
"render_path": [{"type":"controller","class":"StatusesController","method":"show","line":34,"file":"app/controllers/statuses_controller.rb","rendered":{"name":"stream_entries/show","file":"/home/eugr/Projects/gabsocial/app/views/stream_entries/show.html.haml"}}],
"location": {
"type": "template",
"template": "stream_entries/show"
},
"user_input": "params[:id]",
"confidence": "Weak",
"note": ""
}
],
"updated": "2019-02-21 02:30:29 +0100",
"brakeman_version": "4.4.0"
}

35
config/database.yml Normal file
View File

@@ -0,0 +1,35 @@
default: &default
adapter: postgresql
pool: <%= ENV["DB_POOL"] || ENV['MAX_THREADS'] || 5 %>
timeout: 5000
encoding: unicode
sslmode: <%= ENV['DB_SSLMODE'] || "prefer" %>
development:
<<: *default
database: <%= ENV['DB_NAME'] || 'gabsocial_development' %>
username: <%= ENV['DB_USER'] %>
password: <%= ENV['DB_PASS'] %>
host: <%= ENV['DB_HOST'] %>
port: <%= ENV['DB_PORT'] %>
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
<<: *default
database: <%= ENV['DB_NAME'] || 'gabsocial' %>_test<%= ENV['TEST_ENV_NUMBER'] %>
username: <%= ENV['DB_USER'] %>
password: <%= ENV['DB_PASS'] %>
host: <%= ENV['DB_HOST'] %>
port: <%= ENV['DB_PORT'] %>
production:
<<: *default
database: <%= ENV['DB_NAME'] || 'gabsocial_production' %>
username: <%= ENV['DB_USER'] || 'gabsocial' %>
password: <%= ENV['DB_PASS'] || '' %>
host: <%= ENV['DB_HOST'] || 'localhost' %>
port: <%= ENV['DB_PORT'] || 5432 %>
prepared_statements: <%= ENV['PREPARED_STATEMENTS'] || 'true' %>

14
config/deploy.rb Normal file
View File

@@ -0,0 +1,14 @@
# frozen_string_literal: true
lock '3.11.0'
set :repo_url, ENV.fetch('REPO', 'https://github.com/gab-ai-inc/gab-social.git')
set :branch, ENV.fetch('BRANCH', 'master')
set :application, 'gabsocial'
set :rbenv_type, :user
set :rbenv_ruby, File.read('.ruby-version').strip
set :migration_role, :app
append :linked_files, '.env.production', 'public/robots.txt'
append :linked_dirs, 'vendor/bundle', 'node_modules', 'public/system'

7
config/environment.rb Normal file
View File

@@ -0,0 +1,7 @@
# Load the Rails application.
require_relative 'application'
# Initialize the Rails application.
Rails.application.initialize!
ActiveRecord::SchemaDumper.ignore_tables = ['deprecated_preview_cards']

View File

@@ -0,0 +1,96 @@
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# In the development environment your application's code is reloaded on
# every request. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
config.cache_classes = false
# Do not eager load code on boot.
config.eager_load = false
# Show full error reports.
config.consider_all_requests_local = true
# Enable/disable caching. By default caching is disabled.
# Run rails dev:cache to toggle caching.
if Rails.root.join('tmp/caching-dev.txt').exist?
config.action_controller.perform_caching = true
config.cache_store = :redis_store, ENV['REDIS_URL'], REDIS_CACHE_PARAMS
config.public_file_server.headers = {
'Cache-Control' => "public, max-age=#{2.days.to_i}",
}
else
config.action_controller.perform_caching = false
config.cache_store = :null_store
end
ActiveSupport::Logger.new(STDOUT).tap do |logger|
logger.formatter = config.log_formatter
config.logger = ActiveSupport::TaggedLogging.new(logger)
end
# Generate random VAPID keys
vapid_key = Webpush.generate_key
config.x.vapid_private_key = vapid_key.private_key
config.x.vapid_public_key = vapid_key.public_key
# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false
config.action_mailer.perform_caching = false
# Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log
# Raise an error on page load if there are pending migrations.
config.active_record.migration_error = :page_load
# Debug mode disables concatenation and preprocessing of assets.
# This option may cause significant delays in view rendering with a large
# number of complex assets.
config.assets.debug = true
# Suppress logger output for asset requests.
config.assets.quiet = true
# Adds additional error checking when serving assets at runtime.
# Checks for improperly declared sprockets dependencies.
# Raises helpful error messages.
config.assets.raise_runtime_errors = true
# Raises error for missing translations
# config.action_view.raise_on_missing_translations = true
# Use an evented file watcher to asynchronously detect changes in source code,
# routes, locales, etc. This feature depends on the listen gem.
# config.file_watcher = ActiveSupport::EventedFileUpdateChecker
config.action_mailer.default_options = { from: 'notifications@localhost' }
# If using a Heroku, Vagrant or generic remote development environment,
# use letter_opener_web, accessible at /letter_opener.
# Otherwise, use letter_opener, which launches a browser window to view sent mail.
config.action_mailer.delivery_method = (ENV['HEROKU'] || ENV['VAGRANT'] || ENV['REMOTE_DEV']) ? :letter_opener_web : :letter_opener
config.after_initialize do
Bullet.enable = true
Bullet.bullet_logger = true
Bullet.rails_logger = false
Bullet.add_whitelist type: :n_plus_one_query, class_name: 'User', association: :account
end
config.x.otp_secret = ENV.fetch('OTP_SECRET', '1fc2b87989afa6351912abeebe31ffc5c476ead9bf8b3d74cbc4a302c7b69a45b40b1bbef3506ddad73e942e15ed5ca4b402bf9a66423626051104f4b5f05109')
end
ActiveRecordQueryTrace.enabled = ENV['QUERY_TRACE_ENABLED'] == 'true'
module PrivateAddressCheck
def self.private_address?(*)
false
end
end

View File

@@ -0,0 +1,105 @@
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# Code is not reloaded between requests.
config.cache_classes = true
# Eager load code on boot. This eager loads most of Rails and
# your application in memory, allowing both threaded web servers
# and those relying on copy on write to perform better.
# Rake tasks automatically ignore this option for performance.
config.eager_load = true
# Full error reports are disabled and caching is turned on.
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
config.action_controller.asset_host = ENV['CDN_HOST'] if ENV['CDN_HOST'].present?
# Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
# or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
# config.require_master_key = true
# Disable serving static files from the `/public` folder by default since
# Apache or NGINX already handles this.
config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
ActiveSupport::Logger.new(STDOUT).tap do |logger|
logger.formatter = config.log_formatter
config.logger = ActiveSupport::TaggedLogging.new(logger)
end
# Compress JavaScripts and CSS.
# config.assets.js_compressor = Uglifier.new(mangle: false)
# config.assets.css_compressor = :sass
# Do not fallback to assets pipeline if a precompiled asset is missed.
config.assets.compile = false
# `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
# Specifies the header that your server uses for sending files.
# config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
# Allow to specify public IP of reverse proxy if it's needed
config.action_dispatch.trusted_proxies = ENV['TRUSTED_PROXY_IP'].split.map { |item| IPAddr.new(item) } if ENV['TRUSTED_PROXY_IP'].present?
# Use the lowest log level to ensure availability of diagnostic information
# when problems arise.
config.log_level = ENV.fetch('RAILS_LOG_LEVEL', 'info').to_sym
# Prepend all log lines with the following tags.
config.log_tags = [:request_id]
# Use a different cache store in production.
config.cache_store = :redis_store, ENV['CACHE_REDIS_URL'], REDIS_CACHE_PARAMS
# Ignore bad email addresses and do not raise email delivery errors.
# Set this to true and configure the email server for immediate delivery to raise delivery errors.
# config.action_mailer.raise_delivery_errors = false
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# English when a translation cannot be found).
config.i18n.fallbacks = [:en]
# Send deprecation notices to registered listeners.
config.active_support.deprecation = :notify
# Use default logging formatter so that PID and timestamp are not suppressed.
config.log_formatter = ::Logger::Formatter.new
# Better log formatting
config.lograge.enabled = true
# Do not dump schema after migrations.
config.active_record.dump_schema_after_migration = false
config.action_mailer.perform_caching = false
# E-mails
config.action_mailer.default_options = { from: ENV.fetch('SMTP_FROM_ADDRESS', 'notifications@localhost') }
config.action_mailer.smtp_settings = {
:port => ENV['SMTP_PORT'],
:address => ENV['SMTP_SERVER'],
:user_name => ENV['SMTP_LOGIN'].presence,
:password => ENV['SMTP_PASSWORD'].presence,
:domain => ENV['SMTP_DOMAIN'] || ENV['LOCAL_DOMAIN'],
:authentication => ENV['SMTP_AUTH_METHOD'] == 'none' ? nil : ENV['SMTP_AUTH_METHOD'] || :plain,
:ca_file => ENV['SMTP_CA_FILE'].presence,
:openssl_verify_mode => ENV['SMTP_OPENSSL_VERIFY_MODE'],
:enable_starttls_auto => ENV['SMTP_ENABLE_STARTTLS_AUTO'] || true,
:tls => ENV['SMTP_TLS'].presence,
}
config.action_mailer.delivery_method = ENV.fetch('SMTP_DELIVERY_METHOD', 'smtp').to_sym
config.action_dispatch.default_headers = {
'Server' => 'GabSocial',
'X-Frame-Options' => 'DENY',
'X-Content-Type-Options' => 'nosniff',
'X-XSS-Protection' => '1; mode=block',
}
config.x.otp_secret = ENV.fetch('OTP_SECRET')
end

View File

@@ -0,0 +1,72 @@
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# The test environment is used exclusively to run your application's
# test suite. You never need to work with it otherwise. Remember that
# your test database is "scratch space" for the test suite and is wiped
# and recreated between test runs. Don't rely on the data there!
config.cache_classes = true
# Do not eager load code on boot. This avoids loading your whole application
# just for the purpose of running a single test. If you are using a tool that
# preloads Rails for running tests, you may have to set it to true.
config.eager_load = false
# Configure public file server for tests with Cache-Control for performance.
config.public_file_server.enabled = true
config.public_file_server.headers = {
'Cache-Control' => "public, max-age=#{1.hour.to_i}"
}
config.assets.digest = false
# Show full error reports and disable caching.
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
# The default store, file_store is shared by processes parallelly executed
# and should not be used.
config.cache_store = :memory_store
# Raise exceptions instead of rendering exception templates.
config.action_dispatch.show_exceptions = false
# Disable request forgery protection in test environment.
config.action_controller.allow_forgery_protection = false
config.action_mailer.perform_caching = false
config.action_mailer.default_options = { from: 'notifications@localhost' }
# Tell Action Mailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
# Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr
config.x.otp_secret = '100c7faeef00caa29242f6b04156742bf76065771fd4117990c4282b8748ff3d99f8fdae97c982ab5bd2e6756a159121377cce4421f4a8ecd2d67bd7749a3fb4'
# Generate random VAPID keys
vapid_key = Webpush.generate_key
config.x.vapid_private_key = vapid_key.private_key
config.x.vapid_public_key = vapid_key.public_key
# Raises error for missing translations
# config.action_view.raise_on_missing_translations = true
config.i18n.default_locale = :en
config.i18n.fallbacks = true
end
Paperclip::Attachment.default_options[:path] = "#{Rails.root}/spec/test_files/:class/:id_partition/:style.:extension"
# set fake_data for pam, don't do real calls, just use fake data
if ENV['PAM_ENABLED'] == 'true'
Rpam2.fake_data =
{
usernames: Set['pam_user1', 'pam_user2'],
servicenames: Set['pam_test', 'pam_test_controlled'],
password: '123456',
env: { email: 'pam@example.com' }
}
end

62
config/i18n-tasks.yml Normal file
View File

@@ -0,0 +1,62 @@
# i18n-tasks finds and manages missing and unused translations: https://github.com/glebm/i18n-tasks
# The "main" locale.
base_locale: en
data:
read:
- config/locales/%{locale}.yml
- config/locales/**/*.%{locale}.yml
write:
- ['{devise, simple_form, doorkeeper}.*', 'config/locales/\1.%{locale}.yml']
- config/locales/%{locale}.yml
yaml:
write:
line_width: -1
search:
paths:
- app/
- config/navigation.rb
relative_roots:
- app/controllers
- app/helpers
- app/mailers
- app/views
exclude:
- app/assets/images
- app/assets/fonts
- app/assets/videos
ignore_missing:
- 'activemodel.errors.*'
- 'activerecord.attributes.*'
- 'activerecord.errors.*'
- '{pagination,doorkeeper}.*'
- '{date,datetime,time,number}.*'
- 'errors.messages.*'
- 'activerecord.errors.models.doorkeeper/*'
- 'sessions.{browsers,platforms}.*'
- 'terms.body_html'
- 'application_mailer.salutation'
- 'errors.500'
- 'auth.providers.*'
ignore_unused:
- 'activemodel.errors.*'
- 'activerecord.attributes.*'
- 'activerecord.errors.*'
- '{devise,pagination,doorkeeper}.*'
- '{date,datetime,time,number}.*'
- 'simple_form.{yes,no}'
- 'simple_form.{placeholders,hints,labels}.*'
- 'simple_form.{error_notification,required}.:'
- 'errors.messages.*'
- 'activerecord.errors.models.doorkeeper/*'
- 'errors.429'
- 'admin.accounts.roles.*'
- 'admin.action_logs.actions.*'
- 'statuses.attached.*'

View File

@@ -0,0 +1,15 @@
# Post deployment migrations are included by default. This file must be loaded
# before other initializers as Rails may otherwise memoize a list of migrations
# excluding the post deployment migrations.
unless ENV['SKIP_POST_DEPLOYMENT_MIGRATIONS']
Rails.application.config.paths['db'].each do |db_path|
path = Rails.root.join(db_path, 'post_migrate').to_s
Rails.application.config.paths['db/migrate'] << path
# Rails memoizes migrations at certain points where it won't read the above
# path just yet. As such we must also update the following list of paths.
ActiveRecord::Migrator.migrations_paths << path
end
end

View File

@@ -0,0 +1,29 @@
# frozen_string_literal: true
port = ENV.fetch('PORT') { 3000 }
host = ENV.fetch('LOCAL_DOMAIN') { "localhost:#{port}" }
web_host = ENV.fetch('WEB_DOMAIN') { host }
alternate_domains = ENV.fetch('ALTERNATE_DOMAINS') { '' }
Rails.application.configure do
https = Rails.env.production? || ENV['LOCAL_HTTPS'] == 'true'
config.x.local_domain = host
config.x.web_domain = web_host
config.x.use_https = https
config.x.use_s3 = ENV['S3_ENABLED'] == 'true'
config.x.use_swift = ENV['SWIFT_ENABLED'] == 'true'
config.x.alternate_domains = alternate_domains.split(/\s*,\s*/)
config.action_mailer.default_url_options = { host: web_host, protocol: https ? 'https://' : 'http://', trailing_slash: false }
config.x.streaming_api_base_url = ENV.fetch('STREAMING_API_BASE_URL') do
if Rails.env.production?
"ws#{https ? 's' : ''}://#{web_host}"
else
"ws://#{ENV['REMOTE_DEV'] == 'true' ? host.split(':').first : 'localhost'}:4000"
end
end
end

View File

@@ -0,0 +1,24 @@
ActiveModelSerializers.config.tap do |config|
config.default_includes = '**'
end
ActiveSupport::Notifications.unsubscribe(ActiveModelSerializers::Logging::RENDER_EVENT)
class ActiveModel::Serializer::Reflection
# We monkey-patch this method so that when we include associations in a serializer,
# the nested serializers can send information about used contexts upwards back to
# the root. We do this via instance_options because the nesting can be dynamic.
def build_association(parent_serializer, parent_serializer_options, include_slice = {})
serializer = options[:serializer]
parent_serializer_options.merge!(named_contexts: serializer._named_contexts, context_extensions: serializer._context_extensions) if serializer.respond_to?(:_named_contexts)
association_options = {
parent_serializer: parent_serializer,
parent_serializer_options: parent_serializer_options,
include_slice: include_slice,
}
ActiveModel::Serializer::Association.new(self, association_options)
end
end

View File

@@ -0,0 +1,6 @@
# Be sure to restart your server when you modify this file.
# ApplicationController.renderer.defaults.merge!(
# http_host: 'example.org',
# https: false
# )

View File

@@ -0,0 +1,13 @@
# Be sure to restart your server when you modify this file.
# Version of your assets, change this if you want to expire all your assets.
Rails.application.config.assets.version = '1.0'
# Add additional assets to the asset load path
# Rails.application.config.assets.paths << 'node_modules'
# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
# Rails.application.config.assets.precompile += %w()
Rails.application.config.assets.initialize_on_precompile = true

View File

@@ -0,0 +1,7 @@
# Be sure to restart your server when you modify this file.
# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
# Rails.backtrace_cleaner.remove_silencers!

View File

@@ -0,0 +1,6 @@
# frozen_string_literal: true
Rails.application.configure do
config.x.email_domains_blacklist = ENV.fetch('EMAIL_DOMAIN_BLACKLIST') { 'mvrht.com' }
config.x.email_domains_whitelist = ENV.fetch('EMAIL_DOMAIN_WHITELIST') { '' }
end

View File

@@ -0,0 +1,24 @@
enabled = ENV['ES_ENABLED'] == 'true'
host = ENV.fetch('ES_HOST') { 'localhost' }
port = ENV.fetch('ES_PORT') { 9200 }
fallback_prefix = ENV.fetch('REDIS_NAMESPACE') { nil }
prefix = ENV.fetch('ES_PREFIX') { fallback_prefix }
Chewy.settings = {
host: "#{host}:#{port}",
prefix: prefix,
enabled: enabled,
journal: false,
sidekiq: { queue: 'pull' },
}
Chewy.root_strategy = enabled ? :sidekiq : :bypass
Chewy.request_strategy = enabled ? :sidekiq : :bypass
module Chewy
class << self
def enabled?
settings[:enabled]
end
end
end

View File

@@ -0,0 +1,34 @@
# Define an application-wide content security policy
# For further information see the following documentation
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
base_host = Rails.configuration.x.web_domain
assets_host = Rails.configuration.action_controller.asset_host
assets_host ||= "http#{Rails.configuration.x.use_https ? 's' : ''}://#{base_host}"
Rails.application.config.content_security_policy do |p|
p.base_uri :none
p.default_src :none
p.frame_ancestors :none
p.font_src :self, assets_host
p.img_src :self, :https, :data, :blob, assets_host
p.style_src :self, :unsafe_inline, assets_host
p.media_src :self, :https, :data, assets_host
p.frame_src :self, :https
p.manifest_src :self, assets_host
if Rails.env.development?
webpacker_urls = %w(ws http).map { |protocol| "#{protocol}#{Webpacker.dev_server.https? ? 's' : ''}://#{Webpacker.dev_server.host_with_port}" }
p.connect_src :self, :blob, assets_host, Rails.configuration.x.streaming_api_base_url, *webpacker_urls
p.script_src :self, :unsafe_inline, :unsafe_eval, assets_host
else
p.connect_src :self, :blob, assets_host, Rails.configuration.x.streaming_api_base_url
p.script_src :self, assets_host
end
end
# Report CSP violations to a specified URI
# For further information see the following documentation:
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
# Rails.application.config.content_security_policy_report_only = true

View File

@@ -0,0 +1,5 @@
# Be sure to restart your server when you modify this file.
# Specify a serializer for the signed and encrypted cookie jars.
# Valid options are :json, :marshal, and :hybrid.
Rails.application.config.action_dispatch.cookies_serializer = :json

View File

@@ -0,0 +1,34 @@
# Be sure to restart your server when you modify this file.
# Avoid CORS issues when API is called from the frontend app.
# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.
# Read more: https://github.com/cyu/rack-cors
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins '*'
resource '/.well-known/*',
headers: :any,
methods: [:get],
credentials: false
resource '/@:username',
headers: :any,
methods: [:get],
credentials: false
resource '/users/:username',
headers: :any,
methods: [:get],
credentials: false
resource '/api/*',
headers: :any,
methods: [:post, :put, :delete, :get, :patch, :options],
credentials: false,
expose: ['Link', 'X-RateLimit-Reset', 'X-RateLimit-Limit', 'X-RateLimit-Remaining', 'X-Request-Id']
resource '/oauth/token',
headers: :any,
methods: [:post],
credentials: false
end
end

View File

@@ -0,0 +1,3 @@
ActionMailer::DeliveryJob.class_eval do
discard_on ActiveJob::DeserializationError
end

View File

@@ -0,0 +1,369 @@
Warden::Manager.after_set_user except: :fetch do |user, warden|
if user.session_active?(warden.cookies.signed['_session_id'] || warden.raw_session['auth_id'])
session_id = warden.cookies.signed['_session_id'] || warden.raw_session['auth_id']
else
session_id = user.activate_session(warden.request)
end
warden.cookies.signed['_session_id'] = {
value: session_id,
expires: 1.year.from_now,
httponly: true,
secure: (Rails.env.production? || ENV['LOCAL_HTTPS'] == 'true'),
}
end
Warden::Manager.after_fetch do |user, warden|
if user.session_active?(warden.cookies.signed['_session_id'] || warden.raw_session['auth_id'])
warden.cookies.signed['_session_id'] = {
value: warden.cookies.signed['_session_id'] || warden.raw_session['auth_id'],
expires: 1.year.from_now,
httponly: true,
secure: (Rails.env.production? || ENV['LOCAL_HTTPS'] == 'true'),
}
else
warden.logout
throw :warden, message: :unauthenticated
end
end
Warden::Manager.before_logout do |_, warden|
SessionActivation.deactivate warden.cookies.signed['_session_id']
warden.cookies.delete('_session_id')
end
module Devise
mattr_accessor :pam_authentication
@@pam_authentication = false
mattr_accessor :pam_controlled_service
@@pam_controlled_service = nil
mattr_accessor :check_at_sign
@@check_at_sign = false
mattr_accessor :ldap_authentication
@@ldap_authentication = false
mattr_accessor :ldap_host
@@ldap_host = nil
mattr_accessor :ldap_port
@@ldap_port = nil
mattr_accessor :ldap_method
@@ldap_method = nil
mattr_accessor :ldap_base
@@ldap_base = nil
mattr_accessor :ldap_uid
@@ldap_uid = nil
mattr_accessor :ldap_bind_dn
@@ldap_bind_dn = nil
mattr_accessor :ldap_password
@@ldap_password = nil
mattr_accessor :ldap_tls_no_verify
@@ldap_tls_no_verify = false
mattr_accessor :ldap_search_filter
@@ldap_search_filter = nil
class Strategies::PamAuthenticatable
def valid?
super && ::Devise.pam_authentication
end
end
end
Devise.setup do |config|
config.warden do |manager|
manager.default_strategies(scope: :user).unshift :ldap_authenticatable if Devise.ldap_authentication
manager.default_strategies(scope: :user).unshift :pam_authenticatable if Devise.pam_authentication
manager.default_strategies(scope: :user).unshift :two_factor_authenticatable
manager.default_strategies(scope: :user).unshift :two_factor_backupable
end
# The secret key used by Devise. Devise uses this key to generate
# random tokens. Changing this key will render invalid all existing
# confirmation, reset password and unlock tokens in the database.
# Devise will use the `secret_key_base` on Rails 4+ applications as its `secret_key`
# by default. You can change it below and use your own secret key.
# config.secret_key = '2f86974c4dd7735170fd70fbf399f7a477ffd635ef240d07a22cf4bd7cd13dbae17c4383a2996d0c1e79a991ec18a91a17424c53e4771adb75a8b21904bd1403'
# ==> Mailer Configuration
# Configure the e-mail address which will be shown in Devise::Mailer,
# note that it will be overwritten if you use your own mailer class
# with default "from" parameter.
# config.mailer_sender = ENV['SMTP_FROM_ADDRESS'] || 'notifications@localhost'
# Configure the class responsible to send e-mails.
config.mailer = 'UserMailer'
# ==> ORM configuration
# Load and configure the ORM. Supports :active_record (default) and
# :mongoid (bson_ext recommended) by default. Other ORMs may be
# available as additional gems.
require 'devise/orm/active_record'
# ==> Configuration for any authentication mechanism
# Configure which keys are used when authenticating a user. The default is
# just :email. You can configure it to use [:username, :subdomain], so for
# authenticating a user, both parameters are required. Remember that those
# parameters are used only when authenticating and not when retrieving from
# session. If you need permissions, you should implement that in a before filter.
# You can also supply a hash where the value is a boolean determining whether
# or not authentication should be aborted when the value is not present.
# config.authentication_keys = [:email]
# Configure parameters from the request object used for authentication. Each entry
# given should be a request method and it will automatically be passed to the
# find_for_authentication method and considered in your model lookup. For instance,
# if you set :request_keys to [:subdomain], :subdomain will be used on authentication.
# The same considerations mentioned for authentication_keys also apply to request_keys.
# config.request_keys = []
# Configure which authentication keys should be case-insensitive.
# These keys will be downcased upon creating or modifying a user and when used
# to authenticate or find a user. Default is :email.
config.case_insensitive_keys = [:email]
# Configure which authentication keys should have whitespace stripped.
# These keys will have whitespace before and after removed upon creating or
# modifying a user and when used to authenticate or find a user. Default is :email.
config.strip_whitespace_keys = [:email]
# Tell if authentication through request.params is enabled. True by default.
# It can be set to an array that will enable params authentication only for the
# given strategies, for example, `config.params_authenticatable = [:database]` will
# enable it only for database (email + password) authentication.
# config.params_authenticatable = true
# Tell if authentication through HTTP Auth is enabled. False by default.
# It can be set to an array that will enable http authentication only for the
# given strategies, for example, `config.http_authenticatable = [:database]` will
# enable it only for database authentication. The supported strategies are:
# :database = Support basic authentication with authentication key + password
config.http_authenticatable = [:pam, :database]
# If 401 status code should be returned for AJAX requests. True by default.
# config.http_authenticatable_on_xhr = true
# The realm used in Http Basic Authentication. 'Application' by default.
# config.http_authentication_realm = 'Application'
# It will change confirmation, password recovery and other workflows
# to behave the same regardless if the e-mail provided was right or wrong.
# Does not affect registerable.
# See : https://github.com/plataformatec/devise/wiki/How-To:-Using-paranoid-mode,-avoid-user-enumeration-on-registerable
config.paranoid = true
# By default Devise will store the user in session. You can skip storage for
# particular strategies by setting this option.
# Notice that if you are skipping storage for all authentication paths, you
# may want to disable generating routes to Devise's sessions controller by
# passing skip: :sessions to `devise_for` in your config/routes.rb
config.skip_session_storage = [:http_auth]
# By default, Devise cleans up the CSRF token on authentication to
# avoid CSRF token fixation attacks. This means that, when using AJAX
# requests for sign in and sign up, you need to get a new CSRF token
# from the server. You can disable this option at your own risk.
# config.clean_up_csrf_token_on_authentication = true
# ==> Configuration for :database_authenticatable
# For bcrypt, this is the cost for hashing the password and defaults to 10. If
# using other encryptors, it sets how many times you want the password re-encrypted.
#
# Limiting the stretches to just one in testing will increase the performance of
# your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use
# a value less than 10 in other environments. Note that, for bcrypt (the default
# encryptor), the cost increases exponentially with the number of stretches (e.g.
# a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation).
config.stretches = Rails.env.test? ? 1 : 10
# Setup a pepper to generate the encrypted password.
# config.pepper = '104d16705f794923e77c5e5167b52452d00646dc952a2d30b541c24086e647012c7b9625f253c51912e455981e503446772973d5f1638631196c819d7137fad4'
# Send a notification to the original email when the user's email is changed.
config.send_email_changed_notification = true
# Send a notification email when the user's password is changed
config.send_password_change_notification = true
# ==> Configuration for :confirmable
# A period that the user is allowed to access the website even without
# confirming their account. For instance, if set to 2.days, the user will be
# able to access the website for two days without confirming their account,
# access will be blocked just in the third day. Default is 0.days, meaning
# the user cannot access the website without confirming their account.
# config.allow_unconfirmed_access_for = 2.days
# A period that the user is allowed to confirm their account before their
# token becomes invalid. For example, if set to 3.days, the user can confirm
# their account within 3 days after the mail was sent, but on the fourth day
# their account can't be confirmed with the token any more.
# Default is nil, meaning there is no restriction on how long a user can take
# before confirming their account.
config.confirm_within = 2.days
# If true, requires any email changes to be confirmed (exactly the same way as
# initial account confirmation) to be applied. Requires additional unconfirmed_email
# db field (see migrations). Until confirmed, new email is stored in
# unconfirmed_email column, and copied to email column on successful confirmation.
config.reconfirmable = true
# Defines which key will be used when confirming an account
# config.confirmation_keys = [:email]
# ==> Configuration for :rememberable
# The time the user will be remembered without asking for credentials again.
config.remember_for = 1.year
# Invalidates all the remember me tokens when the user signs out.
config.expire_all_remember_me_on_sign_out = true
# If true, extends the user's remember period when remembered via cookie.
# config.extend_remember_period = false
# Options to be passed to the created cookie. For instance, you can set
# secure: true in order to force SSL only cookies.
config.rememberable_options = { secure: true }
# ==> Configuration for :validatable
# Range for password length.
config.password_length = 8..72
# Email regex used to validate email formats. It simply asserts that
# one (and only one) @ exists in the given string. This is mainly
# to give user feedback and not to assert the e-mail validity.
# config.email_regexp = /\A[^@]+@[^@]+\z/
# ==> Configuration for :timeoutable
# The time you want to timeout the user session without activity. After this
# time the user will be asked for credentials again. Default is 30 minutes.
# config.timeout_in = 30.minutes
# ==> Configuration for :lockable
# Defines which strategy will be used to lock an account.
# :failed_attempts = Locks an account after a number of failed attempts to sign in.
# :none = No lock strategy. You should handle locking by yourself.
# config.lock_strategy = :failed_attempts
# Defines which key will be used when locking and unlocking an account
# config.unlock_keys = [:email]
# Defines which strategy will be used to unlock an account.
# :email = Sends an unlock link to the user email
# :time = Re-enables login after a certain amount of time (see :unlock_in below)
# :both = Enables both strategies
# :none = No unlock strategy. You should handle unlocking by yourself.
# config.unlock_strategy = :both
# Number of authentication tries before locking an account if lock_strategy
# is failed attempts.
# config.maximum_attempts = 20
# Time interval to unlock the account if :time is enabled as unlock_strategy.
# config.unlock_in = 1.hour
# Warn on the last attempt before the account is locked.
# config.last_attempt_warning = true
# ==> Configuration for :recoverable
#
# Defines which key will be used when recovering the password for an account
# config.reset_password_keys = [:email]
# Time interval you can reset your password with a reset password key.
# Don't put a too small interval or your users won't have the time to
# change their passwords.
config.reset_password_within = 6.hours
# When set to false, does not sign a user in automatically after their password is
# reset. Defaults to true, so a user is signed in automatically after a reset.
config.sign_in_after_reset_password = false
# ==> Configuration for :encryptable
# Allow you to use another encryption algorithm besides bcrypt (default). You can use
# :sha1, :sha512 or encryptors from others authentication tools as :clearance_sha1,
# :authlogic_sha512 (then you should set stretches above to 20 for default behavior)
# and :restful_authentication_sha1 (then you should set stretches to 10, and copy
# REST_AUTH_SITE_KEY to pepper).
#
# Require the `devise-encryptable` gem when using anything other than bcrypt
# config.encryptor = :sha512
# ==> Scopes configuration
# Turn scoped views on. Before rendering "sessions/new", it will first check for
# "users/sessions/new". It's turned off by default because it's slower if you
# are using only default views.
# config.scoped_views = false
# Configure the default scope given to Warden. By default it's the first
# devise role declared in your routes (usually :user).
# config.default_scope = :user
# Set this configuration to false if you want /users/sign_out to sign out
# only the current scope. By default, Devise signs out all scopes.
# config.sign_out_all_scopes = true
# ==> Navigation configuration
# Lists the formats that should be treated as navigational. Formats like
# :html, should redirect to the sign in page when the user does not have
# access, but formats like :xml or :json, should return 401.
#
# If you have any extra navigational formats, like :iphone or :mobile, you
# should add them to the navigational formats lists.
#
# The "*/*" below is required to match Internet Explorer requests.
# config.navigational_formats = ['*/*', :html]
# The default HTTP method used to sign out a resource. Default is :delete.
config.sign_out_via = :delete
# ==> OmniAuth
# Add a new OmniAuth provider. Check the wiki for more information on setting
# up on your models and hooks.
# config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'
# ==> Warden configuration
# If you want to use other strategies, that are not supported by Devise, or
# change the failure app, you can configure them inside the config.warden block.
#
# config.warden do |manager|
# manager.intercept_401 = false
# manager.default_strategies(scope: :user).unshift :some_external_strategy
# end
# ==> Mountable engine configurations
# When using Devise inside an engine, let's call it `MyEngine`, and this engine
# is mountable, there are some extra configurations to be taken into account.
# The following options are available, assuming the engine is mounted as:
#
# mount MyEngine, at: '/my_engine'
#
# The router that invoked `devise_for`, in the example above, would be:
# config.router_name = :my_engine
#
# When using OmniAuth, Devise cannot automatically set OmniAuth path,
# so you need to do it manually. For the users scope, it would be:
# config.omniauth_path_prefix = '/my_engine/users/auth'
if ENV['PAM_ENABLED'] == 'true'
config.pam_authentication = true
config.usernamefield = nil
config.emailfield = 'email'
config.check_at_sign = true
config.pam_default_suffix = ENV.fetch('PAM_EMAIL_DOMAIN') { ENV['LOCAL_DOMAIN'] }
config.pam_default_service = ENV.fetch('PAM_DEFAULT_SERVICE') { 'rpam' }
config.pam_controlled_service = ENV.fetch('PAM_CONTROLLED_SERVICE') { nil }
end
if ENV['LDAP_ENABLED'] == 'true'
config.ldap_authentication = true
config.check_at_sign = true
config.ldap_host = ENV.fetch('LDAP_HOST', 'localhost')
config.ldap_port = ENV.fetch('LDAP_PORT', 389).to_i
config.ldap_method = ENV.fetch('LDAP_METHOD', :simple_tls).to_sym
config.ldap_base = ENV.fetch('LDAP_BASE')
config.ldap_bind_dn = ENV.fetch('LDAP_BIND_DN')
config.ldap_password = ENV.fetch('LDAP_PASSWORD')
config.ldap_uid = ENV.fetch('LDAP_UID', 'cn')
config.ldap_tls_no_verify = ENV['LDAP_TLS_NO_VERIFY'] == 'true'
config.ldap_search_filter = ENV.fetch('LDAP_SEARCH_FILTER', '%{uid}=%{email}')
end
end

View File

@@ -0,0 +1,138 @@
Doorkeeper.configure do
# Change the ORM that doorkeeper will use (needs plugins)
orm :active_record
# This block will be called to check whether the resource owner is authenticated or not.
resource_owner_authenticator do
current_user || redirect_to(new_user_session_url)
end
resource_owner_from_credentials do |_routes|
user = User.find_by(email: request.params[:username])
user if !user&.otp_required_for_login? && user&.valid_password?(request.params[:password])
end
# If you want to restrict access to the web interface for adding oauth authorized applications, you need to declare the block below.
admin_authenticator do
current_user&.admin? || redirect_to(new_user_session_url)
end
# Authorization Code expiration time (default 10 minutes).
# authorization_code_expires_in 10.minutes
# Access token expiration time (default 2 hours).
# If you want to disable expiration, set this to nil.
access_token_expires_in nil
# Assign a custom TTL for implicit grants.
# custom_access_token_expires_in do |oauth_client|
# oauth_client.application.additional_settings.implicit_oauth_expiration
# end
# Use a custom class for generating the access token.
# https://github.com/doorkeeper-gem/doorkeeper#custom-access-token-generator
# access_token_generator "::Doorkeeper::JWT"
# The controller Doorkeeper::ApplicationController inherits from.
# Defaults to ActionController::Base.
# https://github.com/doorkeeper-gem/doorkeeper#custom-base-controller
base_controller 'ApplicationController'
# Reuse access token for the same resource owner within an application (disabled by default)
# Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/383
reuse_access_token
# Issue access tokens with refresh token (disabled by default)
# use_refresh_token
# Provide support for an owner to be assigned to each registered application (disabled by default)
# Optional parameter :confirmation => true (default false) if you want to enforce ownership of
# a registered application
# Note: you must also run the rails g doorkeeper:application_owner generator to provide the necessary support
enable_application_owner
# Define access token scopes for your provider
# For more information go to
# https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes
default_scopes :read
optional_scopes :write,
:'write:accounts',
:'write:blocks',
:'write:conversations',
:'write:favourites',
:'write:filters',
:'write:follows',
:'write:lists',
:'write:media',
:'write:mutes',
:'write:notifications',
:'write:reports',
:'write:statuses',
:read,
:'read:accounts',
:'read:blocks',
:'read:favourites',
:'read:filters',
:'read:follows',
:'read:lists',
:'read:mutes',
:'read:notifications',
:'read:search',
:'read:statuses',
:follow,
:push
# Change the way client credentials are retrieved from the request object.
# By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
# falls back to the `:client_id` and `:client_secret` params from the `params` object.
# Check out the wiki for more information on customization
# client_credentials :from_basic, :from_params
# Change the way access token is authenticated from the request object.
# By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
# falls back to the `:access_token` or `:bearer_token` params from the `params` object.
# Check out the wiki for more information on customization
# access_token_methods :from_bearer_authorization, :from_access_token_param, :from_bearer_param
# Change the native redirect uri for client apps
# When clients register with the following redirect uri, they won't be redirected to any server and the authorization code will be displayed within the provider
# The value can be any string. Use nil to disable this feature. When disabled, clients must provide a valid URL
# (Similar behaviour: https://developers.google.com/accounts/docs/OAuth2InstalledApp#choosingredirecturi)
#
# native_redirect_uri 'urn:ietf:wg:oauth:2.0:oob'
# Forces the usage of the HTTPS protocol in non-native redirect uris (enabled
# by default in non-development environments). OAuth2 delegates security in
# communication to the HTTPS protocol so it is wise to keep this enabled.
#
force_ssl_in_redirect_uri false
# Specify what grant flows are enabled in array of Strings. The valid
# strings and the flows they enable are:
#
# "authorization_code" => Authorization Code Grant Flow
# "implicit" => Implicit Grant Flow
# "password" => Resource Owner Password Credentials Grant Flow
# "client_credentials" => Client Credentials Grant Flow
#
# If not specified, Doorkeeper enables authorization_code and
# client_credentials.
#
# implicit and password grant flows have risks that you should understand
# before enabling:
# http://tools.ietf.org/html/rfc6819#section-4.4.2
# http://tools.ietf.org/html/rfc6819#section-4.4.3
#
grant_flows %w(authorization_code password client_credentials)
# Under some circumstances you might want to have applications auto-approved,
# so that the user skips the authorization step.
# For example if dealing with a trusted application.
skip_authorization do |resource_owner, client|
client.application.superapp?
end
# WWW-Authenticate Realm (default "Doorkeeper").
# realm "Doorkeeper"
end

View File

@@ -0,0 +1,5 @@
if String.method_defined?(:blank_as?)
class String
alias_method :blank?, :blank_as?
end
end

View File

@@ -0,0 +1,3 @@
if ENV['FFMPEG_BINARY'].present?
FFMPEG.ffmpeg_binary = ENV['FFMPEG_BINARY']
end

View File

@@ -0,0 +1,4 @@
# Be sure to restart your server when you modify this file.
# Configure sensitive parameters which will be filtered from the log file.
Rails.application.config.filter_parameters += [:password, :private_key, :public_key, :otp_attempt]

View File

@@ -0,0 +1,24 @@
Rails.application.configure do
config.x.http_client_proxy = {}
if ENV['http_proxy'].present?
proxy = URI.parse(ENV['http_proxy'])
raise "Unsupported proxy type: #{proxy.scheme}" unless %w(http https).include? proxy.scheme
raise "No proxy host" unless proxy.host
host = proxy.host
host = host[1...-1] if host[0] == '[' # for IPv6 address
config.x.http_client_proxy[:proxy] = { proxy_address: host, proxy_port: proxy.port, proxy_username: proxy.user, proxy_password: proxy.password }.compact
end
config.x.access_to_hidden_service = ENV['ALLOW_ACCESS_TO_HIDDEN_SERVICE'] == 'true'
end
module Goldfinger
def self.finger(uri, opts = {})
to_hidden = /\.(onion|i2p)(:\d+)?$/.match(uri)
raise GabSocial::HostValidationError, 'Instance does not support hidden service connections' if !Rails.configuration.x.access_to_hidden_service && to_hidden
opts = { ssl: !to_hidden, headers: {} }.merge(Rails.configuration.x.http_client_proxy).merge(opts)
opts[:headers]['User-Agent'] ||= GabSocial::Version.user_agent
Goldfinger::Client.new(uri, opts).finger
end
end

View File

@@ -0,0 +1,5 @@
HttpLog.configure do |config|
config.logger = Rails.logger
config.color = { color: :yellow }
config.compact_log = true
end

View File

@@ -0,0 +1,21 @@
# Be sure to restart your server when you modify this file.
# Add new inflection rules using the following format. Inflections
# are locale specific, and you may define rules for as many different
# locales as you wish. All of these examples are active by default:
# ActiveSupport::Inflector.inflections(:en) do |inflect|
# inflect.plural /^(ox)$/i, '\1en'
# inflect.singular /^(ox)en/i, '\1'
# inflect.irregular 'person', 'people'
# inflect.uncountable %w( fish sheep )
# end
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.acronym 'StatsD'
inflect.acronym 'OEmbed'
inflect.acronym 'OStatus'
inflect.acronym 'ActivityPub'
inflect.acronym 'PubSubHubbub'
inflect.acronym 'ActivityStreams'
inflect.acronym 'JsonLd'
end

View File

@@ -0,0 +1,18 @@
# frozen_string_literal: true
instrumentation_hostname = ENV.fetch('INSTRUMENTATION_HOSTNAME') { 'localhost' }
ActiveSupport::Notifications.subscribe(/process_action.action_controller/) do |*args|
event = ActiveSupport::Notifications::Event.new(*args)
controller = event.payload[:controller]
action = event.payload[:action]
format = event.payload[:format] || 'all'
format = 'all' if format == '*/*'
status = event.payload[:status]
key = "#{controller}.#{action}.#{format}.#{instrumentation_hostname}"
ActiveSupport::Notifications.instrument :performance, action: :measure, measurement: "#{key}.total_duration", value: event.duration
ActiveSupport::Notifications.instrument :performance, action: :measure, measurement: "#{key}.db_time", value: event.payload[:db_runtime]
ActiveSupport::Notifications.instrument :performance, action: :measure, measurement: "#{key}.view_time", value: event.payload[:view_runtime]
ActiveSupport::Notifications.instrument :performance, measurement: "#{key}.status.#{status}"
end

View File

@@ -0,0 +1,3 @@
# frozen_string_literal: true
require_relative '../../lib/json_ld/security'

View File

@@ -0,0 +1,7 @@
# frozen_string_literal: true
Kaminari.configure do |config|
config.default_per_page = 40
config.window = 1
config.outer_window = 1
end

View File

@@ -0,0 +1,4 @@
# Be sure to restart your server when you modify this file.
Mime::Type.register 'application/json', :json, %w(text/x-json application/jsonrequest application/jrd+json application/activity+json application/ld+json)
Mime::Type.register 'text/xml', :xml, %w(application/xml application/atom+xml application/xrd+xml)

View File

@@ -0,0 +1 @@
Oj.default_options = { mode: :compat, time_format: :ruby, use_to_json: true }

View File

@@ -0,0 +1,65 @@
Rails.application.config.middleware.use OmniAuth::Builder do
# Vanilla omniauth stategies
end
Devise.setup do |config|
# Devise omniauth strategies
options = {}
options[:redirect_at_sign_in] = ENV['OAUTH_REDIRECT_AT_SIGN_IN'] == 'true'
# CAS strategy
if ENV['CAS_ENABLED'] == 'true'
cas_options = options
cas_options[:url] = ENV['CAS_URL'] if ENV['CAS_URL']
cas_options[:host] = ENV['CAS_HOST'] if ENV['CAS_HOST']
cas_options[:port] = ENV['CAS_PORT'] if ENV['CAS_PORT']
cas_options[:ssl] = ENV['CAS_SSL'] == 'true' if ENV['CAS_SSL']
cas_options[:service_validate_url] = ENV['CAS_VALIDATE_URL'] if ENV['CAS_VALIDATE_URL']
cas_options[:callback_url] = ENV['CAS_CALLBACK_URL'] if ENV['CAS_CALLBACK_URL']
cas_options[:logout_url] = ENV['CAS_LOGOUT_URL'] if ENV['CAS_LOGOUT_URL']
cas_options[:login_url] = ENV['CAS_LOGIN_URL'] if ENV['CAS_LOGIN_URL']
cas_options[:uid_field] = ENV['CAS_UID_FIELD'] || 'user' if ENV['CAS_UID_FIELD']
cas_options[:ca_path] = ENV['CAS_CA_PATH'] if ENV['CAS_CA_PATH']
cas_options[:disable_ssl_verification] = ENV['CAS_DISABLE_SSL_VERIFICATION'] == 'true'
cas_options[:uid_key] = ENV['CAS_UID_KEY'] || 'user'
cas_options[:name_key] = ENV['CAS_NAME_KEY'] || 'name'
cas_options[:email_key] = ENV['CAS_EMAIL_KEY'] || 'email'
cas_options[:nickname_key] = ENV['CAS_NICKNAME_KEY'] || 'nickname'
cas_options[:first_name_key] = ENV['CAS_FIRST_NAME_KEY'] || 'firstname'
cas_options[:last_name_key] = ENV['CAS_LAST_NAME_KEY'] || 'lastname'
cas_options[:location_key] = ENV['CAS_LOCATION_KEY'] || 'location'
cas_options[:image_key] = ENV['CAS_IMAGE_KEY'] || 'image'
cas_options[:phone_key] = ENV['CAS_PHONE_KEY'] || 'phone'
config.omniauth :cas, cas_options
end
# SAML strategy
if ENV['SAML_ENABLED'] == 'true'
saml_options = options
saml_options[:assertion_consumer_service_url] = ENV['SAML_ACS_URL'] if ENV['SAML_ACS_URL']
saml_options[:issuer] = ENV['SAML_ISSUER'] if ENV['SAML_ISSUER']
saml_options[:idp_sso_target_url] = ENV['SAML_IDP_SSO_TARGET_URL'] if ENV['SAML_IDP_SSO_TARGET_URL']
saml_options[:idp_sso_target_url_runtime_params] = ENV['SAML_IDP_SSO_TARGET_PARAMS'] if ENV['SAML_IDP_SSO_TARGET_PARAMS'] # FIXME: Should be parsable Hash
saml_options[:idp_cert] = ENV['SAML_IDP_CERT'] if ENV['SAML_IDP_CERT']
saml_options[:idp_cert_fingerprint] = ENV['SAML_IDP_CERT_FINGERPRINT'] if ENV['SAML_IDP_CERT_FINGERPRINT']
saml_options[:idp_cert_fingerprint_validator] = ENV['SAML_IDP_CERT_FINGERPRINT_VALIDATOR'] if ENV['SAML_IDP_CERT_FINGERPRINT_VALIDATOR'] # FIXME: Should be Lambda { |fingerprint| }
saml_options[:name_identifier_format] = ENV['SAML_NAME_IDENTIFIER_FORMAT'] if ENV['SAML_NAME_IDENTIFIER_FORMAT']
saml_options[:request_attributes] = {}
saml_options[:certificate] = ENV['SAML_CERT'] if ENV['SAML_CERT']
saml_options[:private_key] = ENV['SAML_PRIVATE_KEY'] if ENV['SAML_PRIVATE_KEY']
saml_options[:security] = {}
saml_options[:security][:want_assertions_signed] = ENV['SAML_SECURITY_WANT_ASSERTION_SIGNED'] == 'true'
saml_options[:security][:want_assertions_encrypted] = ENV['SAML_SECURITY_WANT_ASSERTION_ENCRYPTED'] == 'true'
saml_options[:security][:assume_email_is_verified] = ENV['SAML_SECURITY_ASSUME_EMAIL_IS_VERIFIED'] == 'true'
saml_options[:attribute_statements] = {}
saml_options[:attribute_statements][:uid] = [ENV['SAML_ATTRIBUTES_STATEMENTS_UID']] if ENV['SAML_ATTRIBUTES_STATEMENTS_UID']
saml_options[:attribute_statements][:email] = [ENV['SAML_ATTRIBUTES_STATEMENTS_EMAIL']] if ENV['SAML_ATTRIBUTES_STATEMENTS_EMAIL']
saml_options[:attribute_statements][:full_name] = [ENV['SAML_ATTRIBUTES_STATEMENTS_FULL_NAME']] if ENV['SAML_ATTRIBUTES_STATEMENTS_FULL_NAME']
saml_options[:attribute_statements][:first_name] = [ENV['SAML_ATTRIBUTES_STATEMENTS_FIRST_NAME']] if ENV['SAML_ATTRIBUTES_STATEMENTS_FIRST_NAME']
saml_options[:attribute_statements][:last_name] = [ENV['SAML_ATTRIBUTES_STATEMENTS_LAST_NAME']] if ENV['SAML_ATTRIBUTES_STATEMENTS_LAST_NAME']
saml_options[:attribute_statements][:verified] = [ENV['SAML_ATTRIBUTES_STATEMENTS_VERIFIED']] if ENV['SAML_ATTRIBUTES_STATEMENTS_VERIFIED']
saml_options[:attribute_statements][:verified_email] = [ENV['SAML_ATTRIBUTES_STATEMENTS_VERIFIED_EMAIL']] if ENV['SAML_ATTRIBUTES_STATEMENTS_VERIFIED_EMAIL']
saml_options[:uid_attribute] = ENV['SAML_UID_ATTRIBUTE'] if ENV['SAML_UID_ATTRIBUTE']
config.omniauth :saml, saml_options
end
end

View File

@@ -0,0 +1,8 @@
require 'open-uri'
module OpenURI
def self.redirectable?(uri1, uri2) # :nodoc:
uri1.scheme.casecmp(uri2.scheme).zero? ||
(/\A(?:http|https|ftp)\z/i =~ uri1.scheme && /\A(?:http|https|ftp)\z/i =~ uri2.scheme)
end
end

View File

View File

@@ -0,0 +1,86 @@
# frozen_string_literal: true
Paperclip.options[:read_timeout] = 60
Paperclip.interpolates :filename do |attachment, style|
return attachment.original_filename if style == :original
[basename(attachment, style), extension(attachment, style)].delete_if(&:blank?).join('.')
end
Paperclip::Attachment.default_options.merge!(
use_timestamp: false,
path: ':class/:attachment/:id_partition/:style/:filename',
storage: :fog
)
if ENV['S3_ENABLED'] == 'true'
require 'aws-sdk-s3'
s3_region = ENV.fetch('S3_REGION') { 'us-east-1' }
s3_protocol = ENV.fetch('S3_PROTOCOL') { 'https' }
s3_hostname = ENV.fetch('S3_HOSTNAME') { "s3-#{s3_region}.amazonaws.com" }
Paperclip::Attachment.default_options.merge!(
storage: :s3,
s3_protocol: s3_protocol,
s3_host_name: s3_hostname,
s3_headers: {
'Cache-Control' => 'public, max-age=315576000, immutable',
},
s3_permissions: ENV.fetch('S3_PERMISSION') { 'public-read' },
s3_region: s3_region,
s3_credentials: {
bucket: ENV['S3_BUCKET'],
access_key_id: ENV['AWS_ACCESS_KEY_ID'],
secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'],
},
s3_options: {
signature_version: ENV.fetch('S3_SIGNATURE_VERSION') { 'v4' },
http_open_timeout: 5,
http_read_timeout: 5,
http_idle_timeout: 5,
}
)
if ENV.has_key?('S3_ENDPOINT')
Paperclip::Attachment.default_options[:s3_options].merge!(
endpoint: ENV['S3_ENDPOINT'],
force_path_style: true
)
Paperclip::Attachment.default_options[:url] = ':s3_path_url'
end
if ENV.has_key?('S3_ALIAS_HOST') || ENV.has_key?('S3_CLOUDFRONT_HOST')
Paperclip::Attachment.default_options.merge!(
url: ':s3_alias_url',
s3_host_alias: ENV['S3_ALIAS_HOST'] || ENV['S3_CLOUDFRONT_HOST']
)
end
elsif ENV['SWIFT_ENABLED'] == 'true'
require 'fog/openstack'
Paperclip::Attachment.default_options.merge!(
fog_credentials: {
provider: 'OpenStack',
openstack_username: ENV['SWIFT_USERNAME'],
openstack_project_id: ENV['SWIFT_PROJECT_ID'],
openstack_project_name: ENV['SWIFT_TENANT'],
openstack_tenant: ENV['SWIFT_TENANT'], # Some OpenStack-v2 ignores project_name but needs tenant
openstack_api_key: ENV['SWIFT_PASSWORD'],
openstack_auth_url: ENV['SWIFT_AUTH_URL'],
openstack_domain_name: ENV.fetch('SWIFT_DOMAIN_NAME') { 'default' },
openstack_region: ENV['SWIFT_REGION'],
openstack_cache_ttl: ENV.fetch('SWIFT_CACHE_TTL') { 60 },
},
fog_directory: ENV['SWIFT_CONTAINER'],
fog_host: ENV['SWIFT_OBJECT_URL'],
fog_public: true
)
else
Paperclip::Attachment.default_options.merge!(
storage: :filesystem,
use_timestamp: true,
path: (ENV['PAPERCLIP_ROOT_PATH'] || ':rails_root/public/system') + '/:class/:attachment/:id_partition/:style/:filename',
url: (ENV['PAPERCLIP_ROOT_URL'] || '/system') + '/:class/:attachment/:id_partition/:style/:filename',
)
end

View File

@@ -0,0 +1,6 @@
require_relative '../../lib/gabsocial/premailer_webpack_strategy'
Premailer::Rails.config.merge!(remove_ids: true,
adapter: :nokogiri,
generate_text_part: false,
strategies: [PremailerWebpackStrategy])

View File

@@ -0,0 +1,109 @@
# frozen_string_literal: true
require 'doorkeeper/grape/authorization_decorator'
class Rack::Attack
class Request
def authenticated_token
return @token if defined?(@token)
@token = Doorkeeper::OAuth::Token.authenticate(
Doorkeeper::Grape::AuthorizationDecorator.new(self),
*Doorkeeper.configuration.access_token_methods
)
end
def remote_ip
@remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s
end
def authenticated_user_id
authenticated_token&.resource_owner_id
end
def unauthenticated?
!authenticated_user_id
end
def api_request?
path.start_with?('/api')
end
def web_request?
!api_request?
end
def paging_request?
params['page'].present? || params['min_id'].present? || params['max_id'].present? || params['since_id'].present?
end
end
PROTECTED_PATHS = %w(
/auth/sign_in
/auth
/auth/password
).freeze
PROTECTED_PATHS_REGEX = Regexp.union(PROTECTED_PATHS.map { |path| /\A#{Regexp.escape(path)}/ })
# Always allow requests from localhost
# (blocklist & throttles are skipped)
Rack::Attack.safelist('allow from localhost') do |req|
# Requests are allowed if the return value is truthy
req.remote_ip == '127.0.0.1' || req.remote_ip == '::1'
end
throttle('throttle_authenticated_api', limit: 300, period: 5.minutes) do |req|
req.authenticated_user_id if req.api_request?
end
throttle('throttle_unauthenticated_api', limit: 300, period: 5.minutes) do |req|
req.remote_ip if req.api_request? && req.unauthenticated?
end
throttle('throttle_api_media', limit: 30, period: 30.minutes) do |req|
req.authenticated_user_id if req.post? && req.path.start_with?('/api/v1/media')
end
throttle('throttle_media_proxy', limit: 30, period: 30.minutes) do |req|
req.remote_ip if req.path.start_with?('/media_proxy')
end
throttle('throttle_api_sign_up', limit: 5, period: 30.minutes) do |req|
req.remote_ip if req.post? && req.path == '/api/v1/accounts'
end
# Throttle paging, as it is mainly used for public pages and AP collections
throttle('throttle_authenticated_paging', limit: 300, period: 15.minutes) do |req|
req.authenticated_user_id if req.paging_request?
end
throttle('throttle_unauthenticated_paging', limit: 300, period: 15.minutes) do |req|
req.remote_ip if req.paging_request? && req.unauthenticated?
end
API_DELETE_REBLOG_REGEX = /\A\/api\/v1\/statuses\/[\d]+\/unreblog/.freeze
API_DELETE_STATUS_REGEX = /\A\/api\/v1\/statuses\/[\d]+/.freeze
throttle('throttle_api_delete', limit: 30, period: 30.minutes) do |req|
req.authenticated_user_id if (req.post? && req.path =~ API_DELETE_REBLOG_REGEX) || (req.delete? && req.path =~ API_DELETE_STATUS_REGEX)
end
throttle('protected_paths', limit: 25, period: 5.minutes) do |req|
req.remote_ip if req.post? && req.path =~ PROTECTED_PATHS_REGEX
end
self.throttled_response = lambda do |env|
now = Time.now.utc
match_data = env['rack.attack.match_data']
headers = {
'Content-Type' => 'application/json',
'X-RateLimit-Limit' => match_data[:limit].to_s,
'X-RateLimit-Remaining' => '0',
'X-RateLimit-Reset' => (now + (match_data[:period] - now.to_i % match_data[:period])).iso8601(6),
}
[429, headers, [{ error: I18n.t('errors.429') }.to_json]]
end
end

View File

@@ -0,0 +1,6 @@
ActiveSupport::Notifications.subscribe(/rack_attack/) do |_name, _start, _finish, _request_id, payload|
req = payload[:request]
next unless [:throttle, :blacklist].include? req.env['rack.attack.match_type']
Rails.logger.info("Rate limit hit (#{req.env['rack.attack.match_type']}): #{req.ip} #{req.request_method} #{req.fullpath}")
end

View File

@@ -0,0 +1,14 @@
# frozen_string_literal: true
redis_connection = Redis.new(
url: ENV['REDIS_URL'],
driver: :hiredis
)
namespace = ENV.fetch('REDIS_NAMESPACE') { nil }
if namespace
Redis.current = Redis::Namespace.new(namespace, redis: redis_connection)
else
Redis.current = redis_connection
end

View File

@@ -0,0 +1,5 @@
# frozen_string_literal: true
Rails.application.configure do
config.x.max_session_activations = ENV['MAX_SESSION_ACTIVATIONS'] || 10
end

View File

@@ -0,0 +1,3 @@
# Be sure to restart your server when you modify this file.
Rails.application.config.session_store :cookie_store, key: '_gabsocial_session', secure: (Rails.env.production? || ENV['LOCAL_HTTPS'] == 'true')

View File

@@ -0,0 +1,22 @@
# frozen_string_literal: true
namespace = ENV.fetch('REDIS_NAMESPACE') { nil }
redis_params = { url: ENV['REDIS_URL'] }
if namespace
redis_params[:namespace] = namespace
end
Sidekiq.configure_server do |config|
config.redis = redis_params
config.server_middleware do |chain|
chain.add SidekiqErrorHandler
end
end
Sidekiq.configure_client do |config|
config.redis = redis_params
end
Sidekiq::Logging.logger.level = ::Logger.const_get(ENV.fetch('RAILS_LOG_LEVEL', 'info').upcase.to_s)

View File

@@ -0,0 +1,206 @@
# Use this setup block to configure all options available in SimpleForm.
module AppendComponent
def append(wrapper_options = nil)
@append ||= begin
options[:append].to_s.html_safe if options[:append].present?
end
end
end
SimpleForm.include_component(AppendComponent)
SimpleForm.setup do |config|
# Wrappers are used by the form builder to generate a
# complete input. You can remove any component from the
# wrapper, change the order or even add your own to the
# stack. The options given below are used to wrap the
# whole input.
config.wrappers :default, class: :input, hint_class: :field_with_hint, error_class: :field_with_errors do |b|
## Extensions enabled by default
# Any of these extensions can be disabled for a
# given input by passing: `f.input EXTENSION_NAME => false`.
# You can make any of these extensions optional by
# renaming `b.use` to `b.optional`.
# Determines whether to use HTML5 (:email, :url, ...)
# and required attributes
b.use :html5
# Calculates placeholders automatically from I18n
# You can also pass a string as f.input placeholder: "Placeholder"
b.use :placeholder
## Optional extensions
# They are disabled unless you pass `f.input EXTENSION_NAME => true`
# to the input. If so, they will retrieve the values from the model
# if any exists. If you want to enable any of those
# extensions by default, you can change `b.optional` to `b.use`.
# Calculates maxlength from length validations for string inputs
b.optional :maxlength
# Calculates pattern from format validations for string inputs
b.optional :pattern
# Calculates min and max from length validations for numeric inputs
b.optional :min_max
# Calculates readonly automatically from readonly attributes
b.optional :readonly
## Inputs
b.use :input
b.use :hint, wrap_with: { tag: :span, class: :hint }
b.use :error, wrap_with: { tag: :span, class: :error }
## full_messages_for
# If you want to display the full error message for the attribute, you can
# use the component :full_error, like:
#
# b.use :full_error, wrap_with: { tag: :span, class: :error }
end
config.wrappers :with_label, class: [:input, :with_label], hint_class: :field_with_hint, error_class: :field_with_errors do |b|
b.use :html5
b.wrapper tag: :div, class: :label_input do |ba|
ba.use :label
ba.wrapper tag: :div, class: :label_input__wrapper do |bb|
bb.use :input
bb.optional :append, wrap_with: { tag: :div, class: 'label_input__append' }
end
end
b.use :hint, wrap_with: { tag: :span, class: :hint }
b.use :error, wrap_with: { tag: :span, class: :error }
end
config.wrappers :with_floating_label, class: [:input, :with_floating_label], hint_class: :field_with_hint, error_class: :field_with_errors do |b|
b.use :html5
b.use :label_input, wrap_with: { tag: :div, class: :label_input }
b.use :hint, wrap_with: { tag: :span, class: :hint }
b.use :error, wrap_with: { tag: :span, class: :error }
end
config.wrappers :with_block_label, class: [:input, :with_block_label], hint_class: :field_with_hint, error_class: :field_with_errors do |b|
b.use :html5
b.use :label
b.use :hint, wrap_with: { tag: :span, class: :hint }
b.use :input
b.use :error, wrap_with: { tag: :span, class: :error }
end
# The default wrapper to be used by the FormBuilder.
config.default_wrapper = :default
# Define the way to render check boxes / radio buttons with labels.
# Defaults to :nested for bootstrap config.
# inline: input + label
# nested: label > input
config.boolean_style = :nested
# Default class for buttons
config.button_class = 'btn'
# Method used to tidy up errors. Specify any Rails Array method.
# :first lists the first message for each field.
# Use :to_sentence to list all errors for each field.
# config.error_method = :first
# Default tag used for error notification helper.
config.error_notification_tag = :div
# CSS class to add for error notification helper.
config.error_notification_class = 'error_notification'
# ID to add for error notification helper.
# config.error_notification_id = nil
# Series of attempts to detect a default label method for collection.
# config.collection_label_methods = [ :to_label, :name, :title, :to_s ]
# Series of attempts to detect a default value method for collection.
# config.collection_value_methods = [ :id, :to_s ]
# You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none.
# config.collection_wrapper_tag = nil
# You can define the class to use on all collection wrappers. Defaulting to none.
# config.collection_wrapper_class = nil
# You can wrap each item in a collection of radio/check boxes with a tag,
# defaulting to :span.
# config.item_wrapper_tag = :span
# You can define a class to use in all item wrappers. Defaulting to none.
# config.item_wrapper_class = nil
# How the label text should be generated altogether with the required text.
config.label_text = lambda { |label, required, explicit_label| "#{label} #{required}" }
# You can define the class to use on all labels. Default is nil.
# config.label_class = nil
# You can define the default class to be used on forms. Can be overridden
# with `html: { :class }`. Defaulting to none.
# config.default_form_class = nil
# You can define which elements should obtain additional classes
# config.generate_additional_classes_for = [:wrapper, :label, :input]
# Whether attributes are required by default (or not). Default is true.
# config.required_by_default = true
# Tell browsers whether to use the native HTML5 validations (novalidate form option).
# These validations are enabled in SimpleForm's internal config but disabled by default
# in this configuration, which is recommended due to some quirks from different browsers.
# To stop SimpleForm from generating the novalidate option, enabling the HTML5 validations,
# change this configuration to true.
config.browser_validations = false
# Collection of methods to detect if a file type was given.
# config.file_methods = [ :mounted_as, :file?, :public_filename ]
# Custom mappings for input types. This should be a hash containing a regexp
# to match as key, and the input type that will be used when the field name
# matches the regexp as value.
# config.input_mappings = { /count/ => :integer }
# Custom wrappers for input types. This should be a hash containing an input
# type as key and the wrapper that will be used for all inputs with specified type.
# config.wrapper_mappings = { string: :prepend }
# Namespaces where SimpleForm should look for custom input classes that
# override default inputs.
# config.custom_inputs_namespaces << "CustomInputs"
# Default priority for time_zone inputs.
# config.time_zone_priority = nil
# Default priority for country inputs.
# config.country_priority = nil
# When false, do not use translations for labels.
# config.translate_labels = true
# Automatically discover new inputs in Rails' autoload path.
# config.inputs_discovery = true
# Cache SimpleForm inputs discovery
# config.cache_discovery = !Rails.env.development?
# Default class for inputs
# config.input_class = nil
# Define the default class of the input wrapper of the boolean input.
config.boolean_label_class = 'checkbox'
# Defines if the default input wrapper class should be included in radio
# collection wrappers.
# config.include_default_input_wrapper_class = true
# Defines which i18n scope will be used in Simple Form.
# config.i18n_scope = 'simple_form'
end

View File

@@ -0,0 +1,5 @@
# frozen_string_literal: true
Rails.application.configure do
config.x.single_user_mode = ENV['SINGLE_USER_MODE'] == 'true'
end

View File

@@ -0,0 +1,15 @@
# frozen_string_literal: true
if ENV['STATSD_ADDR'].present?
host, port = ENV['STATSD_ADDR'].split(':')
statsd = ::Statsd.new(host, port)
statsd.namespace = ENV.fetch('STATSD_NAMESPACE') { ['GabSocial', Rails.env].join('.') }
::NSA.inform_statsd(statsd) do |informant|
informant.collect(:action_controller, :web)
informant.collect(:active_record, :db)
informant.collect(:active_support_cache, :cache)
informant.collect(:sidekiq, :sidekiq)
end
end

View File

@@ -0,0 +1,4 @@
require 'stoplight'
Stoplight::Light.default_data_store = Stoplight::DataStore::Redis.new(Redis.current)
Stoplight::Light.default_notifiers = [Stoplight::Notifier::Logger.new(Rails.logger)]

View File

@@ -0,0 +1,3 @@
# frozen_string_literal: true
StrongMigrations.start_after = 20170924022025

View File

@@ -0,0 +1,3 @@
# frozen_string_literal: true
ActionController::Base.log_warning_on_csrf_failure = false

View File

@@ -0,0 +1,11 @@
module Rack
class Request
def trusted_proxy?(ip)
if Rails.application.config.action_dispatch.trusted_proxies.nil?
super
else
Rails.application.config.action_dispatch.trusted_proxies.any? { |proxy| proxy === ip }
end
end
end
end

View File

@@ -0,0 +1,41 @@
module Twitter
class Regex
REGEXEN[:valid_general_url_path_chars] = /[^\p{White_Space}<>\(\)\?]/iou
REGEXEN[:valid_url_path_ending_chars] = /[^\p{White_Space}\(\)\?!\*"'「」<>;:=\,\.\$%\[\]~&\|@]|(?:#{REGEXEN[:valid_url_balanced_parens]})/iou
REGEXEN[:valid_url_balanced_parens] = /
\(
(?:
#{REGEXEN[:valid_general_url_path_chars]}+
|
# allow one nested level of balanced parentheses
(?:
#{REGEXEN[:valid_general_url_path_chars]}*
\(
#{REGEXEN[:valid_general_url_path_chars]}+
\)
#{REGEXEN[:valid_general_url_path_chars]}*
)
)
\)
/iox
REGEXEN[:valid_url_path] = /(?:
(?:
#{REGEXEN[:valid_general_url_path_chars]}*
(?:#{REGEXEN[:valid_url_balanced_parens]} #{REGEXEN[:valid_general_url_path_chars]}*)*
#{REGEXEN[:valid_url_path_ending_chars]}
)|(?:#{REGEXEN[:valid_general_url_path_chars]}+\/)
)/iox
REGEXEN[:valid_url] = %r{
( # $1 total match
(#{REGEXEN[:valid_url_preceding_chars]}) # $2 Preceding character
( # $3 URL
((https?|dat|dweb|ipfs|ipns|ssb|gopher):\/\/)? # $4 Protocol (optional)
(#{REGEXEN[:valid_domain]}) # $5 Domain(s)
(?::(#{REGEXEN[:valid_port_number]}))? # $6 Port number (optional)
(/#{REGEXEN[:valid_url_path]}*)? # $7 URL Path and anchor
(\?#{REGEXEN[:valid_url_query_chars]}*#{REGEXEN[:valid_url_query_ending_chars]})? # $8 Query String
)
)
}iox
end
end

View File

@@ -0,0 +1,16 @@
# frozen_string_literal: true
Rails.application.configure do
# You can generate the keys using the following command (first is the private key, second is the public one)
# You should only generate this once per instance. If you later decide to change it, all push subscription will
# be invalidated, requiring the users to access the website again to resubscribe.
#
# Generate with `rake gabsocial:webpush:generate_vapid_key` task (`docker-compose run --rm web rake gabsocial:webpush:generate_vapid_key` if you use docker compose)
#
# For more information visit https://rossta.net/blog/using-the-web-push-api-with-vapid.html
if Rails.env.production?
config.x.vapid_private_key = ENV['VAPID_PRIVATE_KEY']
config.x.vapid_public_key = ENV['VAPID_PUBLIC_KEY']
end
end

View File

@@ -0,0 +1,14 @@
# Be sure to restart your server when you modify this file.
# This file contains settings for ActionController::ParamsWrapper which
# is enabled by default.
# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
ActiveSupport.on_load(:action_controller) do
wrap_parameters format: [:json]
end
# To enable root element in JSON for ActiveRecord objects.
# ActiveSupport.on_load(:active_record) do
# self.include_root_in_json = true
# end

View File

@@ -0,0 +1,13 @@
---
ar:
activerecord:
errors:
models:
account:
attributes:
username:
invalid: فقط حروف و أرقام و سطور سفلية
status:
attributes:
reblog:
taken: المنشور موجود مِن قبل

View File

@@ -0,0 +1,9 @@
---
ast:
activerecord:
errors:
models:
account:
attributes:
username:
invalid: namái lletres, númberos y guiones baxos

View File

@@ -0,0 +1,16 @@
---
ca:
activerecord:
attributes:
status:
owned_poll: Enquesta
errors:
models:
account:
attributes:
username:
invalid: només lletres, números i subratllats
status:
attributes:
reblog:
taken: de l'estat ja existeix

View File

@@ -0,0 +1,17 @@
---
co:
activerecord:
attributes:
poll:
expires_at: Fine
options: Scelte
errors:
models:
account:
attributes:
username:
invalid: solu lettere, numeri è liniette basse
status:
attributes:
reblog:
taken: di u statutu esista digià

View File

@@ -0,0 +1,17 @@
---
cs:
activerecord:
attributes:
poll:
expires_at: Uzávěrka
options: Volby
errors:
models:
account:
attributes:
username:
invalid: pouze písmena, číslice a podtržítka
status:
attributes:
reblog:
taken: příspěvku již existuje

View File

@@ -0,0 +1,13 @@
---
cy:
activerecord:
errors:
models:
account:
attributes:
username:
invalid: dim ond llythrennau, rhifau a tanlinellau
status:
attributes:
reblog:
taken: o'r statws yn bodoli'n barod

View File

@@ -0,0 +1,16 @@
---
da:
activerecord:
attributes:
status:
owned_poll: Afstemning
errors:
models:
account:
attributes:
username:
invalid: kun tal, bogstaver og understreger
status:
attributes:
reblog:
taken: af allerede eksisterende status

View File

@@ -0,0 +1,19 @@
---
de:
activerecord:
attributes:
poll:
expires_at: Frist
options: Wahlen
status:
owned_poll: Umfrage
errors:
models:
account:
attributes:
username:
invalid: nur Buchstaben, Ziffern und Unterstriche
status:
attributes:
reblog:
taken: des Status existiert schon

View File

@@ -0,0 +1,19 @@
---
el:
activerecord:
attributes:
poll:
expires_at: Προθεσμία
options: Επιλογές
status:
owned_poll: Ψηφοφορία
errors:
models:
account:
attributes:
username:
invalid: μόνο γράμματα, αριθμοί και κάτω παύλες
status:
attributes:
reblog:
taken: της κατάστασης ήδη υπάρχει

View File

@@ -0,0 +1,17 @@
---
en:
activerecord:
attributes:
poll:
expires_at: Deadline
options: Choices
errors:
models:
account:
attributes:
username:
invalid: only letters, numbers and underscores
status:
attributes:
reblog:
taken: of status already exists

View File

@@ -0,0 +1,13 @@
---
es:
activerecord:
errors:
models:
account:
attributes:
username:
invalid: solo letras, números y guiones bajos
status:
attributes:
reblog:
taken: del estado ya existe

View File

@@ -0,0 +1,13 @@
---
eu:
activerecord:
errors:
models:
account:
attributes:
username:
invalid: letrak, zenbakiak eta gidoi baxuak besterik ez
status:
attributes:
reblog:
taken: mezu honentzat bazegoen aurretik

View File

@@ -0,0 +1,16 @@
---
fa:
activerecord:
attributes:
status:
owned_poll: رأی‌گیری
errors:
models:
account:
attributes:
username:
invalid: تنها حروف، اعداد، و زیرخط
status:
attributes:
reblog:
taken: نوشته‌ها وجود دارند

View File

@@ -0,0 +1,17 @@
---
fr:
activerecord:
attributes:
poll:
expires_at: Date butoir
options: Choix
errors:
models:
account:
attributes:
username:
invalid: seulement des lettres, des nombres et des tirets bas
status:
attributes:
reblog:
taken: du statut existe déjà

View File

@@ -0,0 +1,19 @@
---
gl:
activerecord:
attributes:
poll:
expires_at: Caducidade
options: Opcións
status:
owned_poll: Sondaxe
errors:
models:
account:
attributes:
username:
invalid: só letras, números e liñas baixas
status:
attributes:
reblog:
taken: do estado xa existe

View File

@@ -0,0 +1,13 @@
---
he:
activerecord:
errors:
models:
account:
attributes:
username:
invalid: ספרות, אותיות לטיניות וקו תחתי בלבד
status:
attributes:
reblog:
taken: של החצרוץ כבר קיים

View File

@@ -0,0 +1,13 @@
---
id:
activerecord:
errors:
models:
account:
attributes:
username:
invalid: hanya boleh berisi huruf, angka, dan underscore
status:
attributes:
reblog:
taken: status sudah ada

View File

@@ -0,0 +1,13 @@
---
it:
activerecord:
errors:
models:
account:
attributes:
username:
invalid: solo lettere, numeri e trattino basso
status:
attributes:
reblog:
taken: dello stato esiste già

View File

@@ -0,0 +1,19 @@
---
ja:
activerecord:
attributes:
poll:
expires_at: 期限
options: 項目
user:
email: メールアドレス
errors:
models:
account:
attributes:
username:
invalid: アルファベット・数字・アンダーバーの組み合わせで入力してください
status:
attributes:
reblog:
taken: は既にブーストされています

View File

@@ -0,0 +1,13 @@
---
ka:
activerecord:
errors:
models:
account:
attributes:
username:
invalid: მხოლოდ ასოები, ციფრები და "ქვედა-ტირე"
status:
attributes:
reblog:
taken: სტატუსის უკვე არსებობს

View File

@@ -0,0 +1,17 @@
---
kk:
activerecord:
attributes:
poll:
expires_at: Дедлайн
options: Таңдаулар
errors:
models:
account:
attributes:
username:
invalid: тек әріптер, сандар және асты сызылған таңбалар
status:
attributes:
reblog:
taken: жазбасы бұрыннан бар

View File

@@ -0,0 +1,19 @@
---
nl:
activerecord:
attributes:
poll:
expires_at: Deadline
options: Keuzes
status:
owned_poll: Poll
errors:
models:
account:
attributes:
username:
invalid: alleen letters, nummers en underscores
status:
attributes:
reblog:
taken: van toot bestaat al

View File

@@ -0,0 +1,13 @@
---
'no':
activerecord:
errors:
models:
account:
attributes:
username:
invalid: bare bokstaver, tall og understreker
status:
attributes:
reblog:
taken: av status eksisterer allerede

View File

@@ -0,0 +1,17 @@
---
oc:
activerecord:
attributes:
poll:
expires_at: Data limita
options: Opcions
errors:
models:
account:
attributes:
username:
invalid: solament letras, nombres e tirets basses
status:
attributes:
reblog:
taken: de lestatut existís ja

View File

@@ -0,0 +1,16 @@
---
pl:
activerecord:
attributes:
user:
email: adres e-mail
errors:
models:
account:
attributes:
username:
invalid: może składać się tylko z liter, cyfr i podkreślników
status:
attributes:
reblog:
taken: status już istnieje

View File

@@ -0,0 +1,17 @@
---
pt-BR:
activerecord:
attributes:
poll:
expires_at: Expira em
options: Escolhas
errors:
models:
account:
attributes:
username:
invalid: apenas letras, números e underscores
status:
attributes:
reblog:
taken: do status já existe

View File

@@ -0,0 +1,13 @@
---
pt:
activerecord:
errors:
models:
account:
attributes:
username:
invalid: apenas letras, números e underscores
status:
attributes:
reblog:
taken: do status já existe

View File

@@ -0,0 +1,17 @@
---
ru:
activerecord:
attributes:
poll:
expires_at: Крайний срок
options: Варианты
errors:
models:
account:
attributes:
username:
invalid: только буквы, цифры и символ подчёркивания
status:
attributes:
reblog:
taken: статуса уже существует

View File

@@ -0,0 +1,19 @@
---
sk:
activerecord:
attributes:
poll:
expires_at: Trvá do
options: Voľby
status:
owned_poll: Anketa
errors:
models:
account:
attributes:
username:
invalid: iba písmená, číslice a podčiarkovníky
status:
attributes:
reblog:
taken: príspevku už existuje

View File

@@ -0,0 +1,13 @@
---
sl:
activerecord:
errors:
models:
account:
attributes:
username:
invalid: samo črke, številke in podčrtaji
status:
attributes:
reblog:
taken: od statusa že obstajajo

View File

@@ -0,0 +1,13 @@
---
sq:
activerecord:
errors:
models:
account:
attributes:
username:
invalid: vetëm shkronja, numra dhe nënvija
status:
attributes:
reblog:
taken: e gjendjes ekziston tashmë

View File

@@ -0,0 +1,13 @@
---
sr-Latn:
activerecord:
errors:
models:
account:
attributes:
username:
invalid: samo slova, brojevi i donje crte
status:
attributes:
reblog:
taken: statusa već postoji

View File

@@ -0,0 +1,13 @@
---
sr:
activerecord:
errors:
models:
account:
attributes:
username:
invalid: само слова, бројеви и доње црте
status:
attributes:
reblog:
taken: статуса већ постоји

View File

@@ -0,0 +1,9 @@
---
sv:
activerecord:
errors:
models:
account:
attributes:
username:
invalid: enbart bokstäver, siffror och understreck

View File

@@ -0,0 +1,13 @@
---
th:
activerecord:
errors:
models:
account:
attributes:
username:
invalid: ตัวอักษร, ตัวเลข และขีดล่างเท่านั้น
status:
attributes:
reblog:
taken: มีสถานะอยู่แล้ว

View File

@@ -0,0 +1,13 @@
---
tr:
activerecord:
errors:
models:
account:
attributes:
username:
invalid: sadece harfler, sayılar ve alt çizgiler
status:
attributes:
reblog:
taken: durum zaten var

View File

@@ -0,0 +1,13 @@
---
uk:
activerecord:
errors:
models:
account:
attributes:
username:
invalid: тільки букви, цифри та нижні підкреслювання
status:
attributes:
reblog:
taken: статусу вже існує

View File

@@ -0,0 +1,13 @@
---
zh-CN:
activerecord:
errors:
models:
account:
attributes:
username:
invalid: 只能使用字母、数字和下划线
status:
attributes:
reblog:
taken: 已经被转嘟过

View File

@@ -0,0 +1,13 @@
---
zh-HK:
activerecord:
errors:
models:
account:
attributes:
username:
invalid: 只能使用字母、數字和下劃線
status:
attributes:
reblog:
taken: 已經被轉推過

View File

@@ -0,0 +1,15 @@
zh_Hant:
activerecord:
attributes:
status:
owned_poll: 投票
errors:
models:
account:
attributes:
username:
invalid: 只允許使用字母、數字和底線
status:
attributes:
reblog:
taken: 的嘟文已經存在

929
config/locales/ar.yml Normal file
View File

@@ -0,0 +1,929 @@
---
ar:
about:
about_hashtag_html: هذه تبويقات متاحة للجمهور تحتوي على الكلمات الدلالية <strong>#%{hashtag}</strong>. يمكنك التفاعل معها إن كان لديك حساب في أي مكان على الفديفرس.
about_gabsocial_html: ماستدون شبكة إجتماعية مبنية على أسُس بروتوكولات برمجيات الويب الحرة و مفتوحة المصدر. و هو لامركزي تمامًا كالبريد الإلكتروني.
about_this: عن مثيل الخادوم هذا
administered_by: 'يُديره :'
api: واجهة برمجة التطبيقات
apps: تطبيقات الأجهزة المحمولة
contact: للتواصل معنا
contact_missing: لم يتم تعيينه
contact_unavailable: غير متوفر
documentation: الدليل
extended_description_html: |
<h3>مكان جيد للقواعد</h3>
<p>لم يتم بعد إدخال الوصف الطويل.</p>
generic_description: "%{domain} هو سيرفر من بين سيرفرات الشبكة"
hosted_on: ماستدون مُستضاف على %{domain}
learn_more: تعلم المزيد
privacy_policy: سياسة الخصوصية
source_code: الشفرة المصدرية
status_count_after:
few: منشورات
many: منشورات
one: منشور
other: منشورات
two: منشورات
zero: منشورات
status_count_before: نشروا
terms: شروط الخدمة
user_count_after:
few: مستخدمين
many: مستخدمين
one: مستخدم
other: مستخدمين
two: مستخدمين
zero: مستخدمين
user_count_before: يستضيف
what_is_gabsocial: ما هو ماستدون ؟
accounts:
choices_html: 'توصيات %{name} :'
follow: إتبع
followers:
few: متابِعون
many: متابِعون
one: متابِع
other: متابِعون
two: متابِعون
zero: متابِعون
following: مُتابَع
joined: انضم·ت في %{date}
last_active: آخر نشاط
link_verified_on: تم التحقق مِن مالك هذا الرابط بتاريخ %{date}
media: الوسائط
moved_html: "%{name} إنتقلَ إلى %{new_profile_link} :"
network_hidden: إنّ المعطيات غير متوفرة
nothing_here: لا يوجد أي شيء هنا !
people_followed_by: الأشخاص الذين يتبعهم %{name}
people_who_follow: الأشخاص الذين يتبعون %{name}
pin_errors:
following: يجب أن تكون مِن متابعي حساب الشخص الذي تريد إبرازه
posts:
few: تبويقات
many: تبويقات
one: تبويق
other: تبويقات
two: تبويقات
zero: تبويقات
posts_tab_heading: تبويقات
posts_with_replies: التبويقات و الردود
reserved_username: إسم المستخدم محجوز
roles:
admin: المدير
bot: روبوت
moderator: مُشرِف
unfollow: إلغاء المتابعة
admin:
account_actions:
action: تنفيذ الاجراء
title: اتخاذ إجراء إشراف على %{acct}
account_moderation_notes:
create: إترك ملاحظة
created_msg: تم إنشاء ملاحظة الإشراف بنجاح !
delete: حذف
destroyed_msg: تم تدمير ملاحظة الإشراف بنجاح !
accounts:
are_you_sure: متأكد ؟
avatar: الصورة الرمزية
by_domain: النطاق
change_email:
changed_msg: تم تعديل عنوان البريد الإلكتروني الخاص بالحساب بنجاح !
current_email: عنوان البريد الإلكتروني الحالي
label: تعديل عنوان البريد الإلكتروني
new_email: عنوان البريد الإلكتروني الجديد
submit: تعديل عنوان البريد الإلكتروني
title: تعديل عنوان البريد الإلكتروني الخاص بـ %{username}
confirm: تأكيد
confirmed: مؤكَّد
confirming: التأكد
deleted: تمت إزالته
demote: إنزال الرُتبة الوظيفية
disable: تعطيل
disable_two_factor_authentication: تعطيل المصادقة بخطوتين
disabled: معطَّل
display_name: عرض الإسم
domain: النطاق
edit: تعديل
email: البريد الإلكتروني
email_status: حالة البريد الإلكتروني
enable: تفعيل
enabled: مفعَّل
feed_url: عنوان رابط التغذية
followers: المتابِعون
followers_url: عنوان رابط المتابِعين
follows: يتابع
header: الرأسية
inbox_url: رابط صندوق الوارد
invited_by: تمت دعوته مِن طرف
ip: عنوان الإيبي
joined: انضم
location:
all: الكل
local: المحلي
remote: عن بُعد
title: الموقع
login_status: وضع الدخول
media_attachments: الوسائط المرفقة
memorialize: تحويل الحساب إلى صفحة ذكرى
moderation:
active: نشِط
all: الكل
silenced: تم كتمه
suspended: مُجَمَّد
title: الإشراف
moderation_notes: ملاحظات الإشراف
most_recent_activity: آخر نشاط حديث
most_recent_ip: أحدث عنوان إيبي
no_limits_imposed: مِن دون حدود مشروطة
not_subscribed: غير مشترك
outbox_url: رابط صندوق الصادر
perform_full_suspension: تعليق الحساب
profile_url: رابط الملف الشخصي
promote: ترقية
protocol: البروتوكول
public: عمومي
push_subscription_expires: انتهاء الاشتراك ”PuSH“
redownload: تحديث الصفحة الشخصية
remove_avatar: حذف الصورة الرمزية
remove_header: حذف الرأسية
resend_confirmation:
already_confirmed: هذا المستخدم مؤكد بالفعل
send: أعد إرسال رسالة البريد الالكتروني الخاصة بالتأكيد
success: تم إرسال رسالة التأكيد بنجاح!
reset: إعادة التعيين
reset_password: إعادة ضبط كلمة السر
resubscribe: إعادة الإشتراك
role: الصلاحيات
roles:
admin: مدير
moderator: مشرف
staff: الفريق
user: مستخدِم
salmon_url: عنوان رابط سالمون Salmon
search: البحث
shared_inbox_url: رابط الصندوق المُشترَك للبريد الوارد
show:
created_reports: البلاغات التي أنشأها هذا الحساب
targeted_reports: الشكاوي التي أُنشِأت مِن طرف الآخَرين
silence: كتم
silenced: تم كتمه
statuses: المنشورات
subscribe: اشترك
suspended: تم تعليقه
title: الحسابات
unconfirmed_email: البريد الإلكتروني غير مؤكد
undo_silenced: رفع الصمت
undo_suspension: إلغاء تعليق الحساب
unsubscribe: إلغاء الاشتراك
username: إسم المستخدم
warn: تحذير
web: الويب
action_logs:
actions:
assigned_to_self_report: قام %{name} بتعيين التقرير %{target} لأنفسهم
change_email_user: غيّر %{name} عنوان البريد الإلكتروني للمستخدم %{target}
confirm_user: "%{name} قد قام بتأكيد عنوان البريد الإلكتروني لـ %{target}"
create_account_warning: قام %{name} بإرسال تحذير إلى %{target}
create_custom_emoji: "%{name} قام برفع إيموجي جديد %{target}"
create_domain_block: "%{name} قام بحجب نطاق %{target}"
create_email_domain_block: "%{name} قد قام بحظر نطاق البريد الإلكتروني %{target}"
demote_user: "%{name} قد قام بإنزال الرتبة الوظيفية لـ %{target}"
destroy_custom_emoji: قام %{name} بحذف الإيموجي %{target}
destroy_domain_block: "%{name} قام بإلغاء الحجب عن النطاق %{target}"
destroy_email_domain_block: قام %{name} بإضافة نطاق البريد الإلكتروني %{target} إلى اللائحة البيضاء
destroy_status: لقد قام %{name} بحذف منشور %{target}
disable_2fa_user: "%{name} لقد قام بتعطيل ميزة المصادقة بخطوتين للمستخدم %{target}"
disable_custom_emoji: "%{name} قام بتعطيل الإيموجي %{target}"
disable_user: "%{name} لقد قام بتعطيل تسجيل الدخول للمستخدِم %{target}"
enable_custom_emoji: "%{name} قام بتنشيط الإيموجي %{target}"
enable_user: لقد قام %{name} بتنشيط تسجيل الدخول للمستخدِم %{target}
memorialize_account: لقد قام %{name} بتحويل حساب %{target} إلى صفحة تذكارية
promote_user: "%{name} قام بترقية المستخدم %{target}"
remove_avatar_user: تمت إزالة %{name} الصورة الرمزية %{target}
reopen_report: تمت إعادة فتح الشكوى %{name} %{target}
reset_password_user: "%{name} لقد قام بإعادة تعيين الكلمة السرية الخاصة بـ %{target}"
resolve_report: قام %{name} بحل الشكوى %{target}
silence_account: لقد قام %{name} بكتم حساب %{target}
suspend_account: لقد قام %{name} بتعليق حساب %{target}
unassigned_report: "%{name} تقرير غير معتمد %{target}"
unsilence_account: لقد قام %{name} بإلغاء الكتم عن حساب %{target}
unsuspend_account: لقد قام %{name} بإلغاء التعليق المفروض على حساب %{target}
update_custom_emoji: "%{name} قام بتحديث الإيموجي %{target}"
update_status: لقد قام %{name} بتحديث منشور %{target}
deleted_status: "(منشور محذوف)"
title: سِجلّ التفتيش و المعاينة
custom_emojis:
by_domain: النطاق
copied_msg: تم إنشاء نسخة محلية للإيموجي بنجاح
copy: نسخ
copy_failed_msg: فشلت عملية إنشاء نسخة محلية لهذا الإيموجي
created_msg: تم إنشاء الإيموجي بنجاح !
delete: حذف
destroyed_msg: تمت عملية تدمير الإيموجي بنجاح !
disable: تعطيل
disabled_msg: تمت عملية تعطيل ذلك الإيموجي بنجاح
emoji: إيموجي
enable: تفعيل
enabled_msg: تم تنشيط ذاك الإيموجي بنجاح
image_hint: ملف PNG إلى غاية حجم 50 ك.ب
listed: مُدرَج
new:
title: إضافة إيموجي خاص جديد
overwrite: إعادة الكتابة
shortcode: الترميز المُصَغّر
shortcode_hint: على الأقل حرفين، و فقط رموز أبجدية عددية و أسطر سفلية
title: الإيموجي الخاصة
unlisted: غير مدرج
update_failed_msg: تعذرت عملية تحذيث ذاك الإيموجي
updated_msg: تم تحديث الإيموجي بنجاح !
upload: رفع
dashboard:
backlog: الأعمال المتراكمة
config: الإعداد
feature_deletions: الحسابات المحذوفة
feature_invites: روابط الدعوات
feature_profile_directory: دليل الحسابات
feature_registrations: التسجيلات
feature_relay: المُرحّل الفديرالي
features: الميّزات
hidden_service: الفيديرالية مع الخدمات الخفية
open_reports: فتح الشكاوي
recent_users: أحدث المستخدِمين
search: البحث النصي الكامل
single_user_mode: وضع المستخدِم الأوحد
software: البرنامج
space: المساحة المستخدَمة
title: لوح المراقبة
total_users: إجمالي المستخدِمين
trends: المؤشرات
week_interactions: تفاعُلات هذا الأسبوع
week_users_active: نشط هذا الأسبوع
week_users_new: مستخدِمين هذا الأسبوع
domain_blocks:
add_new: إضافة حجب جديد لنطاق
created_msg: إنّ حجب النطاق حيز التشغيل
destroyed_msg: تم إلغاء الحجب المفروض على النطاق
domain: النطاق
new:
create: إنشاء حظر
hint: لن تمنع كتلة المجال إنشاء إدخالات حساب في قاعدة البيانات ، ولكنها ستطبق طرق الإشراف المحددة بأثر رجعي وتلقائي على هذه الحسابات.
severity:
desc_html: "<strong>Silence</strong> سيجعل مشاركات الحساب غير مرئية لأي شخص لا يتبعها. <strong>Suspend</strong> سيزيل كل محتوى الحساب ووسائطه وبيانات ملفه الشخصي. Use <strong>None</strong> إذا كنت تريد فقط رفض ملفات الوسائط."
noop: لا شيء
silence: كتم
suspend: تعليق
title: حجب نطاق جديد
reject_media: رفض ملفات الوسائط
reject_media_hint: يزيل ملفات الوسائط المخزنة محليًا ويرفض تنزيل أي ملفات في المستقبل. غير ذي صلة للتعليق
reject_reports: رفض التقارير
severity:
silence: تم كتمه
suspend: تم تعليقه
show:
affected_accounts:
few: "%{count} حسابات معنية في قاعدة البيانات"
many: "%{count} حسابات معنية في قاعدة البيانات"
one: حساب واحد معني في قاعدة البيانات
other: "%{count} حسابات معنية في قاعدة البيانات"
two: حسابات معنية في قاعدة البيانات
zero: حسابات معنية في قاعدة البيانات
retroactive:
silence: إلغاء الكتم عن كافة الحسابات المتواجدة على هذا النطاق
suspend: إلغاء التعليق المفروض على كافة حسابات هذا النطاق
title: رفع حظر النطاق عن %{domain}
undo: إلغاء
undo: إلغاء حجب النطاق
email_domain_blocks:
add_new: إضافة
created_msg: لقد دخل حظر نطاق البريد الإلكتروني حيّز الخدمة
delete: حذف
destroyed_msg: تم حذف نطاق البريد الإلكتروني من اللائحة السوداء بنجاح
domain: النطاق
new:
create: إضافة نطاق
title: إضافة نطاق بريد جديد إلى اللائحة السوداء
title: القائمة السوداء للبريد الإلكتروني
followers:
back_to_account: العودة إلى الحساب
title: "%{acct} مُتابِعون"
instances:
by_domain: النطاق
delivery_available: التسليم متوفر
known_accounts:
few: "%{count} حسابات معروفة"
many: "%{count} حسابات معروفة"
one: حساب معروف %{count}
other: "%{count} حسابات معروفة"
two: "%{count} حسابات معروفة"
zero: "%{count} حسابات معروفة"
moderation:
all: كافتها
title: الإشراف
title: الفديرالية
total_blocked_by_us: المحجوبة مِن طرفنا
total_followed_by_them: يُتابِعونها
total_followed_by_us: التي نُتابِعها
total_reported: تقارير عنهم
total_storage: الوسائط المُرفَقة
invites:
deactivate_all: تعطيلها كافة
filter:
all: الكل
available: المتوفرة
expired: المنتهي صلاحيتها
title: التصفية
title: الدعوات
relays:
add_new: إضافة مُرحّل جديد
delete: حذف
disable: تعطيل
disabled: مُعطَّل
enable: تشغيل
enable_hint: عندما تقوم بتنشيط هذه الميزة، سوف يشترك خادومك في جميع التبويقات القادمة مِن هذا المُرحِّل و سيشرع كذلك بإرسال كافة التبويقات العمومية إليه.
enabled: مُشغَّل
inbox_url: رابط المُرحّل
pending: في انتظار تسريح المُرحِّل
save_and_enable: حفظ وتشغيل
setup: إعداد اتصال بمُرحّل
status: الحالة
title: المُرحّلات
report_notes:
created_msg: تم إنشاء ملاحظة الشكوى بنجاح!
destroyed_msg: تم حذف ملاحظة الشكوى بنجاح!
reports:
account:
note: ملحوظة
report: تقرير
action_taken_by: تم اتخاذ الإجراء مِن طرف
are_you_sure: هل أنت متأكد ؟
assign_to_self: عين لي
assigned: تعين رئيس
comment:
none: لا شيء
created_at: ذكرت
mark_as_resolved: إعتبار الشكوى كمحلولة
mark_as_unresolved: علام كغير محلولة
notes:
create: اضف ملاحظة
create_and_resolve: الحل مع ملاحظة
create_and_unresolve: إعادة فتح مع ملاحظة
delete: حذف
placeholder: قم بوصف الإجراءات التي تم اتخاذها أو أي تحديثات أخرى ذات علاقة …
reopen: إعادة فتح الشكوى
report: 'الشكوى #%{id}'
reported_account: حساب مُبلّغ عنه
reported_by: أبلغ عنه من طرف
resolved: معالجة
resolved_msg: تم حل تقرير بنجاح!
status: الحالة
title: الشكاوي
unassign: إلغاء تعيين
unresolved: غير معالجة
updated_at: محدث
settings:
activity_api_enabled:
desc_html: عدد المنشورات المحلية و المستخدمين النشطين و التسجيلات الأسبوعية الجديدة
title: نشر مُجمل الإحصائيات عن نشاط المستخدمين
bootstrap_timeline_accounts:
desc_html: افصل بين أسماء المستخدمين المتعددة بواسطة الفاصلة. استعمل الحسابات المحلية والمفتوحة فقط. الافتراضي عندما تكون فارغة كل المسؤولين المحليين.
title: الإشتراكات الإفتراضية للمستخدمين الجدد
contact_information:
email: البريد الإلكتروني المهني
username: الإتصال بالمستخدِم
custom_css:
desc_html: يقوم بتغيير المظهر بواسطة سي أس أس يُحمَّل على كافة الصفحات
title: سي أس أس مخصص
hero:
desc_html: معروض على الصفحة الأولى. لا يقل عن 600 × 100 بكسل. عند عدم التعيين ، تعود الصورة إلى النسخة المصغرة على سبيل المثال
title: الصورة الرأسية
peers_api_enabled:
desc_html: أسماء النطاقات التي إلتقى بها مثيل الخادوم على البيئة الموحَّدة فيديفرس
title: نشر عدد مثيلات الخوادم التي تم مصادفتها
preview_sensitive_media:
desc_html: روابط المُعَاينة على مواقع الويب الأخرى ستقوم بعرض صُوَر مصغّرة حتى و إن كانت الوسائط حساسة
title: إظهار الصور الحساسة في مُعاينات أوبن غراف
profile_directory:
desc_html: السماح للمستخدمين الكشف عن حساباتهم
title: تفعيل سجل الملفات الشخصية
registrations:
closed_message:
desc_html: يتم عرضه على الصفحة الرئيسية عندما يتم غلق تسجيل الحسابات الجديدة. يمكنكم إستخدام علامات الأيتش تي أم أل HTML
title: رسالة التسجيلات المقفلة
deletion:
desc_html: السماح لأي مستخدم إغلاق حسابه
title: السماح بحذف الحسابات
min_invite_role:
disabled: لا أحد
title: المستخدِمون المصرح لهم لإرسال الدعوات
show_known_fediverse_at_about_page:
desc_html: عند التثبت ، سوف تظهر toots من جميع fediverse المعروفة على عرض مسبق. وإلا فإنه سيعرض فقط toots المحلية.
title: إظهار الفيديفرس الموحَّد في خيط المُعايَنة
show_staff_badge:
desc_html: عرض شارة الموظفين على صفحة المستخدم
title: إظهار شارة الموظفين
site_description:
desc_html: فقرة تمهيدية على الصفحة الأولى. صف ميزات خادوم ماستدون هذا و ما يميّزه عن الآخرين. يمكنك استخدام علامات HTML ، ولا سيما <code>&lt;a&gt;</code> و <code>&lt;em&gt;</code>.
title: وصف مثيل الخادوم
site_description_extended:
desc_html: مكان جيد لمدونة قواعد السلوك والقواعد والإرشادات وغيرها من الأمور التي تحدد حالتك. يمكنك استخدام علامات HTML
title: الوصف المُفصّل للموقع
site_short_description:
desc_html: يتم عرضه في لوحة جانبية و في البيانات الوصفية. قم بوصف ماستدون و ما يميز هذا السيرفر عن الآخرين في فقرة موجزة. إن تركت الحقل فارغا فسوف يتم عرض الوصف الإفتراضي لمثيل الخادوم.
title: مقدمة وصفية قصيرة عن مثيل الخادوم
site_terms:
desc_html: يمكنك كتابة سياسة الخصوصية الخاصة بك ، شروط الخدمة أو غيرها من القوانين. يمكنك استخدام علامات HTML
title: شروط الخدمة المخصصة
site_title: إسم مثيل الخادم
thumbnail:
desc_html: يستخدم للعروض السابقة عبر Open Graph و API. 1200x630px موصى به
title: الصورة الرمزية المصغرة لمثيل الخادوم
timeline_preview:
desc_html: عرض الخيط العمومي على صفحة الإستقبال
title: مُعاينة الخيط العام
title: إعدادات الموقع
statuses:
back_to_account: العودة إلى صفحة الحساب
batch:
delete: حذف
nsfw_off: تعيينه كمنشور غير حساس
nsfw_on: تعيينه كمنشور حساس
failed_to_execute: خطأ في التفعيل
media:
title: الوسائط
no_media: لا تحتوي على وسائط
no_status_selected: لم يطرأ أي تغيير على أي منشور بما أنه لم يتم اختيار أي واحد
title: منشورات الحساب
with_media: تحتوي على وسائط
subscriptions:
callback_url: عاود الاتصال بالعنوان
confirmed: مؤكَّد
expires_in: تنتهي مدة صلاحيتها في
last_delivery: آخر إيداع
title: WebSub
topic: الموضوع
tags:
accounts: الحسابات
hidden: المخفية
hide: الإخفاء عن سجل الحسابات
name: الوسم
title: الوسوم
unhide: إظهاره في سجل حسابات المستخدمين
visible: ظاهر
title: الإدارة
warning_presets:
add_new: إضافة واحد جديد
delete: حذف
edit: تعديل
edit_preset: تعديل نموذج التحذير
title: إدارة نماذج التحذير
admin_mailer:
new_report:
body: قام %{reporter} بالإبلاغ عن %{target}
body_remote: أبلغ شخص ما من %{domain} عن %{target}
subject: تقرير جديد ل%{instance} (#%{id})
application_mailer:
notification_preferences: تعديل خيارات البريد الإلكتروني
salutation: "%{name}،"
settings: 'تغيير تفضيلات البريد الإلكتروني : %{link}'
view: 'View:'
view_profile: عرض الملف الشخصي
view_status: عرض المنشور
applications:
created: تم إنشاء التطبيق بنجاح
destroyed: تم حذف التطبيق بنجاح
invalid_url: إن الرابط المقدم غير صالح
regenerate_token: إعادة توليد رمز النفاذ
token_regenerated: تم إعادة إنشاء الرمز الوصول بنجاح
warning: كن حذرا مع هذه البيانات. لا تقم أبدا بمشاركتها مع الآخَرين !
your_token: رمز نفاذك
auth:
change_password: الكلمة السرية
confirm_email: تأكيد عنوان البريد الإلكتروني
delete_account: حذف حساب
delete_account_html: إن كنت ترغب في حذف حسابك يُمكنك <a href="%{path}">المواصلة هنا</a>. سوف يُطلَبُ منك التأكيد قبل الحذف.
didnt_get_confirmation: لم تتلق تعليمات التأكيد ؟
forgot_password: نسيت كلمة المرور ؟
invalid_reset_password_token: رمز إعادة تعيين كلمة المرور غير صالح أو منتهي الصلاحية. يرجى طلب واحد جديد.
login: تسجيل الدخول
logout: خروج
migrate_account: الإنتقال إلى حساب آخر
migrate_account_html: إن كنت ترغب في تحويل هذا الحساب نحو حساب آخَر، يُمكِنُك <a href="%{path}">إعداده هنا</a>.
or_log_in_with: أو قم بتسجيل الدخول بواسطة
providers:
cas: CAS
saml: SAML
register: إنشاء حساب
resend_confirmation: إعادة إرسال تعليمات التأكيد
reset_password: إعادة تعيين كلمة المرور
security: الأمان
set_new_password: إدخال كلمة مرور جديدة
authorize_follow:
already_following: أنت تتابع بالفعل هذا الحساب
error: يا للأسف، وقع هناك خطأ إثر عملية البحث عن الحساب عن بعد
follow: إتبع
follow_request: 'لقد قمت بإرسال طلب متابعة إلى :'
following: 'مرحى ! أنت الآن تتبع :'
post_follow:
close: أو يمكنك إغلاق هذه النافذة.
return: عرض الملف الشخصي للمستخدم
web: واصل إلى الويب
title: إتباع %{acct}
datetime:
distance_in_words:
about_x_hours: "%{count}سا"
about_x_months: "%{count} شهر"
about_x_years: "%{count} سنة"
almost_x_years: "%{count} سنوات"
half_a_minute: الآن
less_than_x_minutes: "%{count} د"
less_than_x_seconds: الآن
over_x_years: "%{count} سنين"
x_days: "%{count} أيام"
x_minutes: "%{count}د"
x_months: "%{count} شه"
x_seconds: "%{count}ث"
deletes:
bad_password_msg: محاولة جيدة يا هاكرز ! كلمة السر خاطئة
confirm_password: قم بإدخال كلمتك السرية الحالية للتحقق من هويتك
proceed: حذف حساب
success_msg: تم حذف حسابك بنجاح
warning_title: توافر المحتوى المنشور و المبعثَر
directories:
directory: سِجلّ الحسابات
enabled: إنّ حسابك الآن ضمن فهرس المستخدِمين.
explanation: استكشف مستخدِمين آخرين حسب المواضيع التي تهمهم
explore_gabsocial: استكشف %{title}
people:
few: "%{count} شخص"
many: "%{count} شخص"
one: "%{count} شخص"
other: "%{count} شخص"
two: "%{count} شخص"
zero: "%{count} شخص"
errors:
'403': ليس لك الصلاحيات الكافية لعرض هذه الصفحة.
'404': إنّ الصفحة التي تبحث عنها لا وجود لها أصلا.
'410': إنّ الصفحة التي تبحث عنها لم تعد موجودة.
'422':
content: فشل التحقق الآمن. ربما منعتَ كعكات الكوكيز ؟
title: فشِل التحقق الآمن
'429': طلبات كثيرة جدا
'500':
content: نحن متأسفون، لقد حدث خطأ ما مِن جانبنا.
title: هذه الصفحة خاطئة
noscript_html: يرجى تفعيل الجافا سكريبت لاستخدام تطبيق الويب لماستدون، أو عِوض ذلك قوموا بتجريب إحدى <a href="%{apps_path}">التطبيقات الأصلية</a> الدّاعمة لماستدون على منصّتكم.
exports:
archive_takeout:
date: التاريخ
download: تنزيل نسخة لحسابك
hint_html: بإمكانك طلب نسخة كاملة لـ <strong>كافة تبويقاتك و الوسائط التي قمت بنشرها</strong>. البيانات المُصدَّرة ستكون محفوظة على شكل نسق ActivityPub و باستطاعتك قراءتها بأي برنامج يدعم هذا النسق. يُمكنك طلب نسخة كل 7 أيام.
in_progress: عملية جمع نسخة لبيانات حسابك جارية …
request: طلب نسخة لحسابك
size: الحجم
blocks: قمت بحظر
csv: CSV
domain_blocks: النطاقات المحظورة
follows: أنت تتبع
lists: القوائم
mutes: قُمتَ بكتم
storage: ذاكرة التخزين
featured_tags:
add_new: إضافة واحد
filters:
contexts:
home: الخيط الزمني الرئيسي
notifications: الإخطارات
public: الخيوط الزمنية العامة
thread: المحادثات
edit:
title: تعديل عامل التصفية
errors:
invalid_context: لم تقم بتحديد أي مجال أو أنّ المجال غير صالح
invalid_irreversible: إلّا مجالات الإشعارات و الخيط الرئيسي معنية بالتصفية اللارجعية
index:
delete: إزالة
title: عوامل التصفية
new:
title: إضافة عامل تصفية جديد
footer:
developers: المطورون
more: المزيد …
resources: الموارد
generic:
changes_saved_msg: تم حفظ التعديلات بنجاح !
copy: نسخ
save_changes: حفظ التغييرات
validation_errors:
few: هناك شيء ما ليس على ما يرام! يُرجى مراجعة الأخطاء الـ %{count} أدناه
many: هناك شيء ما ليس على ما يرام! يُرجى مراجعة الأخطاء الـ %{count} أدناه
one: هناك شيء ما ليس على ما يرام! يُرجى مراجعة الخطأ أدناه
other: هناك شيء ما ليس على ما يرام! يُرجى مراجعة الأخطاء الـ %{count} أدناه
two: هناك شيء ما ليس على ما يرام! يُرجى مراجعة الأخطاء الـ %{count} أدناه
zero: هناك شيء ما ليس على ما يرام! يُرجى مراجعة الأخطاء الـ %{count} أدناه
imports:
modes:
merge: دمج
merge_long: الإبقاء علي التسجيلات الحالية وإضافة الجديدة
overwrite: إعادة الكتابة
overwrite_long: استبدال التسجيلات الحالية بالجديدة
preface: بإمكانك استيراد بيانات قد قُمتَ بتصديرها مِن مثيل خادوم آخَر، كقوائم المستخدِمين الذين كنتَ تتابِعهم أو قُمتَ بحظرهم.
success: تم تحميل بياناتك بنجاح وسيتم معالجتها في الوقت المناسب
types:
blocking: قائمة المحظورين
domain_blocking: قائمة النطاقات المحظورة
following: قائمة المستخدمين المتبوعين
muting: قائمة الكتم
upload: تحميل
in_memoriam_html: في ذكرى.
invites:
delete: تعطيل
expired: إنتهت صلاحيتها
expires_in:
'1800': 30 دقيقة
'21600': 6 ساعات
'3600': ساعة
'43200': 12 ساعة
'604800': أسبوع
'86400': يوم واحد
expires_in_prompt: أبدا
generate: توليد
invited_by: 'تمت دعوتك من طرف :'
max_uses:
few: "%{count} استخدامات"
many: "%{count} استخدامات"
one: استخدام واحد
other: "%{count} استخدامات"
two: استخدامات
zero: استخدامات
max_uses_prompt: بلا حدود
prompt: توليد و مشاركة روابط للسماح للآخَرين بالنفاذ إلى مثيل الخادوم هذا
table:
expires_at: تنتهي مدة صلاحيتها في
uses: عدد الاستخدامات
title: دعوة أشخاص
lists:
errors:
limit: لقد بلغت الحد الأقصى للقوائم
media_attachments:
validations:
images_and_video: ليس بالإمكان إرفاق فيديو في منشور يحتوي مسبقا على صور
too_many: لا يمكن إرفاق أكثر من 4 ملفات
migrations:
acct: username@domain للحساب الجديد
currently_redirecting: 'تم تحويل رابط ملفك الشخصي إلى :'
proceed: حفظ
updated_msg: تم تحديث إعدادات ترحيل حسابك بنجاح !
moderation:
title: الإشراف
notification_mailer:
digest:
action: معاينة كافة الإشعارات
body: هذا هو مُلَخَّص الرسائل التي فاتتك وذلك منذ آخر زيارة لك في %{since}
mention: "%{name} أشار إليك في :"
new_followers_summary:
few: رائع، لقد قام بمتابَعتك %{count} مُتابِعون جُدد أثناء فترة غيابك عن ماستدون!
many: رائع، لقد قام بمتابَعتك %{count} مُتابِعون جُدد أثناء فترة غيابك عن ماستدون!
one: و لقد تحصّلتَ كذلك على مُتابِع آخَر بينما كنتَ غائبًا! هذا شيء رائع!
other: رائع، لقد قام بمتابَعتك %{count} مُتابِعون جُدد أثناء فترة غيابك عن ماستدون!
two: رائع، لقد قام بمتابَعتك %{count} مُتابِعون جُدد أثناء فترة غيابك عن ماستدون!
zero: رائع، لقد قام بمتابَعتك %{count} مُتابِعون جُدد أثناء فترة غيابك عن ماستدون!
subject:
few: "%{count} إشعارات جديدة منذ آخر زيارة لك إلى \U0001F418"
many: "%{count} إشعارات جديدة منذ آخر زيارة لك إلى \U0001F418"
one: "إشعار واحد 1 منذ آخر زيارة لك لـ \U0001F418"
other: "%{count} إشعارات جديدة منذ آخر زيارة لك إلى \U0001F418"
two: "إشعارات جديدة منذ آخر زيارة لك إلى \U0001F418"
zero: "إشعارات جديدة منذ آخر زيارة لك إلى \U0001F418"
title: أثناء فترة غيابك …
favourite:
body: 'أُعجب %{name} بمنشورك :'
subject: أُعجِب %{name} بمنشورك
title: مفضّلة جديدة
follow:
body: "%{name} من متتبعيك الآن !"
subject: "%{name} من متتبعيك الآن"
title: متابِع جديد
follow_request:
action: إدارة طلبات المتابَعة
body: طلب %{name} متابعتك
subject: 'متابع مُعلّق : %{name}'
title: طلب متابَعة جديد
mention:
action: الرد
body: 'أشار إليك %{name} في :'
subject: لقد قام %{name} بذِكرك
title: إشارة جديدة
reblog:
body: 'قام %{name} بترقية منشورك :'
subject: قام %{name} بترقية منشورك
title: ترقية جديدة
number:
human:
decimal_units:
format: "%n%u"
units:
billion: B
million: M
quadrillion: كواد
thousand: ألف
trillion: T
unit: ''
pagination:
newer: الأحدَث
next: التالي
older: الأقدَم
prev: السابق
truncate: و
preferences:
languages: اللغات
other: إعدادات أخرى
publishing: النشر
web: الويب
remote_follow:
acct: قم بإدخال عنوان حسابك username@domain الذي من خلاله تود النشاط
missing_resource: تعذر العثور على رابط التحويل المطلوب الخاص بحسابك
no_account_html: أليس عندك حساب بعدُ ؟ يُمْكنك <a href='%{sign_up_path}' target='_blank'>التسجيل مِن هنا</a>
proceed: أكمل المتابعة
prompt: 'إنك بصدد متابعة :'
remote_interaction:
favourite:
proceed: المواصلة إلى المفضلة
prompt: 'ترغب في إضافة هذا التبويق إلى مفضلتك:'
reblog:
proceed: المواصلة إلى الترقية
prompt: 'ترغب في ترقية هذا التبويق:'
reply:
proceed: المواصلة إلى الرد
prompt: 'ترغب في الرد على هذا التبويق:'
remote_unfollow:
error: خطأ
title: العنوان
unfollowed: غير متابَع
sessions:
activity: آخر نشاط
browser: المتصفح
browsers:
alipay: أليباي
blackberry: بلاك بيري
chrome: كروم
edge: مايكروسوفت إيدج
electron: إلكترون
firefox: فايرفكس
generic: متصفح مجهول
ie: إنترنت إكسبلورر
micro_messenger: مايكرو ميسنجر
nokia: متصفح Nokia S40 Ovi
opera: أوبرا
otter: أوتر
phantom_js: فانتوم جي آس
qq: متصفح كيوكيو
safari: سفاري
uc_browser: متصفح يوسي براوزر
weibo: وايبو
current_session: الجلسة الحالية
description: "%{browser} على %{platform}"
explanation: ها هي قائمة مُتصفِّحات الويب التي تستخدِم حاليًا حساب ماستدون الخاص بك.
ip: عنوان الإيبي
platforms:
adobe_air: أدوبي إيير
android: أندرويد
blackberry: بلاك بيري
chrome_os: نظام كروم أواس
firefox_os: نظام فايرفكس أواس
ios: نظام آي أواس
linux: لينكس
mac: ماك
other: نظام مجهول
windows: ويندوز
windows_mobile: ويندوز موبايل
windows_phone: ويندوز فون
revoke: إبطال
revoke_success: تم إبطال الجلسة بنجاح
title: الجلسات
settings:
authorized_apps: التطبيقات المرخص لها
back: عودة إلى ماستدون
delete: حذف الحسابات
development: التطوير
edit_profile: تعديل الملف الشخصي
export: تصدير البيانات
import: إستيراد
migrate: تهجير الحساب
notifications: الإخطارات
preferences: التفضيلات
two_factor_authentication: المُصادقة بخُطوَتَيْن
statuses:
attached:
description: 'مُرفَق : %{attached}'
image:
few: "%{count} صور"
many: "%{count} صور"
one: صورة %{count}
other: "%{count} صور"
two: صور
zero: صور
video:
few: "%{count} فيديوهات"
many: "%{count} فيديوهات"
one: فيديو %{count}
other: "%{count} فيديوهات"
two: فيديوهات
zero: فيديوهات
boosted_from_html: تم إعادة ترقيته مِن %{acct_link}
content_warning: 'تحذير عن المحتوى : %{warning}'
disallowed_hashtags:
few: 'يحتوي على وسوم غير مسموح بها: %{tags}'
many: 'يحتوي على وسوم غير مسموح بها: %{tags}'
one: 'يحتوي على وسم غير مسموح به: %{tags}'
other: 'يحتوي على وسوم غير مسموح بها: %{tags}'
two: 'يحتوي على وسوم غير مسموح بها: %{tags}'
zero: 'يحتوي على وسوم غير مسموح بها: %{tags}'
language_detection: اكتشاف اللغة تلقائيا
open_in_web: إفتح في الويب
over_character_limit: تم تجاوز حد الـ %{max} حرف المسموح بها
pin_errors:
limit: لقد بلغت الحد الأقصى للتبويقات المدبسة
ownership: لا يمكن تدبيس تبويق نشره شخص آخر
private: لا يمكن تدبيس تبويق لم يُنشر للعامة
reblog: لا يمكن تثبيت ترقية
show_more: أظهر المزيد
sign_in_to_participate: قم بتسجيل الدخول للمشاركة في هذه المحادثة
title: '%{name} : "%{quote}"'
visibilities:
private: إعرض فقط لمتتبعيك
private_long: إعرضه لمتتبعيك فقط
public: للعامة
public_long: يمكن للجميع رؤيته
unlisted: غير مُدرَج
unlisted_long: يُمكن لأيٍ كان رُؤيتَه و لكن لن يُعرَض على الخيوط العامة
stream_entries:
pinned: تبويق مثبّت
reblogged: رقّاه
sensitive_content: محتوى حساس
terms:
title: شروط الخدمة وسياسة الخصوصية على %{instance}
themes:
contrast: تباين عالٍ
default: ماستدون
gabsocial-light: ماستدون (فاتح)
time:
formats:
default: "%b %d, %Y, %H:%M"
month: "%b %Y"
two_factor_authentication:
code_hint: قم بإدخال الرمز المُوَلّد عبر تطبيق المصادقة للتأكيد
description_html: في حال تفعيل <strong>المصادقة بخطوتين </strong>، فتسجيل الدخول يتطلب منك أن يكون بحوزتك هاتفك النقال قصد توليد الرمز الذي سيتم إدخاله.
disable: تعطيل
enable: تفعيل
enabled: نظام المصادقة بخطوتين مُفعَّل
enabled_success: تم تفعيل المصادقة بخطوتين بنجاح
generate_recovery_codes: توليد رموز الإسترجاع
instructions_html: "<strong>قم بمسح رمز الكيو آر عبر Google Authenticator أو أي تطبيق TOTP على جهازك</strong>. من الآن فصاعدا سوف يقوم ذاك التطبيق بتوليد رموز يجب عليك إدخالها عند تسجيل الدخول."
lost_recovery_codes: تُمكّنك رموز الإسترجاع الإحتاطية مِن استرجاع النفاذ إلى حسابك في حالة فقدان جهازك المحمول. إن ضاعت منك هذه الرموز فبإمكانك إعادة توليدها مِن هنا و إبطال الرموز القديمة.
manual_instructions: 'في حالة تعذّر مسح رمز الكيو آر أو طُلب منك إدخال يدوي، يُمْكِنك إدخال هذا النص السري على التطبيق :'
recovery_codes: النسخ الإحتياطي لرموز الإسترجاع
recovery_codes_regenerated: تم إعادة توليد رموز الإسترجاع الإحتياطية بنجاح
setup: تنشيط
wrong_code: الرمز الذي أدخلته غير صالح ! تحقق من صحة الوقت على الخادم و الجهاز ؟
user_mailer:
backup_ready:
explanation: لقد قمت بطلب نسخة كاملة لحسابك على ماستدون. إنها متوفرة الآن للتنزيل !
subject: نسخة بيانات حسابك جاهزة للتنزيل
title: المغادرة بأرشيف الحساب
warning:
review_server_policies: مراجعة شروط السيرفر
subject:
disable: تم تجميد حسابك %{acct}
none: تحذير إلى %{acct}
suspend: لقد تم تعليق حسابك %{acct}
title:
disable: الحساب مُجمَّد
none: تحذير
suspend: الحساب مُعلَّق
welcome:
edit_profile_action: تهيئة الملف الشخصي
edit_profile_step: يُمكنك·كي تخصيص ملفك الشخصي عن طريق تحميل صورة رمزية ورأسية و بتعديل إسمك·كي العلني وأكثر. و إن أردت·تي معاينة المتابِعين و المتابعات الجُدد قبيل السماح لهم·ن بمتابَعتك فيمكنك·كي تأمين حسابك·كي.
explanation: ها هي بعض النصائح قبل بداية الإستخدام
final_action: اشرَع في النشر
final_step: |-
يمكنك الشروع في النشر في الحين ! حتى و إن لم كنت لا تمتلك متابِعين بعدُ، يمكن للآخرين الإطلاع على منشوراتك الموجهة للجمهور على الخيط المحلي أو إن قمت باستخدام وسوم.
إبدأ بتقديم نفسك باستعمال وسم #introductions.
full_handle: عنوانك الكامل
full_handle_hint: هذا هو ما يجب تقديمه لأصدقائك قصد أن يكون بإمكانهم متابَعتك أو مُراسَلتك حتى و إن كانت حساباتهم على خوادم أخرى.
review_preferences_action: تعديل التفضيلات
subject: أهلًا بك على ماستدون
tip_federated_timeline: الخيط الزمني الفديرالي هو بمثابة شبه نظرة شاملة على شبكة ماستدون. غير أنه لا يشمل إلا على الأشخاص المتابَعين مِن طرف جيرانك و جاراتك، لذا فهذا الخيط لا يعكس كافة الشبكة برُمّتها.
tip_following: أنت تتبع تلقائيا مديري و مديرات الخادم. للعثور على أشخاص مميزين أو قد تهمك حساباتهم بإمكانك الإطلاع على الخيوط المحلية و كذا الفدرالية.
tip_local_timeline: الخيط الزمني المحلي هو بمثابة نظرة سريعة على الأشخاص المتواجدين على %{instance} يمكن اعتبارهم كجيرانك وجاراتك الأقرب إليك!
tips: نصائح
title: أهلاً بك، %{name} !
users:
follow_limit_reached: لا يمكنك متابعة أكثر مِن %{limit} أشخاص
invalid_email: عنوان البريد الإلكتروني غير صالح
invalid_otp_token: رمز المصادقة بخطوتين غير صالح
otp_lost_help_html: إن فقدتَهُما ، يمكنك الإتصال بـ %{email}
seamless_external_login: لقد قمت بتسجيل الدخول عبر خدمة خارجية، إنّ إعدادات الكلمة السرية و البريد الإلكتروني غير متوفرة.
signed_in_as: 'تم تسجيل دخولك بصفة :'
verification:
verification: التحقق

347
config/locales/ast.yml Normal file
View File

@@ -0,0 +1,347 @@
---
ast:
about:
about_gabsocial_html: Gab Social ye una rede social basada en protocolos abiertos y software de códigu llibre. Ye descentralizada, como'l corréu electrónicu.
about_this: Tocante a
administered_by: 'Alministráu por:'
api: API
contact: Contautu
contact_missing: Nun s'afitó
contact_unavailable: N/D
documentation: Documentación
extended_description_html: |
<h3>Un llugar bonu pa les regles</h3>
<p>Entá nun se configuró la descripción estendida.</p>
hosted_on: Gab Social ta agospiáu en %{domain}
learn_more: Deprendi más
source_code: Códigu fonte
status_count_after: estaos
status_count_before: Que crearon
terms: Términos del serviciu
user_count_after:
one: usuariu
other: usuarios
user_count_before: Ye'l llar de
what_is_gabsocial: "¿Qué ye Gab Social?"
accounts:
followers:
one: Xente que te sigue
other: Siguidores
joined: Xunióse en %{date}
moved_html: "%{name} mudóse a %{new_profile_link}:"
network_hidden: Esta información nun ta disponible
nothing_here: "¡Equí nun hai nada!"
people_followed_by: Persones a les que sigue %{name}
people_who_follow: Persones que siguen a %{name}
posts:
one: Toot
other: Toots
posts_tab_heading: Toots
posts_with_replies: Toots y rempuestes
reserved_username: El nome d'usuariu ta acutáu
roles:
bot: Robó
admin:
accounts:
are_you_sure: "¿De xuru?"
avatar: Avatar
by_domain: Dominiu
domain: Dominiu
email: Corréu
followers: Siguidores
ip: IP
location:
local: Llocal
title: Allugamientu
protocol: Protocolu
resend_confirmation:
already_confirmed: Esti usuariu yá ta confirmáu
role: Permisos
roles:
admin: Alministrador
moderator: Llendador
user: Usuariu
statuses: Estaos
title: Cuentes
username: Nome d'usuariu
web: Web
action_logs:
actions:
create_domain_block: "%{name} bloquió'l dominiu %{target}"
disable_custom_emoji: "%{name} desactivó'l fustaxe %{target}"
disable_user: "%{name} desactivó l'aniciu de sesión del usuariu %{target}"
custom_emojis:
by_domain: Dominiu
copy_failed_msg: Nun pudo facese una copia llocal d'esi fustaxe
emoji: Fustaxe
update_failed_msg: Nun pudo anovase esi fustaxe
dashboard:
config: Configuración
feature_registrations: Rexistros
features: Carauterístiques
hidden_service: Federación con servicios anubríos
recent_users: Usuarios recientes
software: Software
total_users: usuarios en total
week_interactions: interaiciones d'esta selmana
week_users_new: usuarios d'esta selmana
domain_blocks:
domain: Dominiu
email_domain_blocks:
domain: Dominiu
instances:
title: Instancies conocíes
invites:
filter:
available: Disponible
expired: Caducó
title: Invitaciones
relays:
save_and_enable: Guardar y activar
reports:
are_you_sure: "¿De xuru?"
status: Estáu
settings:
registrations:
min_invite_role:
disabled: Naide
site_description:
title: Descipción de la instancia
site_title: Nome de la instancia
title: Axustes del sitiu
statuses:
failed_to_execute: Fallu al executar
subscriptions:
title: WebSub
title: Alministración
admin_mailer:
new_report:
body_remote: Daquién dende %{domain} informó de %{target}
application_mailer:
salutation: "%{name},"
applications:
invalid_url: La URL apurrida nun ye válida
warning: Ten curiáu con estos datos, ¡enxamás nun los compartas con naide!
auth:
change_password: Contraseña
delete_account: Desaniciu de la cuenta
delete_account_html: Si deseyes desaniciar la to cuenta, pues <a href="%{path}">siguir equí</a>. Va pidísete la confirmación.
forgot_password: "¿Escaeciesti la contraseña?"
login: Aniciar sesión
migrate_account: Mudase a otra cuenta
migrate_account_html: Si deseyes redirixir esta cuenta a otra, pues <a href="%{path}"> configuralo equí</a>.
providers:
cas: CAS
saml: SAML
register: Rexistrase
security: Seguranza
authorize_follow:
already_following: Yá tas siguiendo a esta cuenta
error: Desafortunadamente, hebo un fallu guetando la cuenta remota
follow_request: 'Unviesti una solicitú de siguimientu a:'
post_follow:
close: O pues zarrar esta ventana.
return: Amosar el perfil del usuariu
web: Dir a la web
datetime:
distance_in_words:
half_a_minute: Púramente agora
less_than_x_seconds: Púramente agora
deletes:
bad_password_msg: "¡Bon intentu, crackers! Contraseña incorreuta"
confirm_password: Introduz la contraseña pa verificar la to identidá
directories:
people:
one: "%{count} persona"
other: "%{count} persones"
errors:
'403': Nun tienes permisu pa ver esta páxina.
'404': La páxina que tabes guetando nun esiste.
'410': La páxina que tabes guetando yá nun esiste.
'422':
content: Falló la verificación de seguranza. ¿Tas bloquiando les cookies?
title: Falló la verificación de seguranza
'429': Ficiéronse milenta solicitúes
exports:
archive_takeout:
date: Data
hint_html: Pues solicitar un archivu colos tos <strong>toots y ficheros xubíos</strong>. Los datos esportaos van tar nel formatu ActivityPub, llexible pa cualesquier software que seya compatible. Pues solicitar un archivu cada 7 díes.
request: Solicitar l'archivu
size: Tamañu
blocks: Xente que bloquiesti
csv: CSV
follows: Xente que sigues
mutes: Xente que silenciesti
filters:
contexts:
notifications: Avisos
public: Llinies temporales públiques
thread: Conversaciones
index:
title: Peñeres
new:
title: Amestar una peñera nueva
generic:
changes_saved_msg: "¡Los cambeos guardáronse con ésitu!"
save_changes: Guardar cambeos
imports:
preface: Pues importar los datos qu'esportares dende otra instancia, como por exemplu la llista de persones que bloquiares o tubieres siguiendo.
types:
blocking: Llista de xente bloquiao
following: Llista de siguidores
muting: Llista de xente silenciao
upload: Xubir
invites:
delete: Desactivar
expired: Caducó
expires_in:
'1800': 30 minutos
'21600': 6 hores
'3600': 1 hora
'43200': 12 hores
'604800': 1 selmana
'86400': 1 día
expires_in_prompt: Enxamás
invited_by: 'Convidóte:'
max_uses:
one: 1 usu
other: "%{count} usos"
table:
expires_at: Data de caducidá
uses: Usos
lists:
errors:
limit: Algamesti la cantidá máxima de llistes
media_attachments:
validations:
images_and_video: Nun pue axuntase un videu a un estáu que yá contién imáxenes
too_many: Nun puen axuntase más de 4 ficheros
migrations:
acct: nome_usuariu@dominiu de la cuenta nueva
proceed: Guardar
notification_mailer:
digest:
body: Equí hai un resume de los mensaxes que nun viesti dende la última visita'l %{since}
mention: "%{name} mentóte en:"
subject:
other: "%{count} avisos nuevos dende la última visita \U0001F418"
follow:
body: "¡Agora %{name} ta siguiéndote!"
title: Siguidor nuevu
follow_request:
body: "%{name} solicitó siguite"
title: Petición nueva de siguimientu
mention:
body: "%{name} mentóte en:"
subject: "%{name} mentóte"
title: Mención nueva
reblog:
body: "%{name} compartió'l to estáu:"
subject: "%{name} compartió'l to estáu"
title: Compartición nueva de toot
number:
human:
decimal_units:
format: "%n%u"
pagination:
next: Siguiente
preferences:
languages: Llingües
publishing: Espublización
web: Web
remote_follow:
acct: Introduz el nome_usuariu@dominiu dende'l que lo quies facer
no_account_html: "¿Nun tienes una cuenta? Pues <a href='%{sign_up_path}' target='_blank'>rexistrate equí</a>"
proceed: Siguir
prompt: 'Vas siguir a:'
remote_unfollow:
error: Fallu
sessions:
browser: Restolador
browsers:
alipay: Alipay
blackberry: Blackberry
chrome: Chrome
edge: Microsoft Edge
electron: Electron
firefox: Firefox
generic: Restolador desconocíu
ie: Internet Explorer
micro_messenger: MicroMessenger
opera: Opera
otter: Otter
phantom_js: PhantomJS
qq: QQ Browser
safari: Safari
uc_browser: UCBrowser
weibo: Weibo
current_session: Sesión actual
description: "%{browser} en %{platform}"
ip: IP
platforms:
adobe_air: Adobe Air
android: Android
blackberry: Blackberry
chrome_os: ChromeOS
firefox_os: Firefox OS
ios: iOS
linux: Linux
mac: Mac
other: plataforma desconocida
windows: Windows
windows_mobile: Windows Mobile
windows_phone: Windows Phone
title: Sesiones
settings:
authorized_apps: Aplicaciones autorizaes
back: Volver a Gab Social
edit_profile: Edición del perfil
export: Esportación de datos
import: Importación
notifications: Avisos
preferences: Preferencies
two_factor_authentication: Autenticación en dos pasos
statuses:
attached:
image:
one: "%{count} imaxe"
other: "%{count} imáxenes"
video:
one: "%{count} videu"
other: "%{count} vídeos"
boosted_from_html: Compartióse'l toot dende %{acct_link}
language_detection: Deteutala automáticamente
pin_errors:
limit: Yá fixesti'l númberu máxiumu de toots
ownership: Nun pue fixase'l toot d'otra persona
private: Nun puen fixase los toots que nun seyan públicos
reblog: Nun pue fixase un toot compartíu
show_more: Amosar más
title: "%{name}: «%{quote}»"
visibilities:
private: Namái siguidores
stream_entries:
reblogged: compartióse
sensitive_content: Conteníu sensible
themes:
default: Gab Social
two_factor_authentication:
code_hint: Introduz el códigu xeneráu pola aplicación autenticadora pa confirmar
disable: Desactivar
enabled: L'autenticación en dos pasos ta activada
enabled_success: L'autenticación en dos pasos activóse con ésitu
generate_recovery_codes: Xenerar códigos de recuperación
lost_recovery_codes: Los códigos de recuperación permítente recuperar l'accesu a la cuenta si pierdes el teléfonu. Si tamién pierdes esos códigos, pues xeneralos de nueves equí. Los códigos de recuperación vieyos van invalidase.
manual_instructions: 'Si nun pues escaniar el códigu QR y precises introducilu a mano, equí ta''l secretu en testu planu:'
recovery_codes: Códigos de recuperación
recovery_codes_regenerated: Los códigos de recuperación rexeneráronse con ésitu
user_mailer:
welcome:
full_handle_hint: Esto ye lo que-yos diríes a los collacios pa que puean unviate mensaxes o siguite dende otra instancia.
subject: Afáyate en Gab Social
tips: Conseyos
users:
invalid_email: La direición de corréu nun ye válida
seamless_external_login: Aniciesti sesión pente un serviciu esternu, polo que los axustes de la contraseña y corréu nun tán disponibles.
verification:
verification: Verificación

147
config/locales/bg.yml Normal file
View File

@@ -0,0 +1,147 @@
---
bg:
about:
about_gabsocial_html: Gab Social е <em>безплатен</em> сървър с <em>отворен код</em> за социални мрежи. Като <em>децентрализирана</em> алтернатива на комерсиалните платформи, той позволява избягването на риска от монополизация на твоята комуникация от единични компании. Изберете си сървър, на който се доверявате, и ще можете да контактувате с всички останали. Всеки може да пусне Gab Social и лесно да вземе участие в <em>социалната мрежа</em>.
about_this: За тази инстанция
contact: За контакти
source_code: Програмен код
status_count_after: публикации
status_count_before: Написали
user_count_after: потребители
user_count_before: Дом на
accounts:
follow: Последвай
followers: Последователи
following: Следва
nothing_here: Тук няма никого!
people_followed_by: Хора, които %{name} следва
people_who_follow: Хора, които следват %{name}
posts: Публикации
unfollow: Не следвай
application_mailer:
settings: 'Промяна на предпочитанията за e-mail: %{link}'
view: 'Преглед:'
applications:
invalid_url: Предоставеният URL е невалиден
auth:
didnt_get_confirmation: Не получих инструкции за потвърждение
forgot_password: Забравих си паролата
login: Влизане
logout: Излизане
register: Регистрация
resend_confirmation: Изпрати отново инструкции за потвърждение
reset_password: Подновяване на паролата
security: Идентификационни данни
set_new_password: Задай нова парола
authorize_follow:
error: Възникна грешка в откриването на потребителя
follow: Последвай
title: Последвай %{acct}
datetime:
distance_in_words:
about_x_hours: "%{count} ч."
about_x_months: "%{count} м."
about_x_years: "%{count} г."
almost_x_years: "%{count} г."
half_a_minute: Току-що
less_than_x_minutes: "%{count} мин."
less_than_x_seconds: Току-що
over_x_years: "%{count} г"
x_days: "%{count} дни"
x_minutes: "%{count} мин"
x_months: "%{count} м"
x_seconds: "%{count} сек"
exports:
blocks: Вашите блокирания
csv: CSV
follows: Вашите следвания
storage: Съхранение на мултимедия
generic:
changes_saved_msg: Успешно запазване на промените!
save_changes: Запази промените
validation_errors: Нещо все още не е наред! Моля, прегледай грешките по-долу
imports:
preface: Можеш да импортираш някои данни, като например всички хора, които следваш или блокираш в акаунта си на тази инстанция, от файлове, създадени чрез експорт в друга инстанция.
success: Твоите данни бяха успешно качени и ще бъдат обработени впоследствие
types:
blocking: Списък на блокираните
following: Списък на последователите
upload: Качване
media_attachments:
validations:
images_and_video: Не мога да прикача видеоклип към публикация, която вече съдържа изображения
too_many: Не мога да прикача повече от 4 файла
notification_mailer:
digest:
body: Ето кратко резюме на нещата, които се случиха от последното ти посещение на %{since}
mention: "%{name} те спомена в:"
new_followers_summary:
one: Имаш един нов последовател! Ура!
other: Имаш %{count} нови последователи! Изумително!
subject:
one: "1 ново известие от последното ти посещение \U0001F418"
other: "%{count} нови известия от последното ти посещение \U0001F418"
favourite:
body: 'Публикацията ти беше харесана от %{name}:'
subject: "%{name} хареса твоята публикация"
follow:
body: "%{name} те последва!"
subject: "%{name} те последва"
follow_request:
body: "%{name} помоли за разрешение да те последва"
subject: 'Чакащ последовател: %{name}'
mention:
body: "%{name} те спомена в:"
subject: "%{name} те спомена"
reblog:
body: 'Твоята публикация беше споделена от %{name}:'
subject: "%{name} сподели публикацията ти"
number:
human:
decimal_units:
format: "%n%u"
units:
billion: B
million: M
quadrillion: Q
thousand: K
trillion: T
unit: ''
pagination:
next: Напред
prev: Назад
remote_follow:
acct: Въведи потребителско_име@домейн, от които искаш да следваш
missing_resource: Неуспешно търсене на нужния URL за пренасочване за твоя акаунт
proceed: Започни следване
prompt: 'Ще последваш:'
settings:
authorized_apps: Упълномощени приложения
back: Обратно към Gab Social
edit_profile: Редактирай профила си
export: Експортиране на данни
import: Импортиране
preferences: Предпочитания
two_factor_authentication: Двустепенно удостоверяване
statuses:
open_in_web: Отвори в уеб
over_character_limit: прехвърлен лимит от %{max} символа
show_more: Покажи повече
visibilities:
private: Покажи само на последователите си
public: Публично
unlisted: Публично, но не показвай в публичния канал
stream_entries:
reblogged: споделено
sensitive_content: Деликатно съдържание
time:
formats:
default: "%d %b, %Y, %H:%M"
two_factor_authentication:
description_html: При активация на <strong>двустепенно удостоверяване</strong>, за да влезеш в приложението, ще трябва да използваш телефона си. През него ще се генерира код, който да въвеждаш при влизане.
disable: Деактивирай
enable: Активирай
instructions_html: "<strong>Сканирай този QR код с Google Authenticator или подобно приложение от своя телефон</strong>. Oтсега нататък, това приложение ще генерира код, който ще трябва да въвеждаш при всяко влизане."
users:
invalid_email: E-mail адресът е невалиден
invalid_otp_token: Невалиден код

141
config/locales/bn.yml Normal file
View File

@@ -0,0 +1,141 @@
---
bn:
about:
about_hashtag_html: এগুলো প্রকাশ্য লেখা যার হ্যাশট্যাগ <strong>#%{hashtag}</strong>। আপনি এগুলোর ব্যবহার বা সাথে যুক্ত হতে পারবেন যদি আপনার যুক্তবিশ্বের কোথাও নিবন্ধন থেকে থাকে।
about_gabsocial_html: মাস্টাডন উন্মুক্ত ইন্টারনেটজালের নিয়ম এবং স্বাধীন ও মুক্ত উৎসের সফটওয়্যারের ভিত্তিতে তৈরী একটি সামাজিক যোগাযোগ মাধ্যম। এটি ইমেইলের মত বিকেন্দ্রীভূত।
about_this: কি
active_count_after: চালু
active_footnote: মাসিক সক্রিয় ব্যবহারকারী
administered_by: 'পরিচালনা করছেন:'
api: সফটওয়্যার তৈরীর নিয়ম (API)
apps: মোবাইল অ্যাপ
apps_platforms: মাস্টাডন আইওএস, এন্ড্রোইড বা অন্য মাধ্যমে ব্যবহার করুন
browse_directory: একটি ব্যবহারকারীদের তালিকা দেখুন এবং পছন্দ অনুসারে খুজুন
browse_public_posts: মাস্টাডনে নতুন প্রকাশ্য লেখাগুলো সরাসরি দেখুন
contact: যোগাযোগ
contact_missing: নেই
contact_unavailable: প্রযোজ্য নয়
discover_users: ব্যবহারকারীদের দেখুন
documentation: ব্যবহারবিলি
extended_description_html: |
<h3>নিয়মের জন্য উপযুক্ত জায়গা</h3>
<p>বিস্তারিত বিবরণ এখনো যুক্ত করা হয়নি</p>
federation_hint_html: "%{instance}তে একটা নিবন্ধন থাকলে আপনি যেকোনো মাস্টাডন বা এধরণের অন্যান্য সার্ভারের মানুষের সাথে যুক্ত হতে পারবেন ।"
generic_description: নেটওয়ার্কের ভেতরে %{domain} একটি সার্ভার
get_apps: মোবাইল এপ্প একটা ব্যবহার করতে পারেন
hosted_on: এই মাস্টাডনটি আছে %{domain} এ
learn_more: বিস্তারিত জানুন
privacy_policy: গোপনীয়তা নীতি
see_whats_happening: কী কী হচ্ছে দেখুন
server_stats: 'সার্ভারের অবস্থা:'
source_code: আসল তৈরীপত্র
status_count_after:
one: অবস্থা
other: স্থিতিগুলি
status_count_before: কে লিখেছে
tagline: পরিচিতজনদের সাথে যুক্ত হন এবং নতুনদের সাথে পরিচিত হন
terms: ব্যবহারের শর্তাবলী
user_count_after:
one: ব্যবহারকারী
other: জনের
user_count_before: বাসা
what_is_gabsocial: মাস্টাডনটি কি ?
accounts:
choices_html: "%{name} বাছাই:"
follow: যুক্ত
followers:
one: যুক্ত আছে
other: যারা যুক্ত হয়েছে
following: যুক্ত করা
joined: যোগদান হয় %{date}
last_active: শেষ সক্রিয় ছিল
link_verified_on: এই লিংকের মালিকানা শেষ চেক করা হয় %{date} তারিখে
media: ছবি বা ভিডিও
moved_html: "%{name} চলে গেছে %{new_profile_link} তে:"
network_hidden: এই তথ্যটি নেই
nothing_here: এখানে কিছুই নেই!
people_followed_by: "%{name} যাদেরকে অনুসরণ করে"
people_who_follow: যারা %{name} কে অনুসরণ করে
pin_errors:
following: সমর্থন করতে অনুসরণ থাকা লাগবে
posts:
one: লেখা
other: লেখাগুলো
posts_tab_heading: লেখাগুলো
posts_with_replies: লেখা এবং মতামত
reserved_username: নামটি সংরক্ষিত
roles:
admin: পরিচালক
bot: রোবট
moderator: পরিচালক
unfollow: অনুসরণ বাদ
admin:
account_actions:
action: করা
title: 'প্রশাসনা করুন এর উপর : %{acct}'
account_moderation_notes:
create: কিছু লিখুন
created_msg: প্রশাসনবস্তুত লেখাটি সঠিকভাবে তৈরী হয়েছে!
delete: মুছে ফেলা
destroyed_msg: প্রশাসনবস্তুত লেখাটি সঠিকভাবে মুছে ফেলা হয়েছে!
accounts:
approve: অনুমোদন দিন
are_you_sure: আপনি কি নিশ্চিত ?
avatar: অবতার
by_domain: ওয়েবসাইট/কার্যক্ষেত্র
change_email:
changed_msg: নিবন্ধনের ইমেইল সঠিকভাবে পরিবর্তন হয়েছে!
current_email: এখনকার ইমেইল
label: ইমেইল পরিবর্তন
new_email: নতুন ইমেইল
submit: ইমেইল পরিবর্তন
title: "%{username} এর ইমেইল পরিবর্তন"
confirm: নিশ্চিত করুন
confirmed: নিশ্চিত হয়েছে
confirming: নিশ্চিত করা হচ্ছে
deleted: মুছে ফেলা হয়েছে
demote: নিচের পদে দিন
disable: বন্ধ করুন
disable_two_factor_authentication: দুই পদ্ধতির প্রমাণীকরণ(2FA) বন্ধ করুন
disabled: বন্ধ করা হয়েছে
display_name: দেখানোর জন্য নাম
domain: ওয়েবসাইট/কার্যক্ষেত্র
edit: বদলান
email: ইমেইল
email_status: ইমেইলের অবস্থা
enable: চালু করুন
enabled: চালু করুন
feed_url: সম্মিলিত(feed) লিংক
followers: অনুসরকারীরা
followers_url: অনুসরণকারীদের লিংক
follows: অনুসরণ করে
header: শিরোলেখা
inbox_url: চিঠি পাওয়ার বক্স লিংক
invited_by: আমন্ত্রণ করেছে
ip: আইপি(IP)
joined: যোগ দিয়েছে
location:
all: সব
local: স্থানীয়
remote: দূরবর্তী
title: জায়গা
login_status: নিবন্ধনধারীভাবে প্রবেশের অবস্থা
media_attachments: ছবি/ভিডিও যুক্ত
memorialize: স্মরণিকা বানান
moderation:
active: চালু
all: সব
pending: অপেক্ষিত আছে
silenced: নীরব করা হয়েছে
suspended: স্থগিত করা হয়েছে
title: প্রশাসনা
moderation_notes: প্রশাসনের কিছু লেখা
most_recent_activity: সর্বশেষ কার্যক্রম
most_recent_ip: সর্বশেষ আইপি(IP)
no_limits_imposed: কোন সীমা আরোপ করা নেই
not_subscribed: সাবস্ক্রাইব নেই
outbox_url: চিঠি পাঠানোর বাক্স লিংক
pending: পয্র্যবেক্ষণের অপেক্ষায় আছে
perform_full_suspension: বাতিল করা
verification:
verification: সত্যতা নির্ধারণ

Some files were not shown because too many files have changed in this diff Show More