misuzu/src/MisuzuContext.php
2023-08-31 21:33:34 +00:00

371 lines
12 KiB
PHP

<?php
namespace Misuzu;
use Index\Data\IDbConnection;
use Index\Data\Migration\IDbMigrationRepo;
use Index\Data\Migration\DbMigrationManager;
use Index\Data\Migration\FsDbMigrationRepo;
use Index\Http\HttpFx;
use Index\Http\HttpRequest;
use Index\Routing\Router;
use Sasae\SasaeEnvironment;
use Misuzu\Template;
use Misuzu\Auth\AuthInfo;
use Misuzu\Auth\AuthTokenPacker;
use Misuzu\Auth\LoginAttempts;
use Misuzu\Auth\RecoveryTokens;
use Misuzu\Auth\Sessions;
use Misuzu\Auth\TwoFactorAuthSessions;
use Misuzu\AuditLog\AuditLog;
use Misuzu\Changelog\Changelog;
use Misuzu\Changelog\ChangelogRoutes;
use Misuzu\Comments\Comments;
use Misuzu\Config\IConfig;
use Misuzu\Counters\Counters;
use Misuzu\Emoticons\Emotes;
use Misuzu\Forum\Forum;
use Misuzu\Home\HomeRoutes;
use Misuzu\Info\InfoRoutes;
use Misuzu\News\News;
use Misuzu\News\NewsRoutes;
use Misuzu\Perms\Permissions;
use Misuzu\Profile\ProfileFields;
use Misuzu\Satori\SatoriRoutes;
use Misuzu\SharpChat\SharpChatRoutes;
use Misuzu\Users\Bans;
use Misuzu\Users\BanInfo;
use Misuzu\Users\ModNotes;
use Misuzu\Users\Roles;
use Misuzu\Users\Users;
use Misuzu\Users\UserInfo;
use Misuzu\Users\Warnings;
use Misuzu\Users\Assets\AssetsRoutes;
// this class should function as the root for everything going forward
// no more magical static classes that are just kind of assumed to exist
// it currently looks Pretty Messy, but most everything else will be holding instances of other classes
// instances of certain classes should only be made as needed,
// dunno if i want null checks some maybe some kind of init func should be called first like is the case
// with the http shit
class MisuzuContext {
private IDbConnection $dbConn;
private IConfig $config;
private HttpFx $router;
private AuditLog $auditLog;
private Emotes $emotes;
private Changelog $changelog;
private News $news;
private Comments $comments;
private LoginAttempts $loginAttempts;
private RecoveryTokens $recoveryTokens;
private ModNotes $modNotes;
private Bans $bans;
private Warnings $warnings;
private TwoFactorAuthSessions $tfaSessions;
private Roles $roles;
private Users $users;
private Sessions $sessions;
private Counters $counters;
private ProfileFields $profileFields;
private Forum $forum;
private Permissions $perms;
private AuthInfo $authInfo;
private SasaeEnvironment $templating;
public function __construct(IDbConnection $dbConn, IConfig $config) {
$this->dbConn = $dbConn;
$this->config = $config;
$this->perms = new Permissions($this->dbConn);
$this->authInfo = new AuthInfo($this->perms);
$this->auditLog = new AuditLog($this->dbConn);
$this->bans = new Bans($this->dbConn);
$this->changelog = new Changelog($this->dbConn);
$this->comments = new Comments($this->dbConn);
$this->counters = new Counters($this->dbConn);
$this->emotes = new Emotes($this->dbConn);
$this->forum = new Forum($this->dbConn);
$this->loginAttempts = new LoginAttempts($this->dbConn);
$this->modNotes = new ModNotes($this->dbConn);
$this->news = new News($this->dbConn);
$this->profileFields = new ProfileFields($this->dbConn);
$this->recoveryTokens = new RecoveryTokens($this->dbConn);
$this->roles = new Roles($this->dbConn);
$this->sessions = new Sessions($this->dbConn);
$this->tfaSessions = new TwoFactorAuthSessions($this->dbConn);
$this->users = new Users($this->dbConn);
$this->warnings = new Warnings($this->dbConn);
}
public function getDbConn(): IDbConnection {
return $this->dbConn;
}
public function getDbQueryCount(): int {
$result = $this->dbConn->query('SHOW SESSION STATUS LIKE "Questions"');
return $result->next() ? $result->getInteger(1) : 0;
}
public function createMigrationManager(): DbMigrationManager {
return new DbMigrationManager($this->dbConn, 'msz_' . DbMigrationManager::DEFAULT_TABLE);
}
public function createMigrationRepo(): IDbMigrationRepo {
return new FsDbMigrationRepo(MSZ_MIGRATIONS);
}
public function getConfig(): IConfig {
return $this->config;
}
public function getRouter(): Router {
return $this->router->getRouter();
}
public function getEmotes(): Emotes {
return $this->emotes;
}
public function getChangelog(): Changelog {
return $this->changelog;
}
public function getNews(): News {
return $this->news;
}
public function getComments(): Comments {
return $this->comments;
}
public function getAuditLog(): AuditLog {
return $this->auditLog;
}
public function getLoginAttempts(): LoginAttempts {
return $this->loginAttempts;
}
public function getRecoveryTokens(): RecoveryTokens {
return $this->recoveryTokens;
}
public function getModNotes(): ModNotes {
return $this->modNotes;
}
public function getBans(): Bans {
return $this->bans;
}
public function getWarnings(): Warnings {
return $this->warnings;
}
public function getTFASessions(): TwoFactorAuthSessions {
return $this->tfaSessions;
}
public function getRoles(): Roles {
return $this->roles;
}
public function getUsers(): Users {
return $this->users;
}
public function getSessions(): Sessions {
return $this->sessions;
}
public function getCounters(): Counters {
return $this->counters;
}
public function getProfileFields(): ProfileFields {
return $this->profileFields;
}
public function getForum(): Forum {
return $this->forum;
}
public function getPerms(): Permissions {
return $this->perms;
}
public function createAuthTokenPacker(): AuthTokenPacker {
return new AuthTokenPacker($this->config->getString('auth.secret', 'meow'));
}
public function getAuthInfo(): AuthInfo {
return $this->authInfo;
}
// isLoggedIn and getActiveUser are proxied for convenience, supply authInfo to things in the future
public function isLoggedIn(): bool {
return $this->authInfo->isLoggedIn();
}
public function getActiveUser(): ?UserInfo {
return $this->authInfo->getUserInfo();
}
private array $activeBansCache = [];
public function tryGetActiveBan(UserInfo|string|null $userInfo = null): ?BanInfo {
if($userInfo === null) {
if($this->isLoggedIn())
$userInfo = $this->getActiveUser();
else return null;
}
$userId = (string)$userInfo->getId();
if(!array_key_exists($userId, $this->activeBansCache))
$this->activeBansCache[$userId] = $this->bans->tryGetActiveBan($userId);
return $this->activeBansCache[$userId];
}
public function hasActiveBan(UserInfo|string|null $userInfo = null): bool {
return $this->tryGetActiveBan($userInfo) !== null;
}
public function createAuditLog(string $action, array $params = [], UserInfo|string|null $userInfo = null): void {
if($userInfo === null && $this->isLoggedIn())
$userInfo = $this->getActiveUser();
$this->auditLog->createLog(
$userInfo,
$action,
$params,
$_SERVER['REMOTE_ADDR'] ?? '::1',
$_SERVER['COUNTRY_CODE'] ?? 'XX'
);
}
private ?bool $hasManageAccess = null;
public function hasManageAccess(): bool {
$this->hasManageAccess ??= $this->authInfo->isLoggedIn() && !$this->hasActiveBan()
&& $this->getAuthInfo()->getPerms('global')->check(Perm::G_IS_JANITOR);
return $this->hasManageAccess;
}
public function getWebAssetInfo(): ?object {
return json_decode(file_get_contents(MSZ_ASSETS . '/current.json'));
}
private ?string $chatUrl = null;
public function getChatURL(): string {
$this->chatUrl ??= $this->config->getString('sockChat.chatPath.normal');
return $this->chatUrl;
}
public function startTemplating(): void {
$globals = $this->config->getValues([
['site.name:s', 'Misuzu', 'site_name'],
['site.desc:s', '', 'site_description'],
['site.url:s', '', 'site_url'],
['eeprom.path:s', '', 'eeprom_path'],
['eeprom.app:s', '', 'eeprom_app'],
]);
$authInfo = $this->getAuthInfo();
$globals['assets'] = $this->getWebAssetInfo();
$globals['auth_info'] = $authInfo;
$globals['active_ban_info'] = $this->tryGetActiveBan();
$globals['display_timings_info'] = MSZ_DEBUG
|| $authInfo->getPerms('global')->check(Perm::G_TIMINGS_VIEW);
$templating = new SasaeEnvironment(
MSZ_TEMPLATES,
cache: MSZ_DEBUG ? null : ['Misuzu', GitInfo::hash(true)],
debug: MSZ_DEBUG
);
$templating->addExtension(new MisuzuSasaeExtension($this));
$templating->addGlobal('globals', $globals);
Template::init($templating);
}
public function startRouter(): void {
$this->router = new HttpFx;
$this->router->use('/', function($response) {
$response->setPoweredBy('Misuzu');
});
$this->registerErrorPages();
$this->registerHttpRoutes();
}
public function dispatchRouter(?HttpRequest $request = null): void {
$this->router->dispatch($request);
}
private function registerErrorPages(): void {
$this->router->addErrorHandler(400, function($response) {
$response->setContent(Template::renderRaw('errors.400'));
});
$this->router->addErrorHandler(403, function($response) {
$response->setContent(Template::renderRaw('errors.403'));
});
$this->router->addErrorHandler(404, function($response) {
$response->setContent(Template::renderRaw('errors.404'));
});
$this->router->addErrorHandler(500, function($response) {
$response->setContent(file_get_contents(MSZ_TEMPLATES . '/500.html'));
});
$this->router->addErrorHandler(503, function($response) {
$response->setContent(file_get_contents(MSZ_TEMPLATES . '/503.html'));
});
}
private function registerHttpRoutes(): void {
new HomeRoutes(
$this->router, $this->config, $this->dbConn, $this->authInfo,
$this->changelog, $this->comments, $this->counters, $this->news,
$this->users
);
new AssetsRoutes($this->router, $this->authInfo, $this->bans, $this->users);
new InfoRoutes($this->router);
new NewsRoutes(
$this->router, $this->config, $this->authInfo,
$this->news, $this->users, $this->comments
);
new ChangelogRoutes(
$this->router, $this->config, $this->changelog,
$this->users, $this->authInfo, $this->comments
);
new SharpChatRoutes(
$this->router, $this->config->scopeTo('sockChat'),
$this->bans, $this->emotes, $this->users,
$this->sessions, $this->perms, $this->authInfo,
$this->createAuthTokenPacker(...)
);
new SatoriRoutes(
$this->dbConn, $this->config->scopeTo('satori'),
$this->router, $this->users, $this->profileFields
);
// below is still only otherwise available as stinky php files
$this->router->get('/auth.php', function($response, $request) {
$response->redirect(url([
'logout' => 'auth-logout',
'reset' => 'auth-reset',
'forgot' => 'auth-forgot',
'register' => 'auth-register',
][$request->getParam('m')] ?? 'auth-login'), true);
});
$this->router->get('/settings.php', function($response) {
$response->redirect(url('settings-index'), true);
});
}
}