12 Commits
1.2.1 ... 1.3.2

Author SHA1 Message Date
IanM
77ad7422e7 fix: disable fof/redis settings cache to avoid circular boot dependency (#7)
Some checks failed
GB Redis Setup PHP / run (push) Has been cancelled
GB Redis Setup JS / run (push) Has been cancelled
fof/redis >=1.1 introduces a `settings` service that replaces
SettingsRepositoryInterface with a Redis-backed caching layer.
Enabling it here creates a circular dependency: this extension reads
settings to decide which Redis services to enable, but with the
settings cache active those reads require Redis to already be wired.

Always disable the `settings` service when delegating to fof/redis.
Also fixes a cast-precedence bug in the database env var helpers
where `(int) getenv(...) ? getenv(...) : N` applied the cast to the
ternary condition rather than the result, causing REDIS_DATABASE_*=0
to return the default instead of 0.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 17:29:17 +00:00
Davide Iadeluca
4f261dbee4 chore: adjust workflows
[skip ci]
2025-11-30 11:51:35 +01:00
IanM
d59ebfa684 fix: extend not called (#5)
Some checks failed
GB Redis Setup PHP / run (push) Has been cancelled
GB Redis Setup JS / run (push) Has been cancelled
* fix: extend not called

* chore: phpstan
2025-11-18 22:01:50 +00:00
IanM
b03ca35d54 Extend Redis functionality with new method 2025-11-18 20:44:52 +00:00
flarum-bot
4c7a5d377d Bundled output for commit 6545bfdffc
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2025-10-10 09:51:12 +00:00
IanM
6545bfdffc chore: migrate to fof/redis, repo maintenance (#4)
* chore: migrate to fof/redis, repo maintenance

* chore: newline

* chore: use new fof horizon initializer

* chore: revert initializer  change

Only changed for 2.x ..

* chore: remove unused secrets

Shouldn't have been added in the first place

---------

Co-authored-by: Davide Iadeluca <146922689+DavideIadeluca@users.noreply.github.com>
Co-authored-by: Davide Iadeluca <davide.iadeluca@glowingblue.com>
2025-10-10 10:50:31 +01:00
Davide Iadeluca
2f2cce1abe chore: remove sync workflow 2024-01-31 11:21:07 +01:00
flarum-bot
78082dc842 Bundled output for commit e02001333b
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2024-01-31 09:45:51 +00:00
Davide Iadeluca
e02001333b Merge pull request #2 from glowingblue/di/update-deps
chore(deps): update dependencies
2024-01-31 10:45:12 +01:00
Davide Iadeluca
44fa68c278 chore: add custom repository 2024-01-31 10:37:20 +01:00
Davide Iadeluca
139d687e1b ci: update workflows 2024-01-31 10:25:04 +01:00
Davide Iadeluca
9061552db9 chore(deps): update dependencies 2024-01-30 13:59:01 +01:00
19 changed files with 1467 additions and 1480 deletions

11
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,11 @@
**Resolves**
<!-- include a link to the issue -->
**Changes proposed in this pull request:**
<!-- mention the pages and/or components which have been impacted -->
**Reviewers should focus on:**
<!-- ask for feedback on specific changes you are unsure about -->
**Screenshot**
<!-- include an image of the most relevant user-facing change, if any -->

13
.github/workflows/backend.yml vendored Normal file
View File

@@ -0,0 +1,13 @@
name: GB Redis Setup PHP
on: [workflow_dispatch, push, pull_request]
jobs:
run:
uses: flarum/framework/.github/workflows/REUSABLE_backend.yml@1.x
with:
enable_backend_testing: false
enable_phpstan: true
php_versions: '["8.1", "8.2", "8.3", "8.4"]'
backend_directory: .

View File

@@ -1,91 +0,0 @@
name: JS
on: [workflow_dispatch, push, pull_request]
env:
NODE_VERSION: 16
jobs:
prettier:
name: Prettier
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Set up Node
uses: actions/setup-node@v2
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'yarn'
cache-dependency-path: js/yarn.lock
- name: Install JS dependencies
run: yarn install --immutable
working-directory: ./js
- name: Check JS formatting
run: yarn run format-check
working-directory: ./js
build-prod:
name: Build and commit
runs-on: ubuntu-latest
needs: [prettier]
# Only commit JS on push to master branch
# Remember to change in `build-test` job too
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Set up Node
uses: actions/setup-node@v2
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'yarn'
cache-dependency-path: js/yarn.lock
# Our action will install node, npm and yarn, cd into `./js`, run `yarn run build` (and
# `yarn run build-typings` if desired), then commit and upload any changes
- name: Build production JS
uses: flarum/action-build@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
build_script: build
package_manager: yarn
# typings_script: build-typings
build-test:
name: Test build
runs-on: ubuntu-latest
needs: [prettier]
# Inverse check of `build-prod`
# Remember to change in `build-prod` job too
if: github.ref != 'refs/heads/master' || github.event_name != 'push'
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Set up Node
uses: actions/setup-node@v2
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'yarn'
cache-dependency-path: js/yarn.lock
# Our action will install node, npm and yarn, cd into `./js`, run `yarn run build` (and
# `yarn run build-typings` if desired). It will NOT commit and upload.
- name: Build production JS
uses: flarum/action-build@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
build_script: build
package_manager: yarn
# typings_script: build-typings
do_not_commit: true

23
.github/workflows/frontend.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: GB Redis Setup JS
on: [workflow_dispatch, push, pull_request]
jobs:
run:
uses: flarum/framework/.github/workflows/REUSABLE_frontend.yml@1.x
with:
enable_bundlewatch: false
enable_prettier: true
enable_typescript: false
frontend_directory: ./js
backend_directory: .
js_package_manager: yarn
main_git_branch: 1.x
git_actor_name: ${{ vars.GIT_ACTOR_NAME }}
git_actor_email: ${{ vars.GIT_ACTOR_EMAIL }}
secrets:
bundlewatch_github_token: ${{ secrets.BUNDLEWATCH_GITHUB_TOKEN }}
git_actor_token: ${{ secrets.GIT_ACTOR_TOKEN }}

View File

@@ -38,6 +38,14 @@ composer update glowingblue/redis-setup
php flarum cache:clear php flarum cache:clear
``` ```
## Compatibility with `fof/redis`
This extension depends on [`fof/redis`](https://github.com/FriendsOfFlarum/redis) and deliberately disables the **`settings` service** that `fof/redis` ≥ 1.1 provides.
`fof/redis`'s settings service replaces Flarum's `SettingsRepositoryInterface` with a Redis-backed caching layer. However, this extension reads settings (e.g. which Redis services to enable) during its own boot sequence — *before* Redis is fully wired into the container. Enabling the Redis settings cache here would create a circular dependency: configuring Redis requires reading settings, but reading settings requires Redis.
The `settings` service from `fof/redis` is therefore always disabled in this extension's extender. If you want Redis-backed settings caching, configure `fof/redis` directly in your project's root `extend.php` instead of using this extension.
## 🔗 Links ## 🔗 Links
- [Flarum Discuss post](https://discuss.flarum.org/d/27455) - [Flarum Discuss post](https://discuss.flarum.org/d/27455)

View File

@@ -18,8 +18,9 @@
}, },
"homepage": "https://glowingblue.com", "homepage": "https://glowingblue.com",
"require": { "require": {
"flarum/core": "^1.2.0", "php": "^8.1",
"blomstra/flarum-redis": "^0.4.0" "flarum/core": "^1.8.5",
"fof/redis": "^1.1.4"
}, },
"authors": [ "authors": [
{ {
@@ -52,11 +53,22 @@
"color": "#fff" "color": "#fff"
}, },
"optional-dependencies": [ "optional-dependencies": [
"blomstra/horizon" "fof/horizon"
] ]
}, },
"extiverse": { "extiverse": {
"discuss": "https://discuss.flarum.org/d/27455" "discuss": "https://discuss.flarum.org/d/27455"
} }
},
"require-dev": {
"flarum/phpstan": "*",
"fof/horizon": "^1.0"
},
"scripts": {
"analyse:phpstan": "phpstan analyse",
"clear-cache:phpstan": "phpstan clear-result-cache"
},
"scripts-descriptions": {
"analyse:phpstan": "Run static analysis"
} }
} }

2
js/dist/admin.js vendored
View File

@@ -1,2 +1,2 @@
(()=>{var e={n:t=>{var r=t&&t.__esModule?()=>t.default:()=>t;return e.d(r,{a:r}),r},d:(t,r)=>{for(var n in r)e.o(r,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:r[n]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};(()=>{"use strict";e.r(t);const r=flarum.core.compat["admin/app"];var n=e.n(r);const o=flarum.core.compat["common/extend"],a=flarum.core.compat["admin/components/StatusWidget"];var i=e.n(a),l="glowingblue-redis-setup";function s(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r<t;r++)n[r]=e[r];return n}var u=n().translator.trans.bind(n().translator),d=l+".admin.settings";n().initializers.add(l,(function(){n().extensionData.for(l).registerSetting({setting:"glowingblue-redis.enableCache",type:"boolean",label:u(d+".enable_cache")}).registerSetting({setting:"glowingblue-redis.redisSessions",type:"boolean",label:u(d+".enable_redis_sessions")}).registerSetting({setting:"glowingblue-redis.enableQueue",type:"boolean",label:u(d+".enable_queue")}),n().initializers.has("blomstra/horizon")&&n().extensionData.for(l).registerSetting({setting:"glowingblue-redis.horizonConfig",type:"textarea",label:u(d+".horizon_config"),help:u(d+".horizon_help_text")}),(0,o.extend)(i().prototype,"items",(function(e){var t=n().data.blomstraQueuesLoad;if(void 0!==t)for(var r,o=function(e,t){var r="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(r)return(r=r.call(e)).next.bind(r);if(Array.isArray(e)||(r=function(e,t){if(e){if("string"==typeof e)return s(e,t);var r=Object.prototype.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?s(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0;return function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}(n().data.blomstraQueuesSeen);!(r=o()).done;){var a=r.value,i=t[a]||null;e.add("blomstra-queue-size-"+a,[m("strong",null,"Queue ",a),m("br",null),i||"0"])}}))}))})(),module.exports=t})(); (()=>{var e={n:t=>{var r=t&&t.__esModule?()=>t.default:()=>t;return e.d(r,{a:r}),r},d:(t,r)=>{for(var n in r)e.o(r,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:r[n]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};(()=>{"use strict";e.r(t);const r=flarum.core.compat["admin/app"];var n=e.n(r);const o=flarum.core.compat["common/extend"],a=flarum.core.compat["admin/components/StatusWidget"];var i=e.n(a),l="glowingblue-redis-setup";function s(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r<t;r++)n[r]=e[r];return n}var u=n().translator.trans.bind(n().translator),d=l+".admin.settings";n().initializers.add(l,(function(){n().extensionData.for(l).registerSetting({setting:"glowingblue-redis.enableCache",type:"boolean",label:u(d+".enable_cache")}).registerSetting({setting:"glowingblue-redis.redisSessions",type:"boolean",label:u(d+".enable_redis_sessions")}).registerSetting({setting:"glowingblue-redis.enableQueue",type:"boolean",label:u(d+".enable_queue")}),n().initializers.has("fof/horizon")&&n().extensionData.for(l).registerSetting({setting:"glowingblue-redis.horizonConfig",type:"textarea",label:u(d+".horizon_config"),help:u(d+".horizon_help_text")}),(0,o.extend)(i().prototype,"items",(function(e){var t=n().data.fofQueuesLoad;if(void 0!==t)for(var r,o=function(e,t){var r="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(r)return(r=r.call(e)).next.bind(r);if(Array.isArray(e)||(r=function(e,t){if(e){if("string"==typeof e)return s(e,t);var r=Object.prototype.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?s(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0;return function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}(n().data.fofQueuesSeen);!(r=o()).done;){var a=r.value,i=t[a]||null;e.add("fof-queue-size-"+a,[m("strong",null,"Queue ",a),m("br",null),i||"0"])}}))}))})(),module.exports=t})();
//# sourceMappingURL=admin.js.map //# sourceMappingURL=admin.js.map

File diff suppressed because one or more lines are too long

View File

@@ -6,11 +6,11 @@
"dependencies": { "dependencies": {
"flarum-webpack-config": "^2.0.0", "flarum-webpack-config": "^2.0.0",
"webpack": "^5.72.0", "webpack": "^5.72.0",
"webpack-cli": "^4.9.2" "webpack-cli": "^5.0"
}, },
"devDependencies": { "devDependencies": {
"@glowingblue-dev/prettier-config": "^1.0.0", "@glowingblue-dev/prettier-config": "^1.0.0",
"prettier": "^2.6.1" "prettier": "^3.5.1"
}, },
"scripts": { "scripts": {
"dev": "webpack --mode development --watch", "dev": "webpack --mode development --watch",

View File

@@ -36,7 +36,7 @@ app.initializers.add(slug, () => {
label: t(`${prfx}.enable_queue`), label: t(`${prfx}.enable_queue`),
}); });
if (app.initializers.has('blomstra/horizon')) { if (app.initializers.has('fof/horizon')) {
app.extensionData.for(slug).registerSetting({ app.extensionData.for(slug).registerSetting({
setting: 'glowingblue-redis.horizonConfig', setting: 'glowingblue-redis.horizonConfig',
type: 'textarea', type: 'textarea',
@@ -46,15 +46,16 @@ app.initializers.add(slug, () => {
} }
extend(StatusWidget.prototype, 'items', (items) => { extend(StatusWidget.prototype, 'items', (items) => {
const loads = app.data.blomstraQueuesLoad; const loads = app.data.fofQueuesLoad;
if (loads === undefined) { if (loads === undefined) {
return; return;
} }
for (let queue of app.data.blomstraQueuesSeen) { // @ts-ignore
for (let queue of app.data.fofQueuesSeen) {
const load = loads[queue] || null; const load = loads[queue] || null;
items.add('blomstra-queue-size-' + queue, [ items.add('fof-queue-size-' + queue, [
<strong>Queue {queue}</strong>, <strong>Queue {queue}</strong>,
<br />, <br />,
load || '0', load || '0',

17
js/tsconfig.json Normal file
View File

@@ -0,0 +1,17 @@
{
// Use Flarum's tsconfig as a starting point
"extends": "flarum-tsconfig",
// This will match all .ts, .tsx, .d.ts, .js, .jsx files in your `src` folder
// and also tells your Typescript server to read core's global typings for
// access to `dayjs` and `$` in the global namespace.
"include": ["src/**/*", "../vendor/flarum/core/js/dist-typings/@types/**/*"],
"compilerOptions": {
// This will output typings to `dist-typings`
"declarationDir": "./dist-typings",
"baseUrl": ".",
"paths": {
"flarum/*": ["../vendor/flarum/core/js/dist-typings/*"]
}
}
}

File diff suppressed because it is too large Load Diff

13
phpstan.neon Normal file
View File

@@ -0,0 +1,13 @@
includes:
- vendor/flarum/phpstan/extension.neon
parameters:
# The level will be increased in Flarum 2.0
level: 5
paths:
- extend.php
- src
excludePaths:
- *.blade.php
checkMissingIterableValueType: false
databaseMigrationsPath: ['migrations']

View File

@@ -6,4 +6,4 @@ glowingblue-redis-setup:
Enable Redis sessions (all users will be logged out after changing this setting) Enable Redis sessions (all users will be logged out after changing this setting)
enable_queue: Enable Redis queue enable_queue: Enable Redis queue
horizon_config: "Horizon config (format: JSON)." horizon_config: "Horizon config (format: JSON)."
horizon_help_text: This will be passed to <code>(new \Blomstra\Horizon\Extend\Horizon)->config(...))</code> horizon_help_text: This will be passed to <code>(new \FoF\Horizon\Extend\Horizon)->config(...))</code>

View File

@@ -12,7 +12,7 @@
namespace GlowingBlue\RedisSetup\Extend; namespace GlowingBlue\RedisSetup\Extend;
use Blomstra\Horizon\Extend\Horizon; use FoF\Horizon\Extend\Horizon;
use Flarum\Extend\ExtenderInterface; use Flarum\Extend\ExtenderInterface;
use Flarum\Extension\Extension; use Flarum\Extension\Extension;
use Flarum\Extension\ExtensionManager; use Flarum\Extension\ExtensionManager;
@@ -25,7 +25,7 @@ class ConfigureHorizon implements ExtenderInterface
{ {
$extensions = resolve(ExtensionManager::class); $extensions = resolve(ExtensionManager::class);
if (!$extensions->isEnabled('blomstra-horizon') || !class_exists(Horizon::class)) { if (!$extensions->isEnabled('fof-horizon') || !class_exists(Horizon::class)) {
return; return;
} }

View File

@@ -12,7 +12,7 @@
namespace GlowingBlue\RedisSetup\Extend; namespace GlowingBlue\RedisSetup\Extend;
use Blomstra\Redis\Extend\Redis; use FoF\Redis\Extend\Redis;
use Flarum\Extend\ExtenderInterface; use Flarum\Extend\ExtenderInterface;
use Flarum\Extension\Extension; use Flarum\Extension\Extension;
use Flarum\Settings\SettingsRepositoryInterface; use Flarum\Settings\SettingsRepositoryInterface;
@@ -29,9 +29,11 @@ class EnableRedis implements ExtenderInterface
{ {
$config = $this->buildConfig(); $config = $this->buildConfig();
(new Redis($config)) /** @var Redis $redis */
->disable($this->getDisabledServices()) $redis = (new Redis($config))
->extend($container, $extension); ->disable(['settings', ...$this->getDisabledServices()]);
$redis->extend($container, $extension);
} }
private function getDisabledServices(): array private function getDisabledServices(): array
@@ -86,36 +88,42 @@ class EnableRedis implements ExtenderInterface
public static function getHost(): string public static function getHost(): string
{ {
return getenv('REDIS_HOST') ? getenv('REDIS_HOST') : '127.0.0.1'; return getenv('REDIS_HOST') ?: '127.0.0.1';
} }
public static function getPassword(): ?string public static function getPassword(): ?string
{ {
return getenv('REDIS_PASSWORD') ? getenv('REDIS_PASSWORD') : null; return getenv('REDIS_PASSWORD') ?: null;
} }
public static function getPort(): string public static function getPort(): string
{ {
return getenv('REDIS_PORT') ? getenv('REDIS_PORT') : '6379'; return getenv('REDIS_PORT') ?: '6379';
} }
public static function getCacheDatabase(): int public static function getCacheDatabase(): int
{ {
return (int) getenv('REDIS_DATABASE_CACHE') ? getenv('REDIS_DATABASE_CACHE') : 1; $val = getenv('REDIS_DATABASE_CACHE');
return $val !== false ? (int) $val : 1;
} }
public static function getQueueDatabase(): int public static function getQueueDatabase(): int
{ {
return (int) getenv('REDIS_DATABASE_QUEUE') ? getenv('REDIS_DATABASE_QUEUE') : 2; $val = getenv('REDIS_DATABASE_QUEUE');
return $val !== false ? (int) $val : 2;
} }
public static function getSessionDatabase(): int public static function getSessionDatabase(): int
{ {
return (int) getenv('REDIS_DATABASE_SESSION') ? getenv('REDIS_DATABASE_SESSION') : 3; $val = getenv('REDIS_DATABASE_SESSION');
return $val !== false ? (int) $val : 3;
} }
public static function getPrefix(): string public static function getPrefix(): string
{ {
return getenv('REDIS_PREFIX') ? getenv('REDIS_PREFIX') : 'flarum_'; return getenv('REDIS_PREFIX') ?: 'flarum_';
} }
} }

View File

@@ -55,7 +55,7 @@ class QueueProvider extends AbstractServiceProvider
/** @var QueueContract $queue */ /** @var QueueContract $queue */
$queue = resolve(QueueContract::class); $queue = resolve(QueueContract::class);
$queues = $cache->get('blomstra.queue.queues-seen') ?? []; $queues = $cache->get('fof.queue.queues-seen') ?? [];
if ($queue instanceof RedisQueue) { if ($queue instanceof RedisQueue) {
$load = []; $load = [];
@@ -67,8 +67,8 @@ class QueueProvider extends AbstractServiceProvider
} }
} }
$document->payload['blomstraQueuesSeen'] = $queues; $document->payload['fofQueuesSeen'] = $queues;
$document->payload['blomstraQueuesLoad'] = $load ?? null; $document->payload['fofQueuesLoad'] = $load ?? null;
} }
public function trackQueues(Looping $event) public function trackQueues(Looping $event)
@@ -76,8 +76,8 @@ class QueueProvider extends AbstractServiceProvider
/** @var Store $cache */ /** @var Store $cache */
$cache = resolve('cache.store'); $cache = resolve('cache.store');
$queues = $cache->get('blomstra.queue.queues-seen') ?? []; $queues = $cache->get('fof.queue.queues-seen') ?? [];
$queues = array_merge($queues, (array) explode(',', $event->queue)); $queues = array_merge($queues, (array) explode(',', $event->queue));
$cache->put('blomstra.queue.queues-seen', array_unique($queues), 60); $cache->put('fof.queue.queues-seen', array_unique($queues), 60);
} }
} }