Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
e239fc3050 | |||
d022975444 | |||
086d487145 | |||
9cb690c706 | |||
786397e15d | |||
55ca59a66c | |||
48d66a2055 | |||
2c374cd97c | |||
8767a98fbb | |||
a9db42a956 |
@ -1,5 +1,23 @@
|
|||||||
|
# Service dependencies
|
||||||
REDIS_HOST=redis
|
REDIS_HOST=redis
|
||||||
|
REDIS_PORT=6379
|
||||||
|
DB_HOST=db
|
||||||
|
DB_USER=postgres
|
||||||
|
DB_NAME=postgres
|
||||||
|
DB_PASS=
|
||||||
|
DB_PORT=5432
|
||||||
|
|
||||||
|
# Federation
|
||||||
LOCAL_DOMAIN=example.com
|
LOCAL_DOMAIN=example.com
|
||||||
LOCAL_HTTPS=true
|
LOCAL_HTTPS=true
|
||||||
|
|
||||||
|
# Application secrets
|
||||||
PAPERCLIP_SECRET=
|
PAPERCLIP_SECRET=
|
||||||
SECRET_KEY_BASE=
|
SECRET_KEY_BASE=
|
||||||
|
|
||||||
|
# E-mail configuration
|
||||||
|
SMTP_SERVER=smtp.mailgun.org
|
||||||
|
SMTP_PORT=587
|
||||||
|
SMTP_LOGIN=
|
||||||
|
SMTP_PASSWORD=
|
||||||
|
SMTP_FROM_ADDRESS=notifications@example.com
|
||||||
|
@ -2,7 +2,7 @@ FROM ruby:2.2.4
|
|||||||
|
|
||||||
ENV RAILS_ENV=production
|
ENV RAILS_ENV=production
|
||||||
|
|
||||||
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev
|
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev && rm -rf /var/lib/apt/lists/*
|
||||||
RUN mkdir /mastodon
|
RUN mkdir /mastodon
|
||||||
|
|
||||||
WORKDIR /mastodon
|
WORKDIR /mastodon
|
||||||
@ -13,3 +13,5 @@ ADD Gemfile.lock /mastodon/Gemfile.lock
|
|||||||
RUN bundle install --deployment --without test --without development
|
RUN bundle install --deployment --without test --without development
|
||||||
|
|
||||||
ADD . /mastodon
|
ADD . /mastodon
|
||||||
|
|
||||||
|
VOLUME ["/mastodon/public/system", "/mastodon/public/assets"]
|
||||||
|
12
README.md
12
README.md
@ -21,8 +21,8 @@ Mastodon is a federated microblogging engine. An alternative implementation of t
|
|||||||
Missing:
|
Missing:
|
||||||
|
|
||||||
- Media attachments (photos, videos)
|
- Media attachments (photos, videos)
|
||||||
- UI to post, reblog, favourite, follow and unfollow
|
|
||||||
- Streaming API
|
- Streaming API
|
||||||
|
- Blocking users, blocking remote instances
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
@ -30,6 +30,8 @@ Missing:
|
|||||||
- `LOCAL_HTTPS` set it to `true` if HTTPS works on your website. This is used to generate canonical URLs, which is also important when generating and parsing federation-related IDs
|
- `LOCAL_HTTPS` set it to `true` if HTTPS works on your website. This is used to generate canonical URLs, which is also important when generating and parsing federation-related IDs
|
||||||
- `HUB_URL` should be the URL of the PubsubHubbub service that your instance is going to use. By default it is the open service of Superfeedr
|
- `HUB_URL` should be the URL of the PubsubHubbub service that your instance is going to use. By default it is the open service of Superfeedr
|
||||||
|
|
||||||
|
Consult the example configuration file, `.env.production.sample` for the full list.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- PostgreSQL
|
- PostgreSQL
|
||||||
@ -37,7 +39,7 @@ Missing:
|
|||||||
|
|
||||||
## Running with Docker and Docker-Compose
|
## Running with Docker and Docker-Compose
|
||||||
|
|
||||||
The project now includes a Dockerfile and a docker-compose.yml. You need to turn .env.production sample into .env.production with all the variables set before you can:
|
The project now includes a `Dockerfile` and a `docker-compose.yml`. You need to turn `.env.production.sample` into `.env.production` with all the variables set before you can:
|
||||||
|
|
||||||
docker-compose build
|
docker-compose build
|
||||||
|
|
||||||
@ -48,3 +50,9 @@ And finally
|
|||||||
As usual, the first thing you would need to do would be to run migrations:
|
As usual, the first thing you would need to do would be to run migrations:
|
||||||
|
|
||||||
docker-compose run web rake db:migrate
|
docker-compose run web rake db:migrate
|
||||||
|
|
||||||
|
And since the instance running in the container will be running in production mode, you need to pre-compile assets:
|
||||||
|
|
||||||
|
docker-compose run web rake assets:precompile
|
||||||
|
|
||||||
|
The container has two volumes, for the assets and for user uploads. The default docker-compose.yml maps them to the repository's `public/assets` and `public/system` directories, you may wish to put them somewhere else. Likewise, the PostgreSQL and Redis images have data containers that you may wish to map somewhere where you know how to find them and back them up.
|
||||||
|
@ -214,6 +214,12 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error_notification {
|
||||||
|
color: #df405a;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
|
|
||||||
@ -238,7 +244,7 @@
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-family: 'Roboto', sans-serif;
|
font-family: 'Roboto', sans-serif;
|
||||||
|
|
||||||
&:focus {
|
&:focus, &:active {
|
||||||
border-bottom: 2px solid #2b90d9;
|
border-bottom: 2px solid #2b90d9;
|
||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
}
|
}
|
||||||
@ -253,6 +259,24 @@
|
|||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
color: lighten(#282c37, 25%);
|
color: lighten(#282c37, 25%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.field_with_errors {
|
||||||
|
input[type=text], input[type=email], input[type=password], textarea {
|
||||||
|
border-bottom: 2px solid #df405a;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
|
||||||
|
&:focus, &:active {
|
||||||
|
border-bottom: 2px solid #2b90d9;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
display: block;
|
||||||
|
margin-top: 5px;
|
||||||
|
color: #df405a;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ class XrdController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def webfinger
|
def webfinger
|
||||||
@account = Account.find_by!(username: username_from_resource, domain: nil)
|
@account = Account.find_local!(username_from_resource)
|
||||||
@canonical_account_uri = "acct:#{@account.username}@#{Rails.configuration.x.local_domain}"
|
@canonical_account_uri = "acct:#{@account.username}@#{Rails.configuration.x.local_domain}"
|
||||||
@magic_key = pem_to_magic_key(@account.keypair.public_key)
|
@magic_key = pem_to_magic_key(@account.keypair.public_key)
|
||||||
rescue ActiveRecord::RecordNotFound
|
rescue ActiveRecord::RecordNotFound
|
||||||
@ -21,10 +21,10 @@ class XrdController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def username_from_resource
|
def username_from_resource
|
||||||
if params[:resource].start_with?('acct:')
|
if resource_param.start_with?('acct:')
|
||||||
params[:resource].split('@').first.gsub('acct:', '')
|
resource_param.split('@').first.gsub('acct:', '')
|
||||||
else
|
else
|
||||||
url = Addressable::URI.parse(params[:resource])
|
url = Addressable::URI.parse(resource_param)
|
||||||
url.path.gsub('/users/', '')
|
url.path.gsub('/users/', '')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -43,4 +43,8 @@ class XrdController < ApplicationController
|
|||||||
|
|
||||||
(["RSA"] + [modulus, exponent].map { |n| Base64.urlsafe_encode64(n) }).join('.')
|
(["RSA"] + [modulus, exponent].map { |n| Base64.urlsafe_encode64(n) }).join('.')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def resource_param
|
||||||
|
params.require(:resource)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -25,7 +25,7 @@ module StreamEntriesHelper
|
|||||||
status.mentions.each { |m| mention_hash[m.acct] = m }
|
status.mentions.each { |m| mention_hash[m.acct] = m }
|
||||||
coder = HTMLEntities.new
|
coder = HTMLEntities.new
|
||||||
|
|
||||||
auto_link(coder.encode(status.text), link: :urls, html: { target: '_blank', rel: 'nofollow' }).gsub(Account::MENTION_RE) do |m|
|
auto_link(coder.encode(status.text), link: :urls, html: { rel: 'nofollow noopener' }).gsub(Account::MENTION_RE) do |m|
|
||||||
account = mention_hash[Account::MENTION_RE.match(m)[1]]
|
account = mention_hash[Account::MENTION_RE.match(m)[1]]
|
||||||
"#{m.split('@').first}<a href=\"#{url_for_target(account)}\" class=\"mention\">@<span>#{account.acct}</span></a>"
|
"#{m.split('@').first}<a href=\"#{url_for_target(account)}\" class=\"mention\">@<span>#{account.acct}</span></a>"
|
||||||
end.html_safe
|
end.html_safe
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
class Account < ActiveRecord::Base
|
class Account < ActiveRecord::Base
|
||||||
# Local users
|
# Local users
|
||||||
has_one :user, inverse_of: :account
|
has_one :user, inverse_of: :account
|
||||||
validates :username, uniqueness: { scope: :domain }
|
validates :username, uniqueness: { scope: :domain, case_sensitive: false }
|
||||||
|
|
||||||
# Avatar upload
|
# Avatar upload
|
||||||
attr_reader :avatar_remote_url
|
attr_reader :avatar_remote_url
|
||||||
@ -12,6 +12,10 @@ class Account < ActiveRecord::Base
|
|||||||
has_attached_file :header, styles: { medium: '700x335#' }
|
has_attached_file :header, styles: { medium: '700x335#' }
|
||||||
validates_attachment_content_type :header, content_type: /\Aimage\/.*\Z/
|
validates_attachment_content_type :header, content_type: /\Aimage\/.*\Z/
|
||||||
|
|
||||||
|
# Local user profile validations
|
||||||
|
validates :display_name, length: { maximum: 30 }, if: 'local?'
|
||||||
|
validates :note, length: { maximum: 124 }, if: 'local?'
|
||||||
|
|
||||||
# Timelines
|
# Timelines
|
||||||
has_many :stream_entries, inverse_of: :account
|
has_many :stream_entries, inverse_of: :account
|
||||||
has_many :statuses, inverse_of: :account
|
has_many :statuses, inverse_of: :account
|
||||||
@ -32,7 +36,8 @@ class Account < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def unfollow!(other_account)
|
def unfollow!(other_account)
|
||||||
self.active_relationships.find_by(target_account: other_account).destroy
|
follow = self.active_relationships.find_by(target_account: other_account)
|
||||||
|
follow.destroy unless follow.nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
def following?(other_account)
|
def following?(other_account)
|
||||||
@ -93,6 +98,11 @@ class Account < ActiveRecord::Base
|
|||||||
self.username
|
self.username
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.find_local!(username)
|
||||||
|
table = self.arel_table
|
||||||
|
self.where(table[:username].matches(username)).where(domain: nil).take!
|
||||||
|
end
|
||||||
|
|
||||||
before_create do
|
before_create do
|
||||||
if local?
|
if local?
|
||||||
keypair = OpenSSL::PKey::RSA.new(Rails.env.test? ? 1024 : 2048)
|
keypair = OpenSSL::PKey::RSA.new(Rails.env.test? ? 1024 : 2048)
|
||||||
|
@ -2,7 +2,7 @@ class Favourite < ActiveRecord::Base
|
|||||||
belongs_to :account, inverse_of: :favourites
|
belongs_to :account, inverse_of: :favourites
|
||||||
belongs_to :status, inverse_of: :favourites
|
belongs_to :status, inverse_of: :favourites
|
||||||
|
|
||||||
has_one :stream_entry, as: :activity, dependent: :destroy
|
has_one :stream_entry, as: :activity
|
||||||
|
|
||||||
def verb
|
def verb
|
||||||
:favorite
|
:favorite
|
||||||
|
@ -2,7 +2,7 @@ class Follow < ActiveRecord::Base
|
|||||||
belongs_to :account
|
belongs_to :account
|
||||||
belongs_to :target_account, class_name: 'Account'
|
belongs_to :target_account, class_name: 'Account'
|
||||||
|
|
||||||
has_one :stream_entry, as: :activity, dependent: :destroy
|
has_one :stream_entry, as: :activity
|
||||||
|
|
||||||
validates :account, :target_account, presence: true
|
validates :account, :target_account, presence: true
|
||||||
validates :account_id, uniqueness: { scope: :target_account_id }
|
validates :account_id, uniqueness: { scope: :target_account_id }
|
||||||
|
@ -98,7 +98,7 @@ class ProcessFeedService < BaseService
|
|||||||
account = Account.find_by(username: username, domain: domain)
|
account = Account.find_by(username: username, domain: domain)
|
||||||
|
|
||||||
if account.nil?
|
if account.nil?
|
||||||
account = follow_remote_account_service.("acct:#{username}@#{domain}", false)
|
account = follow_remote_account_service.("#{username}@#{domain}", false)
|
||||||
return nil if account.nil?
|
return nil if account.nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ class ProcessInteractionService < BaseService
|
|||||||
account = Account.find_by(username: username, domain: domain)
|
account = Account.find_by(username: username, domain: domain)
|
||||||
|
|
||||||
if account.nil?
|
if account.nil?
|
||||||
account = follow_remote_account_service.("acct:#{username}@#{domain}", false)
|
account = follow_remote_account_service.("#{username}@#{domain}", false)
|
||||||
return if account.nil?
|
return if account.nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ class ProcessInteractionService < BaseService
|
|||||||
end
|
end
|
||||||
|
|
||||||
def verb(xml)
|
def verb(xml)
|
||||||
xml.at_xpath('//activity:verb').content.gsub('http://activitystrea.ms/schema/1.0/', '').to_sym
|
xml.at_xpath('//activity:verb').content.gsub('http://activitystrea.ms/schema/1.0/', '').gsub('http://ostatus.org/schema/1.0/', '').to_sym
|
||||||
rescue
|
rescue
|
||||||
:post
|
:post
|
||||||
end
|
end
|
||||||
|
@ -11,7 +11,7 @@ class ProcessMentionsService < BaseService
|
|||||||
mentioned_account = Account.find_by(username: username, domain: domain)
|
mentioned_account = Account.find_by(username: username, domain: domain)
|
||||||
|
|
||||||
if mentioned_account.nil?
|
if mentioned_account.nil?
|
||||||
mentioned_account = follow_remote_account_service.("acct:#{match.first}")
|
mentioned_account = follow_remote_account_service.("#{match.first}")
|
||||||
end
|
end
|
||||||
|
|
||||||
mentioned_account.mentions.first_or_create(status: status)
|
mentioned_account.mentions.first_or_create(status: status)
|
||||||
|
@ -17,7 +17,8 @@ test:
|
|||||||
|
|
||||||
production:
|
production:
|
||||||
<<: *default
|
<<: *default
|
||||||
database: postgres
|
database: <%= ENV['DB_NAME'] || 'mastodon_production' %>
|
||||||
username: postgres
|
username: <%= ENV['DB_USER'] || 'mastodon' %>
|
||||||
password:
|
password: <%= ENV['DB_PASS'] || '' %>
|
||||||
host: db
|
host: <%= ENV['DB_HOST'] || 'localhost' %>
|
||||||
|
port: <%= ENV['DB_PORT'] || 5432 %>
|
||||||
|
@ -22,7 +22,7 @@ Rails.application.configure do
|
|||||||
|
|
||||||
# Disable serving static files from the `/public` folder by default since
|
# Disable serving static files from the `/public` folder by default since
|
||||||
# Apache or NGINX already handles this.
|
# Apache or NGINX already handles this.
|
||||||
config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present?
|
config.serve_static_files = true
|
||||||
|
|
||||||
# Compress JavaScripts and CSS.
|
# Compress JavaScripts and CSS.
|
||||||
config.assets.js_compressor = :uglifier
|
config.assets.js_compressor = :uglifier
|
||||||
@ -42,7 +42,7 @@ Rails.application.configure do
|
|||||||
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
|
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
|
||||||
|
|
||||||
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
|
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
|
||||||
# config.force_ssl = true
|
config.force_ssl = ENV['LOCAL_HTTPS'] == 'true'
|
||||||
|
|
||||||
# Use the lowest log level to ensure availability of diagnostic information
|
# Use the lowest log level to ensure availability of diagnostic information
|
||||||
# when problems arise.
|
# when problems arise.
|
||||||
@ -76,4 +76,16 @@ Rails.application.configure do
|
|||||||
|
|
||||||
# Do not dump schema after migrations.
|
# Do not dump schema after migrations.
|
||||||
config.active_record.dump_schema_after_migration = false
|
config.active_record.dump_schema_after_migration = false
|
||||||
|
|
||||||
|
# E-mails
|
||||||
|
config.action_mailer.smtp_settings = {
|
||||||
|
:port => ENV['SMTP_PORT'],
|
||||||
|
:address => ENV['SMTP_SERVER'],
|
||||||
|
:user_name => ENV['SMTP_LOGIN'],
|
||||||
|
:password => ENV['SMTP_PASSWORD'],
|
||||||
|
:domain => config.x.local_domain,
|
||||||
|
:authentication => :plain,
|
||||||
|
}
|
||||||
|
|
||||||
|
config.action_mailer.delivery_method = :smtp
|
||||||
end
|
end
|
||||||
|
@ -12,7 +12,7 @@ Devise.setup do |config|
|
|||||||
# Configure the e-mail address which will be shown in Devise::Mailer,
|
# 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
|
# note that it will be overwritten if you use your own mailer class
|
||||||
# with default "from" parameter.
|
# with default "from" parameter.
|
||||||
config.mailer_sender = 'please-change-me-at-config-initializers-devise@example.com'
|
config.mailer_sender = ENV['SMTP_FROM_ADDRESS'] || 'notifications@localhost'
|
||||||
|
|
||||||
# Configure the class responsible to send e-mails.
|
# Configure the class responsible to send e-mails.
|
||||||
# config.mailer = 'Devise::Mailer'
|
# config.mailer = 'Devise::Mailer'
|
||||||
|
@ -23,7 +23,7 @@ Doorkeeper.configure do
|
|||||||
|
|
||||||
# Access token expiration time (default 2 hours).
|
# Access token expiration time (default 2 hours).
|
||||||
# If you want to disable expiration, set this to nil.
|
# If you want to disable expiration, set this to nil.
|
||||||
# access_token_expires_in nil
|
access_token_expires_in nil
|
||||||
|
|
||||||
# Assign a custom TTL for implicit grants.
|
# Assign a custom TTL for implicit grants.
|
||||||
# custom_access_token_expires_in do |oauth_client|
|
# custom_access_token_expires_in do |oauth_client|
|
||||||
|
10
db/migrate/20160316103650_add_missing_indices.rb
Normal file
10
db/migrate/20160316103650_add_missing_indices.rb
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
class AddMissingIndices < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
add_index :users, :account_id
|
||||||
|
add_index :statuses, :account_id
|
||||||
|
add_index :statuses, :in_reply_to_id
|
||||||
|
add_index :statuses, :reblog_of_id
|
||||||
|
add_index :stream_entries, :account_id
|
||||||
|
add_index :stream_entries, [:activity_id, :activity_type]
|
||||||
|
end
|
||||||
|
end
|
@ -11,7 +11,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20160314164231) do
|
ActiveRecord::Schema.define(version: 20160316103650) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
@ -125,6 +125,9 @@ ActiveRecord::Schema.define(version: 20160314164231) do
|
|||||||
t.string "url"
|
t.string "url"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
add_index "statuses", ["account_id"], name: "index_statuses_on_account_id", using: :btree
|
||||||
|
add_index "statuses", ["in_reply_to_id"], name: "index_statuses_on_in_reply_to_id", using: :btree
|
||||||
|
add_index "statuses", ["reblog_of_id"], name: "index_statuses_on_reblog_of_id", using: :btree
|
||||||
add_index "statuses", ["uri"], name: "index_statuses_on_uri", unique: true, using: :btree
|
add_index "statuses", ["uri"], name: "index_statuses_on_uri", unique: true, using: :btree
|
||||||
|
|
||||||
create_table "stream_entries", force: :cascade do |t|
|
create_table "stream_entries", force: :cascade do |t|
|
||||||
@ -135,6 +138,9 @@ ActiveRecord::Schema.define(version: 20160314164231) do
|
|||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
add_index "stream_entries", ["account_id"], name: "index_stream_entries_on_account_id", using: :btree
|
||||||
|
add_index "stream_entries", ["activity_id", "activity_type"], name: "index_stream_entries_on_activity_id_and_activity_type", using: :btree
|
||||||
|
|
||||||
create_table "users", force: :cascade do |t|
|
create_table "users", force: :cascade do |t|
|
||||||
t.string "email", default: "", null: false
|
t.string "email", default: "", null: false
|
||||||
t.integer "account_id", null: false
|
t.integer "account_id", null: false
|
||||||
@ -151,6 +157,7 @@ ActiveRecord::Schema.define(version: 20160314164231) do
|
|||||||
t.inet "last_sign_in_ip"
|
t.inet "last_sign_in_ip"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
add_index "users", ["account_id"], name: "index_users_on_account_id", using: :btree
|
||||||
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
|
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
|
||||||
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
|
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
|
||||||
|
|
||||||
|
@ -12,4 +12,7 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
- redis
|
- redis
|
||||||
|
volumes:
|
||||||
|
- ./public/assets:/mastodon/public/assets
|
||||||
|
- ./public/system:/mastodon/public/system
|
||||||
env_file: .env.production
|
env_file: .env.production
|
||||||
|
Reference in New Issue
Block a user