2025-03-24 22:15:37 +00:00
|
|
|
<?php
|
|
|
|
namespace Misuzu\Users;
|
|
|
|
|
|
|
|
use RuntimeException;
|
|
|
|
use Misuzu\SiteInfo;
|
|
|
|
use Misuzu\Auth\AuthInfo;
|
|
|
|
use Misuzu\Users\Assets\UserAvatarAsset;
|
|
|
|
use Index\XArray;
|
|
|
|
use Index\Colour\{Colour,ColourRgb};
|
|
|
|
use Index\Http\{HttpRequest,HttpResponseBuilder};
|
2025-03-25 13:51:39 +00:00
|
|
|
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
|
|
|
use Index\Http\Routing\AccessControl\AccessControl;
|
|
|
|
use Index\Http\Routing\Routes\ExactRoute;
|
2025-03-24 22:15:37 +00:00
|
|
|
use Index\Urls\UrlRegistry;
|
|
|
|
|
|
|
|
final class UsersApiRoutes implements RouteHandler {
|
|
|
|
use RouteHandlerCommon;
|
|
|
|
|
|
|
|
public function __construct(
|
|
|
|
private SiteInfo $siteInfo,
|
|
|
|
private UrlRegistry $urls,
|
|
|
|
private UsersContext $usersCtx,
|
|
|
|
private AuthInfo $authInfo,
|
|
|
|
) {}
|
|
|
|
|
|
|
|
/** @return int|mixed[] */
|
2025-03-25 13:51:39 +00:00
|
|
|
#[AccessControl]
|
|
|
|
#[ExactRoute('GET', '/api/v1/me')]
|
|
|
|
public function getMe(HttpResponseBuilder $response, HttpRequest $request): int|array {
|
|
|
|
$response->setHeader('Cache-Control', 'no-store');
|
2025-03-24 22:15:37 +00:00
|
|
|
|
|
|
|
$openid = $this->authInfo->hasScope('openid');
|
|
|
|
if(!$openid
|
|
|
|
&& !$this->authInfo->hasScope('identify')
|
|
|
|
&& !$this->authInfo->hasScope('beans'))
|
|
|
|
return 403;
|
|
|
|
|
|
|
|
$includeProfile = !$openid || $this->authInfo->hasScope('profile');
|
|
|
|
$includeEMail = $openid
|
|
|
|
? $this->authInfo->hasScope('email')
|
|
|
|
: $this->authInfo->hasScope('identify:email');
|
|
|
|
|
|
|
|
try {
|
|
|
|
$userInfo = $this->usersCtx->getUserInfo($this->authInfo->userId, UsersData::GET_USER_ID);
|
|
|
|
} catch(RuntimeException) {
|
|
|
|
return 404;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: there should be some kinda privacy controls for users
|
|
|
|
|
|
|
|
$output = ['id' => $userInfo->id];
|
|
|
|
|
|
|
|
if($includeProfile) {
|
|
|
|
$output['name'] = $userInfo->name;
|
|
|
|
|
|
|
|
$colour = $this->usersCtx->getUserColour($userInfo);
|
|
|
|
if($colour->inherits) {
|
|
|
|
$colourRaw = null;
|
|
|
|
$colourCSS = (string)$colour;
|
|
|
|
} else {
|
|
|
|
$colourRaw = Colour::toRawRgb($colour);
|
|
|
|
$colourCSS = (string)ColourRgb::convert($colour);
|
|
|
|
}
|
|
|
|
|
|
|
|
$output['colour_raw'] = $colourRaw;
|
|
|
|
$output['colour_css'] = $colourCSS;
|
|
|
|
$output['country_code'] = $userInfo->countryCode;
|
|
|
|
|
|
|
|
if($this->usersCtx->hasActiveBan($userInfo)) {
|
|
|
|
$output['rank'] = 0;
|
|
|
|
$output['roles'] = ['x-banned'];
|
|
|
|
} else {
|
|
|
|
$roles = XArray::select(
|
|
|
|
$this->usersCtx->roles->getRoles(userInfo: $userInfo, hasString: true, orderByRank: true),
|
|
|
|
fn($roleInfo) => $roleInfo->string,
|
|
|
|
);
|
|
|
|
|
|
|
|
$output['rank'] = $this->usersCtx->getUserRank($userInfo);
|
|
|
|
if(!empty($roles))
|
|
|
|
$output['roles'] = $roles;
|
|
|
|
if($userInfo->super)
|
|
|
|
$output['is_super'] = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!empty($userInfo->title))
|
|
|
|
$output['title'] = $userInfo->title;
|
|
|
|
|
|
|
|
$output['created_at'] = $userInfo->createdAt->toIso8601ZuluString();
|
|
|
|
if($userInfo->lastActiveTime !== null)
|
|
|
|
$output['last_active_at'] = $userInfo->lastActiveAt->toIso8601ZuluString();
|
|
|
|
|
|
|
|
$baseUrl = $this->siteInfo->url;
|
|
|
|
$output['profile_url'] = $baseUrl . $this->urls->format('user-profile', ['user' => $userInfo->id]);
|
|
|
|
$output['avatar_url'] = $baseUrl . $this->urls->format('user-avatar', ['user' => $userInfo->id]);
|
|
|
|
|
|
|
|
if($userInfo->deleted)
|
|
|
|
$output['is_deleted'] = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if($includeEMail)
|
|
|
|
$output['email'] = $userInfo->emailAddress;
|
|
|
|
|
|
|
|
return $output;
|
|
|
|
}
|
|
|
|
}
|