mirror of
https://github.com/flarum/core.git
synced 2025-08-21 07:41:49 +02:00
Compare commits
12 Commits
v1.1.0
...
dk/advance
Author | SHA1 | Date | |
---|---|---|---|
|
55050914f3 | ||
|
961390da46 | ||
|
60300939bc | ||
|
c1754af74a | ||
|
731fae666f | ||
|
6006ad00a2 | ||
|
7cd67720d3 | ||
|
65a5ed4e86 | ||
|
72780f514f | ||
|
93dbd4ec86 | ||
|
df2c4323ff | ||
|
06e5922be5 |
@@ -34,6 +34,7 @@ import EditCustomCssModal from './components/EditCustomCssModal';
|
|||||||
import EditGroupModal from './components/EditGroupModal';
|
import EditGroupModal from './components/EditGroupModal';
|
||||||
import routes from './routes';
|
import routes from './routes';
|
||||||
import AdminApplication from './AdminApplication';
|
import AdminApplication from './AdminApplication';
|
||||||
|
import AdvancedPage from './components/AdvancedPage';
|
||||||
|
|
||||||
export default Object.assign(compat, {
|
export default Object.assign(compat, {
|
||||||
'utils/saveSettings': saveSettings,
|
'utils/saveSettings': saveSettings,
|
||||||
@@ -68,6 +69,7 @@ export default Object.assign(compat, {
|
|||||||
'components/AdminHeader': AdminHeader,
|
'components/AdminHeader': AdminHeader,
|
||||||
'components/EditCustomCssModal': EditCustomCssModal,
|
'components/EditCustomCssModal': EditCustomCssModal,
|
||||||
'components/EditGroupModal': EditGroupModal,
|
'components/EditGroupModal': EditGroupModal,
|
||||||
|
'components/AdvancedPage': AdvancedPage,
|
||||||
routes: routes,
|
routes: routes,
|
||||||
AdminApplication: AdminApplication,
|
AdminApplication: AdminApplication,
|
||||||
});
|
});
|
||||||
|
@@ -75,6 +75,16 @@ export default class AdminNav extends Component {
|
|||||||
</LinkButton>
|
</LinkButton>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// We only display the advanced pane when a certain threshold is reached or it is manually activated.
|
||||||
|
if (app.data.settings['advanced_settings_pane_enabled']) {
|
||||||
|
items.add(
|
||||||
|
'advanced',
|
||||||
|
<LinkButton href={app.route('advanced')} icon="fas fa-rocket" title={app.translator.trans('core.admin.nav.advanced_title')}>
|
||||||
|
{app.translator.trans('core.admin.nav.advanced_button')}
|
||||||
|
</LinkButton>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
items.add(
|
items.add(
|
||||||
'mail',
|
'mail',
|
||||||
<LinkButton href={app.route('mail')} icon="fas fa-envelope" title={app.translator.trans('core.admin.nav.email_title')}>
|
<LinkButton href={app.route('mail')} icon="fas fa-envelope" title={app.translator.trans('core.admin.nav.email_title')}>
|
||||||
|
40
js/src/admin/components/AdvancedPage.js
Normal file
40
js/src/admin/components/AdvancedPage.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import FieldSet from '../../common/components/FieldSet';
|
||||||
|
import ItemList from '../../common/utils/ItemList';
|
||||||
|
import AdminPage from './AdminPage';
|
||||||
|
import Alert from '../../common/components/Alert';
|
||||||
|
|
||||||
|
export default class AdvancedPage extends AdminPage {
|
||||||
|
oninit(vnode) {
|
||||||
|
super.oninit(vnode);
|
||||||
|
|
||||||
|
this.queueDrivers = {};
|
||||||
|
|
||||||
|
app.data.queueDrivers.forEach((driver) => {
|
||||||
|
this.queueDrivers[driver] = app.translator.trans('core.admin.queue.' + driver);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
headerInfo() {
|
||||||
|
return {
|
||||||
|
className: 'AdvancedPage',
|
||||||
|
icon: 'fas fa-rocket',
|
||||||
|
title: app.translator.trans('core.admin.advanced.title'),
|
||||||
|
description: app.translator.trans('core.admin.advanced.description'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
content() {
|
||||||
|
return [
|
||||||
|
<div className="Form">
|
||||||
|
{this.buildSettingComponent({
|
||||||
|
type: 'select',
|
||||||
|
setting: 'queue_driver',
|
||||||
|
options: Object.keys(this.queueDrivers).reduce((memo, val) => ({ ...memo, [val]: val }), {}),
|
||||||
|
label: app.translator.trans('core.admin.queue.driver_heading'),
|
||||||
|
className: 'AdvancedPage-QueueSettings',
|
||||||
|
})}
|
||||||
|
{this.submitButton()}
|
||||||
|
</div>,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
158
js/src/admin/components/AdvancedPage.tsx
Normal file
158
js/src/admin/components/AdvancedPage.tsx
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
import Mithril from 'mithril';
|
||||||
|
import Link from '../../common/components/Link';
|
||||||
|
import classList from '../../common/utils/classList';
|
||||||
|
import ItemList from '../../common/utils/ItemList';
|
||||||
|
import app from '../app';
|
||||||
|
|
||||||
|
import AdminPage from './AdminPage';
|
||||||
|
|
||||||
|
export interface IAdvancedPageAttrs extends Mithril.Attributes {}
|
||||||
|
|
||||||
|
export interface ICreateDriverComponentOptions<Options extends string[]> {
|
||||||
|
/**
|
||||||
|
* The default driver value.
|
||||||
|
*
|
||||||
|
* This will appear selected if the driver is not specified.
|
||||||
|
*/
|
||||||
|
defaultValue: Options[number];
|
||||||
|
/**
|
||||||
|
* Custom class to apply to the `<select>` component.
|
||||||
|
*
|
||||||
|
* This is applied in addition to the default `AdvancedPage-driverSelect` class.
|
||||||
|
*/
|
||||||
|
className: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class AdvancedPage extends AdminPage {
|
||||||
|
oninit(vnode: Mithril.Vnode<IAdvancedPageAttrs, this>) {
|
||||||
|
super.oninit(vnode);
|
||||||
|
}
|
||||||
|
|
||||||
|
headerInfo() {
|
||||||
|
return {
|
||||||
|
className: 'AdvancedPage',
|
||||||
|
icon: 'fas fa-rocket',
|
||||||
|
title: app.translator.trans('core.admin.advanced.title'),
|
||||||
|
description: app.translator.trans('core.admin.advanced.description'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
content() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<form class="Form">{this.items().toArray()}</form>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
items(): ItemList {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
if (!app.data.settings.advanced_settings_pane_enabled) {
|
||||||
|
items.add(
|
||||||
|
'page_not_enabled',
|
||||||
|
// TODO: Add link to docs page
|
||||||
|
<p class="AdvancedPage-notEnabledWarning">
|
||||||
|
{app.translator.trans('core.admin.advanced.not_enabled_warning', {
|
||||||
|
a: <Link external href="https://docs.flarum.org/" />,
|
||||||
|
icon: <span aria-label={app.translator.trans('core.admin.advanced.warning_icon_accessible_label')} class="fas fa-exclamation-triangle" />,
|
||||||
|
})}
|
||||||
|
</p>,
|
||||||
|
110
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
items.add(
|
||||||
|
'large_community_text',
|
||||||
|
// TODO: Add link to docs page
|
||||||
|
<p class="AdvancedPage-congratsText">
|
||||||
|
{app.translator.trans('core.admin.advanced.large_community_note', {
|
||||||
|
a: <Link external href="https://docs.flarum.org/" />,
|
||||||
|
icon: <span aria-label={app.translator.trans('core.admin.advanced.info_icon_accessible_label')} class="fas fa-info-circle" />,
|
||||||
|
})}
|
||||||
|
</p>,
|
||||||
|
110
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
items.add(
|
||||||
|
'drivers',
|
||||||
|
<fieldset class="Form-group AdvancedPage-category">
|
||||||
|
<legend>{app.translator.trans('core.admin.advanced.drivers.legend')}</legend>
|
||||||
|
|
||||||
|
{this.drivers().toArray()}
|
||||||
|
</fieldset>,
|
||||||
|
90
|
||||||
|
);
|
||||||
|
|
||||||
|
items.add('save', this.submitButton(), -10);
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
drivers(): ItemList {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
items.add(
|
||||||
|
'queueDriver',
|
||||||
|
this.createDriverComponent('queue_driver', 'core.admin.advanced.drivers.queue', app.data.queueDrivers, {
|
||||||
|
className: 'AdvancedPage-queueDriver',
|
||||||
|
defaultValue: 'database',
|
||||||
|
}),
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a form component for a given driver.
|
||||||
|
*
|
||||||
|
* Requires the follow translations under the given prefix:
|
||||||
|
* - `driver_heading` (shown as legend for the form group)
|
||||||
|
* - `driver_label` (shown as the label for the select box)
|
||||||
|
* - `names.{driver_id}` (shown as the options for the select box)
|
||||||
|
*
|
||||||
|
* @param settingKey The setting key for the driver.
|
||||||
|
* @param driverTranslatorPrefix The prefix used for translations.
|
||||||
|
* @param driverOptions An array of possible driver values.
|
||||||
|
* @param options Optional settings for the component.
|
||||||
|
*
|
||||||
|
* @example <caption>Queue driver</caption>
|
||||||
|
* this.createDriverComponent(
|
||||||
|
* 'queue_driver',
|
||||||
|
* 'core.admin.advanced.drivers.queue',
|
||||||
|
* [ 'database', 'sync' ],
|
||||||
|
* },
|
||||||
|
* {
|
||||||
|
* defaultValue: 'database',
|
||||||
|
* },
|
||||||
|
* );
|
||||||
|
*/
|
||||||
|
createDriverComponent<Options extends string[]>(
|
||||||
|
settingKey: string,
|
||||||
|
driverTranslatorPrefix: string,
|
||||||
|
driverOptions: Options,
|
||||||
|
options: Partial<ICreateDriverComponentOptions<Options>> = {}
|
||||||
|
): JSX.Element {
|
||||||
|
return (
|
||||||
|
<fieldset class="Form-group">
|
||||||
|
<legend>{app.translator.trans(`${driverTranslatorPrefix}.driver_heading`)}</legend>
|
||||||
|
|
||||||
|
{this.buildSettingComponent({
|
||||||
|
type: 'select',
|
||||||
|
setting: settingKey,
|
||||||
|
options: driverOptions.reduce(
|
||||||
|
(acc, value) => ({
|
||||||
|
...acc,
|
||||||
|
[value]: app.translator.trans(`${driverTranslatorPrefix}.names.${value}`),
|
||||||
|
}),
|
||||||
|
{} as Record<Options[number], ReturnType<typeof app.translator.trans>>
|
||||||
|
),
|
||||||
|
default: options.defaultValue,
|
||||||
|
label: app.translator.trans(`${driverTranslatorPrefix}.driver_label`),
|
||||||
|
className: classList('AdvancedPage-driverSelect', options.className),
|
||||||
|
})}
|
||||||
|
</fieldset>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,5 @@
|
|||||||
import DashboardPage from './components/DashboardPage';
|
import DashboardPage from './components/DashboardPage';
|
||||||
|
import AdvancedPage from './components/AdvancedPage';
|
||||||
import BasicsPage from './components/BasicsPage';
|
import BasicsPage from './components/BasicsPage';
|
||||||
import PermissionsPage from './components/PermissionsPage';
|
import PermissionsPage from './components/PermissionsPage';
|
||||||
import AppearancePage from './components/AppearancePage';
|
import AppearancePage from './components/AppearancePage';
|
||||||
@@ -8,16 +9,18 @@ import ExtensionPage from './components/ExtensionPage';
|
|||||||
import ExtensionPageResolver from './resolvers/ExtensionPageResolver';
|
import ExtensionPageResolver from './resolvers/ExtensionPageResolver';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `routes` initializer defines the forum app's routes.
|
* The `routes` initializer defines the admin app's routes.
|
||||||
*
|
*
|
||||||
* @param {App} app
|
* @param {import('./app').default} app
|
||||||
*/
|
*/
|
||||||
export default function (app) {
|
export default function (app) {
|
||||||
app.routes = {
|
app.routes = {
|
||||||
dashboard: { path: '/', component: DashboardPage },
|
dashboard: { path: '/', component: DashboardPage },
|
||||||
basics: { path: '/basics', component: BasicsPage },
|
basics: { path: '/basics', component: BasicsPage },
|
||||||
|
advanced: { path: '/advanced', component: AdvancedPage },
|
||||||
permissions: { path: '/permissions', component: PermissionsPage },
|
permissions: { path: '/permissions', component: PermissionsPage },
|
||||||
appearance: { path: '/appearance', component: AppearancePage },
|
appearance: { path: '/appearance', component: AppearancePage },
|
||||||
|
advanced: { path: '/advanced', component: AdvancedPage },
|
||||||
mail: { path: '/mail', component: MailPage },
|
mail: { path: '/mail', component: MailPage },
|
||||||
users: { path: '/users', component: UserListPage },
|
users: { path: '/users', component: UserListPage },
|
||||||
extension: { path: '/extension/:id', component: ExtensionPage, resolverClass: ExtensionPageResolver },
|
extension: { path: '/extension/:id', component: ExtensionPage, resolverClass: ExtensionPageResolver },
|
||||||
|
@@ -11,3 +11,4 @@
|
|||||||
@import "admin/AppearancePage";
|
@import "admin/AppearancePage";
|
||||||
@import "admin/MailPage";
|
@import "admin/MailPage";
|
||||||
@import "admin/UsersListPage.less";
|
@import "admin/UsersListPage.less";
|
||||||
|
@import "admin/AdvancedPage.less";
|
||||||
|
33
less/admin/AdvancedPage.less
Normal file
33
less/admin/AdvancedPage.less
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
.AdvancedPage {
|
||||||
|
&-notEnabledWarning,
|
||||||
|
&-congratsText {
|
||||||
|
padding: 8px 8px 8px 12px;
|
||||||
|
max-width: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-notEnabledWarning {
|
||||||
|
border-left: 8px solid @error-color;
|
||||||
|
background: fade(@error-color, 5%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-congratsText {
|
||||||
|
border-left: 8px solid @control-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-category {
|
||||||
|
&:first-of-type {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> legend {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
> legend {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -6,6 +6,35 @@ core:
|
|||||||
|
|
||||||
# Translations in this namespace are used by the admin interface.
|
# Translations in this namespace are used by the admin interface.
|
||||||
admin:
|
admin:
|
||||||
|
advanced:
|
||||||
|
description: Settings relating to Flarum's scalability.
|
||||||
|
|
||||||
|
drivers:
|
||||||
|
legend: Drivers
|
||||||
|
|
||||||
|
queue:
|
||||||
|
driver_heading: Queue Driver
|
||||||
|
driver_label: Choose a Queue Driver
|
||||||
|
|
||||||
|
names:
|
||||||
|
database: Database
|
||||||
|
sync: Sync
|
||||||
|
|
||||||
|
info_icon_accessible_label: information symbol
|
||||||
|
# Shown on the page when it's meant to be enabled
|
||||||
|
large_community_note: |
|
||||||
|
<icon></icon> This page is intended for very active communities, like yours! Great job! Modifying
|
||||||
|
some of these settings may require advanced setup or create extra load for your
|
||||||
|
installation. <a>Learn more about these settings.</a>
|
||||||
|
# Shown on the page when it's hidden and was directly navigated to
|
||||||
|
not_enabled_warning: |
|
||||||
|
<icon></icon> This page is intended for very active communities. This page is hidden for your
|
||||||
|
forum because you don't meet the recommended criteria for modifying these settings.
|
||||||
|
Doing so may require advanced setup or create extra load for your installation.
|
||||||
|
<a>Learn more about these settings.</a>
|
||||||
|
|
||||||
|
title: Advanced settings
|
||||||
|
warning_icon_accessible_label: warning symbol
|
||||||
|
|
||||||
# These translations are used in the Appearance page.
|
# These translations are used in the Appearance page.
|
||||||
appearance:
|
appearance:
|
||||||
@@ -143,6 +172,8 @@ core:
|
|||||||
|
|
||||||
# These translations are used in the navigation bar.
|
# These translations are used in the navigation bar.
|
||||||
nav:
|
nav:
|
||||||
|
advanced_button: => core.admin.advanced.title
|
||||||
|
advanced_title: => core.admin.advanced.description
|
||||||
appearance_button: => core.admin.appearance.title
|
appearance_button: => core.admin.appearance.title
|
||||||
appearance_title: => core.admin.appearance.description
|
appearance_title: => core.admin.appearance.description
|
||||||
basics_button: => core.admin.basics.title
|
basics_button: => core.admin.basics.title
|
||||||
|
24
migrations/2021_07_13_141000_queue_failed_jobs_table.php
Normal file
24
migrations/2021_07_13_141000_queue_failed_jobs_table.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Flarum\Database\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
|
||||||
|
return Migration::createTable(
|
||||||
|
'queue_failed_jobs',
|
||||||
|
function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('uuid')->unique();
|
||||||
|
$table->text('connection')->nullable();
|
||||||
|
$table->text('queue');
|
||||||
|
$table->longText('payload');
|
||||||
|
$table->longText('exception');
|
||||||
|
$table->timestamp('failed_at')->useCurrent();
|
||||||
|
}
|
||||||
|
);
|
24
migrations/2021_07_13_141000_queue_jobs_table.php
Normal file
24
migrations/2021_07_13_141000_queue_jobs_table.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Flarum\Database\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
|
||||||
|
return Migration::createTable(
|
||||||
|
'queue_jobs',
|
||||||
|
function (Blueprint $table) {
|
||||||
|
$table->bigIncrements('id');
|
||||||
|
$table->string('queue')->index();
|
||||||
|
$table->longText('payload');
|
||||||
|
$table->unsignedTinyInteger('attempts');
|
||||||
|
$table->unsignedInteger('reserved_at')->nullable();
|
||||||
|
$table->unsignedInteger('available_at');
|
||||||
|
$table->unsignedInteger('created_at');
|
||||||
|
}
|
||||||
|
);
|
@@ -79,6 +79,7 @@ class AdminPayload
|
|||||||
$document->payload['slugDrivers'] = array_map(function ($resourceDrivers) {
|
$document->payload['slugDrivers'] = array_map(function ($resourceDrivers) {
|
||||||
return array_keys($resourceDrivers);
|
return array_keys($resourceDrivers);
|
||||||
}, $this->container->make('flarum.http.slugDrivers'));
|
}, $this->container->make('flarum.http.slugDrivers'));
|
||||||
|
$document->payload['queueDrivers'] = array_keys($this->container->make('flarum.queue.supported_drivers'));
|
||||||
|
|
||||||
$document->payload['phpVersion'] = PHP_VERSION;
|
$document->payload['phpVersion'] = PHP_VERSION;
|
||||||
$document->payload['mysqlVersion'] = $this->db->selectOne('select version() as version')->version;
|
$document->payload['mysqlVersion'] = $this->db->selectOne('select version() as version')->version;
|
||||||
|
@@ -112,7 +112,7 @@ class ApiServiceProvider extends AbstractServiceProvider
|
|||||||
HttpMiddleware\AuthenticateWithSession::class,
|
HttpMiddleware\AuthenticateWithSession::class,
|
||||||
HttpMiddleware\AuthenticateWithHeader::class,
|
HttpMiddleware\AuthenticateWithHeader::class,
|
||||||
HttpMiddleware\CheckCsrfToken::class,
|
HttpMiddleware\CheckCsrfToken::class,
|
||||||
HttpMiddleware\RememberFromCookie::class,
|
// HttpMiddleware\RememberFromCookie::class,
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -14,6 +14,7 @@ use Illuminate\Contracts\Container\Container;
|
|||||||
use Illuminate\Database\Capsule\Manager;
|
use Illuminate\Database\Capsule\Manager;
|
||||||
use Illuminate\Database\ConnectionInterface;
|
use Illuminate\Database\ConnectionInterface;
|
||||||
use Illuminate\Database\ConnectionResolverInterface;
|
use Illuminate\Database\ConnectionResolverInterface;
|
||||||
|
use Illuminate\Database\DatabaseTransactionsManager;
|
||||||
|
|
||||||
class DatabaseServiceProvider extends AbstractServiceProvider
|
class DatabaseServiceProvider extends AbstractServiceProvider
|
||||||
{
|
{
|
||||||
@@ -63,6 +64,10 @@ class DatabaseServiceProvider extends AbstractServiceProvider
|
|||||||
$this->container->singleton('flarum.database.model_private_checkers', function () {
|
$this->container->singleton('flarum.database.model_private_checkers', function () {
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$this->container->singleton('db.transactions', function () {
|
||||||
|
return new DatabaseTransactionsManager;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function boot(Container $container)
|
public function boot(Container $container)
|
||||||
|
@@ -131,6 +131,7 @@ class InstalledSite implements SiteInterface
|
|||||||
$laravel->register(NotificationServiceProvider::class);
|
$laravel->register(NotificationServiceProvider::class);
|
||||||
$laravel->register(PostServiceProvider::class);
|
$laravel->register(PostServiceProvider::class);
|
||||||
$laravel->register(QueueServiceProvider::class);
|
$laravel->register(QueueServiceProvider::class);
|
||||||
|
$laravel->register(ScalabilityServiceProvider::class);
|
||||||
$laravel->register(SearchServiceProvider::class);
|
$laravel->register(SearchServiceProvider::class);
|
||||||
$laravel->register(SessionServiceProvider::class);
|
$laravel->register(SessionServiceProvider::class);
|
||||||
$laravel->register(SettingsServiceProvider::class);
|
$laravel->register(SettingsServiceProvider::class);
|
||||||
|
62
src/Foundation/ScalabilityServiceProvider.php
Normal file
62
src/Foundation/ScalabilityServiceProvider.php
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Foundation;
|
||||||
|
|
||||||
|
use Flarum\Settings\Event\Deserializing;
|
||||||
|
use Flarum\Settings\SettingsRepositoryInterface;
|
||||||
|
use Illuminate\Contracts\Cache\Repository;
|
||||||
|
use Illuminate\Contracts\Events\Dispatcher;
|
||||||
|
use Illuminate\Contracts\Queue\Queue;
|
||||||
|
use Illuminate\Queue\Events\JobProcessing;
|
||||||
|
use Illuminate\Queue\SyncQueue;
|
||||||
|
|
||||||
|
class ScalabilityServiceProvider extends AbstractServiceProvider
|
||||||
|
{
|
||||||
|
public function boot(Dispatcher $events, Queue $queue)
|
||||||
|
{
|
||||||
|
if ($queue instanceof SyncQueue) {
|
||||||
|
$events->listen(JobProcessing::class, [$this, 'trackQueueLoad']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$events->listen(Deserializing::class, [$this, 'recommendations']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function trackQueueLoad(JobProcessing $event)
|
||||||
|
{
|
||||||
|
/** @var Repository $cache */
|
||||||
|
$cache = resolve('cache.store');
|
||||||
|
|
||||||
|
// Retrieve existing queue load.
|
||||||
|
$count = (int) $cache->get('flarum.scalability.queue-load', 0);
|
||||||
|
|
||||||
|
$count++;
|
||||||
|
|
||||||
|
// Store the queue load, but only for one minute.
|
||||||
|
$cache->set('flarum.scalability.queue-load', $count, 60);
|
||||||
|
|
||||||
|
// If within that minute 10 queue tasks were fired, we need to suggest an alternative driver.
|
||||||
|
if ($count > 10) {
|
||||||
|
/** @var SettingsRepositoryInterface $settings */
|
||||||
|
$settings = resolve(SettingsRepositoryInterface::class);
|
||||||
|
$settings->set('flarum.scalability.queue-recommended', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function recommendations(Deserializing $event)
|
||||||
|
{
|
||||||
|
/** @var Config $config */
|
||||||
|
$config = resolve(Config::class);
|
||||||
|
|
||||||
|
// Toggles the advanced pane for admins.
|
||||||
|
$event->settings['advanced_settings_pane_enabled'] = $event->settings['flarum.scalability.queue-recommended']
|
||||||
|
?? $config->offsetGet('advancedSettings')
|
||||||
|
?? false;
|
||||||
|
}
|
||||||
|
}
|
@@ -14,6 +14,7 @@ use Flarum\Foundation\Config;
|
|||||||
use Flarum\Foundation\ErrorHandling\Registry;
|
use Flarum\Foundation\ErrorHandling\Registry;
|
||||||
use Flarum\Foundation\ErrorHandling\Reporter;
|
use Flarum\Foundation\ErrorHandling\Reporter;
|
||||||
use Flarum\Foundation\Paths;
|
use Flarum\Foundation\Paths;
|
||||||
|
use Flarum\Settings\SettingsRepositoryInterface;
|
||||||
use Illuminate\Contracts\Cache\Factory as CacheFactory;
|
use Illuminate\Contracts\Cache\Factory as CacheFactory;
|
||||||
use Illuminate\Contracts\Container\Container;
|
use Illuminate\Contracts\Container\Container;
|
||||||
use Illuminate\Contracts\Debug\ExceptionHandler as ExceptionHandling;
|
use Illuminate\Contracts\Debug\ExceptionHandler as ExceptionHandling;
|
||||||
@@ -22,11 +23,13 @@ use Illuminate\Contracts\Queue\Factory;
|
|||||||
use Illuminate\Contracts\Queue\Queue;
|
use Illuminate\Contracts\Queue\Queue;
|
||||||
use Illuminate\Queue\Connectors\ConnectorInterface;
|
use Illuminate\Queue\Connectors\ConnectorInterface;
|
||||||
use Illuminate\Queue\Console as Commands;
|
use Illuminate\Queue\Console as Commands;
|
||||||
|
use Illuminate\Queue\DatabaseQueue;
|
||||||
use Illuminate\Queue\Events\JobFailed;
|
use Illuminate\Queue\Events\JobFailed;
|
||||||
use Illuminate\Queue\Failed\NullFailedJobProvider;
|
use Illuminate\Queue\Failed\DatabaseFailedJobProvider;
|
||||||
use Illuminate\Queue\Listener as QueueListener;
|
use Illuminate\Queue\Listener as QueueListener;
|
||||||
use Illuminate\Queue\SyncQueue;
|
use Illuminate\Queue\SyncQueue;
|
||||||
use Illuminate\Queue\Worker;
|
use Illuminate\Queue\Worker;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
class QueueServiceProvider extends AbstractServiceProvider
|
class QueueServiceProvider extends AbstractServiceProvider
|
||||||
{
|
{
|
||||||
@@ -42,6 +45,34 @@ class QueueServiceProvider extends AbstractServiceProvider
|
|||||||
|
|
||||||
public function register()
|
public function register()
|
||||||
{
|
{
|
||||||
|
$this->container->singleton('flarum.queue.supported_drivers', function () {
|
||||||
|
return [
|
||||||
|
'sync' => SyncQueue::class,
|
||||||
|
'database' => DatabaseQueue::class,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->container->singleton('flarum.queue.connection', function (Container $container) {
|
||||||
|
/** @var array $drivers */
|
||||||
|
$drivers = $container->make('flarum.queue.supported_drivers');
|
||||||
|
/** @var SettingsRepositoryInterface $settings */
|
||||||
|
$settings = $container->make(SettingsRepositoryInterface::class);
|
||||||
|
$driverName = $settings->get('queue_driver', 'sync');
|
||||||
|
|
||||||
|
$driverClass = Arr::get($drivers, $driverName);
|
||||||
|
|
||||||
|
/** @var Queue $driver */
|
||||||
|
$driver = $container->make($driverClass);
|
||||||
|
|
||||||
|
// This method only exists on the Laravel abstract Queue implementation, not the contract,
|
||||||
|
// for simplicity we will try to inject the container if the method is available on the driver.
|
||||||
|
if (method_exists($driver, 'setContainer')) {
|
||||||
|
$driver->setContainer($container);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $driver;
|
||||||
|
});
|
||||||
|
|
||||||
// Register a simple connection factory that always returns the same
|
// Register a simple connection factory that always returns the same
|
||||||
// connection, as that is enough for our purposes.
|
// connection, as that is enough for our purposes.
|
||||||
$this->container->singleton(Factory::class, function (Container $container) {
|
$this->container->singleton(Factory::class, function (Container $container) {
|
||||||
@@ -50,15 +81,6 @@ class QueueServiceProvider extends AbstractServiceProvider
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Extensions can override this binding if they want to make Flarum use
|
|
||||||
// a different queuing backend.
|
|
||||||
$this->container->singleton('flarum.queue.connection', function (Container $container) {
|
|
||||||
$queue = new SyncQueue;
|
|
||||||
$queue->setContainer($container);
|
|
||||||
|
|
||||||
return $queue;
|
|
||||||
});
|
|
||||||
|
|
||||||
$this->container->singleton(ExceptionHandling::class, function (Container $container) {
|
$this->container->singleton(ExceptionHandling::class, function (Container $container) {
|
||||||
return new ExceptionHandler($container['log']);
|
return new ExceptionHandler($container['log']);
|
||||||
});
|
});
|
||||||
@@ -78,7 +100,7 @@ class QueueServiceProvider extends AbstractServiceProvider
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Override the Laravel native Listener, so that we can ignore the environment
|
// Override the Laravel native Listener, so that we can ignore the environment
|
||||||
// option and force the binary to flarum.
|
// option and force the binary to Flarum.
|
||||||
$this->container->singleton(QueueListener::class, function (Container $container) {
|
$this->container->singleton(QueueListener::class, function (Container $container) {
|
||||||
return new Listener($container->make(Paths::class)->base);
|
return new Listener($container->make(Paths::class)->base);
|
||||||
});
|
});
|
||||||
@@ -110,10 +132,21 @@ class QueueServiceProvider extends AbstractServiceProvider
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->container->singleton('queue.failer', function () {
|
$this->container->singleton('queue.failer', function (Container $container) {
|
||||||
return new NullFailedJobProvider();
|
/** @var Config $config */
|
||||||
|
$config = $container->make(Config::class);
|
||||||
|
|
||||||
|
return new DatabaseFailedJobProvider(
|
||||||
|
$container->make('db'),
|
||||||
|
$config->offsetGet('database.database'),
|
||||||
|
'queue_failed_jobs'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$this->container->when(DatabaseQueue::class)
|
||||||
|
->needs('$table')
|
||||||
|
->give('queue_jobs');
|
||||||
|
|
||||||
$this->container->alias('flarum.queue.connection', Queue::class);
|
$this->container->alias('flarum.queue.connection', Queue::class);
|
||||||
|
|
||||||
$this->container->alias(ConnectorInterface::class, 'queue.connection');
|
$this->container->alias(ConnectorInterface::class, 'queue.connection');
|
||||||
|
Reference in New Issue
Block a user