Compare commits
18 commits
Author | SHA1 | Date | |
---|---|---|---|
23d47fa6d2 | |||
bdad34e065 | |||
fc6a899f16 | |||
1f16de2239 | |||
1550a5da57 | |||
7ef1974c88 | |||
0f45a5f60f | |||
324fe21d73 | |||
153abde3a2 | |||
f8aaa71260 | |||
37a3bc1ee6 | |||
f547812d5a | |||
8a06836985 | |||
34528ae413 | |||
0bf7ca0d52 | |||
cc9fccdf18 | |||
ca77b501e7 | |||
2439f87df9 |
151 changed files with 2007 additions and 2157 deletions
|
@ -1,6 +1,6 @@
|
||||||
.news__feeds {
|
.news__feeds {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr;
|
||||||
grid-gap: 2px;
|
grid-gap: 2px;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
{
|
{
|
||||||
"minimum-stability": "dev",
|
|
||||||
"prefer-stable": true,
|
|
||||||
"require": {
|
"require": {
|
||||||
"flashwave/index": "dev-master",
|
"flashwave/index": "^0.2410",
|
||||||
"flashwave/sasae": "dev-master",
|
"flashwave/aiwass": "^1.1",
|
||||||
"erusev/parsedown": "~1.6",
|
"erusev/parsedown": "~1.6",
|
||||||
"chillerlan/php-qrcode": "^4.3",
|
"chillerlan/php-qrcode": "^4.3",
|
||||||
"symfony/mailer": "^6.0",
|
"symfony/mailer": "^6.0",
|
||||||
"matomo/device-detector": "^6.1",
|
"matomo/device-detector": "^6.1",
|
||||||
"sentry/sdk": "^4.0",
|
"sentry/sdk": "^4.0",
|
||||||
"flashwave/syokuhou": "dev-master"
|
"nesbot/carbon": "^3.7"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"classmap": [
|
"classmap": [
|
||||||
|
@ -34,6 +32,6 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpstan/phpstan": "^1.10"
|
"phpstan/phpstan": "^1.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1007
composer.lock
generated
1007
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,11 +1,11 @@
|
||||||
<?php
|
<?php
|
||||||
use Index\Data\IDbConnection;
|
use Index\Db\DbConnection;
|
||||||
use Index\Data\Migration\IDbMigration;
|
use Index\Db\Migration\DbMigration;
|
||||||
|
|
||||||
// Switching to the Index migration system!!!!!!
|
// Switching to the Index migration system!!!!!!
|
||||||
|
|
||||||
final class InitialStructureNdx_20230107_023235 implements IDbMigration {
|
final class InitialStructureNdx_20230107_023235 implements DbMigration {
|
||||||
public function migrate(IDbConnection $conn): void {
|
public function migrate(DbConnection $conn): void {
|
||||||
$hasMszTrack = false;
|
$hasMszTrack = false;
|
||||||
|
|
||||||
// check if the old migrations table exists
|
// check if the old migrations table exists
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
use Index\Data\IDbConnection;
|
use Index\Db\DbConnection;
|
||||||
use Index\Data\Migration\IDbMigration;
|
use Index\Db\Migration\DbMigration;
|
||||||
|
|
||||||
final class CreateTopicRedirsTable_20230430_001226 implements IDbMigration {
|
final class CreateTopicRedirsTable_20230430_001226 implements DbMigration {
|
||||||
public function migrate(IDbConnection $conn): void {
|
public function migrate(DbConnection $conn): void {
|
||||||
$conn->execute('
|
$conn->execute('
|
||||||
CREATE TABLE msz_forum_topics_redirects (
|
CREATE TABLE msz_forum_topics_redirects (
|
||||||
topic_id INT(10) UNSIGNED NOT NULL,
|
topic_id INT(10) UNSIGNED NOT NULL,
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
use Index\Data\IDbConnection;
|
use Index\Db\DbConnection;
|
||||||
use Index\Data\Migration\IDbMigration;
|
use Index\Db\Migration\DbMigration;
|
||||||
use Misuzu\ClientInfo;
|
use Misuzu\ClientInfo;
|
||||||
|
|
||||||
final class UpdateUserAgentStorage_20230721_121854 implements IDbMigration {
|
final class UpdateUserAgentStorage_20230721_121854 implements DbMigration {
|
||||||
public function migrate(IDbConnection $conn): void {
|
public function migrate(DbConnection $conn): void {
|
||||||
// convert user agent fields to BLOB and add field for client info storage
|
// convert user agent fields to BLOB and add field for client info storage
|
||||||
$conn->execute('
|
$conn->execute('
|
||||||
ALTER TABLE msz_login_attempts
|
ALTER TABLE msz_login_attempts
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
use Index\Data\IDbConnection;
|
use Index\Db\DbConnection;
|
||||||
use Index\Data\Migration\IDbMigration;
|
use Index\Db\Migration\DbMigration;
|
||||||
|
|
||||||
final class AddModeratorNotesTable_20230724_201010 implements IDbMigration {
|
final class AddModeratorNotesTable_20230724_201010 implements DbMigration {
|
||||||
public function migrate(IDbConnection $conn): void {
|
public function migrate(DbConnection $conn): void {
|
||||||
$conn->execute('
|
$conn->execute('
|
||||||
CREATE TABLE msz_users_modnotes (
|
CREATE TABLE msz_users_modnotes (
|
||||||
note_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
note_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
use Index\Data\IDbConnection;
|
use Index\Db\DbConnection;
|
||||||
use Index\Data\Migration\IDbMigration;
|
use Index\Db\Migration\DbMigration;
|
||||||
|
|
||||||
final class AddNewBansTable_20230726_175936 implements IDbMigration {
|
final class AddNewBansTable_20230726_175936 implements DbMigration {
|
||||||
public function migrate(IDbConnection $conn): void {
|
public function migrate(DbConnection $conn): void {
|
||||||
$conn->execute('
|
$conn->execute('
|
||||||
CREATE TABLE msz_users_bans (
|
CREATE TABLE msz_users_bans (
|
||||||
ban_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
ban_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
use Index\Data\IDbConnection;
|
use Index\Db\DbConnection;
|
||||||
use Index\Data\Migration\IDbMigration;
|
use Index\Db\Migration\DbMigration;
|
||||||
|
|
||||||
final class RedoWarningsTable_20230726_210150 implements IDbMigration {
|
final class RedoWarningsTable_20230726_210150 implements DbMigration {
|
||||||
public function migrate(IDbConnection $conn): void {
|
public function migrate(DbConnection $conn): void {
|
||||||
$conn->execute('
|
$conn->execute('
|
||||||
CREATE TABLE msz_users_warnings (
|
CREATE TABLE msz_users_warnings (
|
||||||
warn_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
warn_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
use Index\Data\IDbConnection;
|
use Index\Db\DbConnection;
|
||||||
use Index\Data\Migration\IDbMigration;
|
use Index\Db\Migration\DbMigration;
|
||||||
|
|
||||||
final class PluraliseUsersForRoleRelations_20230727_130516 implements IDbMigration {
|
final class PluraliseUsersForRoleRelations_20230727_130516 implements DbMigration {
|
||||||
public function migrate(IDbConnection $conn): void {
|
public function migrate(DbConnection $conn): void {
|
||||||
$conn->execute('RENAME TABLE msz_user_roles TO msz_users_roles');
|
$conn->execute('RENAME TABLE msz_user_roles TO msz_users_roles');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
use Index\Data\IDbConnection;
|
use Index\Db\DbConnection;
|
||||||
use Index\Data\Migration\IDbMigration;
|
use Index\Db\Migration\DbMigration;
|
||||||
|
|
||||||
final class CreateCountersTable_20230728_212101 implements IDbMigration {
|
final class CreateCountersTable_20230728_212101 implements DbMigration {
|
||||||
public function migrate(IDbConnection $conn): void {
|
public function migrate(DbConnection $conn): void {
|
||||||
$conn->execute('
|
$conn->execute('
|
||||||
CREATE TABLE msz_counters (
|
CREATE TABLE msz_counters (
|
||||||
counter_name VARBINARY(64) NOT NULL,
|
counter_name VARBINARY(64) NOT NULL,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
use Index\Data\IDbConnection;
|
use Index\Db\DbConnection;
|
||||||
use Index\Data\Migration\IDbMigration;
|
use Index\Db\Migration\DbMigration;
|
||||||
|
|
||||||
final class UpdateCollationsInVariousTables_20230803_114403 implements IDbMigration {
|
final class UpdateCollationsInVariousTables_20230803_114403 implements DbMigration {
|
||||||
public function migrate(IDbConnection $conn): void {
|
public function migrate(DbConnection $conn): void {
|
||||||
$conn->execute('
|
$conn->execute('
|
||||||
ALTER TABLE msz_audit_log
|
ALTER TABLE msz_audit_log
|
||||||
CHANGE COLUMN log_action log_action VARCHAR(50) NOT NULL COLLATE "ascii_general_ci" AFTER user_id,
|
CHANGE COLUMN log_action log_action VARCHAR(50) NOT NULL COLLATE "ascii_general_ci" AFTER user_id,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
use Index\Data\IDbConnection;
|
use Index\Db\DbConnection;
|
||||||
use Index\Data\Migration\IDbMigration;
|
use Index\Db\Migration\DbMigration;
|
||||||
|
|
||||||
final class NewPermissionsSystem_20230830_213930 implements IDbMigration {
|
final class NewPermissionsSystem_20230830_213930 implements DbMigration {
|
||||||
public function migrate(IDbConnection $conn): void {
|
public function migrate(DbConnection $conn): void {
|
||||||
// make sure cron doesn't fuck us over
|
// make sure cron doesn't fuck us over
|
||||||
$conn->execute('DELETE FROM msz_config WHERE config_name = "perms.needsRecalc"');
|
$conn->execute('DELETE FROM msz_config WHERE config_name = "perms.needsRecalc"');
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
use Index\Data\IDbConnection;
|
use Index\Db\DbConnection;
|
||||||
use Index\Data\Migration\IDbMigration;
|
use Index\Db\Migration\DbMigration;
|
||||||
|
|
||||||
final class CreateMessagesTable_20240130_233734 implements IDbMigration {
|
final class CreateMessagesTable_20240130_233734 implements DbMigration {
|
||||||
public function migrate(IDbConnection $conn): void {
|
public function migrate(DbConnection $conn): void {
|
||||||
$conn->execute('
|
$conn->execute('
|
||||||
CREATE TABLE msz_messages (
|
CREATE TABLE msz_messages (
|
||||||
msg_id BINARY(8) NOT NULL,
|
msg_id BINARY(8) NOT NULL,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
use Index\Data\IDbConnection;
|
use Index\Db\DbConnection;
|
||||||
use Index\Data\Migration\IDbMigration;
|
use Index\Db\Migration\DbMigration;
|
||||||
|
|
||||||
final class BaseSixtyFourEncodePmsInDb_20240602_194809 implements IDbMigration {
|
final class BaseSixtyFourEncodePmsInDb_20240602_194809 implements DbMigration {
|
||||||
public function migrate(IDbConnection $conn): void {
|
public function migrate(DbConnection $conn): void {
|
||||||
$conn->execute('UPDATE msz_messages SET msg_title = TO_BASE64(msg_title), msg_body = TO_BASE64(msg_body)');
|
$conn->execute('UPDATE msz_messages SET msg_title = TO_BASE64(msg_title), msg_body = TO_BASE64(msg_body)');
|
||||||
$conn->execute('
|
$conn->execute('
|
||||||
ALTER TABLE `msz_messages`
|
ALTER TABLE `msz_messages`
|
||||||
|
|
13
database/2024_09_16_205613_add_role_id_string.php
Normal file
13
database/2024_09_16_205613_add_role_id_string.php
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
use Index\Db\DbConnection;
|
||||||
|
use Index\Db\Migration\DbMigration;
|
||||||
|
|
||||||
|
final class AddRoleIdString_20240916_205613 implements DbMigration {
|
||||||
|
public function migrate(DbConnection $conn): void {
|
||||||
|
$conn->execute(<<<SQL
|
||||||
|
ALTER TABLE msz_roles
|
||||||
|
ADD COLUMN role_string VARCHAR(20) NULL DEFAULT NULL COLLATE 'ascii_general_ci' AFTER role_id,
|
||||||
|
ADD UNIQUE INDEX roles_string_unique (role_string);
|
||||||
|
SQL);
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ Below are a number of links to source code repositories related to Flashii.net a
|
||||||
- [Seria](https://patchii.net/flashii/seria): Software used by the downloads tracker.
|
- [Seria](https://patchii.net/flashii/seria): Software used by the downloads tracker.
|
||||||
- [Mince](https://patchii.net/flashii/mince): Source code for the Minecraft servers subwebsite.
|
- [Mince](https://patchii.net/flashii/mince): Source code for the Minecraft servers subwebsite.
|
||||||
- [Awaki](https://patchii.net/flashii/awaki): Redirect service hosted on fii.moe.
|
- [Awaki](https://patchii.net/flashii/awaki): Redirect service hosted on fii.moe.
|
||||||
|
- [Aleister](https://patchii.net/flashii/aleister): Public API gateway.
|
||||||
|
|
||||||
## Tools & Software
|
## Tools & Software
|
||||||
- [SoFii](https://patchii.net/flashii/sofii): Launcher for Soldier of Fortune 2
|
- [SoFii](https://patchii.net/flashii/sofii): Launcher for Soldier of Fortune 2
|
||||||
|
@ -21,8 +22,7 @@ Below are a number of links to source code repositories related to Flashii.net a
|
||||||
|
|
||||||
## First-Party Libraries
|
## First-Party Libraries
|
||||||
- [Index](https://patchii.net/flash/index): Base library used in almost any component of the website that uses PHP.
|
- [Index](https://patchii.net/flash/index): Base library used in almost any component of the website that uses PHP.
|
||||||
- [Sasae](https://patchii.net/flash/sasae): Extension to the Twig templating library.
|
- [Aiwass](https://patchii.net/flashii/aiwass): Internal RPC extension, mainly used to supply data to the API gateway.
|
||||||
- [Syokuhou](https://patchii.net/flash/syokuhou): Configuration library.
|
|
||||||
|
|
||||||
## Historical
|
## Historical
|
||||||
- [AJAX Chat (fork)](https://patchii.net/flashii/ajax-chat): Old chat software (2013-2015). Still kept on life support for the nostalgia.
|
- [AJAX Chat (fork)](https://patchii.net/flashii/ajax-chat): Old chat software (2013-2015). Still kept on life support for the nostalgia.
|
||||||
|
|
15
misuzu.php
15
misuzu.php
|
@ -1,10 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu;
|
namespace Misuzu;
|
||||||
|
|
||||||
use Index\Environment;
|
use Index\Db\DbBackends;
|
||||||
use Index\Data\DbTools;
|
use Index\Config\Db\DbConfig;
|
||||||
use Syokuhou\DbConfig;
|
use Index\Config\Fs\FsConfig;
|
||||||
use Syokuhou\FileConfig;
|
|
||||||
|
|
||||||
define('MSZ_STARTUP', microtime(true));
|
define('MSZ_STARTUP', microtime(true));
|
||||||
define('MSZ_ROOT', __DIR__);
|
define('MSZ_ROOT', __DIR__);
|
||||||
|
@ -19,11 +18,11 @@ define('MSZ_ASSETS', MSZ_ROOT . '/assets');
|
||||||
|
|
||||||
require_once MSZ_ROOT . '/vendor/autoload.php';
|
require_once MSZ_ROOT . '/vendor/autoload.php';
|
||||||
|
|
||||||
Environment::setDebug(MSZ_DEBUG);
|
error_reporting(MSZ_DEBUG ? -1 : 0);
|
||||||
mb_internal_encoding('UTF-8');
|
mb_internal_encoding('UTF-8');
|
||||||
date_default_timezone_set('UTC');
|
date_default_timezone_set('GMT');
|
||||||
|
|
||||||
$cfg = FileConfig::fromFile(MSZ_CONFIG . '/config.cfg');
|
$cfg = FsConfig::fromFile(MSZ_CONFIG . '/config.cfg');
|
||||||
|
|
||||||
if($cfg->hasValues('sentry:dsn'))
|
if($cfg->hasValues('sentry:dsn'))
|
||||||
(function($cfg) {
|
(function($cfg) {
|
||||||
|
@ -38,7 +37,7 @@ if($cfg->hasValues('sentry:dsn'))
|
||||||
});
|
});
|
||||||
})($cfg->scopeTo('sentry'));
|
})($cfg->scopeTo('sentry'));
|
||||||
|
|
||||||
$db = DbTools::create($cfg->getString('database:dsn', 'null:'));
|
$db = DbBackends::create($cfg->getString('database:dsn', 'null:'));
|
||||||
$db->execute('SET SESSION time_zone = \'+00:00\', sql_mode = \'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION\';');
|
$db->execute('SET SESSION time_zone = \'+00:00\', sql_mode = \'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION\';');
|
||||||
|
|
||||||
$cfg = new DbConfig($db, 'msz_config');
|
$cfg = new DbConfig($db, 'msz_config');
|
||||||
|
|
346
package-lock.json
generated
346
package-lock.json
generated
|
@ -80,14 +80,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core": {
|
"node_modules/@swc/core": {
|
||||||
"version": "1.6.13",
|
"version": "1.7.40",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.6.13.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.40.tgz",
|
||||||
"integrity": "sha512-eailUYex6fkfaQTev4Oa3mwn0/e3mQU4H8y1WPuImYQESOQDtVrowwUGDSc19evpBbHpKtwM+hw8nLlhIsF+Tw==",
|
"integrity": "sha512-0HIzM5vigVT5IvNum+pPuST9p8xFhN6mhdIKju7qYYeNuZG78lwms/2d8WgjTJJlzp6JlPguXGrMMNzjQw0qNg==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@swc/counter": "^0.1.3",
|
"@swc/counter": "^0.1.3",
|
||||||
"@swc/types": "^0.1.9"
|
"@swc/types": "^0.1.13"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
|
@ -97,16 +97,16 @@
|
||||||
"url": "https://opencollective.com/swc"
|
"url": "https://opencollective.com/swc"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@swc/core-darwin-arm64": "1.6.13",
|
"@swc/core-darwin-arm64": "1.7.40",
|
||||||
"@swc/core-darwin-x64": "1.6.13",
|
"@swc/core-darwin-x64": "1.7.40",
|
||||||
"@swc/core-linux-arm-gnueabihf": "1.6.13",
|
"@swc/core-linux-arm-gnueabihf": "1.7.40",
|
||||||
"@swc/core-linux-arm64-gnu": "1.6.13",
|
"@swc/core-linux-arm64-gnu": "1.7.40",
|
||||||
"@swc/core-linux-arm64-musl": "1.6.13",
|
"@swc/core-linux-arm64-musl": "1.7.40",
|
||||||
"@swc/core-linux-x64-gnu": "1.6.13",
|
"@swc/core-linux-x64-gnu": "1.7.40",
|
||||||
"@swc/core-linux-x64-musl": "1.6.13",
|
"@swc/core-linux-x64-musl": "1.7.40",
|
||||||
"@swc/core-win32-arm64-msvc": "1.6.13",
|
"@swc/core-win32-arm64-msvc": "1.7.40",
|
||||||
"@swc/core-win32-ia32-msvc": "1.6.13",
|
"@swc/core-win32-ia32-msvc": "1.7.40",
|
||||||
"@swc/core-win32-x64-msvc": "1.6.13"
|
"@swc/core-win32-x64-msvc": "1.7.40"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@swc/helpers": "*"
|
"@swc/helpers": "*"
|
||||||
|
@ -118,9 +118,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-darwin-arm64": {
|
"node_modules/@swc/core-darwin-arm64": {
|
||||||
"version": "1.6.13",
|
"version": "1.7.40",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.6.13.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.40.tgz",
|
||||||
"integrity": "sha512-SOF4buAis72K22BGJ3N8y88mLNfxLNprTuJUpzikyMGrvkuBFNcxYtMhmomO0XHsgLDzOJ+hWzcgjRNzjMsUcQ==",
|
"integrity": "sha512-LRRrCiRJLb1kpQtxMNNsr5W82Inr0dy5Imho+4HQzVx/Ismi0qX4hQBgzJAnyOBNLK1+OBVb/912UVhKXppdfQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
@ -134,9 +134,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-darwin-x64": {
|
"node_modules/@swc/core-darwin-x64": {
|
||||||
"version": "1.6.13",
|
"version": "1.7.40",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.6.13.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.40.tgz",
|
||||||
"integrity": "sha512-AW8akFSC+tmPE6YQQvK9S2A1B8pjnXEINg+gGgw0KRUUXunvu1/OEOeC5L2Co1wAwhD7bhnaefi06Qi9AiwOag==",
|
"integrity": "sha512-Lpl0XK/4fLzS5jsK48opUuGXrqJXwqJckYYPwyGbCfCXm4MsBe+7dX2hq/Kc4YMY25+NeTmzAXhla8TT4WYD/g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
@ -150,9 +150,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-arm-gnueabihf": {
|
"node_modules/@swc/core-linux-arm-gnueabihf": {
|
||||||
"version": "1.6.13",
|
"version": "1.7.40",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.6.13.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.40.tgz",
|
||||||
"integrity": "sha512-f4gxxvDXVUm2HLYXRd311mSrmbpQF2MZ4Ja6XCQz1hWAxXdhRl1gpnZ+LH/xIfGSwQChrtLLVrkxdYUCVuIjFg==",
|
"integrity": "sha512-4bEvvjptpoc5BRPr/R419h6fXTEuub+frpxxlxBOEKxgXjAF/S3xdxyPijUAakmW/xXBF0u7OC4KYI+38yQp6g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
|
@ -166,9 +166,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-arm64-gnu": {
|
"node_modules/@swc/core-linux-arm64-gnu": {
|
||||||
"version": "1.6.13",
|
"version": "1.7.40",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.6.13.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.40.tgz",
|
||||||
"integrity": "sha512-Nf/eoW2CbG8s+9JoLtjl9FByBXyQ5cjdBsA4efO7Zw4p+YSuXDgc8HRPC+E2+ns0praDpKNZtLvDtmF2lL+2Gg==",
|
"integrity": "sha512-v2fBlHJ/6Ovz0L2xFAI9TRiKyl9DTdx139PuAHD9gyzp16Utl/W0MPd4t2cYdkI6hPXE9PsJCSzMOrduh+YoDg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
@ -182,9 +182,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-arm64-musl": {
|
"node_modules/@swc/core-linux-arm64-musl": {
|
||||||
"version": "1.6.13",
|
"version": "1.7.40",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.6.13.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.40.tgz",
|
||||||
"integrity": "sha512-2OysYSYtdw79prJYuKIiux/Gj0iaGEbpS2QZWCIY4X9sGoETJ5iMg+lY+YCrIxdkkNYd7OhIbXdYFyGs/w5LDg==",
|
"integrity": "sha512-uMkduQuU4LFVkW6txv8AVArT8GjJVJ5IHoWloXaUBMT447iE8NALmpePdZWhMyj6KV7j0y23CM5rzV/I2eNGLg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
@ -198,9 +198,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-x64-gnu": {
|
"node_modules/@swc/core-linux-x64-gnu": {
|
||||||
"version": "1.6.13",
|
"version": "1.7.40",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.6.13.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.40.tgz",
|
||||||
"integrity": "sha512-PkR4CZYJNk5hcd2+tMWBpnisnmYsUzazI1O5X7VkIGFcGePTqJ/bWlfUIVVExWxvAI33PQFzLbzmN5scyIUyGQ==",
|
"integrity": "sha512-4LZdY1MBSnXyTpW5fpBU/+JGAhkuHT+VnFTDNegRboN5nSPh7y0Yvn4LmIioESV+sWzjKkEXujJPGjrp+oSp5w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
@ -214,9 +214,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-x64-musl": {
|
"node_modules/@swc/core-linux-x64-musl": {
|
||||||
"version": "1.6.13",
|
"version": "1.7.40",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.6.13.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.40.tgz",
|
||||||
"integrity": "sha512-OdsY7wryTxCKwGQcwW9jwWg3cxaHBkTTHi91+5nm7hFPpmZMz1HivJrWAMwVE7iXFw+M4l6ugB/wCvpYrUAAjA==",
|
"integrity": "sha512-FPjOwT3SgI6PAwH1O8bhOGBPzuvzOlzKeCtxLaCjruHJu9V8KKBrMTWOZT/FJyYC9mX5Ip1+l9j30UqUZdQxtA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
@ -230,9 +230,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-win32-arm64-msvc": {
|
"node_modules/@swc/core-win32-arm64-msvc": {
|
||||||
"version": "1.6.13",
|
"version": "1.7.40",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.6.13.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.40.tgz",
|
||||||
"integrity": "sha512-ap6uNmYjwk9M/+bFEuWRNl3hq4VqgQ/Lk+ID/F5WGqczNr0L7vEf+pOsRAn0F6EV+o/nyb3ePt8rLhE/wjHpPg==",
|
"integrity": "sha512-//ovXdD9GsTmhPmXJlXnIbRQkeuL6PSrYSr7uCMNcclrUdJG0YkO0GMM2afUKYbdJcunylDDWsSS8PFWn0QxmA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
@ -246,9 +246,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-win32-ia32-msvc": {
|
"node_modules/@swc/core-win32-ia32-msvc": {
|
||||||
"version": "1.6.13",
|
"version": "1.7.40",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.6.13.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.40.tgz",
|
||||||
"integrity": "sha512-IJ8KH4yIUHTnS/U1jwQmtbfQals7zWPG0a9hbEfIr4zI0yKzjd83lmtS09lm2Q24QBWOCFGEEbuZxR4tIlvfzA==",
|
"integrity": "sha512-iD/1auVhHGlhWAPrWmfRWL3w4AvXIWGVXZiSA109/xnRIPiHKb/HqqTp/qB94E/ZHMPRgLKkLTNwamlkueUs8g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
|
@ -262,9 +262,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-win32-x64-msvc": {
|
"node_modules/@swc/core-win32-x64-msvc": {
|
||||||
"version": "1.6.13",
|
"version": "1.7.40",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.6.13.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.40.tgz",
|
||||||
"integrity": "sha512-f6/sx6LMuEnbuxtiSL/EkR0Y6qUHFw1XVrh6rwzKXptTipUdOY+nXpKoh+1UsBm/r7H0/5DtOdrn3q5ZHbFZjQ==",
|
"integrity": "sha512-ZlFAV1WFPhhWQ/8esiygmetkb905XIcMMtHRRG0FBGCllO+HVL5nikUaLDgTClz1onmEY9sMXUFQeoPtvliV+w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
@ -284,9 +284,9 @@
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
},
|
},
|
||||||
"node_modules/@swc/types": {
|
"node_modules/@swc/types": {
|
||||||
"version": "0.1.9",
|
"version": "0.1.13",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.13.tgz",
|
||||||
"integrity": "sha512-qKnCno++jzcJ4lM4NTfYifm1EFSCeIfKiAHAfkENZAV5Kl9PjJIyd2yeeVv6c/2CckuLyv2NmRC5pv6pm2WQBg==",
|
"integrity": "sha512-JL7eeCk6zWCbiYQg2xQSdLXQJl8Qoc9rXmG2cEKvHe3CKwMHwHGpfOb8frzNLmbycOo6I51qxnLnn9ESf4I20Q==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@swc/counter": "^0.1.3"
|
"@swc/counter": "^0.1.3"
|
||||||
|
@ -302,9 +302,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "8.12.1",
|
"version": "8.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
|
||||||
"integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
|
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
|
@ -314,9 +314,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/autoprefixer": {
|
"node_modules/autoprefixer": {
|
||||||
"version": "10.4.19",
|
"version": "10.4.20",
|
||||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz",
|
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
|
||||||
"integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==",
|
"integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
|
@ -333,11 +333,11 @@
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"browserslist": "^4.23.0",
|
"browserslist": "^4.23.3",
|
||||||
"caniuse-lite": "^1.0.30001599",
|
"caniuse-lite": "^1.0.30001646",
|
||||||
"fraction.js": "^4.3.7",
|
"fraction.js": "^4.3.7",
|
||||||
"normalize-range": "^0.1.2",
|
"normalize-range": "^0.1.2",
|
||||||
"picocolors": "^1.0.0",
|
"picocolors": "^1.0.1",
|
||||||
"postcss-value-parser": "^4.2.0"
|
"postcss-value-parser": "^4.2.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@ -357,9 +357,9 @@
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/browserslist": {
|
"node_modules/browserslist": {
|
||||||
"version": "4.23.2",
|
"version": "4.24.2",
|
||||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz",
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz",
|
||||||
"integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==",
|
"integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
|
@ -376,10 +376,10 @@
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"caniuse-lite": "^1.0.30001640",
|
"caniuse-lite": "^1.0.30001669",
|
||||||
"electron-to-chromium": "^1.4.820",
|
"electron-to-chromium": "^1.5.41",
|
||||||
"node-releases": "^2.0.14",
|
"node-releases": "^2.0.18",
|
||||||
"update-browserslist-db": "^1.1.0"
|
"update-browserslist-db": "^1.1.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"browserslist": "cli.js"
|
"browserslist": "cli.js"
|
||||||
|
@ -417,9 +417,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001642",
|
"version": "1.0.30001673",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001642.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001673.tgz",
|
||||||
"integrity": "sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA==",
|
"integrity": "sha512-WTrjUCSMp3LYX0nE12ECkV0a+e6LC85E0Auz75555/qr78Oc8YWhEPNfDd6SHdtlCMSzqtuXY0uyEMNRcsKpKw==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
|
@ -529,12 +529,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cssnano": {
|
"node_modules/cssnano": {
|
||||||
"version": "7.0.4",
|
"version": "7.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.0.6.tgz",
|
||||||
"integrity": "sha512-rQgpZra72iFjiheNreXn77q1haS2GEy69zCMbu4cpXCFPMQF+D4Ik5V7ktMzUF/sA7xCIgcqHwGPnCD+0a1vHg==",
|
"integrity": "sha512-54woqx8SCbp8HwvNZYn68ZFAepuouZW4lTwiMVnBErM3VkO7/Sd4oTOt3Zz3bPx3kxQ36aISppyXj2Md4lg8bw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cssnano-preset-default": "^7.0.4",
|
"cssnano-preset-default": "^7.0.6",
|
||||||
"lilconfig": "^3.1.2"
|
"lilconfig": "^3.1.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -549,41 +549,41 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cssnano-preset-default": {
|
"node_modules/cssnano-preset-default": {
|
||||||
"version": "7.0.4",
|
"version": "7.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.6.tgz",
|
||||||
"integrity": "sha512-jQ6zY9GAomQX7/YNLibMEsRZguqMUGuupXcEk2zZ+p3GUxwCAsobqPYE62VrJ9qZ0l9ltrv2rgjwZPBIFIjYtw==",
|
"integrity": "sha512-ZzrgYupYxEvdGGuqL+JKOY70s7+saoNlHSCK/OGn1vB2pQK8KSET8jvenzItcY+kA7NoWvfbb/YhlzuzNKjOhQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"browserslist": "^4.23.1",
|
"browserslist": "^4.23.3",
|
||||||
"css-declaration-sorter": "^7.2.0",
|
"css-declaration-sorter": "^7.2.0",
|
||||||
"cssnano-utils": "^5.0.0",
|
"cssnano-utils": "^5.0.0",
|
||||||
"postcss-calc": "^10.0.0",
|
"postcss-calc": "^10.0.2",
|
||||||
"postcss-colormin": "^7.0.1",
|
"postcss-colormin": "^7.0.2",
|
||||||
"postcss-convert-values": "^7.0.2",
|
"postcss-convert-values": "^7.0.4",
|
||||||
"postcss-discard-comments": "^7.0.1",
|
"postcss-discard-comments": "^7.0.3",
|
||||||
"postcss-discard-duplicates": "^7.0.0",
|
"postcss-discard-duplicates": "^7.0.1",
|
||||||
"postcss-discard-empty": "^7.0.0",
|
"postcss-discard-empty": "^7.0.0",
|
||||||
"postcss-discard-overridden": "^7.0.0",
|
"postcss-discard-overridden": "^7.0.0",
|
||||||
"postcss-merge-longhand": "^7.0.2",
|
"postcss-merge-longhand": "^7.0.4",
|
||||||
"postcss-merge-rules": "^7.0.2",
|
"postcss-merge-rules": "^7.0.4",
|
||||||
"postcss-minify-font-values": "^7.0.0",
|
"postcss-minify-font-values": "^7.0.0",
|
||||||
"postcss-minify-gradients": "^7.0.0",
|
"postcss-minify-gradients": "^7.0.0",
|
||||||
"postcss-minify-params": "^7.0.1",
|
"postcss-minify-params": "^7.0.2",
|
||||||
"postcss-minify-selectors": "^7.0.2",
|
"postcss-minify-selectors": "^7.0.4",
|
||||||
"postcss-normalize-charset": "^7.0.0",
|
"postcss-normalize-charset": "^7.0.0",
|
||||||
"postcss-normalize-display-values": "^7.0.0",
|
"postcss-normalize-display-values": "^7.0.0",
|
||||||
"postcss-normalize-positions": "^7.0.0",
|
"postcss-normalize-positions": "^7.0.0",
|
||||||
"postcss-normalize-repeat-style": "^7.0.0",
|
"postcss-normalize-repeat-style": "^7.0.0",
|
||||||
"postcss-normalize-string": "^7.0.0",
|
"postcss-normalize-string": "^7.0.0",
|
||||||
"postcss-normalize-timing-functions": "^7.0.0",
|
"postcss-normalize-timing-functions": "^7.0.0",
|
||||||
"postcss-normalize-unicode": "^7.0.1",
|
"postcss-normalize-unicode": "^7.0.2",
|
||||||
"postcss-normalize-url": "^7.0.0",
|
"postcss-normalize-url": "^7.0.0",
|
||||||
"postcss-normalize-whitespace": "^7.0.0",
|
"postcss-normalize-whitespace": "^7.0.0",
|
||||||
"postcss-ordered-values": "^7.0.1",
|
"postcss-ordered-values": "^7.0.1",
|
||||||
"postcss-reduce-initial": "^7.0.1",
|
"postcss-reduce-initial": "^7.0.2",
|
||||||
"postcss-reduce-transforms": "^7.0.0",
|
"postcss-reduce-transforms": "^7.0.0",
|
||||||
"postcss-svgo": "^7.0.1",
|
"postcss-svgo": "^7.0.1",
|
||||||
"postcss-unique-selectors": "^7.0.1"
|
"postcss-unique-selectors": "^7.0.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.12.0 || ^20.9.0 || >=22.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0"
|
||||||
|
@ -703,9 +703,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.4.829",
|
"version": "1.5.47",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.829.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.47.tgz",
|
||||||
"integrity": "sha512-5qp1N2POAfW0u1qGAxXEtz6P7bO1m6gpZr5hdf5ve6lxpLM7MpiM4jIPz7xcrNlClQMafbyUDDWjlIQZ1Mw0Rw==",
|
"integrity": "sha512-zS5Yer0MOYw4rtK2iq43cJagHZ8sXN0jDHDKzB+86gSBSAI4v07S97mcq+Gs2vclAxSh1j7vOAHxSVgduiiuVQ==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/entities": {
|
"node_modules/entities": {
|
||||||
|
@ -721,9 +721,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/escalade": {
|
"node_modules/escalade": {
|
||||||
"version": "3.1.2",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
||||||
"integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
|
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
|
@ -831,9 +831,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/node-releases": {
|
"node_modules/node-releases": {
|
||||||
"version": "2.0.17",
|
"version": "2.0.18",
|
||||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.17.tgz",
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
|
||||||
"integrity": "sha512-Ww6ZlOiEQfPfXM45v17oabk77Z7mg5bOt7AjDyzy7RjK9OrLrLC8dyZQoAPEOtFX9SaNf1Tdvr5gRJWdTJj7GA==",
|
"integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/normalize-range": {
|
"node_modules/normalize-range": {
|
||||||
|
@ -878,15 +878,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/picocolors": {
|
"node_modules/picocolors": {
|
||||||
"version": "1.0.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||||
"integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==",
|
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.39",
|
"version": "8.4.47",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
|
||||||
"integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==",
|
"integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
|
@ -904,20 +904,20 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.7",
|
"nanoid": "^3.3.7",
|
||||||
"picocolors": "^1.0.1",
|
"picocolors": "^1.1.0",
|
||||||
"source-map-js": "^1.2.0"
|
"source-map-js": "^1.2.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^10 || ^12 || >=14"
|
"node": "^10 || ^12 || >=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss-calc": {
|
"node_modules/postcss-calc": {
|
||||||
"version": "10.0.0",
|
"version": "10.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-10.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-10.0.2.tgz",
|
||||||
"integrity": "sha512-OmjhudoNTP0QleZCwl1i6NeBwN+5MZbY5ersLZz69mjJiDVv/p57RjRuKDkHeDWr4T+S97wQfsqRTNoDHB2e3g==",
|
"integrity": "sha512-DT/Wwm6fCKgpYVI7ZEWuPJ4az8hiEHtCUeYjZXqU7Ou4QqYh1Df2yCQ7Ca6N7xqKPFkxN3fhf+u9KSoOCJNAjg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"postcss-selector-parser": "^6.0.16",
|
"postcss-selector-parser": "^6.1.2",
|
||||||
"postcss-value-parser": "^4.2.0"
|
"postcss-value-parser": "^4.2.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -928,12 +928,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss-colormin": {
|
"node_modules/postcss-colormin": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.2.tgz",
|
||||||
"integrity": "sha512-uszdT0dULt3FQs47G5UHCduYK+FnkLYlpu1HpWu061eGsKZ7setoG7kA+WC9NQLsOJf69D5TxGHgnAdRgylnFQ==",
|
"integrity": "sha512-YntRXNngcvEvDbEjTdRWGU606eZvB5prmHG4BF0yLmVpamXbpsRJzevyy6MZVyuecgzI2AWAlvFi8DAeCqwpvA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"browserslist": "^4.23.1",
|
"browserslist": "^4.23.3",
|
||||||
"caniuse-api": "^3.0.0",
|
"caniuse-api": "^3.0.0",
|
||||||
"colord": "^2.9.3",
|
"colord": "^2.9.3",
|
||||||
"postcss-value-parser": "^4.2.0"
|
"postcss-value-parser": "^4.2.0"
|
||||||
|
@ -946,12 +946,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss-convert-values": {
|
"node_modules/postcss-convert-values": {
|
||||||
"version": "7.0.2",
|
"version": "7.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.4.tgz",
|
||||||
"integrity": "sha512-MuZIF6HJ4izko07Q0TgW6pClalI4al6wHRNPkFzqQdwAwG7hPn0lA58VZdxyb2Vl5AYjJ1piO+jgF9EnTjQwQQ==",
|
"integrity": "sha512-e2LSXPqEHVW6aoGbjV9RsSSNDO3A0rZLCBxN24zvxF25WknMPpX8Dm9UxxThyEbaytzggRuZxaGXqaOhxQ514Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"browserslist": "^4.23.1",
|
"browserslist": "^4.23.3",
|
||||||
"postcss-value-parser": "^4.2.0"
|
"postcss-value-parser": "^4.2.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -962,12 +962,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss-discard-comments": {
|
"node_modules/postcss-discard-comments": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.3.tgz",
|
||||||
"integrity": "sha512-GVrQxUOhmle1W6jX2SvNLt4kmN+JYhV7mzI6BMnkAWR9DtVvg8e67rrV0NfdWhn7x1zxvzdWkMBPdBDCls+uwQ==",
|
"integrity": "sha512-q6fjd4WU4afNhWOA2WltHgCbkRhZPgQe7cXF74fuVB/ge4QbM9HEaOIzGSiMvM+g/cOsNAUGdf2JDzqA2F8iLA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"postcss-selector-parser": "^6.1.0"
|
"postcss-selector-parser": "^6.1.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.12.0 || ^20.9.0 || >=22.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0"
|
||||||
|
@ -977,9 +977,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss-discard-duplicates": {
|
"node_modules/postcss-discard-duplicates": {
|
||||||
"version": "7.0.0",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-7.0.1.tgz",
|
||||||
"integrity": "sha512-bAnSuBop5LpAIUmmOSsuvtKAAKREB6BBIYStWUTGq8oG5q9fClDMMuY8i4UPI/cEcDx2TN+7PMnXYIId20UVDw==",
|
"integrity": "sha512-oZA+v8Jkpu1ct/xbbrntHRsfLGuzoP+cpt0nJe5ED2FQF8n8bJtn7Bo28jSmBYwqgqnqkuSXJfSUEE7if4nClQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.12.0 || ^20.9.0 || >=22.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0"
|
||||||
|
@ -1013,13 +1013,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss-merge-longhand": {
|
"node_modules/postcss-merge-longhand": {
|
||||||
"version": "7.0.2",
|
"version": "7.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-7.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-7.0.4.tgz",
|
||||||
"integrity": "sha512-06vrW6ZWi9qeP7KMS9fsa9QW56+tIMW55KYqF7X3Ccn+NI2pIgPV6gFfvXTMQ05H90Y5DvnCDPZ2IuHa30PMUg==",
|
"integrity": "sha512-zer1KoZA54Q8RVHKOY5vMke0cCdNxMP3KBfDerjH/BYHh4nCIh+1Yy0t1pAEQF18ac/4z3OFclO+ZVH8azjR4A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"postcss-value-parser": "^4.2.0",
|
"postcss-value-parser": "^4.2.0",
|
||||||
"stylehacks": "^7.0.2"
|
"stylehacks": "^7.0.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.12.0 || ^20.9.0 || >=22.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0"
|
||||||
|
@ -1029,15 +1029,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss-merge-rules": {
|
"node_modules/postcss-merge-rules": {
|
||||||
"version": "7.0.2",
|
"version": "7.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.4.tgz",
|
||||||
"integrity": "sha512-VAR47UNvRsdrTHLe7TV1CeEtF9SJYR5ukIB9U4GZyZOptgtsS20xSxy+k5wMrI3udST6O1XuIn7cjQkg7sDAAw==",
|
"integrity": "sha512-ZsaamiMVu7uBYsIdGtKJ64PkcQt6Pcpep/uO90EpLS3dxJi6OXamIobTYcImyXGoW0Wpugh7DSD3XzxZS9JCPg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"browserslist": "^4.23.1",
|
"browserslist": "^4.23.3",
|
||||||
"caniuse-api": "^3.0.0",
|
"caniuse-api": "^3.0.0",
|
||||||
"cssnano-utils": "^5.0.0",
|
"cssnano-utils": "^5.0.0",
|
||||||
"postcss-selector-parser": "^6.1.0"
|
"postcss-selector-parser": "^6.1.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.12.0 || ^20.9.0 || >=22.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0"
|
||||||
|
@ -1079,12 +1079,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss-minify-params": {
|
"node_modules/postcss-minify-params": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.2.tgz",
|
||||||
"integrity": "sha512-e+Xt8xErSRPgSRFxHeBCSxMiO8B8xng7lh8E0A5ep1VfwYhY8FXhu4Q3APMjgx9YDDbSp53IBGENrzygbUvgUQ==",
|
"integrity": "sha512-nyqVLu4MFl9df32zTsdcLqCFfE/z2+f8GE1KHPxWOAmegSo6lpV2GNy5XQvrzwbLmiU7d+fYay4cwto1oNdAaQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"browserslist": "^4.23.1",
|
"browserslist": "^4.23.3",
|
||||||
"cssnano-utils": "^5.0.0",
|
"cssnano-utils": "^5.0.0",
|
||||||
"postcss-value-parser": "^4.2.0"
|
"postcss-value-parser": "^4.2.0"
|
||||||
},
|
},
|
||||||
|
@ -1096,13 +1096,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss-minify-selectors": {
|
"node_modules/postcss-minify-selectors": {
|
||||||
"version": "7.0.2",
|
"version": "7.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-7.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-7.0.4.tgz",
|
||||||
"integrity": "sha512-dCzm04wqW1uqLmDZ41XYNBJfjgps3ZugDpogAmJXoCb5oCiTzIX4oPXXKxDpTvWOnKxQKR4EbV4ZawJBLcdXXA==",
|
"integrity": "sha512-JG55VADcNb4xFCf75hXkzc1rNeURhlo7ugf6JjiiKRfMsKlDzN9CXHZDyiG6x/zGchpjQS+UAgb1d4nqXqOpmA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cssesc": "^3.0.0",
|
"cssesc": "^3.0.0",
|
||||||
"postcss-selector-parser": "^6.1.0"
|
"postcss-selector-parser": "^6.1.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.12.0 || ^20.9.0 || >=22.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0"
|
||||||
|
@ -1199,12 +1199,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss-normalize-unicode": {
|
"node_modules/postcss-normalize-unicode": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.2.tgz",
|
||||||
"integrity": "sha512-PTPGdY9xAkTw+8ZZ71DUePb7M/Vtgkbbq+EoI33EuyQEzbKemEQMhe5QSr0VP5UfZlreANDPxSfcdSprENcbsg==",
|
"integrity": "sha512-ztisabK5C/+ZWBdYC+Y9JCkp3M9qBv/XFvDtSw0d/XwfT3UaKeW/YTm/MD/QrPNxuecia46vkfEhewjwcYFjkg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"browserslist": "^4.23.1",
|
"browserslist": "^4.23.3",
|
||||||
"postcss-value-parser": "^4.2.0"
|
"postcss-value-parser": "^4.2.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -1261,12 +1261,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss-reduce-initial": {
|
"node_modules/postcss-reduce-initial": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.2.tgz",
|
||||||
"integrity": "sha512-0JDUSV4bGB5FGM5g8MkS+rvqKukJZ7OTHw/lcKn7xPNqeaqJyQbUO8/dJpvyTpaVwPsd3Uc33+CfNzdVowp2WA==",
|
"integrity": "sha512-pOnu9zqQww7dEKf62Nuju6JgsW2V0KRNBHxeKohU+JkHd/GAH5uvoObqFLqkeB2n20mr6yrlWDvo5UBU5GnkfA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"browserslist": "^4.23.1",
|
"browserslist": "^4.23.3",
|
||||||
"caniuse-api": "^3.0.0"
|
"caniuse-api": "^3.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -1292,9 +1292,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss-selector-parser": {
|
"node_modules/postcss-selector-parser": {
|
||||||
"version": "6.1.1",
|
"version": "6.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
|
||||||
"integrity": "sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==",
|
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cssesc": "^3.0.0",
|
"cssesc": "^3.0.0",
|
||||||
|
@ -1321,12 +1321,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss-unique-selectors": {
|
"node_modules/postcss-unique-selectors": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-7.0.3.tgz",
|
||||||
"integrity": "sha512-MH7QE/eKUftTB5ta40xcHLl7hkZjgDFydpfTK+QWXeHxghVt3VoPqYL5/G+zYZPPIs+8GuqFXSTgxBSoB1RZtQ==",
|
"integrity": "sha512-J+58u5Ic5T1QjP/LDV9g3Cx4CNOgB5vz+kM6+OxHHhFACdcDeKhBXjQmB7fnIZM12YSTvsL0Opwco83DmacW2g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"postcss-selector-parser": "^6.1.0"
|
"postcss-selector-parser": "^6.1.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.12.0 || ^20.9.0 || >=22.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0"
|
||||||
|
@ -1360,9 +1360,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/source-map-js": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
|
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
|
@ -1379,13 +1379,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/stylehacks": {
|
"node_modules/stylehacks": {
|
||||||
"version": "7.0.2",
|
"version": "7.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-7.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-7.0.4.tgz",
|
||||||
"integrity": "sha512-HdkWZS9b4gbgYTdMg4gJLmm7biAUug1qTqXjS+u8X+/pUd+9Px1E+520GnOW3rST9MNsVOVpsJG+mPHNosxjOQ==",
|
"integrity": "sha512-i4zfNrGMt9SB4xRK9L83rlsFCgdGANfeDAYacO1pkqcE7cRHPdWHwnKZVz7WY17Veq/FvyYsRAU++Ga+qDFIww==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"browserslist": "^4.23.1",
|
"browserslist": "^4.23.3",
|
||||||
"postcss-selector-parser": "^6.1.0"
|
"postcss-selector-parser": "^6.1.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.12.0 || ^20.9.0 || >=22.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0"
|
||||||
|
@ -1429,9 +1429,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/terser": {
|
"node_modules/terser": {
|
||||||
"version": "5.31.3",
|
"version": "5.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.31.3.tgz",
|
"resolved": "https://registry.npmjs.org/terser/-/terser-5.36.0.tgz",
|
||||||
"integrity": "sha512-pAfYn3NIZLyZpa83ZKigvj6Rn9c/vd5KfYGX7cN1mnzqgDcxWvrU5ZtAfIKhEXz9nRecw4z3LXkjaq96/qZqAA==",
|
"integrity": "sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/source-map": "^0.3.3",
|
"@jridgewell/source-map": "^0.3.3",
|
||||||
|
@ -1453,15 +1453,15 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/tslib": {
|
"node_modules/tslib": {
|
||||||
"version": "2.6.3",
|
"version": "2.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz",
|
||||||
"integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==",
|
"integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==",
|
||||||
"license": "0BSD"
|
"license": "0BSD"
|
||||||
},
|
},
|
||||||
"node_modules/update-browserslist-db": {
|
"node_modules/update-browserslist-db": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz",
|
||||||
"integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==",
|
"integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
|
@ -1478,8 +1478,8 @@
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"escalade": "^3.1.2",
|
"escalade": "^3.2.0",
|
||||||
"picocolors": "^1.0.1"
|
"picocolors": "^1.1.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"update-browserslist-db": "cli.js"
|
"update-browserslist-db": "cli.js"
|
||||||
|
|
|
@ -4,3 +4,6 @@ parameters:
|
||||||
- src
|
- src
|
||||||
bootstrapFiles:
|
bootstrapFiles:
|
||||||
- misuzu.php
|
- misuzu.php
|
||||||
|
dynamicConstantNames:
|
||||||
|
- MSZ_CLI
|
||||||
|
- MSZ_DEBUG
|
||||||
|
|
|
@ -4,7 +4,7 @@ namespace Misuzu;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Misuzu\Auth\AuthTokenCookie;
|
use Misuzu\Auth\AuthTokenCookie;
|
||||||
|
|
||||||
$urls = $msz->getURLs();
|
$urls = $msz->getUrls();
|
||||||
$authInfo = $msz->getAuthInfo();
|
$authInfo = $msz->getAuthInfo();
|
||||||
if($authInfo->isLoggedIn()) {
|
if($authInfo->isLoggedIn()) {
|
||||||
Tools::redirect($urls->format('index'));
|
Tools::redirect($urls->format('index'));
|
||||||
|
|
|
@ -24,4 +24,4 @@ if($authInfo->isLoggedIn()) {
|
||||||
AuthTokenCookie::apply($tokenPacker->pack($tokenInfo));
|
AuthTokenCookie::apply($tokenPacker->pack($tokenInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
Tools::redirect($msz->getURLs()->format('index'));;
|
Tools::redirect($msz->getUrls()->format('index'));;
|
||||||
|
|
|
@ -4,7 +4,7 @@ namespace Misuzu;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Misuzu\Users\User;
|
use Misuzu\Users\User;
|
||||||
|
|
||||||
$urls = $msz->getURLs();
|
$urls = $msz->getUrls();
|
||||||
$authInfo = $msz->getAuthInfo();
|
$authInfo = $msz->getAuthInfo();
|
||||||
if($authInfo->isLoggedIn()) {
|
if($authInfo->isLoggedIn()) {
|
||||||
Tools::redirect($urls->format('settings-account'));
|
Tools::redirect($urls->format('settings-account'));
|
||||||
|
|
|
@ -4,7 +4,7 @@ namespace Misuzu;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Misuzu\Users\User;
|
use Misuzu\Users\User;
|
||||||
|
|
||||||
$urls = $msz->getURLs();
|
$urls = $msz->getUrls();
|
||||||
$authInfo = $msz->getAuthInfo();
|
$authInfo = $msz->getAuthInfo();
|
||||||
if($authInfo->isLoggedIn()) {
|
if($authInfo->isLoggedIn()) {
|
||||||
Tools::redirect($urls->format('index'));
|
Tools::redirect($urls->format('index'));
|
||||||
|
|
|
@ -3,7 +3,7 @@ namespace Misuzu;
|
||||||
|
|
||||||
use Misuzu\Auth\AuthTokenCookie;
|
use Misuzu\Auth\AuthTokenCookie;
|
||||||
|
|
||||||
$urls = $msz->getURLs();
|
$urls = $msz->getUrls();
|
||||||
if(CSRF::validateRequest()) {
|
if(CSRF::validateRequest()) {
|
||||||
$tokenInfo = $msz->getAuthInfo()->getTokenInfo();
|
$tokenInfo = $msz->getAuthInfo()->getTokenInfo();
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use RuntimeException;
|
||||||
use Misuzu\TOTPGenerator;
|
use Misuzu\TOTPGenerator;
|
||||||
use Misuzu\Auth\AuthTokenCookie;
|
use Misuzu\Auth\AuthTokenCookie;
|
||||||
|
|
||||||
$urls = $msz->getURLs();
|
$urls = $msz->getUrls();
|
||||||
$authInfo = $msz->getAuthInfo();
|
$authInfo = $msz->getAuthInfo();
|
||||||
if($authInfo->isLoggedIn()) {
|
if($authInfo->isLoggedIn()) {
|
||||||
Tools::redirect($urls->format('index'));
|
Tools::redirect($urls->format('index'));
|
||||||
|
|
|
@ -4,7 +4,7 @@ namespace Misuzu;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
|
||||||
$usersCtx = $msz->getUsersContext();
|
$usersCtx = $msz->getUsersContext();
|
||||||
$redirect = filter_input(INPUT_GET, 'return') ?? $_SERVER['HTTP_REFERER'] ?? $msz->getURLs()->format('index');
|
$redirect = filter_input(INPUT_GET, 'return') ?? $_SERVER['HTTP_REFERER'] ?? $msz->getUrls()->format('index');
|
||||||
|
|
||||||
if(!Tools::isLocalURL($redirect))
|
if(!Tools::isLocalURL($redirect))
|
||||||
Template::displayInfo('Possible request forgery detected.', 403);
|
Template::displayInfo('Possible request forgery detected.', 403);
|
||||||
|
|
|
@ -32,14 +32,14 @@ if($mode === 'mark') {
|
||||||
$forumCategories->updateUserReadCategory($userInfo, $categoryInfo);
|
$forumCategories->updateUserReadCategory($userInfo, $categoryInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
Tools::redirect($msz->getURLs()->format($categoryId ? 'forum-category' : 'forum-index', ['forum' => $categoryId]));
|
Tools::redirect($msz->getUrls()->format($categoryId ? 'forum-category' : 'forum-index', ['forum' => $categoryId]));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Template::render('confirm', [
|
Template::render('confirm', [
|
||||||
'title' => 'Mark forum as read',
|
'title' => 'Mark forum as read',
|
||||||
'message' => 'Are you sure you want to mark ' . ($categoryId < 1 ? 'the entire' : 'this') . ' forum as read?',
|
'message' => 'Are you sure you want to mark ' . ($categoryId < 1 ? 'the entire' : 'this') . ' forum as read?',
|
||||||
'return' => $msz->getURLs()->format($categoryId ? 'forum-category' : 'forum-index', ['forum' => $categoryId]),
|
'return' => $msz->getUrls()->format($categoryId ? 'forum-category' : 'forum-index', ['forum' => $categoryId]),
|
||||||
'params' => [
|
'params' => [
|
||||||
'forum' => $categoryId,
|
'forum' => $categoryId,
|
||||||
]
|
]
|
||||||
|
|
|
@ -94,7 +94,7 @@ MD;
|
||||||
$markdown .= sprintf("| %s | [%s](%s%s) | %s |\r\n", $ranking->position,
|
$markdown .= sprintf("| %s | [%s](%s%s) | %s |\r\n", $ranking->position,
|
||||||
$ranking->user?->getName() ?? 'Deleted User',
|
$ranking->user?->getName() ?? 'Deleted User',
|
||||||
$msz->getSiteInfo()->getURL(),
|
$msz->getSiteInfo()->getURL(),
|
||||||
$msz->getURLs()->format('user-profile', ['user' => $ranking->userId]),
|
$msz->getUrls()->format('user-profile', ['user' => $ranking->userId]),
|
||||||
number_format($ranking->postsCount));
|
number_format($ranking->postsCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ namespace Misuzu;
|
||||||
|
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
|
||||||
$urls = $msz->getURLs();
|
$urls = $msz->getUrls();
|
||||||
$forumCtx = $msz->getForumContext();
|
$forumCtx = $msz->getForumContext();
|
||||||
$forumPosts = $forumCtx->getPosts();
|
$forumPosts = $forumCtx->getPosts();
|
||||||
$usersCtx = $msz->getUsersContext();
|
$usersCtx = $msz->getUsersContext();
|
||||||
|
|
|
@ -3,9 +3,10 @@ namespace Misuzu;
|
||||||
|
|
||||||
use stdClass;
|
use stdClass;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Index\DateTime;
|
|
||||||
use Misuzu\Forum\ForumTopicInfo;
|
use Misuzu\Forum\ForumTopicInfo;
|
||||||
use Misuzu\Parsers\Parser;
|
use Misuzu\Parsers\Parser;
|
||||||
|
use Index\XDateTime;
|
||||||
|
use Carbon\CarbonImmutable;
|
||||||
|
|
||||||
$authInfo = $msz->getAuthInfo();
|
$authInfo = $msz->getAuthInfo();
|
||||||
if(!$authInfo->isLoggedIn())
|
if(!$authInfo->isLoggedIn())
|
||||||
|
@ -152,11 +153,11 @@ if(!empty($_POST)) {
|
||||||
if($mode === 'create') {
|
if($mode === 'create') {
|
||||||
$postTimeout = $cfg->getInteger('forum.posting.timeout', 5);
|
$postTimeout = $cfg->getInteger('forum.posting.timeout', 5);
|
||||||
if($postTimeout > 0) {
|
if($postTimeout > 0) {
|
||||||
$postTimeoutThreshold = DateTime::now()->modify(sprintf('-%d seconds', $postTimeout));
|
$postTimeoutThreshold = new CarbonImmutable(sprintf('-%d seconds', $postTimeout));
|
||||||
$lastPostCreatedAt = $forumPosts->getUserLastPostCreatedAt($currentUser);
|
$lastPostCreatedAt = $forumPosts->getUserLastPostCreatedAt($currentUser);
|
||||||
|
|
||||||
if($lastPostCreatedAt->isMoreThan($postTimeoutThreshold)) {
|
if(XDateTime::compare($lastPostCreatedAt, $postTimeoutThreshold) > 0) {
|
||||||
$waitSeconds = $postTimeout + ($lastPostCreatedAt->getUnixTimeSeconds() - time());
|
$waitSeconds = $postTimeout + ((int)$lastPostCreatedAt->format('U') - time());
|
||||||
|
|
||||||
$notices[] = sprintf("You're posting too quickly! Please wait %s seconds before posting again.", number_format($waitSeconds));
|
$notices[] = sprintf("You're posting too quickly! Please wait %s seconds before posting again.", number_format($waitSeconds));
|
||||||
$notices[] = "It's possible that your post went through successfully and you pressed the submit button twice by accident.";
|
$notices[] = "It's possible that your post went through successfully and you pressed the submit button twice by accident.";
|
||||||
|
@ -257,7 +258,7 @@ if(!empty($_POST)) {
|
||||||
|
|
||||||
if(empty($notices)) {
|
if(empty($notices)) {
|
||||||
// does this ternary ever return forum-topic?
|
// does this ternary ever return forum-topic?
|
||||||
$redirect = $msz->getURLs()->format(empty($topicInfo) ? 'forum-topic' : 'forum-post', [
|
$redirect = $msz->getUrls()->format(empty($topicInfo) ? 'forum-topic' : 'forum-post', [
|
||||||
'topic' => $topicId ?? 0,
|
'topic' => $topicId ?? 0,
|
||||||
'post' => $postId ?? 0,
|
'post' => $postId ?? 0,
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -4,7 +4,7 @@ namespace Misuzu;
|
||||||
use stdClass;
|
use stdClass;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
|
||||||
$urls = $msz->getURLs();
|
$urls = $msz->getUrls();
|
||||||
$forumCtx = $msz->getForumContext();
|
$forumCtx = $msz->getForumContext();
|
||||||
$forumCategories = $forumCtx->getCategories();
|
$forumCategories = $forumCtx->getCategories();
|
||||||
$forumTopics = $forumCtx->getTopics();
|
$forumTopics = $forumCtx->getTopics();
|
||||||
|
|
|
@ -3,9 +3,9 @@ namespace Misuzu;
|
||||||
|
|
||||||
use DateTimeInterface;
|
use DateTimeInterface;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Index\DateTime;
|
|
||||||
use Index\XArray;
|
|
||||||
use Misuzu\Changelog\Changelog;
|
use Misuzu\Changelog\Changelog;
|
||||||
|
use Carbon\CarbonImmutable;
|
||||||
|
use Index\{XArray,XDateTime};
|
||||||
|
|
||||||
$authInfo = $msz->getAuthInfo();
|
$authInfo = $msz->getAuthInfo();
|
||||||
if(!$authInfo->getPerms('global')->check(Perm::G_CL_CHANGES_MANAGE))
|
if(!$authInfo->getPerms('global')->check(Perm::G_CL_CHANGES_MANAGE))
|
||||||
|
@ -15,7 +15,7 @@ $changeActions = [];
|
||||||
foreach(Changelog::ACTIONS as $action)
|
foreach(Changelog::ACTIONS as $action)
|
||||||
$changeActions[$action] = Changelog::actionText($action);
|
$changeActions[$action] = Changelog::actionText($action);
|
||||||
|
|
||||||
$urls = $msz->getURLs();
|
$urls = $msz->getUrls();
|
||||||
$changelog = $msz->getChangelog();
|
$changelog = $msz->getChangelog();
|
||||||
$changeId = (string)filter_input(INPUT_GET, 'c', FILTER_SANITIZE_NUMBER_INT);
|
$changeId = (string)filter_input(INPUT_GET, 'c', FILTER_SANITIZE_NUMBER_INT);
|
||||||
$changeInfo = null;
|
$changeInfo = null;
|
||||||
|
@ -58,8 +58,8 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||||
if(empty($createdAt))
|
if(empty($createdAt))
|
||||||
$createdAt = null;
|
$createdAt = null;
|
||||||
else {
|
else {
|
||||||
$createdAt = DateTime::createFromFormat(DateTimeInterface::ATOM, $createdAt . ':00Z');
|
$createdAt = CarbonImmutable::createFromFormat(DateTimeInterface::ATOM, $createdAt . ':00Z');
|
||||||
if($createdAt->getUnixTimeSeconds() < 0)
|
if((int)$createdAt->format('U') < 0)
|
||||||
$createdAt = null;
|
$createdAt = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||||
$summary = null;
|
$summary = null;
|
||||||
if($body === $changeInfo->getBody())
|
if($body === $changeInfo->getBody())
|
||||||
$body = null;
|
$body = null;
|
||||||
if($createdAt !== null && $createdAt->equals($changeInfo->getCreatedAt()))
|
if($createdAt !== null && XDateTime::compare($createdAt, $changeInfo->getCreatedAt()) === 0)
|
||||||
$createdAt = null;
|
$createdAt = null;
|
||||||
$updateUserInfo = $userId !== $changeInfo->getUserId();
|
$updateUserInfo = $userId !== $changeInfo->getUserId();
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use RuntimeException;
|
||||||
if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_CL_TAGS_MANAGE))
|
if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_CL_TAGS_MANAGE))
|
||||||
Template::throwError(403);
|
Template::throwError(403);
|
||||||
|
|
||||||
$urls = $msz->getURLs();
|
$urls = $msz->getUrls();
|
||||||
$changelog = $msz->getChangelog();
|
$changelog = $msz->getChangelog();
|
||||||
$tagId = (string)filter_input(INPUT_GET, 't', FILTER_SANITIZE_NUMBER_INT);
|
$tagId = (string)filter_input(INPUT_GET, 't', FILTER_SANITIZE_NUMBER_INT);
|
||||||
$loadTagInfo = fn() => $changelog->getTag($tagId);
|
$loadTagInfo = fn() => $changelog->getTag($tagId);
|
||||||
|
|
|
@ -5,7 +5,7 @@ $authInfo = $msz->getAuthInfo();
|
||||||
if(!$authInfo->getPerms('global')->check(Perm::G_FORUM_TOPIC_REDIRS_MANAGE))
|
if(!$authInfo->getPerms('global')->check(Perm::G_FORUM_TOPIC_REDIRS_MANAGE))
|
||||||
Template::throwError(403);
|
Template::throwError(403);
|
||||||
|
|
||||||
$urls = $msz->getURLs();
|
$urls = $msz->getUrls();
|
||||||
$forumCtx = $msz->getForumContext();
|
$forumCtx = $msz->getForumContext();
|
||||||
$forumTopicRedirects = $forumCtx->getTopicRedirects();
|
$forumTopicRedirects = $forumCtx->getTopicRedirects();
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||||
[$emoteInfo->getId()]
|
[$emoteInfo->getId()]
|
||||||
);
|
);
|
||||||
|
|
||||||
Tools::redirect($msz->getURLs()->format('manage-general-emoticon', ['emote' => $emoteInfo->getId()]));
|
Tools::redirect($msz->getUrls()->format('manage-general-emoticon', ['emote' => $emoteInfo->getId()]));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ if(CSRF::validateRequest() && !empty($_GET['emote'])) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Tools::redirect($msz->getURLs()->format('manage-general-emoticons'));
|
Tools::redirect($msz->getUrls()->format('manage-general-emoticons'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ if($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||||
$valueName = $valueInfo->getName();
|
$valueName = $valueInfo->getName();
|
||||||
$msz->createAuditLog('CONFIG_DELETE', [$valueName]);
|
$msz->createAuditLog('CONFIG_DELETE', [$valueName]);
|
||||||
$cfg->removeValues($valueName);
|
$cfg->removeValues($valueName);
|
||||||
Tools::redirect($msz->getURLs()->format('manage-general-settings'));
|
Tools::redirect($msz->getUrls()->format('manage-general-settings'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu;
|
namespace Misuzu;
|
||||||
|
|
||||||
use Syokuhou\DbConfig;
|
use Index\Config\Db\DbConfig;
|
||||||
|
|
||||||
if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_CONFIG_MANAGE))
|
if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_CONFIG_MANAGE))
|
||||||
Template::throwError(403);
|
Template::throwError(403);
|
||||||
|
@ -73,7 +73,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||||
|
|
||||||
$msz->createAuditLog($isNew ? 'CONFIG_CREATE' : 'CONFIG_UPDATE', [$sName]);
|
$msz->createAuditLog($isNew ? 'CONFIG_CREATE' : 'CONFIG_UPDATE', [$sName]);
|
||||||
$applyFunc($sName, $sValue);
|
$applyFunc($sName, $sValue);
|
||||||
Tools::redirect($msz->getURLs()->format('manage-general-settings'));
|
Tools::redirect($msz->getUrls()->format('manage-general-settings'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use RuntimeException;
|
||||||
if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_NEWS_CATEGORIES_MANAGE))
|
if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_NEWS_CATEGORIES_MANAGE))
|
||||||
Template::throwError(403);
|
Template::throwError(403);
|
||||||
|
|
||||||
$urls = $msz->getURLs();
|
$urls = $msz->getUrls();
|
||||||
$news = $msz->getNews();
|
$news = $msz->getNews();
|
||||||
$categoryId = (string)filter_input(INPUT_GET, 'c', FILTER_SANITIZE_NUMBER_INT);
|
$categoryId = (string)filter_input(INPUT_GET, 'c', FILTER_SANITIZE_NUMBER_INT);
|
||||||
$loadCategoryInfo = fn() => $news->getCategory(categoryId: $categoryId);
|
$loadCategoryInfo = fn() => $news->getCategory(categoryId: $categoryId);
|
||||||
|
|
|
@ -7,7 +7,7 @@ $authInfo = $msz->getAuthInfo();
|
||||||
if(!$authInfo->getPerms('global')->check(Perm::G_NEWS_POSTS_MANAGE))
|
if(!$authInfo->getPerms('global')->check(Perm::G_NEWS_POSTS_MANAGE))
|
||||||
Template::throwError(403);
|
Template::throwError(403);
|
||||||
|
|
||||||
$urls = $msz->getURLs();
|
$urls = $msz->getUrls();
|
||||||
$news = $msz->getNews();
|
$news = $msz->getNews();
|
||||||
$postId = (string)filter_input(INPUT_GET, 'p', FILTER_SANITIZE_NUMBER_INT);
|
$postId = (string)filter_input(INPUT_GET, 'p', FILTER_SANITIZE_NUMBER_INT);
|
||||||
$loadPostInfo = fn() => $news->getPost($postId);
|
$loadPostInfo = fn() => $news->getPost($postId);
|
||||||
|
|
|
@ -3,13 +3,13 @@ namespace Misuzu;
|
||||||
|
|
||||||
use DateTimeInterface;
|
use DateTimeInterface;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Index\DateTime;
|
use Carbon\CarbonImmutable;
|
||||||
|
|
||||||
$authInfo = $msz->getAuthInfo();
|
$authInfo = $msz->getAuthInfo();
|
||||||
if(!$authInfo->getPerms('user')->check(Perm::U_BANS_MANAGE))
|
if(!$authInfo->getPerms('user')->check(Perm::U_BANS_MANAGE))
|
||||||
Template::throwError(403);
|
Template::throwError(403);
|
||||||
|
|
||||||
$urls = $msz->getURLs();
|
$urls = $msz->getUrls();
|
||||||
$usersCtx = $msz->getUsersContext();
|
$usersCtx = $msz->getUsersContext();
|
||||||
$bans = $usersCtx->getBans();
|
$bans = $usersCtx->getBans();
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||||
if($expires === -1) {
|
if($expires === -1) {
|
||||||
$expires = null;
|
$expires = null;
|
||||||
} elseif($expires === -2) {
|
} elseif($expires === -2) {
|
||||||
$expires = DateTime::createFromFormat(DateTimeInterface::ATOM, $expiresCustom . ':00Z');
|
$expires = CarbonImmutable::createFromFormat(DateTimeInterface::ATOM, $expiresCustom . ':00Z');
|
||||||
} else {
|
} else {
|
||||||
echo 'Invalid duration specified.';
|
echo 'Invalid duration specified.';
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -13,7 +13,7 @@ $hasUserId = filter_has_var(INPUT_GET, 'u');
|
||||||
if((!$hasNoteId && !$hasUserId) || ($hasNoteId && $hasUserId))
|
if((!$hasNoteId && !$hasUserId) || ($hasNoteId && $hasUserId))
|
||||||
Template::throwError(400);
|
Template::throwError(400);
|
||||||
|
|
||||||
$urls = $msz->getURLs();
|
$urls = $msz->getUrls();
|
||||||
$usersCtx = $msz->getUsersContext();
|
$usersCtx = $msz->getUsersContext();
|
||||||
$modNotes = $usersCtx->getModNotes();
|
$modNotes = $usersCtx->getModNotes();
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ namespace Misuzu;
|
||||||
|
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Index\Colour\Colour;
|
use Index\Colour\Colour;
|
||||||
use Index\Colour\ColourRGB;
|
use Index\Colour\ColourRgb;
|
||||||
use Misuzu\Perm;
|
use Misuzu\Perm;
|
||||||
|
|
||||||
$authInfo = $msz->getAuthInfo();
|
$authInfo = $msz->getAuthInfo();
|
||||||
|
@ -42,6 +42,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$roleString = (string)filter_input(INPUT_POST, 'ur_string');
|
||||||
$roleName = (string)filter_input(INPUT_POST, 'ur_name');
|
$roleName = (string)filter_input(INPUT_POST, 'ur_name');
|
||||||
$roleHide = !empty($_POST['ur_hidden']);
|
$roleHide = !empty($_POST['ur_hidden']);
|
||||||
$roleLeavable = !empty($_POST['ur_leavable']);
|
$roleLeavable = !empty($_POST['ur_leavable']);
|
||||||
|
@ -54,6 +55,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||||
$colourBlue = (int)filter_input(INPUT_POST, 'ur_col_blue', FILTER_SANITIZE_NUMBER_INT);
|
$colourBlue = (int)filter_input(INPUT_POST, 'ur_col_blue', FILTER_SANITIZE_NUMBER_INT);
|
||||||
|
|
||||||
Template::set([
|
Template::set([
|
||||||
|
'role_ur_string' => $roleString,
|
||||||
'role_ur_name' => $roleName,
|
'role_ur_name' => $roleName,
|
||||||
'role_ur_hidden' => $roleHide,
|
'role_ur_hidden' => $roleHide,
|
||||||
'role_ur_leavable' => $roleLeavable,
|
'role_ur_leavable' => $roleLeavable,
|
||||||
|
@ -84,7 +86,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||||
|
|
||||||
$roleColour = $colourInherit
|
$roleColour = $colourInherit
|
||||||
? Colour::none()
|
? Colour::none()
|
||||||
: new ColourRGB($colourRed, $colourGreen, $colourBlue);
|
: new ColourRgb($colourRed, $colourGreen, $colourBlue);
|
||||||
|
|
||||||
if(mb_strlen($roleDesc) > 1000) {
|
if(mb_strlen($roleDesc) > 1000) {
|
||||||
echo 'Description may not be longer than 1000 characters.';
|
echo 'Description may not be longer than 1000 characters.';
|
||||||
|
@ -96,11 +98,31 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(strlen($roleString) > 20) {
|
||||||
|
echo 'Role string may not be longer than 20 characters.';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(strlen($roleString) > 1 && !ctype_alpha($roleString[0])) {
|
||||||
|
echo 'Role string most start with an alphabetical character.';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if($isNew) {
|
if($isNew) {
|
||||||
$roleInfo = $roles->createRole($roleName, $roleRank, $roleColour, $roleTitle, $roleDesc, $roleHide, $roleLeavable);
|
$roleInfo = $roles->createRole(
|
||||||
|
$roleName,
|
||||||
|
$roleRank,
|
||||||
|
$roleColour,
|
||||||
|
string: $roleString,
|
||||||
|
title: $roleTitle,
|
||||||
|
description: $roleDesc,
|
||||||
|
hidden: $roleHide,
|
||||||
|
leavable: $roleLeavable
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
if($roleName === $roleInfo->getName())
|
if($roleName === $roleInfo->getName())
|
||||||
$roleName = null;
|
$roleName = null;
|
||||||
|
if($roleString === $roleInfo->getString())
|
||||||
|
$roleString = null;
|
||||||
if($roleHide === $roleInfo->isHidden())
|
if($roleHide === $roleInfo->isHidden())
|
||||||
$roleHide = null;
|
$roleHide = null;
|
||||||
if($roleLeavable === $roleInfo->isLeavable())
|
if($roleLeavable === $roleInfo->isLeavable())
|
||||||
|
@ -115,7 +137,17 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||||
if((string)$roleColour === (string)$roleInfo->getColour())
|
if((string)$roleColour === (string)$roleInfo->getColour())
|
||||||
$roleColour = null;
|
$roleColour = null;
|
||||||
|
|
||||||
$roles->updateRole($roleInfo, $roleName, $roleRank, $roleColour, $roleTitle, $roleDesc, $roleHide, $roleLeavable);
|
$roles->updateRole(
|
||||||
|
$roleInfo,
|
||||||
|
string: $roleString,
|
||||||
|
name: $roleName,
|
||||||
|
rank: $roleRank,
|
||||||
|
colour: $roleColour,
|
||||||
|
title: $roleTitle,
|
||||||
|
description: $roleDesc,
|
||||||
|
hidden: $roleHide,
|
||||||
|
leavable: $roleLeavable
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$msz->createAuditLog(
|
$msz->createAuditLog(
|
||||||
|
@ -136,7 +168,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||||
$msz->getConfig()->setBoolean('perms.needsRecalc', true);
|
$msz->getConfig()->setBoolean('perms.needsRecalc', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Tools::redirect($msz->getURLs()->format('manage-role', ['role' => $roleInfo->getId()]));
|
Tools::redirect($msz->getUrls()->format('manage-role', ['role' => $roleInfo->getId()]));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ $viewerPerms = $authInfo->getPerms('user');
|
||||||
if(!$authInfo->isLoggedIn())
|
if(!$authInfo->isLoggedIn())
|
||||||
Template::throwError(403);
|
Template::throwError(403);
|
||||||
|
|
||||||
$urls = $msz->getURLs();
|
$urls = $msz->getUrls();
|
||||||
$usersCtx = $msz->getUsersContext();
|
$usersCtx = $msz->getUsersContext();
|
||||||
$users = $usersCtx->getUsers();
|
$users = $usersCtx->getUsers();
|
||||||
$roles = $usersCtx->getRoles();
|
$roles = $usersCtx->getRoles();
|
||||||
|
|
|
@ -7,7 +7,7 @@ $authInfo = $msz->getAuthInfo();
|
||||||
if(!$authInfo->getPerms('user')->check(Perm::U_WARNINGS_MANAGE))
|
if(!$authInfo->getPerms('user')->check(Perm::U_WARNINGS_MANAGE))
|
||||||
Template::throwError(403);
|
Template::throwError(403);
|
||||||
|
|
||||||
$urls = $msz->getURLs();
|
$urls = $msz->getUrls();
|
||||||
$usersCtx = $msz->getUsersContext();
|
$usersCtx = $msz->getUsersContext();
|
||||||
$users = $usersCtx->getUsers();
|
$users = $usersCtx->getUsers();
|
||||||
$warns = $usersCtx->getWarnings();
|
$warns = $usersCtx->getWarnings();
|
||||||
|
|
|
@ -5,7 +5,6 @@ use stdClass;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Index\ByteFormat;
|
use Index\ByteFormat;
|
||||||
use Index\DateTime;
|
|
||||||
use Misuzu\Parsers\Parser;
|
use Misuzu\Parsers\Parser;
|
||||||
use Misuzu\Users\User;
|
use Misuzu\Users\User;
|
||||||
use Misuzu\Users\Assets\UserAvatarAsset;
|
use Misuzu\Users\Assets\UserAvatarAsset;
|
||||||
|
@ -15,7 +14,7 @@ $userId = !empty($_GET['u']) && is_string($_GET['u']) ? trim($_GET['u']) : 0;
|
||||||
$profileMode = !empty($_GET['m']) && is_string($_GET['m']) ? (string)$_GET['m'] : '';
|
$profileMode = !empty($_GET['m']) && is_string($_GET['m']) ? (string)$_GET['m'] : '';
|
||||||
$isEditing = !empty($_GET['edit']) && is_string($_GET['edit']) ? (bool)$_GET['edit'] : !empty($_POST) && is_array($_POST);
|
$isEditing = !empty($_GET['edit']) && is_string($_GET['edit']) ? (bool)$_GET['edit'] : !empty($_POST) && is_array($_POST);
|
||||||
|
|
||||||
$urls = $msz->getURLs();
|
$urls = $msz->getUrls();
|
||||||
$usersCtx = $msz->getUsersContext();
|
$usersCtx = $msz->getUsersContext();
|
||||||
$users = $usersCtx->getUsers();
|
$users = $usersCtx->getUsers();
|
||||||
$forumCtx = $msz->getForumContext();
|
$forumCtx = $msz->getForumContext();
|
||||||
|
|
|
@ -3,7 +3,6 @@ namespace Misuzu;
|
||||||
|
|
||||||
use ZipArchive;
|
use ZipArchive;
|
||||||
use Index\XString;
|
use Index\XString;
|
||||||
use Index\IO\FileStream;
|
|
||||||
use Misuzu\Users\UserInfo;
|
use Misuzu\Users\UserInfo;
|
||||||
|
|
||||||
$authInfo = $msz->getAuthInfo();
|
$authInfo = $msz->getAuthInfo();
|
||||||
|
@ -42,7 +41,7 @@ function db_to_zip(ZipArchive $archive, UserInfo $userInfo, string $baseName, ar
|
||||||
}
|
}
|
||||||
|
|
||||||
$tmpName = sys_get_temp_dir() . DIRECTORY_SEPARATOR . sprintf('msz-user-data-%s-%s-%s.tmp', $userId, $baseName, XString::random(8));
|
$tmpName = sys_get_temp_dir() . DIRECTORY_SEPARATOR . sprintf('msz-user-data-%s-%s-%s.tmp', $userId, $baseName, XString::random(8));
|
||||||
$tmpStream = FileStream::newWrite($tmpName);
|
$tmpHandle = fopen($tmpName, 'wb');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$stmt = $dbConn->prepare(sprintf('SELECT %s FROM msz_%s WHERE %s = ?', implode(', ', $fields), $baseName, $userIdField));
|
$stmt = $dbConn->prepare(sprintf('SELECT %s FROM msz_%s WHERE %s = ?', implode(', ', $fields), $baseName, $userIdField));
|
||||||
|
@ -85,12 +84,12 @@ function db_to_zip(ZipArchive $archive, UserInfo $userInfo, string $baseName, ar
|
||||||
$row[$fieldInfo['name']] = $fieldValue;
|
$row[$fieldInfo['name']] = $fieldValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$tmpStream->write(json_encode($row, JSON_INVALID_UTF8_SUBSTITUTE));
|
fwrite($tmpHandle, json_encode($row, JSON_INVALID_UTF8_SUBSTITUTE));
|
||||||
$tmpStream->write("\n");
|
fwrite($tmpHandle, "\n");
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
$tmpStream->flush();
|
fflush($tmpHandle);
|
||||||
$tmpStream->close();
|
fclose($tmpHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
$archive->addFile($tmpName, $baseName . '.jsonl');
|
$archive->addFile($tmpName, $baseName . '.jsonl');
|
||||||
|
|
|
@ -37,7 +37,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if($activeSessionKilled) {
|
if($activeSessionKilled) {
|
||||||
Tools::redirect($msz->getURLs()->format('index'));
|
Tools::redirect($msz->getUrls()->format('index'));
|
||||||
return;
|
return;
|
||||||
} else break;
|
} else break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,7 @@
|
||||||
namespace Misuzu;
|
namespace Misuzu;
|
||||||
|
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Misuzu\Auth\AuthTokenBuilder;
|
use Misuzu\Auth\{AuthTokenBuilder,AuthTokenCookie,AuthTokenInfo};
|
||||||
use Misuzu\Auth\AuthTokenCookie;
|
|
||||||
use Misuzu\Auth\AuthTokenInfo;
|
|
||||||
use Sasae\SasaeEnvironment;
|
|
||||||
|
|
||||||
require_once __DIR__ . '/../misuzu.php';
|
require_once __DIR__ . '/../misuzu.php';
|
||||||
|
|
||||||
|
|
|
@ -2,28 +2,23 @@
|
||||||
namespace Misuzu\AuditLog;
|
namespace Misuzu\AuditLog;
|
||||||
|
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use Index\Data\DbStatementCache;
|
use Index\Db\{DbConnection,DbStatementCache};
|
||||||
use Index\Data\IDbConnection;
|
|
||||||
use Index\Data\IDbResult;
|
|
||||||
use Index\Net\IPAddress;
|
|
||||||
use Misuzu\Pagination;
|
use Misuzu\Pagination;
|
||||||
use Misuzu\Users\UserInfo;
|
use Misuzu\Users\UserInfo;
|
||||||
|
|
||||||
class AuditLog {
|
class AuditLog {
|
||||||
private DbStatementCache $cache;
|
private DbStatementCache $cache;
|
||||||
|
|
||||||
public function __construct(IDbConnection $dbConn) {
|
public function __construct(DbConnection $dbConn) {
|
||||||
$this->cache = new DbStatementCache($dbConn);
|
$this->cache = new DbStatementCache($dbConn);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function countLogs(
|
public function countLogs(
|
||||||
UserInfo|string|null $userInfo = null,
|
UserInfo|string|null $userInfo = null,
|
||||||
IPAddress|string|null $remoteAddr = null
|
?string $remoteAddr = null
|
||||||
): int {
|
): int {
|
||||||
if($userInfo instanceof UserInfo)
|
if($userInfo instanceof UserInfo)
|
||||||
$userInfo = $userInfo->getId();
|
$userInfo = $userInfo->getId();
|
||||||
if($remoteAddr instanceof IPAddress)
|
|
||||||
$remoteAddr = (string)$remoteAddr;
|
|
||||||
|
|
||||||
$hasUserInfo = $userInfo !== null;
|
$hasUserInfo = $userInfo !== null;
|
||||||
$hasRemoteAddr = $remoteAddr !== null;
|
$hasRemoteAddr = $remoteAddr !== null;
|
||||||
|
@ -59,13 +54,11 @@ class AuditLog {
|
||||||
|
|
||||||
public function getLogs(
|
public function getLogs(
|
||||||
UserInfo|string|null $userInfo = null,
|
UserInfo|string|null $userInfo = null,
|
||||||
IPAddress|string|null $remoteAddr = null,
|
?string $remoteAddr = null,
|
||||||
?Pagination $pagination = null
|
?Pagination $pagination = null
|
||||||
): iterable {
|
): iterable {
|
||||||
if($userInfo instanceof UserInfo)
|
if($userInfo instanceof UserInfo)
|
||||||
$userInfo = $userInfo->getId();
|
$userInfo = $userInfo->getId();
|
||||||
if($remoteAddr instanceof IPAddress)
|
|
||||||
$remoteAddr = (string)$remoteAddr;
|
|
||||||
|
|
||||||
$hasUserInfo = $userInfo !== null;
|
$hasUserInfo = $userInfo !== null;
|
||||||
$hasRemoteAddr = $remoteAddr !== null;
|
$hasRemoteAddr = $remoteAddr !== null;
|
||||||
|
@ -106,13 +99,11 @@ class AuditLog {
|
||||||
UserInfo|string|null $userInfo,
|
UserInfo|string|null $userInfo,
|
||||||
string $action,
|
string $action,
|
||||||
array $params = [],
|
array $params = [],
|
||||||
IPAddress|string $remoteAddr = '::1',
|
string $remoteAddr = '::1',
|
||||||
string $countryCode = 'XX'
|
string $countryCode = 'XX'
|
||||||
): void {
|
): void {
|
||||||
if($userInfo instanceof UserInfo)
|
if($userInfo instanceof UserInfo)
|
||||||
$userInfo = $userInfo->getId();
|
$userInfo = $userInfo->getId();
|
||||||
if($remoteAddr instanceof IPAddress)
|
|
||||||
$remoteAddr = (string)$remoteAddr;
|
|
||||||
|
|
||||||
// action names should have stricter validation,
|
// action names should have stricter validation,
|
||||||
// i do want to switch to a lowercase colon separated format later but i'll save that for the unified log in Hanyuu
|
// i do want to switch to a lowercase colon separated format later but i'll save that for the unified log in Hanyuu
|
||||||
|
|
|
@ -2,9 +2,8 @@
|
||||||
namespace Misuzu\AuditLog;
|
namespace Misuzu\AuditLog;
|
||||||
|
|
||||||
use ValueError;
|
use ValueError;
|
||||||
use Index\DateTime;
|
use Carbon\CarbonImmutable;
|
||||||
use Index\Data\IDbResult;
|
use Index\Db\DbResult;
|
||||||
use Index\Net\IPAddress;
|
|
||||||
|
|
||||||
class AuditLogInfo {
|
class AuditLogInfo {
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
@ -16,7 +15,7 @@ class AuditLogInfo {
|
||||||
private string $country,
|
private string $country,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public static function fromResult(IDbResult $result): AuditLogInfo {
|
public static function fromResult(DbResult $result): AuditLogInfo {
|
||||||
return new AuditLogInfo(
|
return new AuditLogInfo(
|
||||||
userId: $result->getStringOrNull(0),
|
userId: $result->getStringOrNull(0),
|
||||||
action: $result->getString(1),
|
action: $result->getString(1),
|
||||||
|
@ -47,18 +46,14 @@ class AuditLogInfo {
|
||||||
return $this->created;
|
return $this->created;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCreatedAt(): DateTime {
|
public function getCreatedAt(): CarbonImmutable {
|
||||||
return DateTime::fromUnixTimeSeconds($this->created);
|
return CarbonImmutable::createFromTimestampUTC($this->created);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRemoteAddressRaw(): string {
|
public function getRemoteAddress(): string {
|
||||||
return $this->address;
|
return $this->address;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRemoteAddress(): IPAddress {
|
|
||||||
return IPAddress::parse($this->address);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getCountryCode(): string {
|
public function getCountryCode(): string {
|
||||||
return $this->country;
|
return $this->country;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu\Auth;
|
namespace Misuzu\Auth;
|
||||||
|
|
||||||
use Index\Data\IDbConnection;
|
use Index\Config\Config;
|
||||||
use Syokuhou\IConfig;
|
use Index\Db\DbConnection;
|
||||||
|
|
||||||
class AuthContext {
|
class AuthContext {
|
||||||
private Sessions $sessions;
|
private Sessions $sessions;
|
||||||
|
@ -10,9 +10,9 @@ class AuthContext {
|
||||||
private RecoveryTokens $recoveryTokens;
|
private RecoveryTokens $recoveryTokens;
|
||||||
private TwoFactorAuthSessions $tfaSessions;
|
private TwoFactorAuthSessions $tfaSessions;
|
||||||
|
|
||||||
private IConfig $config;
|
private Config $config;
|
||||||
|
|
||||||
public function __construct(IDbConnection $dbConn, IConfig $config) {
|
public function __construct(DbConnection $dbConn, Config $config) {
|
||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
$this->sessions = new Sessions($dbConn);
|
$this->sessions = new Sessions($dbConn);
|
||||||
$this->loginAttempts = new LoginAttempts($dbConn);
|
$this->loginAttempts = new LoginAttempts($dbConn);
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu\Auth;
|
namespace Misuzu\Auth;
|
||||||
|
|
||||||
use Index\XArray;
|
|
||||||
use Misuzu\Auth\SessionInfo;
|
use Misuzu\Auth\SessionInfo;
|
||||||
use Misuzu\Forum\ForumCategoryInfo;
|
use Misuzu\Forum\ForumCategoryInfo;
|
||||||
use Misuzu\Perms\IPermissionResult;
|
use Misuzu\Perms\IPermissionResult;
|
||||||
|
|
63
src/Auth/AuthRpcActions.php
Normal file
63
src/Auth/AuthRpcActions.php
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<?php
|
||||||
|
namespace Misuzu\Auth;
|
||||||
|
|
||||||
|
use RuntimeException;
|
||||||
|
use Misuzu\Users\{UsersContext,UserInfo};
|
||||||
|
use Aiwass\Server\{RpcActionHandler,RpcProcedure};
|
||||||
|
use Index\Config\Config;
|
||||||
|
|
||||||
|
final class AuthRpcActions extends RpcActionHandler {
|
||||||
|
public function __construct(
|
||||||
|
private Config $impersonateConfig,
|
||||||
|
private UsersContext $usersCtx,
|
||||||
|
private AuthContext $authCtx
|
||||||
|
) {}
|
||||||
|
|
||||||
|
private function canImpersonateUserId(UserInfo $impersonator, string $targetId): bool {
|
||||||
|
if($impersonator->isSuperUser())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
$whitelist = $this->impersonateConfig->getArray(sprintf('allow.u%s', $impersonator->getId()));
|
||||||
|
return in_array($targetId, $whitelist, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[RpcProcedure('misuzu:auth:attemptMisuzuAuth')]
|
||||||
|
public function procAttemptMisuzuAuth(string $remoteAddr, string $token): array {
|
||||||
|
$tokenInfo = $this->authCtx->createAuthTokenPacker()->unpack($token);
|
||||||
|
if(!$tokenInfo->isEmpty())
|
||||||
|
$token = $tokenInfo->getSessionToken();
|
||||||
|
|
||||||
|
$sessions = $this->authCtx->getSessions();
|
||||||
|
try {
|
||||||
|
$sessionInfo = $sessions->getSession(sessionToken: $token);
|
||||||
|
} catch(RuntimeException $ex) {
|
||||||
|
return ['method' => 'misuzu', 'error' => 'token'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if($sessionInfo->hasExpired()) {
|
||||||
|
$sessions->deleteSessions(sessionInfos: $sessionInfo);
|
||||||
|
return ['method' => 'misuzu', 'error' => 'expired'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$sessions->recordSessionActivity(sessionInfo: $sessionInfo, remoteAddr: $remoteAddr);
|
||||||
|
|
||||||
|
$users = $this->usersCtx->getUsers();
|
||||||
|
$userInfo = $users->getUser($sessionInfo->getUserId(), 'id');
|
||||||
|
if($tokenInfo->hasImpersonatedUserId() && $this->canImpersonateUserId($userInfo, $tokenInfo->getImpersonatedUserId())) {
|
||||||
|
$userInfoReal = $userInfo;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$userInfo = $users->getUser($tokenInfo->getImpersonatedUserId(), 'id');
|
||||||
|
} catch(RuntimeException $ex) {
|
||||||
|
$userInfo = $userInfoReal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'method' => 'misuzu',
|
||||||
|
'type' => 'user',
|
||||||
|
'user' => $userInfo->getId(),
|
||||||
|
'expires' => $sessionInfo->getExpiresTime(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,8 +2,7 @@
|
||||||
namespace Misuzu\Auth;
|
namespace Misuzu\Auth;
|
||||||
|
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Index\IO\MemoryStream;
|
use Index\UriBase64;
|
||||||
use Index\Serialisation\UriBase64;
|
|
||||||
|
|
||||||
class AuthTokenPacker {
|
class AuthTokenPacker {
|
||||||
private const EPOCH_V2 = 1682985600;
|
private const EPOCH_V2 = 1682985600;
|
||||||
|
@ -64,33 +63,41 @@ class AuthTokenPacker {
|
||||||
|
|
||||||
$unpackTime = unpack('Nts', $timestamp);
|
$unpackTime = unpack('Nts', $timestamp);
|
||||||
if($unpackTime === false)
|
if($unpackTime === false)
|
||||||
throw new RuntimeException('$token does not contain a valid timestamp.');
|
return AuthTokenInfo::empty();
|
||||||
|
|
||||||
$timestamp = $unpackTime['ts'] + self::EPOCH_V2;
|
$timestamp = $unpackTime['ts'] + self::EPOCH_V2;
|
||||||
|
|
||||||
$stream = MemoryStream::fromString($data);
|
$handle = fopen('php://memory', 'rb+');
|
||||||
$stream->seek(0);
|
if($handle === false)
|
||||||
|
return AuthTokenInfo::empty();
|
||||||
|
|
||||||
|
try {
|
||||||
|
fwrite($handle, $data);
|
||||||
|
fseek($handle, 0);
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
$length = $stream->readChar();
|
$length = fgetc($handle);
|
||||||
if($length === null)
|
if($length === false)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
$length = ord($length);
|
$length = ord($length);
|
||||||
if($length < 1)
|
if($length < 1)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
$name = $stream->read($length);
|
$name = fread($handle, $length);
|
||||||
$value = null;
|
$value = null;
|
||||||
$length = $stream->readChar();
|
$length = fgetc($handle);
|
||||||
if($length !== null) {
|
if($length !== false) {
|
||||||
$length = ord($length);
|
$length = ord($length);
|
||||||
if($length > 0)
|
if($length > 0)
|
||||||
$value = $stream->read($length);
|
$value = fread($handle, $length);
|
||||||
}
|
}
|
||||||
|
|
||||||
$builder->setProperty($name, $value);
|
$builder->setProperty($name, $value);
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
fclose($handle);
|
||||||
|
}
|
||||||
} else
|
} else
|
||||||
return AuthTokenInfo::empty();
|
return AuthTokenInfo::empty();
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu\Auth;
|
namespace Misuzu\Auth;
|
||||||
|
|
||||||
use Index\DateTime;
|
|
||||||
use Index\Data\IDbResult;
|
|
||||||
use Index\Net\IPAddress;
|
|
||||||
use Misuzu\ClientInfo;
|
use Misuzu\ClientInfo;
|
||||||
|
use Carbon\CarbonImmutable;
|
||||||
|
use Index\Db\DbResult;
|
||||||
|
|
||||||
class LoginAttemptInfo {
|
class LoginAttemptInfo {
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
@ -17,7 +16,7 @@ class LoginAttemptInfo {
|
||||||
private string $clientInfo,
|
private string $clientInfo,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public static function fromResult(IDbResult $result): LoginAttemptInfo {
|
public static function fromResult(DbResult $result): LoginAttemptInfo {
|
||||||
return new LoginAttemptInfo(
|
return new LoginAttemptInfo(
|
||||||
userId: $result->getStringOrNull(0),
|
userId: $result->getStringOrNull(0),
|
||||||
success: $result->getBoolean(1),
|
success: $result->getBoolean(1),
|
||||||
|
@ -41,14 +40,10 @@ class LoginAttemptInfo {
|
||||||
return $this->success;
|
return $this->success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRemoteAddressRaw(): string {
|
public function getRemoteAddress(): string {
|
||||||
return $this->remoteAddr;
|
return $this->remoteAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRemoteAddress(): IPAddress {
|
|
||||||
return IPAddress::parse($this->remoteAddr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getCountryCode(): string {
|
public function getCountryCode(): string {
|
||||||
return $this->countryCode;
|
return $this->countryCode;
|
||||||
}
|
}
|
||||||
|
@ -57,8 +52,8 @@ class LoginAttemptInfo {
|
||||||
return $this->created;
|
return $this->created;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCreatedAt(): DateTime {
|
public function getCreatedAt(): CarbonImmutable {
|
||||||
return DateTime::fromUnixTimeSeconds($this->created);
|
return CarbonImmutable::createFromTimestampUTC($this->created);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUserAgentString(): string {
|
public function getUserAgentString(): string {
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu\Auth;
|
namespace Misuzu\Auth;
|
||||||
|
|
||||||
use Index\TimeSpan;
|
use Index\Db\{DbConnection,DbStatementCache};
|
||||||
use Index\Data\DbStatementCache;
|
|
||||||
use Index\Data\IDbConnection;
|
|
||||||
use Index\Net\IPAddress;
|
|
||||||
use Misuzu\ClientInfo;
|
use Misuzu\ClientInfo;
|
||||||
use Misuzu\Pagination;
|
use Misuzu\Pagination;
|
||||||
use Misuzu\Users\UserInfo;
|
use Misuzu\Users\UserInfo;
|
||||||
|
@ -15,22 +12,18 @@ class LoginAttempts {
|
||||||
|
|
||||||
private DbStatementCache $cache;
|
private DbStatementCache $cache;
|
||||||
|
|
||||||
public function __construct(IDbConnection $dbConn) {
|
public function __construct(DbConnection $dbConn) {
|
||||||
$this->cache = new DbStatementCache($dbConn);
|
$this->cache = new DbStatementCache($dbConn);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function countAttempts(
|
public function countAttempts(
|
||||||
?bool $success = null,
|
?bool $success = null,
|
||||||
UserInfo|string|null $userInfo = null,
|
UserInfo|string|null $userInfo = null,
|
||||||
IPAddress|string|null $remoteAddr = null,
|
?string $remoteAddr = null,
|
||||||
TimeSpan|int|null $timeRange = null
|
int|null $timeRange = null
|
||||||
): int {
|
): int {
|
||||||
if($userInfo instanceof UserInfo)
|
if($userInfo instanceof UserInfo)
|
||||||
$userInfo = $userInfo->getId();
|
$userInfo = $userInfo->getId();
|
||||||
if($remoteAddr instanceof IPAddress)
|
|
||||||
$remoteAddr = (string)$remoteAddr;
|
|
||||||
if($timeRange instanceof TimeSpan)
|
|
||||||
$timeRange = (int)$timeRange->totalSeconds();
|
|
||||||
|
|
||||||
$hasSuccess = $success !== null;
|
$hasSuccess = $success !== null;
|
||||||
$hasUserInfo = $userInfo !== null;
|
$hasUserInfo = $userInfo !== null;
|
||||||
|
@ -69,7 +62,7 @@ class LoginAttempts {
|
||||||
return $count;
|
return $count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function countRemainingAttempts(IPAddress|string $remoteAddr): int {
|
public function countRemainingAttempts(string $remoteAddr): int {
|
||||||
return self::REMAINING_MAX - $this->countAttempts(
|
return self::REMAINING_MAX - $this->countAttempts(
|
||||||
success: false,
|
success: false,
|
||||||
timeRange: self::REMAINING_WINDOW,
|
timeRange: self::REMAINING_WINDOW,
|
||||||
|
@ -80,16 +73,12 @@ class LoginAttempts {
|
||||||
public function getAttempts(
|
public function getAttempts(
|
||||||
?bool $success = null,
|
?bool $success = null,
|
||||||
UserInfo|string|null $userInfo = null,
|
UserInfo|string|null $userInfo = null,
|
||||||
IPAddress|string|null $remoteAddr = null,
|
?string $remoteAddr = null,
|
||||||
TimeSpan|int|null $timeRange = null,
|
?int $timeRange = null,
|
||||||
?Pagination $pagination = null
|
?Pagination $pagination = null
|
||||||
): iterable {
|
): iterable {
|
||||||
if($userInfo instanceof UserInfo)
|
if($userInfo instanceof UserInfo)
|
||||||
$userInfo = $userInfo->getId();
|
$userInfo = $userInfo->getId();
|
||||||
if($remoteAddr instanceof IPAddress)
|
|
||||||
$remoteAddr = (string)$remoteAddr;
|
|
||||||
if($timeRange instanceof TimeSpan)
|
|
||||||
$timeRange = (int)$timeRange->totalSeconds();
|
|
||||||
|
|
||||||
$hasSuccess = $success !== null;
|
$hasSuccess = $success !== null;
|
||||||
$hasUserInfo = $userInfo !== null;
|
$hasUserInfo = $userInfo !== null;
|
||||||
|
@ -132,14 +121,12 @@ class LoginAttempts {
|
||||||
|
|
||||||
public function recordAttempt(
|
public function recordAttempt(
|
||||||
bool $success,
|
bool $success,
|
||||||
IPAddress|string $remoteAddr,
|
string $remoteAddr,
|
||||||
string $countryCode,
|
string $countryCode,
|
||||||
string $userAgentString,
|
string $userAgentString,
|
||||||
?ClientInfo $clientInfo = null,
|
?ClientInfo $clientInfo = null,
|
||||||
UserInfo|string|null $userInfo = null
|
UserInfo|string|null $userInfo = null
|
||||||
): void {
|
): void {
|
||||||
if($remoteAddr instanceof IPAddress)
|
|
||||||
$remoteAddr = (string)$remoteAddr;
|
|
||||||
if($userInfo instanceof UserInfo)
|
if($userInfo instanceof UserInfo)
|
||||||
$userInfo = $userInfo->getId();
|
$userInfo = $userInfo->getId();
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu\Auth;
|
namespace Misuzu\Auth;
|
||||||
|
|
||||||
use Index\DateTime;
|
use Carbon\CarbonImmutable;
|
||||||
use Index\Data\IDbResult;
|
use Index\Db\DbResult;
|
||||||
use Index\Net\IPAddress;
|
|
||||||
|
|
||||||
class RecoveryTokenInfo {
|
class RecoveryTokenInfo {
|
||||||
public const LIFETIME = 60 * 60;
|
public const LIFETIME = 60 * 60;
|
||||||
|
@ -13,7 +12,7 @@ class RecoveryTokenInfo {
|
||||||
private int $created;
|
private int $created;
|
||||||
private ?string $code;
|
private ?string $code;
|
||||||
|
|
||||||
public function __construct(IDbResult $result) {
|
public function __construct(DbResult $result) {
|
||||||
$this->userId = (string)$result->getInteger(0);
|
$this->userId = (string)$result->getInteger(0);
|
||||||
$this->remoteAddr = $result->getString(1);
|
$this->remoteAddr = $result->getString(1);
|
||||||
$this->created = $result->getInteger(2);
|
$this->created = $result->getInteger(2);
|
||||||
|
@ -24,28 +23,24 @@ class RecoveryTokenInfo {
|
||||||
return $this->userId;
|
return $this->userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRemoteAddressRaw(): string {
|
public function getRemoteAddress(): string {
|
||||||
return $this->remoteAddr;
|
return $this->remoteAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRemoteAddress(): IPAddress {
|
|
||||||
return IPAddress::parse($this->remoteAddr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getCreatedTime(): int {
|
public function getCreatedTime(): int {
|
||||||
return $this->created;
|
return $this->created;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCreatedAt(): DateTime {
|
public function getCreatedAt(): CarbonImmutable {
|
||||||
return DateTime::fromUnixTimeSeconds($this->created);
|
return CarbonImmutable::createFromTimestampUTC($this->created);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getExpiresTime(): int {
|
public function getExpiresTime(): int {
|
||||||
return $this->created + self::LIFETIME;
|
return $this->created + self::LIFETIME;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getExpiresAt(): DateTime {
|
public function getExpiresAt(): CarbonImmutable {
|
||||||
return DateTime::fromUnixTimeSeconds($this->created + self::LIFETIME);
|
return CarbonImmutable::createFromTimestampUTC($this->getExpiresTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasExpired(): bool {
|
public function hasExpired(): bool {
|
||||||
|
|
|
@ -3,10 +3,8 @@ namespace Misuzu\Auth;
|
||||||
|
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Index\Data\DbStatementCache;
|
use Index\Base32;
|
||||||
use Index\Data\IDbConnection;
|
use Index\Db\{DbConnection,DbStatementCache};
|
||||||
use Index\Net\IPAddress;
|
|
||||||
use Index\Serialisation\Base32;
|
|
||||||
use Misuzu\ClientInfo;
|
use Misuzu\ClientInfo;
|
||||||
use Misuzu\Pagination;
|
use Misuzu\Pagination;
|
||||||
use Misuzu\Users\UserInfo;
|
use Misuzu\Users\UserInfo;
|
||||||
|
@ -14,7 +12,7 @@ use Misuzu\Users\UserInfo;
|
||||||
class RecoveryTokens {
|
class RecoveryTokens {
|
||||||
private DbStatementCache $cache;
|
private DbStatementCache $cache;
|
||||||
|
|
||||||
public function __construct(IDbConnection $dbConn) {
|
public function __construct(DbConnection $dbConn) {
|
||||||
$this->cache = new DbStatementCache($dbConn);
|
$this->cache = new DbStatementCache($dbConn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,14 +23,12 @@ class RecoveryTokens {
|
||||||
|
|
||||||
public function getToken(
|
public function getToken(
|
||||||
UserInfo|string|null $userInfo = null,
|
UserInfo|string|null $userInfo = null,
|
||||||
IPAddress|string|null $remoteAddr = null,
|
?string $remoteAddr = null,
|
||||||
?string $verifyCode = null,
|
?string $verifyCode = null,
|
||||||
?bool $isUnused = null
|
?bool $isUnused = null
|
||||||
): RecoveryTokenInfo {
|
): RecoveryTokenInfo {
|
||||||
if($userInfo instanceof UserInfo)
|
if($userInfo instanceof UserInfo)
|
||||||
$userInfo = $userInfo->getId();
|
$userInfo = $userInfo->getId();
|
||||||
if($remoteAddr instanceof IPAddress)
|
|
||||||
$remoteAddr = (string)$remoteAddr;
|
|
||||||
|
|
||||||
$hasUserInfo = $userInfo !== null;
|
$hasUserInfo = $userInfo !== null;
|
||||||
$hasRemoteAddr = $remoteAddr !== null;
|
$hasRemoteAddr = $remoteAddr !== null;
|
||||||
|
@ -76,12 +72,11 @@ class RecoveryTokens {
|
||||||
|
|
||||||
public function createToken(
|
public function createToken(
|
||||||
UserInfo|string $userInfo,
|
UserInfo|string $userInfo,
|
||||||
IPAddress|string $remoteAddr
|
string $remoteAddr
|
||||||
): RecoveryTokenInfo {
|
): RecoveryTokenInfo {
|
||||||
if($userInfo instanceof UserInfo)
|
if($userInfo instanceof UserInfo)
|
||||||
$userInfo = $userInfo->getId();
|
$userInfo = $userInfo->getId();
|
||||||
if($remoteAddr instanceof IPAddress)
|
|
||||||
$remoteAddr = (string)$remoteAddr;
|
|
||||||
$verifyCode = self::generateCode();
|
$verifyCode = self::generateCode();
|
||||||
|
|
||||||
$stmt = $this->cache->get('INSERT INTO msz_users_password_resets (user_id, reset_ip, verification_code) VALUES (?, INET6_ATON(?), ?)');
|
$stmt = $this->cache->get('INSERT INTO msz_users_password_resets (user_id, reset_ip, verification_code) VALUES (?, INET6_ATON(?), ?)');
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu\Auth;
|
namespace Misuzu\Auth;
|
||||||
|
|
||||||
use Index\DateTime;
|
|
||||||
use Index\Data\IDbResult;
|
|
||||||
use Index\Net\IPAddress;
|
|
||||||
use Misuzu\ClientInfo;
|
use Misuzu\ClientInfo;
|
||||||
|
use Carbon\CarbonImmutable;
|
||||||
|
use Index\Db\DbResult;
|
||||||
|
|
||||||
class SessionInfo {
|
class SessionInfo {
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
@ -22,7 +21,7 @@ class SessionInfo {
|
||||||
private ?int $lastActive,
|
private ?int $lastActive,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public static function fromResult(IDbResult $result): SessionInfo {
|
public static function fromResult(DbResult $result): SessionInfo {
|
||||||
return new SessionInfo(
|
return new SessionInfo(
|
||||||
id: $result->getString(0),
|
id: $result->getString(0),
|
||||||
userId: $result->getString(1),
|
userId: $result->getString(1),
|
||||||
|
@ -51,26 +50,18 @@ class SessionInfo {
|
||||||
return $this->token;
|
return $this->token;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFirstRemoteAddressRaw(): string {
|
public function getFirstRemoteAddress(): string {
|
||||||
return $this->firstRemoteAddr;
|
return $this->firstRemoteAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFirstRemoteAddress(): IPAddress {
|
|
||||||
return IPAddress::parse($this->firstRemoteAddr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function hasLastRemoteAddress(): bool {
|
public function hasLastRemoteAddress(): bool {
|
||||||
return $this->lastRemoteAddr !== null;
|
return $this->lastRemoteAddr !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLastRemoteAddressRaw(): string {
|
public function getLastRemoteAddress(): ?string {
|
||||||
return $this->lastRemoteAddr;
|
return $this->lastRemoteAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLastRemoteAddress(): ?IPAddress {
|
|
||||||
return $this->lastRemoteAddr === null ? null : IPAddress::parse($this->lastRemoteAddr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getUserAgentString(): string {
|
public function getUserAgentString(): string {
|
||||||
return $this->userAgent;
|
return $this->userAgent;
|
||||||
}
|
}
|
||||||
|
@ -91,8 +82,8 @@ class SessionInfo {
|
||||||
return $this->expires;
|
return $this->expires;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getExpiresAt(): DateTime {
|
public function getExpiresAt(): CarbonImmutable {
|
||||||
return DateTime::fromUnixTimeSeconds($this->expires);
|
return CarbonImmutable::createFromTimestampUTC($this->expires);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function shouldBumpExpires(): bool {
|
public function shouldBumpExpires(): bool {
|
||||||
|
@ -107,8 +98,8 @@ class SessionInfo {
|
||||||
return $this->created;
|
return $this->created;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCreatedAt(): DateTime {
|
public function getCreatedAt(): CarbonImmutable {
|
||||||
return DateTime::fromUnixTimeSeconds($this->created);
|
return CarbonImmutable::createFromTimestampUTC($this->created);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasLastActive(): bool {
|
public function hasLastActive(): bool {
|
||||||
|
@ -119,7 +110,7 @@ class SessionInfo {
|
||||||
return $this->lastActive;
|
return $this->lastActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLastActiveAt(): ?DateTime {
|
public function getLastActiveAt(): ?CarbonImmutable {
|
||||||
return $this->lastActive === null ? null : DateTime::fromUnixTimeSeconds($this->lastActive);
|
return $this->lastActive === null ? null : CarbonImmutable::createFromTimestampUTC($this->lastActive);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,19 +4,16 @@ namespace Misuzu\Auth;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Index\XString;
|
use Index\XString;
|
||||||
use Index\Data\DbStatementCache;
|
use Index\Db\{DbConnection,DbStatementCache,DbTools};
|
||||||
use Index\Data\DbTools;
|
|
||||||
use Index\Data\IDbConnection;
|
|
||||||
use Index\Net\IPAddress;
|
|
||||||
use Misuzu\ClientInfo;
|
use Misuzu\ClientInfo;
|
||||||
use Misuzu\Pagination;
|
use Misuzu\Pagination;
|
||||||
use Misuzu\Users\UserInfo;
|
use Misuzu\Users\UserInfo;
|
||||||
|
|
||||||
class Sessions {
|
class Sessions {
|
||||||
private IDbConnection $dbConn;
|
private DbConnection $dbConn;
|
||||||
private DbStatementCache $cache;
|
private DbStatementCache $cache;
|
||||||
|
|
||||||
public function __construct(IDbConnection $dbConn) {
|
public function __construct(DbConnection $dbConn) {
|
||||||
$this->dbConn = $dbConn;
|
$this->dbConn = $dbConn;
|
||||||
$this->cache = new DbStatementCache($dbConn);
|
$this->cache = new DbStatementCache($dbConn);
|
||||||
}
|
}
|
||||||
|
@ -123,15 +120,13 @@ class Sessions {
|
||||||
|
|
||||||
public function createSession(
|
public function createSession(
|
||||||
UserInfo|string $userInfo,
|
UserInfo|string $userInfo,
|
||||||
IPAddress|string $remoteAddr,
|
string $remoteAddr,
|
||||||
string $countryCode,
|
string $countryCode,
|
||||||
string $userAgentString,
|
string $userAgentString,
|
||||||
?ClientInfo $clientInfo = null
|
?ClientInfo $clientInfo = null
|
||||||
): SessionInfo {
|
): SessionInfo {
|
||||||
if($userInfo instanceof UserInfo)
|
if($userInfo instanceof UserInfo)
|
||||||
$userInfo = $userInfo->getId();
|
$userInfo = $userInfo->getId();
|
||||||
if($remoteAddr instanceof IPAddress)
|
|
||||||
$remoteAddr = (string)$remoteAddr;
|
|
||||||
|
|
||||||
$sessionToken = self::generateToken();
|
$sessionToken = self::generateToken();
|
||||||
$clientInfo = json_encode($clientInfo ?? ClientInfo::parse($userAgentString));
|
$clientInfo = json_encode($clientInfo ?? ClientInfo::parse($userAgentString));
|
||||||
|
@ -243,7 +238,7 @@ class Sessions {
|
||||||
public function recordSessionActivity(
|
public function recordSessionActivity(
|
||||||
SessionInfo|string|null $sessionInfo = null,
|
SessionInfo|string|null $sessionInfo = null,
|
||||||
?string $sessionToken = null,
|
?string $sessionToken = null,
|
||||||
IPAddress|string|null $remoteAddr = null
|
?string $remoteAddr = null
|
||||||
): void {
|
): void {
|
||||||
if($sessionInfo === null && $sessionToken === null)
|
if($sessionInfo === null && $sessionToken === null)
|
||||||
throw new InvalidArgumentException('Either $sessionInfo or $sessionToken needs to be set.');
|
throw new InvalidArgumentException('Either $sessionInfo or $sessionToken needs to be set.');
|
||||||
|
@ -251,8 +246,6 @@ class Sessions {
|
||||||
throw new InvalidArgumentException('Only one of $sessionInfo and $sessionToken may be set at once.');
|
throw new InvalidArgumentException('Only one of $sessionInfo and $sessionToken may be set at once.');
|
||||||
if($sessionInfo instanceof SessionInfo)
|
if($sessionInfo instanceof SessionInfo)
|
||||||
$sessionInfo = $sessionInfo->getId();
|
$sessionInfo = $sessionInfo->getId();
|
||||||
if($remoteAddr instanceof IPAddress)
|
|
||||||
$remoteAddr = (string)$remoteAddr;
|
|
||||||
|
|
||||||
$hasSessionInfo = $sessionInfo !== null;
|
$hasSessionInfo = $sessionInfo !== null;
|
||||||
$hasSessionToken = $sessionToken !== null;
|
$hasSessionToken = $sessionToken !== null;
|
||||||
|
|
|
@ -2,14 +2,13 @@
|
||||||
namespace Misuzu\Auth;
|
namespace Misuzu\Auth;
|
||||||
|
|
||||||
use Index\XString;
|
use Index\XString;
|
||||||
use Index\Data\DbStatementCache;
|
use Index\Db\{DbConnection,DbStatementCache};
|
||||||
use Index\Data\IDbConnection;
|
|
||||||
use Misuzu\Users\UserInfo;
|
use Misuzu\Users\UserInfo;
|
||||||
|
|
||||||
class TwoFactorAuthSessions {
|
class TwoFactorAuthSessions {
|
||||||
private DbStatementCache $cache;
|
private DbStatementCache $cache;
|
||||||
|
|
||||||
public function __construct(IDbConnection $dbConn) {
|
public function __construct(DbConnection $dbConn) {
|
||||||
$this->cache = new DbStatementCache($dbConn);
|
$this->cache = new DbStatementCache($dbConn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu;
|
namespace Misuzu;
|
||||||
|
|
||||||
use Index\Security\CSRFP;
|
use Index\CsrfToken;
|
||||||
|
|
||||||
final class CSRF {
|
final class CSRF {
|
||||||
private static CSRFP $instance;
|
private static CsrfToken $instance;
|
||||||
private static string $secretKey = '';
|
private static string $secretKey = '';
|
||||||
|
|
||||||
public static function create(string $identity, ?string $secretKey = null): CSRFP {
|
public static function create(string $identity, ?string $secretKey = null): CsrfToken {
|
||||||
if($secretKey === null)
|
if($secretKey === null)
|
||||||
$secretKey = self::$secretKey;
|
$secretKey = self::$secretKey;
|
||||||
else
|
else
|
||||||
self::$secretKey = $secretKey;
|
self::$secretKey = $secretKey;
|
||||||
|
|
||||||
return new CSRFP($secretKey, $identity);
|
return new CsrfToken($secretKey, $identity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function init(string $secretKey, string $identity): void {
|
public static function init(string $secretKey, string $identity): void {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu\Changelog;
|
namespace Misuzu\Changelog;
|
||||||
|
|
||||||
use Index\DateTime;
|
use Carbon\CarbonImmutable;
|
||||||
use Index\Data\IDbResult;
|
use Index\Db\DbResult;
|
||||||
|
|
||||||
class ChangeInfo {
|
class ChangeInfo {
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
@ -14,7 +14,7 @@ class ChangeInfo {
|
||||||
private string $body,
|
private string $body,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public static function fromResult(IDbResult $result): ChangeInfo {
|
public static function fromResult(DbResult $result): ChangeInfo {
|
||||||
return new ChangeInfo(
|
return new ChangeInfo(
|
||||||
id: $result->getString(0),
|
id: $result->getString(0),
|
||||||
userId: $result->getStringOrNull(1),
|
userId: $result->getStringOrNull(1),
|
||||||
|
@ -53,8 +53,8 @@ class ChangeInfo {
|
||||||
return $this->created;
|
return $this->created;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCreatedAt(): DateTime {
|
public function getCreatedAt(): CarbonImmutable {
|
||||||
return DateTime::fromUnixTimeSeconds($this->created);
|
return CarbonImmutable::createFromTimestampUTC($this->created);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDate(): string {
|
public function getDate(): string {
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
namespace Misuzu\Changelog;
|
namespace Misuzu\Changelog;
|
||||||
|
|
||||||
use Stringable;
|
use Stringable;
|
||||||
use Index\DateTime;
|
use Carbon\CarbonImmutable;
|
||||||
use Index\Data\IDbResult;
|
use Index\Db\DbResult;
|
||||||
|
|
||||||
class ChangeTagInfo implements Stringable {
|
class ChangeTagInfo implements Stringable {
|
||||||
private string $id;
|
private string $id;
|
||||||
|
@ -13,7 +13,7 @@ class ChangeTagInfo implements Stringable {
|
||||||
private int $archived;
|
private int $archived;
|
||||||
private int $changes;
|
private int $changes;
|
||||||
|
|
||||||
public function __construct(IDbResult $result) {
|
public function __construct(DbResult $result) {
|
||||||
$this->id = (string)$result->getInteger(0);
|
$this->id = (string)$result->getInteger(0);
|
||||||
$this->name = $result->getString(1);
|
$this->name = $result->getString(1);
|
||||||
$this->description = $result->getString(2);
|
$this->description = $result->getString(2);
|
||||||
|
@ -38,16 +38,16 @@ class ChangeTagInfo implements Stringable {
|
||||||
return $this->created;
|
return $this->created;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCreatedAt(): DateTime {
|
public function getCreatedAt(): CarbonImmutable {
|
||||||
return DateTime::fromUnixTimeSeconds($this->created);
|
return CarbonImmutable::createFromTimestampUTC($this->created);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getArchivedTime(): int {
|
public function getArchivedTime(): int {
|
||||||
return $this->archived;
|
return $this->archived;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getArchivedAt(): DateTime {
|
public function getArchivedAt(): CarbonImmutable {
|
||||||
return DateTime::fromUnixTimeSeconds($this->created);
|
return CarbonImmutable::createFromTimestampUTC($this->archived);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isArchived(): bool {
|
public function isArchived(): bool {
|
||||||
|
|
|
@ -3,11 +3,8 @@ namespace Misuzu\Changelog;
|
||||||
|
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Index\DateTime;
|
use DateTimeInterface;
|
||||||
use Index\Data\DbStatementCache;
|
use Index\Db\{DbConnection,DbStatementCache,DbTools};
|
||||||
use Index\Data\DbTools;
|
|
||||||
use Index\Data\IDbConnection;
|
|
||||||
use Index\Data\IDbResult;
|
|
||||||
use Misuzu\Pagination;
|
use Misuzu\Pagination;
|
||||||
use Misuzu\Users\UserInfo;
|
use Misuzu\Users\UserInfo;
|
||||||
|
|
||||||
|
@ -15,12 +12,12 @@ class Changelog {
|
||||||
// not a strict list but useful to have
|
// not a strict list but useful to have
|
||||||
public const ACTIONS = ['add', 'remove', 'update', 'fix', 'import', 'revert'];
|
public const ACTIONS = ['add', 'remove', 'update', 'fix', 'import', 'revert'];
|
||||||
|
|
||||||
private IDbConnection $dbConn;
|
private DbConnection $dbConn;
|
||||||
private DbStatementCache $cache;
|
private DbStatementCache $cache;
|
||||||
|
|
||||||
private array $tags = [];
|
private array $tags = [];
|
||||||
|
|
||||||
public function __construct(IDbConnection $dbConn) {
|
public function __construct(DbConnection $dbConn) {
|
||||||
$this->dbConn = $dbConn;
|
$this->dbConn = $dbConn;
|
||||||
$this->cache = new DbStatementCache($dbConn);
|
$this->cache = new DbStatementCache($dbConn);
|
||||||
}
|
}
|
||||||
|
@ -66,13 +63,13 @@ class Changelog {
|
||||||
|
|
||||||
public function countChanges(
|
public function countChanges(
|
||||||
UserInfo|string|null $userInfo = null,
|
UserInfo|string|null $userInfo = null,
|
||||||
DateTime|int|null $dateTime = null,
|
DateTimeInterface|int|null $dateTime = null,
|
||||||
?array $tags = null
|
?array $tags = null
|
||||||
): int {
|
): int {
|
||||||
if($userInfo instanceof UserInfo)
|
if($userInfo instanceof UserInfo)
|
||||||
$userInfo = $userInfo->getId();
|
$userInfo = $userInfo->getId();
|
||||||
if($dateTime instanceof DateTime)
|
if($dateTime instanceof DateTimeInterface)
|
||||||
$dateTime = $dateTime->getUnixTimeSeconds();
|
$dateTime = (int)$dateTime->format('U');
|
||||||
|
|
||||||
$args = 0;
|
$args = 0;
|
||||||
$hasUserInfo = $userInfo !== null;
|
$hasUserInfo = $userInfo !== null;
|
||||||
|
@ -118,14 +115,14 @@ class Changelog {
|
||||||
|
|
||||||
public function getChanges(
|
public function getChanges(
|
||||||
UserInfo|string|null $userInfo = null,
|
UserInfo|string|null $userInfo = null,
|
||||||
DateTime|int|null $dateTime = null,
|
DateTimeInterface|int|null $dateTime = null,
|
||||||
?array $tags = null,
|
?array $tags = null,
|
||||||
?Pagination $pagination = null
|
?Pagination $pagination = null
|
||||||
): iterable {
|
): iterable {
|
||||||
if($userInfo instanceof UserInfo)
|
if($userInfo instanceof UserInfo)
|
||||||
$userInfo = $userInfo->getId();
|
$userInfo = $userInfo->getId();
|
||||||
if($dateTime instanceof DateTime)
|
if($dateTime instanceof DateTimeInterface)
|
||||||
$dateTime = $dateTime->getUnixTimeSeconds();
|
$dateTime = (int)$dateTime->format('U');
|
||||||
|
|
||||||
$args = 0;
|
$args = 0;
|
||||||
$hasUserInfo = $userInfo !== null;
|
$hasUserInfo = $userInfo !== null;
|
||||||
|
@ -189,14 +186,14 @@ class Changelog {
|
||||||
string $summary,
|
string $summary,
|
||||||
string $body = '',
|
string $body = '',
|
||||||
UserInfo|string|null $userInfo = null,
|
UserInfo|string|null $userInfo = null,
|
||||||
DateTime|int|null $createdAt = null
|
DateTimeInterface|int|null $createdAt = null
|
||||||
): ChangeInfo {
|
): ChangeInfo {
|
||||||
if(is_string($action))
|
if(is_string($action))
|
||||||
$action = self::convertToActionId($action);
|
$action = self::convertToActionId($action);
|
||||||
if($userInfo instanceof UserInfo)
|
if($userInfo instanceof UserInfo)
|
||||||
$userInfo = $userInfo->getId();
|
$userInfo = $userInfo->getId();
|
||||||
if($createdAt instanceof DateTime)
|
if($createdAt instanceof DateTimeInterface)
|
||||||
$createdAt = $createdAt->getUnixTimeSeconds();
|
$createdAt = (int)$createdAt->format('U');
|
||||||
|
|
||||||
$summary = trim($summary);
|
$summary = trim($summary);
|
||||||
if(empty($summary))
|
if(empty($summary))
|
||||||
|
@ -233,7 +230,7 @@ class Changelog {
|
||||||
?string $body = null,
|
?string $body = null,
|
||||||
bool $updateUserInfo = false,
|
bool $updateUserInfo = false,
|
||||||
UserInfo|string|null $userInfo = null,
|
UserInfo|string|null $userInfo = null,
|
||||||
DateTime|int|null $createdAt = null
|
DateTimeInterface|int|null $createdAt = null
|
||||||
): void {
|
): void {
|
||||||
if($infoOrId instanceof ChangeInfo)
|
if($infoOrId instanceof ChangeInfo)
|
||||||
$infoOrId = $infoOrId->getId();
|
$infoOrId = $infoOrId->getId();
|
||||||
|
@ -242,8 +239,8 @@ class Changelog {
|
||||||
$action = self::convertToActionId($action);
|
$action = self::convertToActionId($action);
|
||||||
if($userInfo instanceof UserInfo)
|
if($userInfo instanceof UserInfo)
|
||||||
$userInfo = $userInfo->getId();
|
$userInfo = $userInfo->getId();
|
||||||
if($createdAt instanceof DateTime)
|
if($createdAt instanceof DateTimeInterface)
|
||||||
$createdAt = $createdAt->getUnixTimeSeconds();
|
$createdAt = (int)$createdAt->format('U');
|
||||||
|
|
||||||
if($summary !== null) {
|
if($summary !== null) {
|
||||||
$summary = trim($summary);
|
$summary = trim($summary);
|
||||||
|
|
|
@ -3,18 +3,20 @@ namespace Misuzu\Changelog;
|
||||||
|
|
||||||
use ErrorException;
|
use ErrorException;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Index\Http\Routing\{HttpGet,RouteHandler};
|
use Index\Http\Routing\{HttpGet,RouteHandler,RouteHandlerTrait};
|
||||||
|
use Index\Syndication\FeedBuilder;
|
||||||
|
use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceTrait};
|
||||||
use Misuzu\{Pagination,SiteInfo,Template};
|
use Misuzu\{Pagination,SiteInfo,Template};
|
||||||
use Misuzu\Auth\AuthInfo;
|
use Misuzu\Auth\AuthInfo;
|
||||||
use Misuzu\Comments\{Comments,CommentsEx};
|
use Misuzu\Comments\{Comments,CommentsEx};
|
||||||
use Misuzu\Feeds\{Feed,FeedItem,AtomFeedSerializer,RssFeedSerializer};
|
|
||||||
use Misuzu\URLs\{URLInfo,URLRegistry};
|
|
||||||
use Misuzu\Users\UsersContext;
|
use Misuzu\Users\UsersContext;
|
||||||
|
|
||||||
final class ChangelogRoutes extends RouteHandler {
|
final class ChangelogRoutes implements RouteHandler, UrlSource {
|
||||||
|
use RouteHandlerTrait, UrlSourceTrait;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private SiteInfo $siteInfo,
|
private SiteInfo $siteInfo,
|
||||||
private URLRegistry $urls,
|
private UrlRegistry $urls,
|
||||||
private Changelog $changelog,
|
private Changelog $changelog,
|
||||||
private UsersContext $usersCtx,
|
private UsersContext $usersCtx,
|
||||||
private AuthInfo $authInfo,
|
private AuthInfo $authInfo,
|
||||||
|
@ -27,7 +29,7 @@ final class ChangelogRoutes extends RouteHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[HttpGet('/changelog')]
|
#[HttpGet('/changelog')]
|
||||||
#[URLInfo('changelog-index', '/changelog', ['date' => '<date>', 'user' => '<user>', 'tags' => '<tags>', 'p' => '<page>'])]
|
#[UrlFormat('changelog-index', '/changelog', ['date' => '<date>', 'user' => '<user>', 'tags' => '<tags>', 'p' => '<page>'])]
|
||||||
public function getIndex($response, $request) {
|
public function getIndex($response, $request) {
|
||||||
$filterDate = (string)$request->getParam('date');
|
$filterDate = (string)$request->getParam('date');
|
||||||
$filterUser = (string)$request->getParam('user', FILTER_SANITIZE_NUMBER_INT);
|
$filterUser = (string)$request->getParam('user', FILTER_SANITIZE_NUMBER_INT);
|
||||||
|
@ -96,8 +98,8 @@ final class ChangelogRoutes extends RouteHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[HttpGet('/changelog/change/([0-9]+)')]
|
#[HttpGet('/changelog/change/([0-9]+)')]
|
||||||
#[URLInfo('changelog-change', '/changelog/change/<change>')]
|
#[UrlFormat('changelog-change', '/changelog/change/<change>')]
|
||||||
#[URLInfo('changelog-change-comments', '/changelog/change/<change>', fragment: 'comments')]
|
#[UrlFormat('changelog-change-comments', '/changelog/change/<change>', fragment: 'comments')]
|
||||||
public function getChange($response, $request, string $changeId) {
|
public function getChange($response, $request, string $changeId) {
|
||||||
try {
|
try {
|
||||||
$changeInfo = $this->changelog->getChange($changeId);
|
$changeInfo = $this->changelog->getChange($changeId);
|
||||||
|
@ -117,44 +119,29 @@ final class ChangelogRoutes extends RouteHandler {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createFeed(string $feedMode): Feed {
|
#[HttpGet('/changelog.(xml|rss|atom)')]
|
||||||
|
#[UrlFormat('changelog-feed', '/changelog.xml')]
|
||||||
|
public function getFeed($response) {
|
||||||
|
$response->setContentType('application/rss+xml; charset=utf-8');
|
||||||
|
|
||||||
$siteName = $this->siteInfo->getName();
|
$siteName = $this->siteInfo->getName();
|
||||||
|
$siteUrl = $this->siteInfo->getURL();
|
||||||
$changes = $this->changelog->getChanges(pagination: new Pagination(10));
|
$changes = $this->changelog->getChanges(pagination: new Pagination(10));
|
||||||
|
|
||||||
$feed = (new Feed)
|
$feed = new FeedBuilder;
|
||||||
->setTitle($siteName . ' » Changelog')
|
$feed->setTitle(sprintf('%s » Changelog', $siteName));
|
||||||
->setDescription('Live feed of changes to ' . $siteName . '.')
|
$feed->setDescription(sprintf('Live feed of changes to %s.', $siteName));
|
||||||
->setContentUrl($this->siteInfo->getURL() . $this->urls->format('changelog-index'))
|
$feed->setContentUrl($siteUrl . $this->urls->format('changelog-index'));
|
||||||
->setFeedUrl($this->siteInfo->getURL() . $this->urls->format("changelog-feed-{$feedMode}"));
|
$feed->setFeedUrl($siteUrl . $this->urls->format('changelog-feed'));
|
||||||
|
|
||||||
foreach($changes as $change) {
|
foreach($changes as $change)
|
||||||
$changeUrl = $this->siteInfo->getURL() . $this->urls->format('changelog-change', ['change' => $change->getId()]);
|
$feed->createEntry(function($item) use ($change, $siteUrl) {
|
||||||
$commentsUrl = $this->siteInfo->getURL() . $this->urls->format('changelog-change-comments', ['change' => $change->getId()]);
|
$item->setTitle(sprintf('%s: %s', $change->getActionText(), $change->getSummary()));
|
||||||
|
$item->setCreatedAt($change->getCreatedTime());
|
||||||
|
$item->setContentUrl($siteUrl . $this->urls->format('changelog-change', ['change' => $change->getId()]));
|
||||||
|
$item->setCommentsUrl($siteUrl . $this->urls->format('changelog-change-comments', ['change' => $change->getId()]));
|
||||||
|
});
|
||||||
|
|
||||||
$feedItem = (new FeedItem)
|
return $feed->toXmlString();
|
||||||
->setTitle($change->getActionText() . ': ' . $change->getSummary())
|
|
||||||
->setCreationDate($change->getCreatedTime())
|
|
||||||
->setUniqueId($changeUrl)
|
|
||||||
->setContentUrl($changeUrl)
|
|
||||||
->setCommentsUrl($commentsUrl);
|
|
||||||
|
|
||||||
$feed->addItem($feedItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $feed;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[HttpGet('/changelog.rss')]
|
|
||||||
#[URLInfo('changelog-feed-rss', '/changelog.rss')]
|
|
||||||
public function getFeedRSS($response) {
|
|
||||||
$response->setContentType('application/rss+xml; charset=utf-8');
|
|
||||||
return (new RssFeedSerializer)->serializeFeed($this->createFeed('rss'));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[HttpGet('/changelog.atom')]
|
|
||||||
#[URLInfo('changelog-feed-atom', '/changelog.atom')]
|
|
||||||
public function getFeedAtom($response) {
|
|
||||||
$response->setContentType('application/atom+xml; charset=utf-8');
|
|
||||||
return (new AtomFeedSerializer)->serializeFeed($this->createFeed('atom'));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,12 @@ use stdClass;
|
||||||
use JsonSerializable;
|
use JsonSerializable;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Stringable;
|
use Stringable;
|
||||||
use DeviceDetector\ClientHints;
|
use DeviceDetector\{ClientHints,DeviceDetector};
|
||||||
use DeviceDetector\DeviceDetector;
|
use Index\Json\{JsonProperty,JsonSerializableTrait};
|
||||||
|
|
||||||
class ClientInfo implements Stringable, JsonSerializable {
|
class ClientInfo implements Stringable, JsonSerializable {
|
||||||
|
use JsonSerializableTrait;
|
||||||
|
|
||||||
private const SERIALIZE_VERSION = 1;
|
private const SERIALIZE_VERSION = 1;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
@ -19,6 +21,39 @@ class ClientInfo implements Stringable, JsonSerializable {
|
||||||
private string $modelName
|
private string $modelName
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
#[JsonProperty('version')]
|
||||||
|
public function getVersion(): int {
|
||||||
|
return self::SERIALIZE_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[JsonProperty('bot')]
|
||||||
|
public function getBotInfo(): bool|array|null {
|
||||||
|
if($this->botInfo === true || is_array($this->botInfo))
|
||||||
|
return $this->botInfo;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[JsonProperty('client')]
|
||||||
|
public function getClientInfo(): ?array {
|
||||||
|
return $this->clientInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[JsonProperty('os')]
|
||||||
|
public function getOsInfo(): ?array {
|
||||||
|
return $this->osInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[JsonProperty('vendor', omitIfValue: '')]
|
||||||
|
public function getVendorName(): string {
|
||||||
|
return $this->brandName;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[JsonProperty('model', omitIfValue: '')]
|
||||||
|
public function getModelName(): string {
|
||||||
|
return $this->modelName;
|
||||||
|
}
|
||||||
|
|
||||||
public function __toString(): string {
|
public function __toString(): string {
|
||||||
if($this->botInfo === true || is_array($this->botInfo)) {
|
if($this->botInfo === true || is_array($this->botInfo)) {
|
||||||
if($this->botInfo === true)
|
if($this->botInfo === true)
|
||||||
|
@ -64,24 +99,6 @@ class ClientInfo implements Stringable, JsonSerializable {
|
||||||
return json_encode($this);
|
return json_encode($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function jsonSerialize(): mixed {
|
|
||||||
$data = new stdClass;
|
|
||||||
$data->version = self::SERIALIZE_VERSION;
|
|
||||||
|
|
||||||
if($this->botInfo === true || is_array($this->botInfo))
|
|
||||||
$data->bot = $this->botInfo;
|
|
||||||
if($this->clientInfo !== null)
|
|
||||||
$data->client = $this->clientInfo;
|
|
||||||
if($this->osInfo !== null)
|
|
||||||
$data->os = $this->osInfo;
|
|
||||||
if($this->brandName !== '')
|
|
||||||
$data->vendor = $this->brandName;
|
|
||||||
if($this->modelName !== '')
|
|
||||||
$data->model = $this->modelName;
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function decode(string $encoded): self {
|
public static function decode(string $encoded): self {
|
||||||
$data = json_decode($encoded, true);
|
$data = json_decode($encoded, true);
|
||||||
$version = $data['version'] ?? 0;
|
$version = $data['version'] ?? 0;
|
||||||
|
|
|
@ -3,17 +3,15 @@ namespace Misuzu\Comments;
|
||||||
|
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Index\Data\DbStatementCache;
|
use Index\Db\{DbConnection,DbStatementCache};
|
||||||
use Index\Data\IDbConnection;
|
|
||||||
use Index\Data\IDbResult;
|
|
||||||
use Misuzu\Pagination;
|
use Misuzu\Pagination;
|
||||||
use Misuzu\Users\UserInfo;
|
use Misuzu\Users\UserInfo;
|
||||||
|
|
||||||
class Comments {
|
class Comments {
|
||||||
private IDbConnection $dbConn;
|
private DbConnection $dbConn;
|
||||||
private DbStatementCache $cache;
|
private DbStatementCache $cache;
|
||||||
|
|
||||||
public function __construct(IDbConnection $dbConn) {
|
public function __construct(DbConnection $dbConn) {
|
||||||
$this->dbConn = $dbConn;
|
$this->dbConn = $dbConn;
|
||||||
$this->cache = new DbStatementCache($dbConn);
|
$this->cache = new DbStatementCache($dbConn);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu\Comments;
|
namespace Misuzu\Comments;
|
||||||
|
|
||||||
use Index\DateTime;
|
|
||||||
use Index\Data\IDbResult;
|
|
||||||
use Misuzu\Users\UserInfo;
|
use Misuzu\Users\UserInfo;
|
||||||
|
use Carbon\CarbonImmutable;
|
||||||
|
use Index\Db\DbResult;
|
||||||
|
|
||||||
class CommentsCategoryInfo {
|
class CommentsCategoryInfo {
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
@ -15,7 +15,7 @@ class CommentsCategoryInfo {
|
||||||
private int $comments,
|
private int $comments,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public static function fromResult(IDbResult $result): CommentsCategoryInfo {
|
public static function fromResult(DbResult $result): CommentsCategoryInfo {
|
||||||
return new CommentsCategoryInfo(
|
return new CommentsCategoryInfo(
|
||||||
id: $result->getString(0),
|
id: $result->getString(0),
|
||||||
name: $result->getString(1),
|
name: $result->getString(1),
|
||||||
|
@ -54,16 +54,17 @@ class CommentsCategoryInfo {
|
||||||
return $this->created;
|
return $this->created;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCreatedAt(): DateTime {
|
public function getCreatedAt(): CarbonImmutable {
|
||||||
return DateTime::fromUnixTimeSeconds($this->created);
|
return CarbonImmutable::createFromTimestampUTC($this->created);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLockedTime(): ?int {
|
public function getLockedTime(): ?int {
|
||||||
return $this->locked;
|
return $this->locked;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLockedAt(): ?DateTime {
|
public function getLockedAt(): ?CarbonImmutable {
|
||||||
return $this->locked === null ? null : DateTime::fromUnixTimeSeconds($this->locked);
|
return $this->locked === null ? null : CarbonImmutable::createFromTimestampUTC($this->locked);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isLocked(): bool {
|
public function isLocked(): bool {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu\Comments;
|
namespace Misuzu\Comments;
|
||||||
|
|
||||||
use Index\DateTime;
|
use Carbon\CarbonImmutable;
|
||||||
use Index\Data\IDbResult;
|
use Index\Db\DbResult;
|
||||||
|
|
||||||
class CommentsPostInfo {
|
class CommentsPostInfo {
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
@ -22,7 +22,7 @@ class CommentsPostInfo {
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public static function fromResult(
|
public static function fromResult(
|
||||||
IDbResult $result,
|
DbResult $result,
|
||||||
bool $includeRepliesCount = false,
|
bool $includeRepliesCount = false,
|
||||||
bool $includeVotesCount = false
|
bool $includeVotesCount = false
|
||||||
): CommentsPostInfo {
|
): CommentsPostInfo {
|
||||||
|
@ -86,16 +86,16 @@ class CommentsPostInfo {
|
||||||
return $this->created;
|
return $this->created;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCreatedAt(): DateTime {
|
public function getCreatedAt(): CarbonImmutable {
|
||||||
return DateTime::fromUnixTimeSeconds($this->created);
|
return CarbonImmutable::createFromTimestampUTC($this->created);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPinnedTime(): ?int {
|
public function getPinnedTime(): ?int {
|
||||||
return $this->pinned;
|
return $this->pinned;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPinnedAt(): DateTime {
|
public function getPinnedAt(): ?CarbonImmutable {
|
||||||
return $this->pinned === null ? null : DateTime::fromUnixTimeSeconds($this->pinned);
|
return $this->pinned === null ? null : CarbonImmutable::createFromTimestampUTC($this->pinned);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isPinned(): bool {
|
public function isPinned(): bool {
|
||||||
|
@ -106,8 +106,8 @@ class CommentsPostInfo {
|
||||||
return $this->updated;
|
return $this->updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUpdatedAt(): DateTime {
|
public function getUpdatedAt(): ?CarbonImmutable {
|
||||||
return $this->updated === null ? null : DateTime::fromUnixTimeSeconds($this->updated);
|
return $this->updated === null ? null : CarbonImmutable::createFromTimestampUTC($this->updated);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isEdited(): bool {
|
public function isEdited(): bool {
|
||||||
|
@ -118,8 +118,8 @@ class CommentsPostInfo {
|
||||||
return $this->deleted;
|
return $this->deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDeletedAt(): DateTime {
|
public function getDeletedAt(): ?CarbonImmutable {
|
||||||
return $this->deleted === null ? null : DateTime::fromUnixTimeSeconds($this->deleted);
|
return $this->deleted === null ? null : CarbonImmutable::createFromTimestampUTC($this->deleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isDeleted(): bool {
|
public function isDeleted(): bool {
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu\Comments;
|
namespace Misuzu\Comments;
|
||||||
|
|
||||||
use Index\Data\IDbResult;
|
use Index\Db\DbResult;
|
||||||
|
|
||||||
class CommentsPostVoteInfo {
|
class CommentsPostVoteInfo {
|
||||||
private string $commentId;
|
private string $commentId;
|
||||||
private string $userId;
|
private string $userId;
|
||||||
private int $weight;
|
private int $weight;
|
||||||
|
|
||||||
public function __construct(IDbResult $result) {
|
public function __construct(DbResult $result) {
|
||||||
$this->commentId = (string)$result->getInteger(0);
|
$this->commentId = (string)$result->getInteger(0);
|
||||||
$this->userId = (string)$result->getInteger(1);
|
$this->userId = (string)$result->getInteger(1);
|
||||||
$this->weight = $result->getInteger(2);
|
$this->weight = $result->getInteger(2);
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu\Counters;
|
namespace Misuzu\Counters;
|
||||||
|
|
||||||
use Index\DateTime;
|
use Carbon\CarbonImmutable;
|
||||||
use Index\Data\IDbResult;
|
use Index\Db\DbResult;
|
||||||
|
|
||||||
class CounterInfo {
|
class CounterInfo {
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
@ -11,7 +11,7 @@ class CounterInfo {
|
||||||
private int $updated,
|
private int $updated,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public static function fromResult(IDbResult $result): CounterInfo {
|
public static function fromResult(DbResult $result): CounterInfo {
|
||||||
return new CounterInfo(
|
return new CounterInfo(
|
||||||
name: $result->getString(0),
|
name: $result->getString(0),
|
||||||
value: $result->getInteger(1),
|
value: $result->getInteger(1),
|
||||||
|
@ -31,7 +31,7 @@ class CounterInfo {
|
||||||
return $this->updated;
|
return $this->updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUpdatedAt(): DateTime {
|
public function getUpdatedAt(): CarbonImmutable {
|
||||||
return DateTime::fromUnixTimeSeconds($this->updated);
|
return CarbonImmutable::createFromTimestampUTC($this->updated);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,7 @@
|
||||||
namespace Misuzu\Counters;
|
namespace Misuzu\Counters;
|
||||||
|
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use Index\Data\DbStatementCache;
|
use Index\Db\{DbConnection,DbStatementCache,DbTools};
|
||||||
use Index\Data\DbTools;
|
|
||||||
use Index\Data\IDbConnection;
|
|
||||||
use Misuzu\Pagination;
|
use Misuzu\Pagination;
|
||||||
|
|
||||||
// insert increment and decrement calls in places someday
|
// insert increment and decrement calls in places someday
|
||||||
|
@ -12,7 +10,7 @@ use Misuzu\Pagination;
|
||||||
class Counters {
|
class Counters {
|
||||||
private DbStatementCache $cache;
|
private DbStatementCache $cache;
|
||||||
|
|
||||||
public function __construct(IDbConnection $dbConn) {
|
public function __construct(DbConnection $dbConn) {
|
||||||
$this->cache = new DbStatementCache($dbConn);
|
$this->cache = new DbStatementCache($dbConn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
namespace Misuzu\Emoticons;
|
namespace Misuzu\Emoticons;
|
||||||
|
|
||||||
use Stringable;
|
use Stringable;
|
||||||
use Index\Data\IDbResult;
|
use Index\Db\DbResult;
|
||||||
|
|
||||||
class EmoteInfo implements Stringable {
|
class EmoteInfo implements Stringable {
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
@ -12,7 +12,7 @@ class EmoteInfo implements Stringable {
|
||||||
private string $url,
|
private string $url,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public static function fromResult(IDbResult $result): EmoteInfo {
|
public static function fromResult(DbResult $result): EmoteInfo {
|
||||||
return new EmoteInfo(
|
return new EmoteInfo(
|
||||||
id: $result->getString(0),
|
id: $result->getString(0),
|
||||||
order: $result->getInteger(1),
|
order: $result->getInteger(1),
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
namespace Misuzu\Emoticons;
|
namespace Misuzu\Emoticons;
|
||||||
|
|
||||||
use Stringable;
|
use Stringable;
|
||||||
use Index\Data\IDbResult;
|
use Index\Db\DbResult;
|
||||||
|
|
||||||
class EmoteStringInfo implements Stringable {
|
class EmoteStringInfo implements Stringable {
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
@ -11,7 +11,7 @@ class EmoteStringInfo implements Stringable {
|
||||||
private string $string,
|
private string $string,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public static function fromResult(IDbResult $result): EmoteStringInfo {
|
public static function fromResult(DbResult $result): EmoteStringInfo {
|
||||||
return new EmoteStringInfo(
|
return new EmoteStringInfo(
|
||||||
emoteId: $result->getString(0),
|
emoteId: $result->getString(0),
|
||||||
order: $result->getInteger(1),
|
order: $result->getInteger(1),
|
||||||
|
|
|
@ -3,8 +3,7 @@ namespace Misuzu\Emoticons;
|
||||||
|
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Index\Data\DbStatementCache;
|
use Index\Db\{DbConnection,DbStatementCache};
|
||||||
use Index\Data\IDbConnection;
|
|
||||||
|
|
||||||
class Emotes {
|
class Emotes {
|
||||||
private const EMOTE_ORDER = [
|
private const EMOTE_ORDER = [
|
||||||
|
@ -13,10 +12,10 @@ class Emotes {
|
||||||
'rank' => 'emote_hierarchy',
|
'rank' => 'emote_hierarchy',
|
||||||
];
|
];
|
||||||
|
|
||||||
private IDbConnection $dbConn;
|
private DbConnection $dbConn;
|
||||||
private DbStatementCache $cache;
|
private DbStatementCache $cache;
|
||||||
|
|
||||||
public function __construct(IDbConnection $dbConn) {
|
public function __construct(DbConnection $dbConn) {
|
||||||
$this->dbConn = $dbConn;
|
$this->dbConn = $dbConn;
|
||||||
$this->cache = new DbStatementCache($dbConn);
|
$this->cache = new DbStatementCache($dbConn);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,117 +0,0 @@
|
||||||
<?php
|
|
||||||
namespace Misuzu\Feeds;
|
|
||||||
|
|
||||||
use DOMDocument;
|
|
||||||
use DOMElement;
|
|
||||||
use DOMNode;
|
|
||||||
|
|
||||||
class AtomFeedSerializer extends XmlFeedSerializer {
|
|
||||||
protected function formatTime(int $time): string {
|
|
||||||
return date('c', $time);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createRoot(DOMDocument $document, Feed $feed): DOMNode {
|
|
||||||
$atom = $document->appendChild($document->createElement('feed'));
|
|
||||||
if($atom instanceof DOMElement)
|
|
||||||
$atom->setAttribute('xmlns', 'http://www.w3.org/2005/Atom');
|
|
||||||
|
|
||||||
$atom->appendChild(
|
|
||||||
$document->createElement(
|
|
||||||
'id',
|
|
||||||
$feed->hasContentUrl()
|
|
||||||
? $this->cleanString($feed->getContentUrl())
|
|
||||||
: time()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return $atom;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createTitle(DOMDocument $document, string $title): DOMNode {
|
|
||||||
return $document->createElement('title', $this->cleanString($title));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createDescription(DOMDocument $document, string $description): ?DOMNode {
|
|
||||||
return $document->createElement('subtitle', $this->cleanString($description));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createLastUpdate(DOMDocument $document, int $lastUpdate): ?DOMNode {
|
|
||||||
return $document->createElement('updated', $this->formatTime($lastUpdate));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createContentUrl(DOMDocument $document, string $contentUrl): ?DOMNode {
|
|
||||||
$link = $document->createElement('link');
|
|
||||||
$link->setAttribute('href', $this->cleanString($contentUrl));
|
|
||||||
return $link;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createFeedUrl(DOMDocument $document, string $feedUrl): ?DOMNode {
|
|
||||||
$link = $document->createElement('link');
|
|
||||||
$link->setAttribute('href', $this->cleanString($feedUrl));
|
|
||||||
$link->setAttribute('ref', 'self');
|
|
||||||
return $link;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createItem(DOMDocument $document, FeedItem $feedItem): DOMNode {
|
|
||||||
$elem = $document->createElement('entry');
|
|
||||||
|
|
||||||
$elem->appendChild(
|
|
||||||
$document->createElement(
|
|
||||||
'id',
|
|
||||||
$feedItem->hasContentUrl()
|
|
||||||
? $this->cleanString($feedItem->getContentUrl())
|
|
||||||
: time()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return $elem;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createItemTitle(DOMDocument $document, string $title): DOMNode {
|
|
||||||
return $document->createElement('title', $this->cleanString($title));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createItemSummary(DOMDocument $document, string $summary): ?DOMNode {
|
|
||||||
return $document->createElement('summary', $this->cleanString($summary));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createItemContent(DOMDocument $document, string $content): ?DOMNode {
|
|
||||||
$elem = $document->createElement('content', $this->cleanString($content));
|
|
||||||
$elem->setAttribute('type', 'html');
|
|
||||||
return $elem;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createItemCreationDate(DOMDocument $document, int $creationDate): ?DOMNode {
|
|
||||||
return $document->createElement('updated', $this->formatTime($creationDate));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createItemUniqueId(DOMDocument $document, string $uniqueId): ?DOMNode {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createItemContentUrl(DOMDocument $document, string $contentUrl): ?DOMNode {
|
|
||||||
$elem = $document->createElement('link');
|
|
||||||
$elem->setAttribute('href', $this->cleanString($contentUrl));
|
|
||||||
$elem->setAttribute('type', 'text/html');
|
|
||||||
return $elem;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createItemCommentsUrl(DOMDocument $document, string $commentsUrl): ?DOMNode {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createItemAuthor(DOMDocument $document, ?string $authorName, ?string $authorUrl): ?DOMNode {
|
|
||||||
if(empty($authorName) && empty($authorUrl))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
$elem = $document->createElement('author');
|
|
||||||
|
|
||||||
if(!empty($authorName))
|
|
||||||
$elem->appendChild($document->createElement('name', $this->cleanString($authorName)));
|
|
||||||
|
|
||||||
if(!empty($authorUrl))
|
|
||||||
$elem->appendChild($document->createElement('uri', $this->cleanString($authorUrl)));
|
|
||||||
|
|
||||||
return $elem;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
<?php
|
|
||||||
namespace Misuzu\Feeds;
|
|
||||||
|
|
||||||
class Feed {
|
|
||||||
private string $title = '';
|
|
||||||
private ?string $description = null;
|
|
||||||
private ?int $lastUpdate = null;
|
|
||||||
private ?string $contentUrl = null;
|
|
||||||
private ?string $feedUrl = null;
|
|
||||||
private array $feedItems = [];
|
|
||||||
|
|
||||||
public function getTitle(): string {
|
|
||||||
return $this->title;
|
|
||||||
}
|
|
||||||
public function setTitle(string $title): self {
|
|
||||||
$this->title = $title;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getDescription(): string {
|
|
||||||
return $this->description ?? '';
|
|
||||||
}
|
|
||||||
public function hasDescription(): bool {
|
|
||||||
return isset($this->description);
|
|
||||||
}
|
|
||||||
public function setDescription(?string $description): self {
|
|
||||||
$this->description = $description;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getLastUpdate(): int {
|
|
||||||
return $this->lastUpdate ?? 0;
|
|
||||||
}
|
|
||||||
public function hasLastUpdate(): bool {
|
|
||||||
return isset($this->lastUpdate);
|
|
||||||
}
|
|
||||||
public function setLastUpdate(?int $lastUpdate): self {
|
|
||||||
$this->lastUpdate = $lastUpdate;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getContentUrl(): string {
|
|
||||||
return $this->contentUrl ?? '';
|
|
||||||
}
|
|
||||||
public function hasContentUrl(): bool {
|
|
||||||
return isset($this->contentUrl);
|
|
||||||
}
|
|
||||||
public function setContentUrl(?string $contentUrl): self {
|
|
||||||
$this->contentUrl = $contentUrl;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getFeedUrl(): string {
|
|
||||||
return $this->feedUrl ?? '';
|
|
||||||
}
|
|
||||||
public function hasFeedUrl(): bool {
|
|
||||||
return isset($this->feedUrl);
|
|
||||||
}
|
|
||||||
public function setFeedUrl(?string $feedUrl): self {
|
|
||||||
$this->feedUrl = $feedUrl;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getItems(): array {
|
|
||||||
return $this->feedItems;
|
|
||||||
}
|
|
||||||
public function hasItems(): bool {
|
|
||||||
return count($this->feedItems) > 0;
|
|
||||||
}
|
|
||||||
public function addItem(FeedItem $item): self {
|
|
||||||
$this->feedItems[] = $item;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,110 +0,0 @@
|
||||||
<?php
|
|
||||||
namespace Misuzu\Feeds;
|
|
||||||
|
|
||||||
class FeedItem {
|
|
||||||
private string $title = '';
|
|
||||||
private ?string $summary = null;
|
|
||||||
private ?string $content = null;
|
|
||||||
private ?int $creationDate = null;
|
|
||||||
private ?string $uniqueId = null;
|
|
||||||
private ?string $contentUrl = null;
|
|
||||||
private ?string $commentsUrl = null;
|
|
||||||
private ?string $authorName = null;
|
|
||||||
private ?string $authorUrl = null;
|
|
||||||
|
|
||||||
public function getTitle(): string {
|
|
||||||
return $this->title;
|
|
||||||
}
|
|
||||||
public function setTitle(string $title): self {
|
|
||||||
$this->title = $title;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSummary(): string {
|
|
||||||
return $this->summary ?? '';
|
|
||||||
}
|
|
||||||
public function hasSummary(): bool {
|
|
||||||
return isset($this->summary);
|
|
||||||
}
|
|
||||||
public function setSummary(?string $summary): self {
|
|
||||||
$this->summary = $summary;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getContent(): string {
|
|
||||||
return $this->content ?? '';
|
|
||||||
}
|
|
||||||
public function hasContent(): bool {
|
|
||||||
return isset($this->content);
|
|
||||||
}
|
|
||||||
public function setContent(?string $content): self {
|
|
||||||
$this->content = $content;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getCreationDate(): int {
|
|
||||||
return $this->creationDate;
|
|
||||||
}
|
|
||||||
public function hasCreationDate(): bool {
|
|
||||||
return isset($this->creationDate);
|
|
||||||
}
|
|
||||||
public function setCreationDate(?int $creationDate): self {
|
|
||||||
$this->creationDate = $creationDate;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getUniqueId(): string {
|
|
||||||
return $this->uniqueId ?? '';
|
|
||||||
}
|
|
||||||
public function hasUniqueId(): bool {
|
|
||||||
return isset($this->uniqueId);
|
|
||||||
}
|
|
||||||
public function setUniqueId(?string $uniqueId): self {
|
|
||||||
$this->uniqueId = $uniqueId;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getContentUrl(): string {
|
|
||||||
return $this->contentUrl ?? '';
|
|
||||||
}
|
|
||||||
public function hasContentUrl(): bool {
|
|
||||||
return isset($this->contentUrl);
|
|
||||||
}
|
|
||||||
public function setContentUrl(?string $contentUrl): self {
|
|
||||||
$this->contentUrl = $contentUrl;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getCommentsUrl(): string {
|
|
||||||
return $this->commentsUrl ?? '';
|
|
||||||
}
|
|
||||||
public function hasCommentsUrl(): bool {
|
|
||||||
return isset($this->commentsUrl);
|
|
||||||
}
|
|
||||||
public function setCommentsUrl(?string $commentsUrl): self {
|
|
||||||
$this->commentsUrl = $commentsUrl;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAuthorName(): string {
|
|
||||||
return $this->authorName ?? '';
|
|
||||||
}
|
|
||||||
public function hasAuthorName(): bool {
|
|
||||||
return isset($this->authorName);
|
|
||||||
}
|
|
||||||
public function setAuthorName(?string $authorName): self {
|
|
||||||
$this->authorName = $authorName;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAuthorUrl(): string {
|
|
||||||
return $this->authorUrl ?? '';
|
|
||||||
}
|
|
||||||
public function hasAuthorUrl(): bool {
|
|
||||||
return isset($this->authorUrl);
|
|
||||||
}
|
|
||||||
public function setAuthorUrl(?string $authorUrl): self {
|
|
||||||
$this->authorUrl = $authorUrl;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
<?php
|
|
||||||
namespace Misuzu\Feeds;
|
|
||||||
|
|
||||||
abstract class FeedSerializer {
|
|
||||||
abstract public function serializeFeed(Feed $feed): string;
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
<?php
|
|
||||||
namespace Misuzu\Feeds;
|
|
||||||
|
|
||||||
use DOMDocument;
|
|
||||||
use DOMElement;
|
|
||||||
use DOMNode;
|
|
||||||
|
|
||||||
class RssFeedSerializer extends XmlFeedSerializer {
|
|
||||||
protected function formatTime(int $time): string {
|
|
||||||
return date('r', $time);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createRoot(DOMDocument $document, Feed $feed): DOMNode {
|
|
||||||
$rss = $document->appendChild($document->createElement('rss'));
|
|
||||||
if($rss instanceof DOMElement) {
|
|
||||||
$rss->setAttribute('version', '2.0');
|
|
||||||
$rss->setAttribute('xmlns:atom', 'http://www.w3.org/2005/Atom');
|
|
||||||
}
|
|
||||||
|
|
||||||
$channel = $rss->appendChild($document->createElement('channel'));
|
|
||||||
$channel->appendChild($document->createElement('ttl', '900'));
|
|
||||||
return $channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createTitle(DOMDocument $document, string $title): DOMNode {
|
|
||||||
return $document->createElement('title', $this->cleanString($title));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createDescription(DOMDocument $document, string $description): ?DOMNode {
|
|
||||||
return $document->createElement('description', $this->cleanString($description));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createLastUpdate(DOMDocument $document, int $lastUpdate): ?DOMNode {
|
|
||||||
return $document->createElement('pubDate', $this->formatTime($lastUpdate));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createContentUrl(DOMDocument $document, string $contentUrl): ?DOMNode {
|
|
||||||
return $document->createElement('link', $this->cleanString($contentUrl));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createFeedUrl(DOMDocument $document, string $feedUrl): ?DOMNode {
|
|
||||||
$link = $document->createElement('atom:link');
|
|
||||||
$link->setAttribute('href', $this->cleanString($feedUrl));
|
|
||||||
$link->setAttribute('ref', 'self');
|
|
||||||
return $link;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createItem(DOMDocument $document, FeedItem $feedItem): DOMNode {
|
|
||||||
return $document->createElement('item');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createItemTitle(DOMDocument $document, string $title): DOMNode {
|
|
||||||
return $document->createElement('title', $this->cleanString($title));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createItemSummary(DOMDocument $document, string $summary): ?DOMNode {
|
|
||||||
return $document->createElement('description', $this->cleanString($summary));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createItemContent(DOMDocument $document, string $content): ?DOMNode {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createItemCreationDate(DOMDocument $document, int $creationDate): ?DOMNode {
|
|
||||||
return $document->createElement('pubDate', $this->formatTime($creationDate));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createItemUniqueId(DOMDocument $document, string $uniqueId): ?DOMNode {
|
|
||||||
$elem = $document->createElement('guid', $uniqueId);
|
|
||||||
$elem->setAttribute('isPermaLink', 'true');
|
|
||||||
return $elem;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createItemContentUrl(DOMDocument $document, string $contentUrl): ?DOMNode {
|
|
||||||
return $document->createElement('link', $contentUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createItemCommentsUrl(DOMDocument $document, string $commentsUrl): ?DOMNode {
|
|
||||||
return $document->createElement('comments', $commentsUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createItemAuthor(DOMDocument $document, ?string $authorName, ?string $authorUrl): ?DOMNode {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
<?php
|
|
||||||
namespace Misuzu\Feeds;
|
|
||||||
|
|
||||||
use DOMDocument;
|
|
||||||
use DOMNode;
|
|
||||||
|
|
||||||
abstract class XmlFeedSerializer extends FeedSerializer {
|
|
||||||
public function serializeFeed(Feed $feed): string {
|
|
||||||
$document = new DOMDocument('1.0', 'utf-8');
|
|
||||||
$root = $this->createRoot($document, $feed);
|
|
||||||
$root->appendChild($this->createTitle($document, $feed->getTitle()));
|
|
||||||
|
|
||||||
if($feed->hasDescription())
|
|
||||||
self::appendChild($root, $this->createDescription($document, $feed->getDescription()));
|
|
||||||
if($feed->hasLastUpdate())
|
|
||||||
self::appendChild($root, $this->createLastUpdate($document, $feed->getLastUpdate()));
|
|
||||||
if($feed->hasContentUrl())
|
|
||||||
self::appendChild($root, $this->createContentUrl($document, $feed->getContentUrl()));
|
|
||||||
if($feed->hasFeedUrl())
|
|
||||||
self::appendChild($root, $this->createFeedUrl($document, $feed->getFeedUrl()));
|
|
||||||
|
|
||||||
if($feed->hasItems()) {
|
|
||||||
foreach($feed->getItems() as $item) {
|
|
||||||
$root->appendChild($this->serializeFeedItem($document, $item));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $document->saveXML();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function serializeFeedItem(DOMDocument $document, FeedItem $feedItem): DOMNode {
|
|
||||||
$elem = $this->createItem($document, $feedItem);
|
|
||||||
$elem->appendChild($this->createItemTitle($document, $feedItem->getTitle()));
|
|
||||||
|
|
||||||
if($feedItem->hasSummary())
|
|
||||||
self::appendChild($elem, $this->createItemSummary($document, $feedItem->getSummary()));
|
|
||||||
if($feedItem->hasContent())
|
|
||||||
self::appendChild($elem, $this->createItemContent($document, $feedItem->getContent()));
|
|
||||||
if($feedItem->hasCreationDate())
|
|
||||||
self::appendChild($elem, $this->createItemCreationDate($document, $feedItem->getCreationDate()));
|
|
||||||
if($feedItem->hasUniqueId())
|
|
||||||
self::appendChild($elem, $this->createItemUniqueId($document, $feedItem->getUniqueId()));
|
|
||||||
if($feedItem->hasContentUrl())
|
|
||||||
self::appendChild($elem, $this->createItemContentUrl($document, $feedItem->getContentUrl()));
|
|
||||||
if($feedItem->hasCommentsUrl())
|
|
||||||
self::appendChild($elem, $this->createItemCommentsUrl($document, $feedItem->getCommentsUrl()));
|
|
||||||
if($feedItem->hasAuthorName() || $feedItem->hasAuthorUrl())
|
|
||||||
self::appendChild($elem, $this->createItemAuthor($document, $feedItem->getAuthorName(), $feedItem->getAuthorUrl()));
|
|
||||||
|
|
||||||
return $elem;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cleanString(string $string): string {
|
|
||||||
return htmlspecialchars($string, ENT_XML1 | ENT_COMPAT | ENT_SUBSTITUTE);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function appendChild(DOMNode $parent, ?DOMNode $elem): ?DOMNode {
|
|
||||||
if($elem !== null)
|
|
||||||
return $parent->appendChild($elem);
|
|
||||||
return $elem;
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract protected function formatTime(int $time): string;
|
|
||||||
abstract protected function createRoot(DOMDocument $document, Feed $feed): DOMNode;
|
|
||||||
abstract protected function createTitle(DOMDocument $document, string $title): DOMNode;
|
|
||||||
abstract protected function createDescription(DOMDocument $document, string $description): ?DOMNode;
|
|
||||||
abstract protected function createLastUpdate(DOMDocument $document, int $lastUpdate): ?DOMNode;
|
|
||||||
abstract protected function createContentUrl(DOMDocument $document, string $contentUrl): ?DOMNode;
|
|
||||||
abstract protected function createFeedUrl(DOMDocument $document, string $feedUrl): ?DOMNode;
|
|
||||||
abstract protected function createItem(DOMDocument $document, FeedItem $feedItem): DOMNode;
|
|
||||||
abstract protected function createItemTitle(DOMDocument $document, string $title): DOMNode;
|
|
||||||
abstract protected function createItemSummary(DOMDocument $document, string $summary): ?DOMNode;
|
|
||||||
abstract protected function createItemContent(DOMDocument $document, string $content): ?DOMNode;
|
|
||||||
abstract protected function createItemCreationDate(DOMDocument $document, int $creationDate): ?DOMNode;
|
|
||||||
abstract protected function createItemUniqueId(DOMDocument $document, string $uniqueId): ?DOMNode;
|
|
||||||
abstract protected function createItemContentUrl(DOMDocument $document, string $contentUrl): ?DOMNode;
|
|
||||||
abstract protected function createItemCommentsUrl(DOMDocument $document, string $commentsUrl): ?DOMNode;
|
|
||||||
abstract protected function createItemAuthor(DOMDocument $document, ?string $authorName, ?string $authorUrl): ?DOMNode;
|
|
||||||
}
|
|
|
@ -5,16 +5,14 @@ use InvalidArgumentException;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use stdClass;
|
use stdClass;
|
||||||
use Index\Colour\Colour;
|
use Index\Colour\Colour;
|
||||||
use Index\Data\DbStatementCache;
|
use Index\Db\{DbConnection,DbStatementCache,DbTools};
|
||||||
use Index\Data\DbTools;
|
|
||||||
use Index\Data\IDbConnection;
|
|
||||||
use Misuzu\Pagination;
|
use Misuzu\Pagination;
|
||||||
use Misuzu\Users\UserInfo;
|
use Misuzu\Users\UserInfo;
|
||||||
|
|
||||||
class ForumCategories {
|
class ForumCategories {
|
||||||
private DbStatementCache $cache;
|
private DbStatementCache $cache;
|
||||||
|
|
||||||
public function __construct(IDbConnection $dbConn) {
|
public function __construct(DbConnection $dbConn) {
|
||||||
$this->cache = new DbStatementCache($dbConn);
|
$this->cache = new DbStatementCache($dbConn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu\Forum;
|
namespace Misuzu\Forum;
|
||||||
|
|
||||||
use Index\DateTime;
|
use Carbon\CarbonImmutable;
|
||||||
use Index\Colour\Colour;
|
use Index\Colour\Colour;
|
||||||
use Index\Data\IDbResult;
|
use Index\Db\DbResult;
|
||||||
|
|
||||||
class ForumCategoryInfo {
|
class ForumCategoryInfo {
|
||||||
// should the types just be replaced with flags indicating what's allowed?
|
// should the types just be replaced with flags indicating what's allowed?
|
||||||
|
@ -44,7 +44,7 @@ class ForumCategoryInfo {
|
||||||
private int $postsCount,
|
private int $postsCount,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public static function fromResult(IDbResult $result): ForumCategoryInfo {
|
public static function fromResult(DbResult $result): ForumCategoryInfo {
|
||||||
return new ForumCategoryInfo(
|
return new ForumCategoryInfo(
|
||||||
id: $result->getString(0),
|
id: $result->getString(0),
|
||||||
order: $result->getInteger(1),
|
order: $result->getInteger(1),
|
||||||
|
@ -176,8 +176,8 @@ class ForumCategoryInfo {
|
||||||
return $this->created;
|
return $this->created;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCreatedAt(): DateTime {
|
public function getCreatedAt(): CarbonImmutable {
|
||||||
return DateTime::fromUnixTimeSeconds($this->created);
|
return CarbonImmutable::createFromTimestampUTC($this->created);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isArchived(): bool {
|
public function isArchived(): bool {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
namespace Misuzu\Forum;
|
namespace Misuzu\Forum;
|
||||||
|
|
||||||
use stdClass;
|
use stdClass;
|
||||||
use Index\Data\IDbConnection;
|
use Index\Db\DbConnection;
|
||||||
use Misuzu\Users\UserInfo;
|
use Misuzu\Users\UserInfo;
|
||||||
|
|
||||||
class ForumContext {
|
class ForumContext {
|
||||||
|
@ -14,7 +14,7 @@ class ForumContext {
|
||||||
private array $totalUserTopics = [];
|
private array $totalUserTopics = [];
|
||||||
private array $totalUserPosts = [];
|
private array $totalUserPosts = [];
|
||||||
|
|
||||||
public function __construct(IDbConnection $dbConn) {
|
public function __construct(DbConnection $dbConn) {
|
||||||
$this->categories = new ForumCategories($dbConn);
|
$this->categories = new ForumCategories($dbConn);
|
||||||
$this->topics = new ForumTopics($dbConn);
|
$this->topics = new ForumTopics($dbConn);
|
||||||
$this->topicRedirects = new ForumTopicRedirects($dbConn);
|
$this->topicRedirects = new ForumTopicRedirects($dbConn);
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu\Forum;
|
namespace Misuzu\Forum;
|
||||||
|
|
||||||
use Index\DateTime;
|
|
||||||
use Index\Data\IDbResult;
|
|
||||||
use Index\Net\IPAddress;
|
|
||||||
use Misuzu\Parsers\Parser;
|
use Misuzu\Parsers\Parser;
|
||||||
|
use Carbon\CarbonImmutable;
|
||||||
|
use Index\XDateTime;
|
||||||
|
use Index\Db\DbResult;
|
||||||
|
|
||||||
class ForumPostInfo {
|
class ForumPostInfo {
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
@ -21,7 +21,7 @@ class ForumPostInfo {
|
||||||
private ?int $deleted,
|
private ?int $deleted,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public static function fromResult(IDbResult $result): ForumPostInfo {
|
public static function fromResult(DbResult $result): ForumPostInfo {
|
||||||
return new ForumPostInfo(
|
return new ForumPostInfo(
|
||||||
id: $result->getString(0),
|
id: $result->getString(0),
|
||||||
topicId: $result->getString(1),
|
topicId: $result->getString(1),
|
||||||
|
@ -57,14 +57,10 @@ class ForumPostInfo {
|
||||||
return $this->userId;
|
return $this->userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRemoteAddressRaw(): string {
|
public function getRemoteAddress(): string {
|
||||||
return $this->remoteAddr;
|
return $this->remoteAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRemoteAddress(): IPAddress {
|
|
||||||
return IPAddress::parse($this->remoteAddr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getBody(): string {
|
public function getBody(): string {
|
||||||
return $this->body;
|
return $this->body;
|
||||||
}
|
}
|
||||||
|
@ -93,16 +89,17 @@ class ForumPostInfo {
|
||||||
return $this->created;
|
return $this->created;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCreatedAt(): DateTime {
|
public function getCreatedAt(): CarbonImmutable {
|
||||||
return DateTime::fromUnixTimeSeconds($this->created);
|
return CarbonImmutable::createFromTimestampUTC($this->created);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ?DateTime $markAsEditedThreshold = null;
|
private static ?CarbonImmutable $markAsEditedThreshold = null;
|
||||||
|
|
||||||
public function shouldMarkAsEdited(): bool {
|
public function shouldMarkAsEdited(): bool {
|
||||||
if(self::$markAsEditedThreshold === null)
|
if(self::$markAsEditedThreshold === null)
|
||||||
self::$markAsEditedThreshold = DateTime::now()->modify('-5 minutes');
|
self::$markAsEditedThreshold = new CarbonImmutable('-5 minutes');
|
||||||
|
|
||||||
return $this->getCreatedAt()->isLessThan(self::$markAsEditedThreshold);
|
return XDateTime::compare($this->getCreatedAt(), self::$markAsEditedThreshold) < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isEdited(): bool {
|
public function isEdited(): bool {
|
||||||
|
@ -113,16 +110,17 @@ class ForumPostInfo {
|
||||||
return $this->edited;
|
return $this->edited;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getEditedAt(): ?DateTime {
|
public function getEditedAt(): ?CarbonImmutable {
|
||||||
return $this->edited === null ? null : DateTime::fromUnixTimeSeconds($this->edited);
|
return $this->edited === null ? null : CarbonImmutable::createFromTimestampUTC($this->edited);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ?DateTime $canBeDeletedThreshold = null;
|
private static ?CarbonImmutable $canBeDeletedThreshold = null;
|
||||||
|
|
||||||
public function canBeDeleted(): bool {
|
public function canBeDeleted(): bool {
|
||||||
if(self::$canBeDeletedThreshold === null)
|
if(self::$canBeDeletedThreshold === null)
|
||||||
self::$canBeDeletedThreshold = DateTime::now()->modify('-1 week');
|
self::$canBeDeletedThreshold = new CarbonImmutable('-1 week');
|
||||||
|
|
||||||
return $this->getCreatedAt()->isMoreThanOrEqual(self::$canBeDeletedThreshold);
|
return XDateTime::compare($this->getCreatedAt(), self::$canBeDeletedThreshold) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isDeleted(): bool {
|
public function isDeleted(): bool {
|
||||||
|
@ -133,7 +131,7 @@ class ForumPostInfo {
|
||||||
return $this->deleted;
|
return $this->deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDeletedAt(): ?DateTime {
|
public function getDeletedAt(): ?CarbonImmutable {
|
||||||
return $this->deleted === null ? null : DateTime::fromUnixTimeSeconds($this->deleted);
|
return $this->deleted === null ? null : CarbonImmutable::createFromTimestampUTC($this->deleted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,19 +4,16 @@ namespace Misuzu\Forum;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use stdClass;
|
use stdClass;
|
||||||
use Index\DateTime;
|
use Carbon\CarbonImmutable;
|
||||||
use Index\Data\DbStatementCache;
|
use Index\Db\{DbConnection,DbStatementCache,DbTools};
|
||||||
use Index\Data\DbTools;
|
|
||||||
use Index\Data\IDbConnection;
|
|
||||||
use Index\Net\IPAddress;
|
|
||||||
use Misuzu\Pagination;
|
use Misuzu\Pagination;
|
||||||
use Misuzu\Users\UserInfo;
|
use Misuzu\Users\UserInfo;
|
||||||
|
|
||||||
class ForumPosts {
|
class ForumPosts {
|
||||||
private IDbConnection $dbConn;
|
private DbConnection $dbConn;
|
||||||
private DbStatementCache $cache;
|
private DbStatementCache $cache;
|
||||||
|
|
||||||
public function __construct(IDbConnection $dbConn) {
|
public function __construct(DbConnection $dbConn) {
|
||||||
$this->dbConn = $dbConn;
|
$this->dbConn = $dbConn;
|
||||||
$this->cache = new DbStatementCache($dbConn);
|
$this->cache = new DbStatementCache($dbConn);
|
||||||
}
|
}
|
||||||
|
@ -267,7 +264,7 @@ class ForumPosts {
|
||||||
public function createPost(
|
public function createPost(
|
||||||
ForumTopicInfo|string $topicInfo,
|
ForumTopicInfo|string $topicInfo,
|
||||||
UserInfo|string|null $userInfo,
|
UserInfo|string|null $userInfo,
|
||||||
IPAddress|string $remoteAddr,
|
string $remoteAddr,
|
||||||
string $body,
|
string $body,
|
||||||
int $bodyParser,
|
int $bodyParser,
|
||||||
bool $displaySignature,
|
bool $displaySignature,
|
||||||
|
@ -285,9 +282,6 @@ class ForumPosts {
|
||||||
if($userInfo instanceof UserInfo)
|
if($userInfo instanceof UserInfo)
|
||||||
$userInfo = $userInfo->getId();
|
$userInfo = $userInfo->getId();
|
||||||
|
|
||||||
if($remoteAddr instanceof IPAddress)
|
|
||||||
$remoteAddr = (string)$remoteAddr;
|
|
||||||
|
|
||||||
$stmt = $this->cache->get('INSERT INTO msz_forum_posts (topic_id, forum_id, user_id, post_ip, post_text, post_parse, post_display_signature) VALUES (?, ?, ?, INET6_ATON(?), ?, ?, ?)');
|
$stmt = $this->cache->get('INSERT INTO msz_forum_posts (topic_id, forum_id, user_id, post_ip, post_text, post_parse, post_display_signature) VALUES (?, ?, ?, INET6_ATON(?), ?, ?, ?)');
|
||||||
$stmt->addParameter(1, $topicInfo);
|
$stmt->addParameter(1, $topicInfo);
|
||||||
$stmt->addParameter(2, $categoryInfo);
|
$stmt->addParameter(2, $categoryInfo);
|
||||||
|
@ -303,7 +297,7 @@ class ForumPosts {
|
||||||
|
|
||||||
public function updatePost(
|
public function updatePost(
|
||||||
ForumPostInfo|string $postInfo,
|
ForumPostInfo|string $postInfo,
|
||||||
IPAddress|string|null $remoteAddr = null,
|
?string $remoteAddr = null,
|
||||||
?string $body = null,
|
?string $body = null,
|
||||||
?int $bodyParser = null,
|
?int $bodyParser = null,
|
||||||
?bool $displaySignature = null,
|
?bool $displaySignature = null,
|
||||||
|
@ -316,9 +310,6 @@ class ForumPosts {
|
||||||
$values = [];
|
$values = [];
|
||||||
|
|
||||||
if($remoteAddr !== null) {
|
if($remoteAddr !== null) {
|
||||||
if($remoteAddr instanceof IPAddress)
|
|
||||||
$remoteAddr = (string)$remoteAddr;
|
|
||||||
|
|
||||||
$fields[] = 'post_ip = INET6_ATON(?)';
|
$fields[] = 'post_ip = INET6_ATON(?)';
|
||||||
$values[] = $remoteAddr;
|
$values[] = $remoteAddr;
|
||||||
}
|
}
|
||||||
|
@ -395,8 +386,8 @@ class ForumPosts {
|
||||||
return $result->getInteger(0);
|
return $result->getInteger(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUserLastPostCreatedAt(UserInfo|string $userInfo): DateTime {
|
public function getUserLastPostCreatedAt(UserInfo|string $userInfo): CarbonImmutable {
|
||||||
return DateTime::fromUnixTimeSeconds($this->getUserLastPostCreatedTime($userInfo));
|
return CarbonImmutable::createFromTimestampUTC($this->getUserLastPostCreatedTime($userInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generatePostRankings(
|
public function generatePostRankings(
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu\Forum;
|
namespace Misuzu\Forum;
|
||||||
|
|
||||||
use Index\DateTime;
|
use Carbon\CarbonImmutable;
|
||||||
use Index\Data\IDbResult;
|
use Index\XDateTime;
|
||||||
|
use Index\Db\DbResult;
|
||||||
|
|
||||||
class ForumTopicInfo {
|
class ForumTopicInfo {
|
||||||
public const TYPE_DISCUSSION = 0;
|
public const TYPE_DISCUSSION = 0;
|
||||||
|
@ -32,7 +33,7 @@ class ForumTopicInfo {
|
||||||
private ?int $locked,
|
private ?int $locked,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public static function fromResult(IDbResult $result): ForumTopicInfo {
|
public static function fromResult(DbResult $result): ForumTopicInfo {
|
||||||
return new ForumTopicInfo(
|
return new ForumTopicInfo(
|
||||||
id: $result->getString(0),
|
id: $result->getString(0),
|
||||||
categoryId: $result->getString(1),
|
categoryId: $result->getString(1),
|
||||||
|
@ -136,25 +137,25 @@ class ForumTopicInfo {
|
||||||
return $this->created;
|
return $this->created;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCreatedAt(): DateTime {
|
public function getCreatedAt(): CarbonImmutable {
|
||||||
return DateTime::fromUnixTimeSeconds($this->created);
|
return CarbonImmutable::createFromTimestampUTC($this->created);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ?DateTime $lastActiveAt = null;
|
private static ?CarbonImmutable $lastActiveAt = null;
|
||||||
|
|
||||||
public function isActive(): bool {
|
public function isActive(): bool {
|
||||||
if(self::$lastActiveAt === null)
|
if(self::$lastActiveAt === null)
|
||||||
self::$lastActiveAt = DateTime::now()->modify('-1 month');
|
self::$lastActiveAt = new CarbonImmutable('-1 month');
|
||||||
|
|
||||||
return $this->getBumpedAt()->isMoreThanOrEqual(self::$lastActiveAt);
|
return XDateTime::compare($this->getBumpedAt(), self::$lastActiveAt) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBumpedTime(): int {
|
public function getBumpedTime(): int {
|
||||||
return $this->bumped;
|
return $this->bumped;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBumpedAt(): DateTime {
|
public function getBumpedAt(): CarbonImmutable {
|
||||||
return DateTime::fromUnixTimeSeconds($this->bumped);
|
return CarbonImmutable::createFromTimestampUTC($this->bumped);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isDeleted(): bool {
|
public function isDeleted(): bool {
|
||||||
|
@ -165,8 +166,8 @@ class ForumTopicInfo {
|
||||||
return $this->deleted;
|
return $this->deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDeletedAt(): ?DateTime {
|
public function getDeletedAt(): ?CarbonImmutable {
|
||||||
return $this->deleted === null ? null : DateTime::fromUnixTimeSeconds($this->deleted);
|
return $this->deleted === null ? null : CarbonImmutable::createFromTimestampUTC($this->deleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isLocked(): bool {
|
public function isLocked(): bool {
|
||||||
|
@ -177,7 +178,7 @@ class ForumTopicInfo {
|
||||||
return $this->locked;
|
return $this->locked;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLockedAt(): ?DateTime {
|
public function getLockedAt(): ?CarbonImmutable {
|
||||||
return $this->locked === null ? null : DateTime::fromUnixTimeSeconds($this->locked);
|
return $this->locked === null ? null : CarbonImmutable::createFromTimestampUTC($this->locked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu\Forum;
|
namespace Misuzu\Forum;
|
||||||
|
|
||||||
use Index\DateTime;
|
use Carbon\CarbonImmutable;
|
||||||
use Index\Data\IDbResult;
|
use Index\Db\DbResult;
|
||||||
|
|
||||||
class ForumTopicRedirectInfo {
|
class ForumTopicRedirectInfo {
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
@ -12,7 +12,7 @@ class ForumTopicRedirectInfo {
|
||||||
private int $created,
|
private int $created,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public static function fromResult(IDbResult $result): ForumTopicRedirectInfo {
|
public static function fromResult(DbResult $result): ForumTopicRedirectInfo {
|
||||||
return new ForumTopicRedirectInfo(
|
return new ForumTopicRedirectInfo(
|
||||||
topicId: $result->getString(0),
|
topicId: $result->getString(0),
|
||||||
userId: $result->getStringOrNull(1),
|
userId: $result->getStringOrNull(1),
|
||||||
|
@ -41,7 +41,7 @@ class ForumTopicRedirectInfo {
|
||||||
return $this->created;
|
return $this->created;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCreatedAt(): DateTime {
|
public function getCreatedAt(): CarbonImmutable {
|
||||||
return DateTime::fromUnixTimeSeconds($this->created);
|
return CarbonImmutable::createFromTimestampUTC($this->created);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,15 +2,14 @@
|
||||||
namespace Misuzu\Forum;
|
namespace Misuzu\Forum;
|
||||||
|
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Index\Data\DbStatementCache;
|
use Index\Db\{DbConnection,DbStatementCache};
|
||||||
use Index\Data\IDbConnection;
|
|
||||||
use Misuzu\Pagination;
|
use Misuzu\Pagination;
|
||||||
use Misuzu\Users\UserInfo;
|
use Misuzu\Users\UserInfo;
|
||||||
|
|
||||||
class ForumTopicRedirects {
|
class ForumTopicRedirects {
|
||||||
private DbStatementCache $cache;
|
private DbStatementCache $cache;
|
||||||
|
|
||||||
public function __construct(IDbConnection $dbConn) {
|
public function __construct(DbConnection $dbConn) {
|
||||||
$this->cache = new DbStatementCache($dbConn);
|
$this->cache = new DbStatementCache($dbConn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,17 +4,15 @@ namespace Misuzu\Forum;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use stdClass;
|
use stdClass;
|
||||||
use Index\Data\DbStatementCache;
|
use Index\Db\{DbConnection,DbStatementCache,DbTools};
|
||||||
use Index\Data\DbTools;
|
|
||||||
use Index\Data\IDbConnection;
|
|
||||||
use Misuzu\Pagination;
|
use Misuzu\Pagination;
|
||||||
use Misuzu\Users\UserInfo;
|
use Misuzu\Users\UserInfo;
|
||||||
|
|
||||||
class ForumTopics {
|
class ForumTopics {
|
||||||
private IDbConnection $dbConn;
|
private DbConnection $dbConn;
|
||||||
private DbStatementCache $cache;
|
private DbStatementCache $cache;
|
||||||
|
|
||||||
public function __construct(IDbConnection $dbConn) {
|
public function __construct(DbConnection $dbConn) {
|
||||||
$this->dbConn = $dbConn;
|
$this->dbConn = $dbConn;
|
||||||
$this->cache = new DbStatementCache($dbConn);
|
$this->cache = new DbStatementCache($dbConn);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,49 +1,36 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu\Hanyuu;
|
namespace Misuzu\Hanyuu;
|
||||||
|
|
||||||
use stdClass;
|
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Index\Colour\Colour;
|
|
||||||
use Index\Http\Routing\{HttpGet,HttpMiddleware,HttpPost,RouteHandler};
|
|
||||||
use Index\Serialisation\UriBase64;
|
|
||||||
use Syokuhou\IConfig;
|
|
||||||
use Misuzu\CSRF;
|
use Misuzu\CSRF;
|
||||||
use Misuzu\Auth\{AuthContext,AuthInfo,Sessions};
|
use Misuzu\Auth\AuthContext;
|
||||||
use Misuzu\URLs\URLRegistry;
|
|
||||||
use Misuzu\Users\{UsersContext,UserInfo};
|
use Misuzu\Users\{UsersContext,UserInfo};
|
||||||
|
use Aiwass\Server\{RpcActionHandler,RpcProcedure};
|
||||||
|
use Index\Colour\Colour;
|
||||||
|
use Index\Config\Config;
|
||||||
|
use Index\Urls\UrlRegistry;
|
||||||
|
|
||||||
final class HanyuuRoutes extends RouteHandler {
|
final class HanyuuRpcActions extends RpcActionHandler {
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private IConfig $config,
|
private $getBaseUrl,
|
||||||
private IConfig $impersonateConfig, // this sucks lol
|
private Config $impersonateConfig,
|
||||||
private URLRegistry $urls,
|
private UrlRegistry $urls,
|
||||||
private UsersContext $usersCtx,
|
private UsersContext $usersCtx,
|
||||||
private AuthContext $authCtx,
|
private AuthContext $authCtx
|
||||||
private AuthInfo $authInfo
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
private function getEndpoint(): string {
|
private static function createPayload(string $name, array $attrs = []): array {
|
||||||
return $this->config->getString('endpoint');
|
$payload = ['name' => $name, 'attrs' => []];
|
||||||
}
|
|
||||||
|
|
||||||
private function getSecret(): string {
|
|
||||||
return $this->config->getString('secret');
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function createPayload(string $name, array $attrs = []): object {
|
|
||||||
$payload = new stdClass;
|
|
||||||
$payload->name = $name;
|
|
||||||
$payload->attrs = [];
|
|
||||||
foreach($attrs as $name => $value) {
|
foreach($attrs as $name => $value) {
|
||||||
if($value === null)
|
if($value === null)
|
||||||
continue;
|
continue;
|
||||||
$payload->attrs[(string)$name] = $value;
|
$payload['attrs'][(string)$name] = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $payload;
|
return $payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function createErrorPayload(string $code, ?string $text = null): object {
|
private static function createErrorPayload(string $code, ?string $text = null): array {
|
||||||
$attrs = ['code' => $code];
|
$attrs = ['code' => $code];
|
||||||
if($text !== null && $text !== '')
|
if($text !== null && $text !== '')
|
||||||
$attrs['text'] = $text;
|
$attrs['text'] = $text;
|
||||||
|
@ -51,42 +38,6 @@ final class HanyuuRoutes extends RouteHandler {
|
||||||
return self::createPayload('error', $attrs);
|
return self::createPayload('error', $attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[HttpMiddleware('/_hanyuu/(.*)')]
|
|
||||||
public function verifyRequest($response, $request, string $action) {
|
|
||||||
$userTime = (int)$request->getHeaderLine('X-Hanyuu-Timestamp');
|
|
||||||
$userHash = UriBase64::decode((string)$request->getHeaderLine('X-Hanyuu-Signature'));
|
|
||||||
|
|
||||||
$currentTime = time();
|
|
||||||
if(empty($userHash) || $userTime < $currentTime - 60 || $userTime > $currentTime + 60)
|
|
||||||
return self::createErrorPayload('verification', 'Request verification failed.');
|
|
||||||
|
|
||||||
$url = sprintf('%s/_hanyuu/%s', $this->getEndpoint(), $action);
|
|
||||||
$params = $request->getParamString();
|
|
||||||
if($params !== '')
|
|
||||||
$url .= sprintf('?%s', $params);
|
|
||||||
|
|
||||||
if($request->getMethod() === 'POST') {
|
|
||||||
if(!$request->isFormContent())
|
|
||||||
return self::createErrorPayload('request', 'Request body is not in expect format.');
|
|
||||||
|
|
||||||
$body = $request->getContent()->getParamString();
|
|
||||||
} elseif($request->getMethod() !== 'GET') {
|
|
||||||
return self::createErrorPayload('request', 'Only GET and POST methods are allowed.');
|
|
||||||
} else {
|
|
||||||
$body = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$verifyHash = hash_hmac(
|
|
||||||
'sha256',
|
|
||||||
sprintf('[%s|%s|%s]', $userTime, $url, $body),
|
|
||||||
$this->getSecret(),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
if(!hash_equals($verifyHash, $userHash))
|
|
||||||
return self::createErrorPayload('verification', 'Request verification failed.');
|
|
||||||
}
|
|
||||||
|
|
||||||
private function canImpersonateUserId(UserInfo $impersonator, string $targetId): bool {
|
private function canImpersonateUserId(UserInfo $impersonator, string $targetId): bool {
|
||||||
if($impersonator->isSuperUser())
|
if($impersonator->isSuperUser())
|
||||||
return true;
|
return true;
|
||||||
|
@ -98,18 +49,15 @@ final class HanyuuRoutes extends RouteHandler {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[HttpPost('/_hanyuu/auth-check')]
|
#[RpcProcedure('mszhau:authCheck')]
|
||||||
public function postCheckAuth($response, $request) {
|
public function procAuthCheck(string $method, string $remoteAddr, string $token, string $avatars = '') {
|
||||||
$content = $request->getContent();
|
|
||||||
$method = (string)$content->getParam('method');
|
|
||||||
if($method !== 'Misuzu')
|
if($method !== 'Misuzu')
|
||||||
return self::createErrorPayload('auth:check:method', 'Requested auth method is not supported.');
|
return self::createErrorPayload('auth:check:method', 'Requested auth method is not supported.');
|
||||||
|
|
||||||
$remoteAddr = (string)$content->getParam('remote_addr');
|
|
||||||
if(filter_var($remoteAddr, FILTER_VALIDATE_IP) === false)
|
if(filter_var($remoteAddr, FILTER_VALIDATE_IP) === false)
|
||||||
return self::createErrorPayload('auth:check:remote_addr', 'Provided remote address is not in a valid format.');
|
return self::createErrorPayload('auth:check:remote_addr', 'Provided remote address is not in a valid format.');
|
||||||
|
|
||||||
$avatarResolutions = trim((string)$content->getParam('avatars'));
|
$avatarResolutions = trim($avatars);
|
||||||
if($avatarResolutions === '') {
|
if($avatarResolutions === '') {
|
||||||
$avatarResolutions = [];
|
$avatarResolutions = [];
|
||||||
} else {
|
} else {
|
||||||
|
@ -122,11 +70,12 @@ final class HanyuuRoutes extends RouteHandler {
|
||||||
$avatarResolutions = array_unique($avatarResolutions);
|
$avatarResolutions = array_unique($avatarResolutions);
|
||||||
}
|
}
|
||||||
|
|
||||||
$loginUrl = $this->getEndpoint() . $this->urls->format('auth-login');
|
$baseUrl = ($this->getBaseUrl)();
|
||||||
$registerUrl = $this->getEndpoint() . $this->urls->format('auth-register');
|
$loginUrl = $baseUrl . $this->urls->format('auth-login');
|
||||||
|
$registerUrl = $baseUrl . $this->urls->format('auth-register');
|
||||||
|
|
||||||
$tokenPacker = $this->authCtx->createAuthTokenPacker();
|
$tokenPacker = $this->authCtx->createAuthTokenPacker();
|
||||||
$tokenInfo = $tokenPacker->unpack(trim((string)$content->getParam('token')));
|
$tokenInfo = $tokenPacker->unpack(trim($token));
|
||||||
if($tokenInfo->isEmpty())
|
if($tokenInfo->isEmpty())
|
||||||
return self::createPayload('auth:check:fail', ['reason' => 'empty', 'login_url' => $loginUrl, 'register_url' => $registerUrl]);
|
return self::createPayload('auth:check:fail', ['reason' => 'empty', 'login_url' => $loginUrl, 'register_url' => $registerUrl]);
|
||||||
|
|
||||||
|
@ -156,6 +105,7 @@ final class HanyuuRoutes extends RouteHandler {
|
||||||
|
|
||||||
$response = [];
|
$response = [];
|
||||||
$response['session'] = [
|
$response['session'] = [
|
||||||
|
'token' => $sessionInfo->getToken(),
|
||||||
'created_at' => $sessionInfo->getCreatedTime(),
|
'created_at' => $sessionInfo->getCreatedTime(),
|
||||||
'expires_at' => $sessionInfo->getExpiresTime(),
|
'expires_at' => $sessionInfo->getExpiresTime(),
|
||||||
'lifetime_extends' => $sessionInfo->shouldBumpExpires(),
|
'lifetime_extends' => $sessionInfo->shouldBumpExpires(),
|
||||||
|
@ -173,9 +123,9 @@ final class HanyuuRoutes extends RouteHandler {
|
||||||
'remaining_str' => $banInfo->getRemainingString(),
|
'remaining_str' => $banInfo->getRemainingString(),
|
||||||
];
|
];
|
||||||
|
|
||||||
$gatherRequestedAvatars = function($userInfo) use ($avatarResolutions) {
|
$gatherRequestedAvatars = function($userInfo) use ($avatarResolutions, $baseUrl) {
|
||||||
$formatAvatarUrl = fn($res = 0) => (
|
$formatAvatarUrl = fn($res = 0) => (
|
||||||
$this->getEndpoint() . $this->urls->format('user-avatar', ['user' => $userInfo->getId(), 'res' => $res])
|
$baseUrl . $this->urls->format('user-avatar', ['user' => $userInfo->getId(), 'res' => $res])
|
||||||
);
|
);
|
||||||
|
|
||||||
$avatars = ['original' => $formatAvatarUrl()];
|
$avatars = ['original' => $formatAvatarUrl()];
|
||||||
|
@ -195,7 +145,7 @@ final class HanyuuRoutes extends RouteHandler {
|
||||||
'country_code' => $userInfo->getCountryCode(),
|
'country_code' => $userInfo->getCountryCode(),
|
||||||
'is_deleted' => $userInfo->isDeleted(),
|
'is_deleted' => $userInfo->isDeleted(),
|
||||||
'has_totp' => $userInfo->hasTOTPKey(),
|
'has_totp' => $userInfo->hasTOTPKey(),
|
||||||
'profile_url' => $this->getEndpoint() . $this->urls->format('user-profile', ['user' => $userInfo->getId()]),
|
'profile_url' => $baseUrl . $this->urls->format('user-profile', ['user' => $userInfo->getId()]),
|
||||||
'avatars' => $gatherRequestedAvatars($userInfo),
|
'avatars' => $gatherRequestedAvatars($userInfo),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -204,7 +154,7 @@ final class HanyuuRoutes extends RouteHandler {
|
||||||
$response['guise'] = $extractUserInfo($userInfoReal);
|
$response['guise'] = $extractUserInfo($userInfoReal);
|
||||||
|
|
||||||
$csrfp = CSRF::create($sessionInfo->getToken());
|
$csrfp = CSRF::create($sessionInfo->getToken());
|
||||||
$response['guise']['revert_url'] = $this->getEndpoint() . $this->urls->format('auth-revert', ['csrf' => $csrfp->createToken()]);
|
$response['guise']['revert_url'] = $baseUrl . $this->urls->format('auth-revert', ['csrf' => $csrfp->createToken()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::createPayload('auth:check:success', $response);
|
return self::createPayload('auth:check:success', $response);
|
|
@ -2,23 +2,25 @@
|
||||||
namespace Misuzu\Home;
|
namespace Misuzu\Home;
|
||||||
|
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Index\DateTime;
|
use Index\XDateTime;
|
||||||
use Index\Data\{DbTools,IDbConnection};
|
use Index\Config\Config;
|
||||||
use Index\Http\Routing\{HttpGet,RouteHandler};
|
use Index\Db\{DbConnection,DbTools};
|
||||||
use Syokuhou\IConfig;
|
use Index\Http\Routing\{HttpGet,RouteHandler,RouteHandlerTrait};
|
||||||
|
use Index\Urls\{UrlFormat,UrlSource,UrlSourceTrait};
|
||||||
use Misuzu\{Pagination,SiteInfo,Template};
|
use Misuzu\{Pagination,SiteInfo,Template};
|
||||||
use Misuzu\Auth\AuthInfo;
|
use Misuzu\Auth\AuthInfo;
|
||||||
use Misuzu\Changelog\Changelog;
|
use Misuzu\Changelog\Changelog;
|
||||||
use Misuzu\Comments\Comments;
|
use Misuzu\Comments\Comments;
|
||||||
use Misuzu\Counters\Counters;
|
use Misuzu\Counters\Counters;
|
||||||
use Misuzu\News\News;
|
use Misuzu\News\News;
|
||||||
use Misuzu\URLs\URLInfo;
|
|
||||||
use Misuzu\Users\UsersContext;
|
use Misuzu\Users\UsersContext;
|
||||||
|
|
||||||
class HomeRoutes extends RouteHandler {
|
class HomeRoutes implements RouteHandler, UrlSource {
|
||||||
|
use RouteHandlerTrait, UrlSourceTrait;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private IConfig $config,
|
private Config $config,
|
||||||
private IDbConnection $dbConn,
|
private DbConnection $dbConn,
|
||||||
private SiteInfo $siteInfo,
|
private SiteInfo $siteInfo,
|
||||||
private AuthInfo $authInfo,
|
private AuthInfo $authInfo,
|
||||||
private Changelog $changelog,
|
private Changelog $changelog,
|
||||||
|
@ -151,7 +153,7 @@ class HomeRoutes extends RouteHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[HttpGet('/')]
|
#[HttpGet('/')]
|
||||||
#[URLInfo('index', '/')]
|
#[UrlFormat('index', '/')]
|
||||||
public function getIndex(...$args) {
|
public function getIndex(...$args) {
|
||||||
return $this->authInfo->isLoggedIn()
|
return $this->authInfo->isLoggedIn()
|
||||||
? $this->getHome(...$args)
|
? $this->getHome(...$args)
|
||||||
|
@ -167,7 +169,7 @@ class HomeRoutes extends RouteHandler {
|
||||||
$stats['users:online:recent'] = count($onlineUserInfos);
|
$stats['users:online:recent'] = count($onlineUserInfos);
|
||||||
|
|
||||||
$birthdays = [];
|
$birthdays = [];
|
||||||
$birthdayInfos = $this->usersCtx->getUsers()->getUsers(deleted: false, birthdate: DateTime::now(), orderBy: 'random');
|
$birthdayInfos = $this->usersCtx->getUsers()->getUsers(deleted: false, birthdate: XDateTime::now(), orderBy: 'random');
|
||||||
foreach($birthdayInfos as $birthdayInfo)
|
foreach($birthdayInfos as $birthdayInfo)
|
||||||
$birthdays[] = [
|
$birthdays[] = [
|
||||||
'info' => $birthdayInfo,
|
'info' => $birthdayInfo,
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu\Info;
|
namespace Misuzu\Info;
|
||||||
|
|
||||||
use Index\Http\Routing\{HttpGet,RouteHandler};
|
use Index\Http\Routing\{HttpGet,RouteHandler,RouteHandlerTrait};
|
||||||
|
use Index\Urls\{UrlFormat,UrlSource,UrlSourceTrait};
|
||||||
use Misuzu\Template;
|
use Misuzu\Template;
|
||||||
use Misuzu\Parsers\Parser;
|
use Misuzu\Parsers\Parser;
|
||||||
use Misuzu\URLs\URLInfo;
|
|
||||||
|
|
||||||
class InfoRoutes extends RouteHandler {
|
class InfoRoutes implements RouteHandler, UrlSource {
|
||||||
|
use RouteHandlerTrait, UrlSourceTrait;
|
||||||
|
|
||||||
private const DOCS_PATH = MSZ_ROOT . '/docs';
|
private const DOCS_PATH = MSZ_ROOT . '/docs';
|
||||||
private const PROJECT_PATHS = [
|
private const PROJECT_PATHS = [
|
||||||
'misuzu' => MSZ_ROOT,
|
'misuzu' => MSZ_ROOT,
|
||||||
|
@ -18,14 +20,14 @@ class InfoRoutes extends RouteHandler {
|
||||||
];
|
];
|
||||||
|
|
||||||
#[HttpGet('/info')]
|
#[HttpGet('/info')]
|
||||||
#[URLInfo('info-index', '/info')]
|
#[UrlFormat('info-index', '/info')]
|
||||||
public function getIndex() {
|
public function getIndex() {
|
||||||
return Template::renderRaw('info.index');
|
return Template::renderRaw('info.index');
|
||||||
}
|
}
|
||||||
|
|
||||||
#[HttpGet('/info/([A-Za-z0-9_]+)')]
|
#[HttpGet('/info/([A-Za-z0-9_]+)')]
|
||||||
#[URLInfo('info', '/info/<title>')]
|
#[UrlFormat('info', '/info/<title>')]
|
||||||
#[URLInfo('info-doc', '/info/<title>')]
|
#[UrlFormat('info-doc', '/info/<title>')]
|
||||||
public function getDocsPage($response, $request, string $name) {
|
public function getDocsPage($response, $request, string $name) {
|
||||||
return $this->serveMarkdownDocument(
|
return $this->serveMarkdownDocument(
|
||||||
sprintf('%s/%s.md', self::DOCS_PATH, $name)
|
sprintf('%s/%s.md', self::DOCS_PATH, $name)
|
||||||
|
@ -71,7 +73,7 @@ class InfoRoutes extends RouteHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[HttpGet('/info/([A-Za-z0-9_]+)/([A-Za-z0-9_]+)')]
|
#[HttpGet('/info/([A-Za-z0-9_]+)/([A-Za-z0-9_]+)')]
|
||||||
#[URLInfo('info-project-doc', '/info/<project>/<title>')]
|
#[UrlFormat('info-project-doc', '/info/<project>/<title>')]
|
||||||
public function getProjectPage($response, $request, string $project, string $name) {
|
public function getProjectPage($response, $request, string $project, string $name) {
|
||||||
if(!array_key_exists($project, self::PROJECT_PATHS))
|
if(!array_key_exists($project, self::PROJECT_PATHS))
|
||||||
return 404;
|
return 404;
|
||||||
|
|
|
@ -1,15 +1,19 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu;
|
namespace Misuzu;
|
||||||
|
|
||||||
use Index\Http\Routing\{HttpGet,RouteHandler};
|
use Index\Http\Routing\{HttpGet,RouteHandler,RouteHandlerTrait};
|
||||||
use Misuzu\URLs\{IURLSource,URLInfo,URLRegistry};
|
use Index\Urls\{UrlFormat,UrlRegistry,UrlSource};
|
||||||
|
|
||||||
|
class LegacyRoutes implements RouteHandler, UrlSource {
|
||||||
|
use RouteHandlerTrait;
|
||||||
|
|
||||||
class LegacyRoutes extends RouteHandler implements IURLSource {
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private URLRegistry $urls
|
private UrlRegistry $urls
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function registerURLs(URLRegistry $urls): void {
|
public function registerUrls(UrlRegistry $urls): void {
|
||||||
|
UrlFormat::register($urls, $this);
|
||||||
|
|
||||||
// eventually this should be handled by context classes
|
// eventually this should be handled by context classes
|
||||||
$urls->register('search-index', '/search.php');
|
$urls->register('search-index', '/search.php');
|
||||||
$urls->register('search-query', '/search.php', ['q' => '<query>'], '<section>');
|
$urls->register('search-query', '/search.php', ['q' => '<query>'], '<section>');
|
||||||
|
@ -156,20 +160,6 @@ class LegacyRoutes extends RouteHandler implements IURLSource {
|
||||||
$response->redirect($location, true);
|
$response->redirect($location, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[HttpGet('/news.php/rss')]
|
|
||||||
public function getNewsRssPHP($response, $request): void {
|
|
||||||
$catId = (int)$request->getParam('c', FILTER_SANITIZE_NUMBER_INT);
|
|
||||||
$location = $this->urls->format($catId > 0 ? 'news-category-feed-rss' : 'news-feed-rss', ['category' => $catId]);
|
|
||||||
$response->redirect($location, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[HttpGet('/news.php/atom')]
|
|
||||||
public function getNewsAtomPHP($response, $request): void {
|
|
||||||
$catId = (int)$request->getParam('c', FILTER_SANITIZE_NUMBER_INT);
|
|
||||||
$location = $this->urls->format($catId > 0 ? 'news-category-feed-atom' : 'news-feed-atom', ['category' => $catId]);
|
|
||||||
$response->redirect($location, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[HttpGet('/news/index.php')]
|
#[HttpGet('/news/index.php')]
|
||||||
public function getNewsIndexPHP($response, $request): void {
|
public function getNewsIndexPHP($response, $request): void {
|
||||||
$response->redirect($this->urls->format('news-index', [
|
$response->redirect($this->urls->format('news-index', [
|
||||||
|
@ -192,25 +182,15 @@ class LegacyRoutes extends RouteHandler implements IURLSource {
|
||||||
]), true);
|
]), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[HttpGet('/news.php/rss')]
|
||||||
|
#[HttpGet('/news.php/atom')]
|
||||||
#[HttpGet('/news/feed.php')]
|
#[HttpGet('/news/feed.php')]
|
||||||
public function getNewsFeedPHP($response, $request): int {
|
|
||||||
return 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[HttpGet('/news/feed.php/rss')]
|
#[HttpGet('/news/feed.php/rss')]
|
||||||
public function getNewsFeedRssPHP($response, $request): void {
|
|
||||||
$catId = (int)$request->getParam('c', FILTER_SANITIZE_NUMBER_INT);
|
|
||||||
$response->redirect($this->urls->format(
|
|
||||||
$catId > 0 ? 'news-category-feed-rss' : 'news-feed-rss',
|
|
||||||
['category' => $catId]
|
|
||||||
), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[HttpGet('/news/feed.php/atom')]
|
#[HttpGet('/news/feed.php/atom')]
|
||||||
public function getNewsFeedAtomPHP($response, $request): void {
|
public function getNewsFeedPHP($response, $request): void {
|
||||||
$catId = (int)$request->getParam('c', FILTER_SANITIZE_NUMBER_INT);
|
$catId = (int)$request->getParam('c', FILTER_SANITIZE_NUMBER_INT);
|
||||||
$response->redirect($this->urls->format(
|
$response->redirect($this->urls->format(
|
||||||
$catId > 0 ? 'news-category-feed-atom' : 'news-feed-atom',
|
$catId > 0 ? 'news-category-feed' : 'news-feed',
|
||||||
['category' => $catId]
|
['category' => $catId]
|
||||||
), true);
|
), true);
|
||||||
}
|
}
|
||||||
|
@ -262,7 +242,7 @@ class LegacyRoutes extends RouteHandler implements IURLSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[HttpGet('/manage')]
|
#[HttpGet('/manage')]
|
||||||
#[URLInfo('manage-index', '/manage')]
|
#[UrlFormat('manage-index', '/manage')]
|
||||||
public function getManageIndex($response): void {
|
public function getManageIndex($response): void {
|
||||||
$response->redirect($this->urls->format('manage-general-overview'));
|
$response->redirect($this->urls->format('manage-general-overview'));
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
namespace Misuzu;
|
namespace Misuzu;
|
||||||
|
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use Syokuhou\IConfig;
|
use Index\Config\Config;
|
||||||
use Symfony\Component\Mime\Email as SymfonyMessage;
|
use Symfony\Component\Mime\Email as SymfonyMessage;
|
||||||
use Symfony\Component\Mime\Address as SymfonyAddress;
|
use Symfony\Component\Mime\Address as SymfonyAddress;
|
||||||
use Symfony\Component\Mailer\Transport as SymfonyTransport;
|
use Symfony\Component\Mailer\Transport as SymfonyTransport;
|
||||||
|
@ -10,10 +10,10 @@ use Symfony\Component\Mailer\Transport as SymfonyTransport;
|
||||||
final class Mailer {
|
final class Mailer {
|
||||||
private const TEMPLATE_PATH = MSZ_ROOT . '/config/emails/%s.txt';
|
private const TEMPLATE_PATH = MSZ_ROOT . '/config/emails/%s.txt';
|
||||||
|
|
||||||
private static IConfig $config;
|
private static Config $config;
|
||||||
private static $transport = null;
|
private static $transport = null;
|
||||||
|
|
||||||
public static function init(IConfig $config): void {
|
public static function init(Config $config): void {
|
||||||
self::$config = $config;
|
self::$config = $config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu\Messages;
|
namespace Misuzu\Messages;
|
||||||
|
|
||||||
use Index\DateTime;
|
|
||||||
use Index\Data\IDbResult;
|
|
||||||
use Misuzu\Parsers\Parser;
|
use Misuzu\Parsers\Parser;
|
||||||
|
use Carbon\CarbonImmutable;
|
||||||
|
use Index\Db\DbResult;
|
||||||
|
|
||||||
class MessageInfo {
|
class MessageInfo {
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
@ -21,7 +21,7 @@ class MessageInfo {
|
||||||
private ?int $deleted,
|
private ?int $deleted,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public static function fromResult(IDbResult $result): MessageInfo {
|
public static function fromResult(DbResult $result): MessageInfo {
|
||||||
return new MessageInfo(
|
return new MessageInfo(
|
||||||
messageId: $result->getString(0),
|
messageId: $result->getString(0),
|
||||||
ownerId: $result->getString(1),
|
ownerId: $result->getString(1),
|
||||||
|
@ -98,8 +98,8 @@ class MessageInfo {
|
||||||
return $this->created;
|
return $this->created;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCreatedAt(): DateTime {
|
public function getCreatedAt(): CarbonImmutable {
|
||||||
return DateTime::fromUnixTimeSeconds($this->created);
|
return CarbonImmutable::createFromTimestampUTC($this->created);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isSent(): bool {
|
public function isSent(): bool {
|
||||||
|
@ -110,8 +110,8 @@ class MessageInfo {
|
||||||
return $this->sent;
|
return $this->sent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSentAt(): ?DateTime {
|
public function getSentAt(): ?CarbonImmutable {
|
||||||
return $this->sent === null ? null : DateTime::fromUnixTimeSeconds($this->sent);
|
return $this->sent === null ? null : CarbonImmutable::createFromTimestampUTC($this->sent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isRead(): bool {
|
public function isRead(): bool {
|
||||||
|
@ -122,8 +122,8 @@ class MessageInfo {
|
||||||
return $this->read;
|
return $this->read;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getReadAt(): ?DateTime {
|
public function getReadAt(): ?CarbonImmutable {
|
||||||
return $this->read === null ? null : DateTime::fromUnixTimeSeconds($this->read);
|
return $this->read === null ? null : CarbonImmutable::createFromTimestampUTC($this->read);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isDeleted(): bool {
|
public function isDeleted(): bool {
|
||||||
|
@ -134,8 +134,8 @@ class MessageInfo {
|
||||||
return $this->deleted;
|
return $this->deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDeletedAt(): ?DateTime {
|
public function getDeletedAt(): ?CarbonImmutable {
|
||||||
return $this->deleted === null ? null : DateTime::fromUnixTimeSeconds($this->deleted);
|
return $this->deleted === null ? null : CarbonImmutable::createFromTimestampUTC($this->deleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDisplayTime(): int {
|
public function getDisplayTime(): int {
|
||||||
|
@ -144,7 +144,7 @@ class MessageInfo {
|
||||||
return $this->getCreatedTime();
|
return $this->getCreatedTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDisplayAt(): DateTime {
|
public function getDisplayAt(): CarbonImmutable {
|
||||||
if($this->isSent())
|
if($this->isSent())
|
||||||
return $this->getSentAt();
|
return $this->getSentAt();
|
||||||
return $this->getCreatedAt();
|
return $this->getCreatedAt();
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu\Messages;
|
namespace Misuzu\Messages;
|
||||||
|
|
||||||
use Index\Data\IDbConnection;
|
use Index\Db\DbConnection;
|
||||||
|
|
||||||
class MessagesContext {
|
class MessagesContext {
|
||||||
private MessagesDatabase $database;
|
private MessagesDatabase $database;
|
||||||
|
|
||||||
public function __construct(IDbConnection $dbConn) {
|
public function __construct(DbConnection $dbConn) {
|
||||||
$this->database = new MessagesDatabase($dbConn);
|
$this->database = new MessagesDatabase($dbConn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue