Conflicts: - `app/controllers/settings/preferences_controller.rb`: Upstream dropping `digest` from notifications emails while we have more notification emails settings. Removed `digest` from our list while keeping our extra settings. - `app/javascript/packs/admin.js`: Conflicts caused by glitch-soc's theming system. Applied the changes to `app/javascript/core/admin.js`. - `app/views/settings/preferences/other/show.html.haml`: Upstream removed a setting close to a glitch-soc-only setting. Applied upstream's change.main
commit
077183a121
@ -0,0 +1,99 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::V1::Admin::CanonicalEmailBlocksController < Api::BaseController
|
||||||
|
include Authorization
|
||||||
|
include AccountableConcern
|
||||||
|
|
||||||
|
LIMIT = 100
|
||||||
|
|
||||||
|
before_action -> { authorize_if_got_token! :'admin:read', :'admin:read:canonical_email_blocks' }, only: [:index, :show, :test]
|
||||||
|
before_action -> { authorize_if_got_token! :'admin:write', :'admin:write:canonical_email_blocks' }, except: [:index, :show, :test]
|
||||||
|
|
||||||
|
before_action :set_canonical_email_blocks, only: :index
|
||||||
|
before_action :set_canonical_email_blocks_from_test, only: [:test]
|
||||||
|
before_action :set_canonical_email_block, only: [:show, :destroy]
|
||||||
|
|
||||||
|
after_action :verify_authorized
|
||||||
|
after_action :insert_pagination_headers, only: :index
|
||||||
|
|
||||||
|
PAGINATION_PARAMS = %i(limit).freeze
|
||||||
|
|
||||||
|
def index
|
||||||
|
authorize :canonical_email_block, :index?
|
||||||
|
render json: @canonical_email_blocks, each_serializer: REST::Admin::CanonicalEmailBlockSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
authorize @canonical_email_block, :show?
|
||||||
|
render json: @canonical_email_block, serializer: REST::Admin::CanonicalEmailBlockSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
def test
|
||||||
|
authorize :canonical_email_block, :test?
|
||||||
|
render json: @canonical_email_blocks, each_serializer: REST::Admin::CanonicalEmailBlockSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
authorize :canonical_email_block, :create?
|
||||||
|
|
||||||
|
@canonical_email_block = CanonicalEmailBlock.create!(resource_params)
|
||||||
|
log_action :create, @canonical_email_block
|
||||||
|
|
||||||
|
render json: @canonical_email_block, serializer: REST::Admin::CanonicalEmailBlockSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
authorize @canonical_email_block, :destroy?
|
||||||
|
|
||||||
|
@canonical_email_block.destroy!
|
||||||
|
log_action :destroy, @canonical_email_block
|
||||||
|
|
||||||
|
render json: @canonical_email_block, serializer: REST::Admin::CanonicalEmailBlockSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def resource_params
|
||||||
|
params.permit(:canonical_email_hash, :email)
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_canonical_email_blocks
|
||||||
|
@canonical_email_blocks = CanonicalEmailBlock.order(id: :desc).to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_canonical_email_blocks_from_test
|
||||||
|
@canonical_email_blocks = CanonicalEmailBlock.matching_email(params[:email])
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_canonical_email_block
|
||||||
|
@canonical_email_block = CanonicalEmailBlock.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def insert_pagination_headers
|
||||||
|
set_pagination_headers(next_path, prev_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def next_path
|
||||||
|
api_v1_admin_canonical_email_blocks_url(pagination_params(max_id: pagination_max_id)) if records_continue?
|
||||||
|
end
|
||||||
|
|
||||||
|
def prev_path
|
||||||
|
api_v1_admin_canonical_email_blocks_url(pagination_params(min_id: pagination_since_id)) unless @canonical_email_blocks.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_max_id
|
||||||
|
@canonical_email_blocks.last.id
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_since_id
|
||||||
|
@canonical_email_blocks.first.id
|
||||||
|
end
|
||||||
|
|
||||||
|
def records_continue?
|
||||||
|
@canonical_email_blocks.size == limit_param(LIMIT)
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_params(core_params)
|
||||||
|
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params)
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,90 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::V1::Admin::EmailDomainBlocksController < Api::BaseController
|
||||||
|
include Authorization
|
||||||
|
include AccountableConcern
|
||||||
|
|
||||||
|
LIMIT = 100
|
||||||
|
|
||||||
|
before_action -> { authorize_if_got_token! :'admin:read', :'admin:read:email_domain_blocks' }, only: [:index, :show]
|
||||||
|
before_action -> { authorize_if_got_token! :'admin:write', :'admin:write:email_domain_blocks' }, except: [:index, :show]
|
||||||
|
before_action :set_email_domain_blocks, only: :index
|
||||||
|
before_action :set_email_domain_block, only: [:show, :destroy]
|
||||||
|
|
||||||
|
after_action :verify_authorized
|
||||||
|
after_action :insert_pagination_headers, only: :index
|
||||||
|
|
||||||
|
PAGINATION_PARAMS = %i(
|
||||||
|
limit
|
||||||
|
).freeze
|
||||||
|
|
||||||
|
def create
|
||||||
|
authorize :email_domain_block, :create?
|
||||||
|
|
||||||
|
@email_domain_block = EmailDomainBlock.create!(resource_params)
|
||||||
|
log_action :create, @email_domain_block
|
||||||
|
|
||||||
|
render json: @email_domain_block, serializer: REST::Admin::EmailDomainBlockSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
def index
|
||||||
|
authorize :email_domain_block, :index?
|
||||||
|
render json: @email_domain_blocks, each_serializer: REST::Admin::EmailDomainBlockSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
authorize @email_domain_block, :show?
|
||||||
|
render json: @email_domain_block, serializer: REST::Admin::EmailDomainBlockSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
authorize @email_domain_block, :destroy?
|
||||||
|
|
||||||
|
@email_domain_block.destroy!
|
||||||
|
log_action :destroy, @email_domain_block
|
||||||
|
|
||||||
|
render json: @email_domain_block, serializer: REST::Admin::EmailDomainBlockSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_email_domain_blocks
|
||||||
|
@email_domain_blocks = EmailDomainBlock.order(id: :desc).to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_email_domain_block
|
||||||
|
@email_domain_block = EmailDomainBlock.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def resource_params
|
||||||
|
params.permit(:domain)
|
||||||
|
end
|
||||||
|
|
||||||
|
def insert_pagination_headers
|
||||||
|
set_pagination_headers(next_path, prev_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def next_path
|
||||||
|
api_v1_admin_email_domain_blocks_url(pagination_params(max_id: pagination_max_id)) if records_continue?
|
||||||
|
end
|
||||||
|
|
||||||
|
def prev_path
|
||||||
|
api_v1_admin_email_domain_blocks_url(pagination_params(min_id: pagination_since_id)) unless @email_domain_blocks.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_max_id
|
||||||
|
@email_domain_blocks.last.id
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_since_id
|
||||||
|
@email_domain_blocks.first.id
|
||||||
|
end
|
||||||
|
|
||||||
|
def records_continue?
|
||||||
|
@email_domain_blocks.size == limit_param(LIMIT)
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_params(core_params)
|
||||||
|
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params)
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,99 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::V1::Admin::IpBlocksController < Api::BaseController
|
||||||
|
include Authorization
|
||||||
|
include AccountableConcern
|
||||||
|
|
||||||
|
LIMIT = 100
|
||||||
|
|
||||||
|
before_action -> { authorize_if_got_token! :'admin:read', :'admin:read:ip_blocks' }, only: [:index, :show]
|
||||||
|
before_action -> { authorize_if_got_token! :'admin:write', :'admin:write:ip_blocks' }, except: [:index, :show]
|
||||||
|
before_action :set_ip_blocks, only: :index
|
||||||
|
before_action :set_ip_block, only: [:show, :update, :destroy]
|
||||||
|
|
||||||
|
after_action :verify_authorized
|
||||||
|
after_action :insert_pagination_headers, only: :index
|
||||||
|
|
||||||
|
PAGINATION_PARAMS = %i(
|
||||||
|
limit
|
||||||
|
).freeze
|
||||||
|
|
||||||
|
def create
|
||||||
|
authorize :ip_block, :create?
|
||||||
|
|
||||||
|
@ip_block = IpBlock.create!(resource_params)
|
||||||
|
log_action :create, @ip_block
|
||||||
|
|
||||||
|
render json: @ip_block, serializer: REST::Admin::IpBlockSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
def index
|
||||||
|
authorize :ip_block, :index?
|
||||||
|
render json: @ip_blocks, each_serializer: REST::Admin::IpBlockSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
authorize @ip_block, :show?
|
||||||
|
render json: @ip_block, serializer: REST::Admin::IpBlockSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
authorize @ip_block, :update?
|
||||||
|
|
||||||
|
@ip_block.update(resource_params)
|
||||||
|
log_action :update, @ip_block
|
||||||
|
|
||||||
|
render json: @ip_block, serializer: REST::Admin::IpBlockSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
authorize @ip_block, :destroy?
|
||||||
|
|
||||||
|
@ip_block.destroy!
|
||||||
|
log_action :destroy, @ip_block
|
||||||
|
|
||||||
|
render json: @ip_block, serializer: REST::Admin::IpBlockSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_ip_blocks
|
||||||
|
@ip_blocks = IpBlock.order(id: :desc).to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_ip_block
|
||||||
|
@ip_block = IpBlock.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def resource_params
|
||||||
|
params.permit(:ip, :severity, :comment, :expires_in)
|
||||||
|
end
|
||||||
|
|
||||||
|
def insert_pagination_headers
|
||||||
|
set_pagination_headers(next_path, prev_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def next_path
|
||||||
|
api_v1_admin_ip_blocks_url(pagination_params(max_id: pagination_max_id)) if records_continue?
|
||||||
|
end
|
||||||
|
|
||||||
|
def prev_path
|
||||||
|
api_v1_admin_ip_blocks_url(pagination_params(min_id: pagination_since_id)) unless @ip_blocks.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_max_id
|
||||||
|
@ip_blocks.last.id
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_since_id
|
||||||
|
@ip_blocks.first.id
|
||||||
|
end
|
||||||
|
|
||||||
|
def records_continue?
|
||||||
|
@ip_blocks.size == limit_param(LIMIT)
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_params(core_params)
|
||||||
|
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params)
|
||||||
|
end
|
||||||
|
end
|
@ -1,27 +0,0 @@
|
|||||||
export default () => new Promise((resolve, reject) => {
|
|
||||||
// ServiceWorker is required to synchronize the login state.
|
|
||||||
// Microsoft Edge 17 does not support getAll according to:
|
|
||||||
// Catalog of standard and vendor APIs across browsers - Microsoft Edge Development
|
|
||||||
// https://developer.microsoft.com/en-us/microsoft-edge/platform/catalog/?q=specName%3Aindexeddb
|
|
||||||
if (!('caches' in self && 'getAll' in IDBObjectStore.prototype)) {
|
|
||||||
reject();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const request = indexedDB.open('mastodon');
|
|
||||||
|
|
||||||
request.onerror = reject;
|
|
||||||
request.onsuccess = ({ target }) => resolve(target.result);
|
|
||||||
|
|
||||||
request.onupgradeneeded = ({ target }) => {
|
|
||||||
const accounts = target.result.createObjectStore('accounts', { autoIncrement: true });
|
|
||||||
const statuses = target.result.createObjectStore('statuses', { autoIncrement: true });
|
|
||||||
|
|
||||||
accounts.createIndex('id', 'id', { unique: true });
|
|
||||||
accounts.createIndex('moved', 'moved');
|
|
||||||
|
|
||||||
statuses.createIndex('id', 'id', { unique: true });
|
|
||||||
statuses.createIndex('account', 'account');
|
|
||||||
statuses.createIndex('reblog', 'reblog');
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,211 +0,0 @@
|
|||||||
import openDB from './db';
|
|
||||||
|
|
||||||
const accountAssetKeys = ['avatar', 'avatar_static', 'header', 'header_static'];
|
|
||||||
const storageMargin = 8388608;
|
|
||||||
const storeLimit = 1024;
|
|
||||||
|
|
||||||
// navigator.storage is not present on:
|
|
||||||
// Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.100 Safari/537.36 Edge/16.16299
|
|
||||||
// estimate method is not present on Chrome 57.0.2987.98 on Linux.
|
|
||||||
export const storageFreeable = 'storage' in navigator && 'estimate' in navigator.storage;
|
|
||||||
|
|
||||||
function openCache() {
|
|
||||||
// ServiceWorker and Cache API is not available on iOS 11
|
|
||||||
// https://webkit.org/status/#specification-service-workers
|
|
||||||
return self.caches ? caches.open('mastodon-system') : Promise.reject();
|
|
||||||
}
|
|
||||||
|
|
||||||
function printErrorIfAvailable(error) {
|
|
||||||
if (error) {
|
|
||||||
console.warn(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function put(name, objects, onupdate, oncreate) {
|
|
||||||
return openDB().then(db => (new Promise((resolve, reject) => {
|
|
||||||
const putTransaction = db.transaction(name, 'readwrite');
|
|
||||||
const putStore = putTransaction.objectStore(name);
|
|
||||||
const putIndex = putStore.index('id');
|
|
||||||
|
|
||||||
objects.forEach(object => {
|
|
||||||
putIndex.getKey(object.id).onsuccess = retrieval => {
|
|
||||||
function addObject() {
|
|
||||||
putStore.add(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteObject() {
|
|
||||||
putStore.delete(retrieval.target.result).onsuccess = addObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (retrieval.target.result) {
|
|
||||||
if (onupdate) {
|
|
||||||
onupdate(object, retrieval.target.result, putStore, deleteObject);
|
|
||||||
} else {
|
|
||||||
deleteObject();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (oncreate) {
|
|
||||||
oncreate(object, addObject);
|
|
||||||
} else {
|
|
||||||
addObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
putTransaction.oncomplete = () => {
|
|
||||||
const readTransaction = db.transaction(name, 'readonly');
|
|
||||||
const readStore = readTransaction.objectStore(name);
|
|
||||||
const count = readStore.count();
|
|
||||||
|
|
||||||
count.onsuccess = () => {
|
|
||||||
const excess = count.result - storeLimit;
|
|
||||||
|
|
||||||
if (excess > 0) {
|
|
||||||
const retrieval = readStore.getAll(null, excess);
|
|
||||||
|
|
||||||
retrieval.onsuccess = () => resolve(retrieval.result);
|
|
||||||
retrieval.onerror = reject;
|
|
||||||
} else {
|
|
||||||
resolve([]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
count.onerror = reject;
|
|
||||||
};
|
|
||||||
|
|
||||||
putTransaction.onerror = reject;
|
|
||||||
})).then(resolved => {
|
|
||||||
db.close();
|
|
||||||
return resolved;
|
|
||||||
}, error => {
|
|
||||||
db.close();
|
|
||||||
throw error;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
function evictAccountsByRecords(records) {
|
|
||||||
return openDB().then(db => {
|
|
||||||
const transaction = db.transaction(['accounts', 'statuses'], 'readwrite');
|
|
||||||
const accounts = transaction.objectStore('accounts');
|
|
||||||
const accountsIdIndex = accounts.index('id');
|
|
||||||
const accountsMovedIndex = accounts.index('moved');
|
|
||||||
const statuses = transaction.objectStore('statuses');
|
|
||||||
const statusesIndex = statuses.index('account');
|
|
||||||
|
|
||||||
function evict(toEvict) {
|
|
||||||
toEvict.forEach(record => {
|
|
||||||
openCache()
|
|
||||||
.then(cache => accountAssetKeys.forEach(key => cache.delete(records[key])))
|
|
||||||
.catch(printErrorIfAvailable);
|
|
||||||
|
|
||||||
accountsMovedIndex.getAll(record.id).onsuccess = ({ target }) => evict(target.result);
|
|
||||||
|
|
||||||
statusesIndex.getAll(record.id).onsuccess =
|
|
||||||
({ target }) => evictStatusesByRecords(target.result);
|
|
||||||
|
|
||||||
accountsIdIndex.getKey(record.id).onsuccess =
|
|
||||||
({ target }) => target.result && accounts.delete(target.result);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
evict(records);
|
|
||||||
|
|
||||||
db.close();
|
|
||||||
}).catch(printErrorIfAvailable);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function evictStatus(id) {
|
|
||||||
evictStatuses([id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function evictStatuses(ids) {
|
|
||||||
return openDB().then(db => {
|
|
||||||
const transaction = db.transaction('statuses', 'readwrite');
|
|
||||||
const store = transaction.objectStore('statuses');
|
|
||||||
const idIndex = store.index('id');
|
|
||||||
const reblogIndex = store.index('reblog');
|
|
||||||
|
|
||||||
ids.forEach(id => {
|
|
||||||
reblogIndex.getAllKeys(id).onsuccess =
|
|
||||||
({ target }) => target.result.forEach(reblogKey => store.delete(reblogKey));
|
|
||||||
|
|
||||||
idIndex.getKey(id).onsuccess =
|
|
||||||
({ target }) => target.result && store.delete(target.result);
|
|
||||||
});
|
|
||||||
|
|
||||||
db.close();
|
|
||||||
}).catch(printErrorIfAvailable);
|
|
||||||
}
|
|
||||||
|
|
||||||
function evictStatusesByRecords(records) {
|
|
||||||
return evictStatuses(records.map(({ id }) => id));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function putAccounts(records, avatarStatic) {
|
|
||||||
const avatarKey = avatarStatic ? 'avatar_static' : 'avatar';
|
|
||||||
const newURLs = [];
|
|
||||||
|
|
||||||
put('accounts', records, (newRecord, oldKey, store, oncomplete) => {
|
|
||||||
store.get(oldKey).onsuccess = ({ target }) => {
|
|
||||||
accountAssetKeys.forEach(key => {
|
|
||||||
const newURL = newRecord[key];
|
|
||||||
const oldURL = target.result[key];
|
|
||||||
|
|
||||||
if (newURL !== oldURL) {
|
|
||||||
openCache()
|
|
||||||
.then(cache => cache.delete(oldURL))
|
|
||||||
.catch(printErrorIfAvailable);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const newURL = newRecord[avatarKey];
|
|
||||||
const oldURL = target.result[avatarKey];
|
|
||||||
|
|
||||||
if (newURL !== oldURL) {
|
|
||||||
newURLs.push(newURL);
|
|
||||||
}
|
|
||||||
|
|
||||||
oncomplete();
|
|
||||||
};
|
|
||||||
}, (newRecord, oncomplete) => {
|
|
||||||
newURLs.push(newRecord[avatarKey]);
|
|
||||||
oncomplete();
|
|
||||||
}).then(records => Promise.all([
|
|
||||||
evictAccountsByRecords(records),
|
|
||||||
openCache().then(cache => cache.addAll(newURLs)),
|
|
||||||
])).then(freeStorage, error => {
|
|
||||||
freeStorage();
|
|
||||||
throw error;
|
|
||||||
}).catch(printErrorIfAvailable);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function putStatuses(records) {
|
|
||||||
put('statuses', records)
|
|
||||||
.then(evictStatusesByRecords)
|
|
||||||
.catch(printErrorIfAvailable);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function freeStorage() {
|
|
||||||
return storageFreeable && navigator.storage.estimate().then(({ quota, usage }) => {
|
|
||||||
if (usage + storageMargin < quota) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return openDB().then(db => new Promise((resolve, reject) => {
|
|
||||||
const retrieval = db.transaction('accounts', 'readonly').objectStore('accounts').getAll(null, 1);
|
|
||||||
|
|
||||||
retrieval.onsuccess = () => {
|
|
||||||
if (retrieval.result.length > 0) {
|
|
||||||
resolve(evictAccountsByRecords(retrieval.result).then(freeStorage));
|
|
||||||
} else {
|
|
||||||
resolve(caches.delete('mastodon-system'));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
retrieval.onerror = reject;
|
|
||||||
|
|
||||||
db.close();
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
|
@ -0,0 +1,23 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class CanonicalEmailBlockPolicy < ApplicationPolicy
|
||||||
|
def index?
|
||||||
|
role.can?(:manage_blocks)
|
||||||
|
end
|
||||||
|
|
||||||
|
def show?
|
||||||
|
role.can?(:manage_blocks)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test?
|
||||||
|
role.can?(:manage_blocks)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create?
|
||||||
|
role.can?(:manage_blocks)
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy?
|
||||||
|
role.can?(:manage_blocks)
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,9 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class REST::Admin::CanonicalEmailBlockSerializer < ActiveModel::Serializer
|
||||||
|
attributes :id, :canonical_email_hash
|
||||||
|
|
||||||
|
def id
|
||||||
|
object.id.to_s
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,9 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class REST::Admin::EmailDomainBlockSerializer < ActiveModel::Serializer
|
||||||
|
attributes :id, :domain, :created_at, :history
|
||||||
|
|
||||||
|
def id
|
||||||
|
object.id.to_s
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,14 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class REST::Admin::IpBlockSerializer < ActiveModel::Serializer
|
||||||
|
attributes :id, :ip, :severity, :comment,
|
||||||
|
:created_at, :expires_at
|
||||||
|
|
||||||
|
def id
|
||||||
|
object.id.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def ip
|
||||||
|
"#{object.ip}/#{object.ip.prefix}"
|
||||||
|
end
|
||||||
|
end
|
@ -1,44 +0,0 @@
|
|||||||
%table.email-table{ cellspacing: 0, cellpadding: 0 }
|
|
||||||
%tbody
|
|
||||||
%tr
|
|
||||||
%td.email-body
|
|
||||||
.email-container
|
|
||||||
%table.content-section{ cellspacing: 0, cellpadding: 0 }
|
|
||||||
%tbody
|
|
||||||
%tr
|
|
||||||
%td.content-cell.darker.hero-with-button
|
|
||||||
.email-row
|
|
||||||
.col-6
|
|
||||||
%table.column{ cellspacing: 0, cellpadding: 0 }
|
|
||||||
%tbody
|
|
||||||
%tr
|
|
||||||
%td.column-cell.text-center.padded
|
|
||||||
%h1= t 'notification_mailer.digest.title'
|
|
||||||
%p.lead= t('notification_mailer.digest.body', since: l((@me.user_current_sign_in_at || @since).to_date, format: :short), instance: site_hostname)
|
|
||||||
%table.button{ align: 'center', cellspacing: 0, cellpadding: 0 }
|
|
||||||
%tbody
|
|
||||||
%tr
|
|
||||||
%td.button-primary
|
|
||||||
= link_to web_url do
|
|
||||||
%span= t 'notification_mailer.digest.action'
|
|
||||||
|
|
||||||
- @notifications.each_with_index do |n, i|
|
|
||||||
= render 'status', status: n.target_status, i: i
|
|
||||||
|
|
||||||
- unless @follows_since.zero?
|
|
||||||
%table.email-table{ cellspacing: 0, cellpadding: 0 }
|
|
||||||
%tbody
|
|
||||||
%tr
|
|
||||||
%td.email-body
|
|
||||||
.email-container
|
|
||||||
%table.content-section{ cellspacing: 0, cellpadding: 0 }
|
|
||||||
%tbody
|
|
||||||
%tr
|
|
||||||
%td.content-cell.content-start.border-top
|
|
||||||
.email-row
|
|
||||||
.col-6
|
|
||||||
%table.column{ cellspacing: 0, cellpadding: 0 }
|
|
||||||
%tbody
|
|
||||||
%tr
|
|
||||||
%td.column-cell.text-center
|
|
||||||
%p= t('notification_mailer.digest.new_followers_summary', count: @follows_since)
|
|
@ -1,15 +0,0 @@
|
|||||||
<%= raw t('application_mailer.salutation', name: display_name(@me)) %>
|
|
||||||
|
|
||||||
<%= raw t('notification_mailer.digest.body', since: l(@me.user_current_sign_in_at || @since), instance: root_url) %>
|
|
||||||
<% @notifications.each do |notification| %>
|
|
||||||
|
|
||||||
* <%= raw t('notification_mailer.digest.mention', name: notification.from_account.pretty_acct) %>
|
|
||||||
|
|
||||||
<%= raw extract_status_plain_text(notification.target_status) %>
|
|
||||||
|
|
||||||
<%= raw t('application_mailer.view')%> <%= web_url("statuses/#{notification.target_status.id}") %>
|
|
||||||
<% end %>
|
|
||||||
<% if @follows_since > 0 %>
|
|
||||||
|
|
||||||
<%= raw t('notification_mailer.digest.new_followers_summary', count: @follows_since) %>
|
|
||||||
<% end %>
|
|
@ -1,21 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class DigestMailerWorker
|
|
||||||
include Sidekiq::Worker
|
|
||||||
|
|
||||||
sidekiq_options queue: 'mailers'
|
|
||||||
|
|
||||||
attr_reader :user
|
|
||||||
|
|
||||||
def perform(user_id)
|
|
||||||
@user = User.find(user_id)
|
|
||||||
deliver_digest if @user.allows_digest_emails?
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def deliver_digest
|
|
||||||
NotificationMailer.digest(user.account).deliver_now!
|
|
||||||
user.touch(:last_emailed_at)
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,25 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Scheduler::EmailScheduler
|
|
||||||
include Sidekiq::Worker
|
|
||||||
|
|
||||||
sidekiq_options retry: 0
|
|
||||||
|
|
||||||
FREQUENCY = 7.days.freeze
|
|
||||||
SIGN_IN_OFFSET = 1.day.freeze
|
|
||||||
|
|
||||||
def perform
|
|
||||||
eligible_users.reorder(nil).find_each do |user|
|
|
||||||
next unless user.allows_digest_emails?
|
|
||||||
DigestMailerWorker.perform_async(user.id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def eligible_users
|
|
||||||
User.emailable
|
|
||||||
.where('current_sign_in_at < ?', (FREQUENCY + SIGN_IN_OFFSET).ago)
|
|
||||||
.where('last_emailed_at IS NULL OR last_emailed_at < ?', FREQUENCY.ago)
|
|
||||||
end
|
|
||||||
end
|
|
@ -0,0 +1,7 @@
|
|||||||
|
class AddHumanIdentifierToAdminActionLogs < ActiveRecord::Migration[6.1]
|
||||||
|
def change
|
||||||
|
add_column :admin_action_logs, :human_identifier, :string
|
||||||
|
add_column :admin_action_logs, :route_param, :string
|
||||||
|
add_column :admin_action_logs, :permalink, :string
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,5 @@
|
|||||||
|
class ChangeCanonicalEmailBlocksNullable < ActiveRecord::Migration[6.1]
|
||||||
|
def change
|
||||||
|
safety_assured { change_column :canonical_email_blocks, :reference_account_id, :bigint, null: true, default: nil }
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,20 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class FixCustomFilterKeywordsIdSeq < ActiveRecord::Migration[6.1]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
# 20220613110711 manually inserts items with set `id` in the database, but
|
||||||
|
# we also need to bump the sequence number, otherwise
|
||||||
|
safety_assured do
|
||||||
|
execute <<-SQL.squish
|
||||||
|
BEGIN;
|
||||||
|
LOCK TABLE custom_filter_keywords IN EXCLUSIVE MODE;
|
||||||
|
SELECT setval('custom_filter_keywords_id_seq'::regclass, id) FROM custom_filter_keywords ORDER BY id DESC LIMIT 1;
|
||||||
|
COMMIT;
|
||||||
|
SQL
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down; end
|
||||||
|
end
|
@ -0,0 +1,9 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class RemoveRecordedChangesFromAdminActionLogs < ActiveRecord::Migration[5.2]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def change
|
||||||
|
safety_assured { remove_column :admin_action_logs, :recorded_changes, :text }
|
||||||
|
end
|
||||||
|
end
|
@ -1 +1 @@
|
|||||||
assets/sw.js
|
packs/sw.js
|
@ -0,0 +1 @@
|
|||||||
|
packs/sw.js.map
|
@ -1,36 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
describe DigestMailerWorker do
|
|
||||||
describe 'perform' do
|
|
||||||
let(:user) { Fabricate(:user, last_emailed_at: 3.days.ago) }
|
|
||||||
|
|
||||||
context 'for a user who receives digests' do
|
|
||||||
it 'sends the email' do
|
|
||||||
service = double(deliver_now!: nil)
|
|
||||||
allow(NotificationMailer).to receive(:digest).and_return(service)
|
|
||||||
update_user_digest_setting(true)
|
|
||||||
described_class.perform_async(user.id)
|
|
||||||
|
|
||||||
expect(NotificationMailer).to have_received(:digest)
|
|
||||||
expect(user.reload.last_emailed_at).to be_within(1).of(Time.now.utc)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'for a user who does not receive digests' do
|
|
||||||
it 'does not send the email' do
|
|
||||||
allow(NotificationMailer).to receive(:digest)
|
|
||||||
update_user_digest_setting(false)
|
|
||||||
described_class.perform_async(user.id)
|
|
||||||
|
|
||||||
expect(NotificationMailer).not_to have_received(:digest)
|
|
||||||
expect(user.last_emailed_at).to be_within(1).of(3.days.ago)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_user_digest_setting(value)
|
|
||||||
user.settings['notification_emails'] = user.settings['notification_emails'].merge('digest' => value)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Reference in new issue