mirror of
https://github.com/AnimeThemes/animethemes-server.git
synced 2026-07-11 01:24:46 +02:00
feat: added a proper function to the token callback controller (#738)
This commit is contained in:
+7
-1
@@ -253,8 +253,13 @@ DISCORD_BOT_API_URL=
|
||||
DISCORD_BOT_API_KEY=
|
||||
DB_UPDATES_DISCORD_CHANNEL=
|
||||
ADMIN_DISCORD_CHANNEL=
|
||||
MAL_CLIENT_ID=null
|
||||
OPENAI_BEARER_TOKEN=null
|
||||
MAL_CLIENT_ID=null
|
||||
MAL_CLIENT_SECRET=null
|
||||
MAL_REDIRECT_URI=null
|
||||
ANILIST_CLIENT_ID=null
|
||||
ANILIST_CLIENT_SECRET=null
|
||||
ANILIST_REDIRECT_URI=null
|
||||
|
||||
# session
|
||||
SESSION_DRIVER=database
|
||||
@@ -294,3 +299,4 @@ WEB_PATH=
|
||||
# wiki
|
||||
WIKI_LOGIN=http://localhost/login
|
||||
WIKI_RESET_PASSWORD=http://localhost/reset-password
|
||||
WIKI_EXTERNAL_PROFILE=http://localhost/external
|
||||
|
||||
+7
-1
@@ -251,8 +251,13 @@ DISCORD_BOT_API_URL=
|
||||
DISCORD_BOT_API_KEY=
|
||||
DB_UPDATES_DISCORD_CHANNEL=
|
||||
ADMIN_DISCORD_CHANNEL=
|
||||
MAL_CLIENT_ID=null
|
||||
OPENAI_BEARER_TOKEN=null
|
||||
MAL_CLIENT_ID=null
|
||||
MAL_CLIENT_SECRET=null
|
||||
MAL_REDIRECT_URI=null
|
||||
ANILIST_CLIENT_ID=null
|
||||
ANILIST_CLIENT_SECRET=null
|
||||
ANILIST_REDIRECT_URI=null
|
||||
|
||||
# session
|
||||
SESSION_DRIVER=database
|
||||
@@ -292,3 +297,4 @@ WEB_PATH=
|
||||
# wiki
|
||||
WIKI_LOGIN=http://localhost/login
|
||||
WIKI_RESET_PASSWORD=http://localhost/reset-password
|
||||
WIKI_EXTERNAL_PROFILE=http://localhost/external
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Models\List;
|
||||
|
||||
use App\Enums\Models\List\ExternalProfileSite;
|
||||
use App\Models\Wiki\Anime;
|
||||
use App\Models\Wiki\ExternalResource;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class BaseStoreExternalProfileAction.
|
||||
*/
|
||||
abstract class BaseStoreExternalProfileAction
|
||||
{
|
||||
protected Collection $resources;
|
||||
|
||||
/**
|
||||
* Preload the resources for performance proposals.
|
||||
*
|
||||
* @param ExternalProfileSite $profileSite
|
||||
* @param array $entries
|
||||
* @return void
|
||||
*/
|
||||
protected function preloadResources(ExternalProfileSite $profileSite, array $entries): void
|
||||
{
|
||||
$externalResources = ExternalResource::query()
|
||||
->where(ExternalResource::ATTRIBUTE_SITE, $profileSite->getResourceSite()->value)
|
||||
->whereIn(ExternalResource::ATTRIBUTE_EXTERNAL_ID, Arr::pluck($entries, 'external_id'))
|
||||
->with(ExternalResource::RELATION_ANIME)
|
||||
->get()
|
||||
->mapWithKeys(fn (ExternalResource $resource) => [$resource->external_id => $resource->anime]);
|
||||
|
||||
$this->resources = $externalResources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the animes by the external id.
|
||||
*
|
||||
* @param int $externalId
|
||||
* @return Collection<int, Anime>
|
||||
*/
|
||||
protected function getAnimesByExternalId(int $externalId): Collection
|
||||
{
|
||||
return $this->resources[$externalId] ?? new Collection();
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@ use Illuminate\Support\Arr;
|
||||
*/
|
||||
abstract class BaseExternalEntryAction
|
||||
{
|
||||
protected ?array $response = null;
|
||||
|
||||
/**
|
||||
* Create a new action instance.
|
||||
*
|
||||
@@ -54,6 +56,13 @@ abstract class BaseExternalEntryAction
|
||||
return Arr::get($this->profileParameters, 'name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the id of the external user.
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
abstract public function getId(): ?int;
|
||||
|
||||
/**
|
||||
* Get the entries of the response.
|
||||
*
|
||||
@@ -64,7 +73,7 @@ abstract class BaseExternalEntryAction
|
||||
/**
|
||||
* Make the request to the external api.
|
||||
*
|
||||
* @return array|null
|
||||
* @return static
|
||||
*/
|
||||
abstract public function makeRequest(): ?array;
|
||||
abstract protected function makeRequest(): static;
|
||||
}
|
||||
|
||||
+26
-10
@@ -4,6 +4,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Models\List\ExternalProfile\ExternalEntry;
|
||||
|
||||
use App\Models\List\External\ExternalToken;
|
||||
use Firebase\JWT\JWT;
|
||||
use Firebase\JWT\Key;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class BaseExternalEntryTokenAction
|
||||
*
|
||||
@@ -11,23 +16,24 @@ namespace App\Actions\Models\List\ExternalProfile\ExternalEntry;
|
||||
*/
|
||||
abstract class BaseExternalEntryTokenAction
|
||||
{
|
||||
protected ?array $response = null;
|
||||
protected ?int $id = null;
|
||||
|
||||
/**
|
||||
* Create a new action instance.
|
||||
*
|
||||
* @param array $parameters
|
||||
*/
|
||||
public function __construct(protected array $parameters)
|
||||
public function __construct(protected ExternalToken $token)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the username of the profile.
|
||||
* Get the id of the external user.
|
||||
*
|
||||
* @return string
|
||||
* @return int|null
|
||||
*/
|
||||
public function getUsername(): string
|
||||
public function getId(): ?int
|
||||
{
|
||||
return ''; // TODO
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,7 +43,17 @@ abstract class BaseExternalEntryTokenAction
|
||||
*/
|
||||
public function getToken(): string
|
||||
{
|
||||
return ''; // TODO
|
||||
return $this->token->access_token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the username.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getUsername(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,7 +66,7 @@ abstract class BaseExternalEntryTokenAction
|
||||
/**
|
||||
* Make the request to the external api.
|
||||
*
|
||||
* @return array|null
|
||||
* @return static
|
||||
*/
|
||||
abstract public function makeRequest(): ?array;
|
||||
abstract protected function makeRequest(): static;
|
||||
}
|
||||
|
||||
+34
-18
@@ -26,39 +26,55 @@ class AnilistExternalEntryAction extends BaseExternalEntryAction
|
||||
public function getEntries(): array
|
||||
{
|
||||
$entries = [];
|
||||
$response = $this->makeRequest();
|
||||
|
||||
if ($response !== null) {
|
||||
$favorites = Arr::map(Arr::get($response, 'data.User.favourites.anime.nodes'), fn ($value) => $value['id']);
|
||||
$lists = Arr::where(Arr::get($response, 'data.MediaListCollection.lists'), fn ($value) => $value['isCustomList'] === false);
|
||||
if ($this->response === null) {
|
||||
$this->makeRequest();
|
||||
}
|
||||
|
||||
foreach ($lists as $list) {
|
||||
foreach (Arr::get($list, 'entries') as $entry) {
|
||||
$entryId = intval(Arr::get($entry, 'media.id'));
|
||||
$entries[] = [
|
||||
ExternalResource::ATTRIBUTE_EXTERNAL_ID => $entryId,
|
||||
ExternalEntry::ATTRIBUTE_SCORE => Arr::get($entry, 'score'),
|
||||
ExternalEntry::ATTRIBUTE_WATCH_STATUS => ExternalEntryWatchStatus::getAnilistMapping(Arr::get($entry, 'status'))->value,
|
||||
ExternalEntry::ATTRIBUTE_IS_FAVORITE => in_array($entryId, $favorites),
|
||||
];
|
||||
}
|
||||
$favorites = Arr::map(Arr::get($this->response, 'data.User.favourites.anime.nodes'), fn ($value) => $value['id']);
|
||||
$lists = Arr::where(Arr::get($this->response, 'data.MediaListCollection.lists'), fn ($value) => $value['isCustomList'] === false);
|
||||
|
||||
foreach ($lists as $list) {
|
||||
foreach (Arr::get($list, 'entries') as $entry) {
|
||||
$entryId = intval(Arr::get($entry, 'media.id'));
|
||||
$entries[] = [
|
||||
ExternalResource::ATTRIBUTE_EXTERNAL_ID => $entryId,
|
||||
ExternalEntry::ATTRIBUTE_SCORE => Arr::get($entry, 'score'),
|
||||
ExternalEntry::ATTRIBUTE_WATCH_STATUS => ExternalEntryWatchStatus::getAnilistMapping(Arr::get($entry, 'status'))->value,
|
||||
ExternalEntry::ATTRIBUTE_IS_FAVORITE => in_array($entryId, $favorites),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the id of the external user.
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getId(): ?int
|
||||
{
|
||||
if ($this->response === null) {
|
||||
$this->makeRequest();
|
||||
}
|
||||
|
||||
return Arr::get($this->response, 'data.User.id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the request to the external api.
|
||||
*
|
||||
* @return array|null
|
||||
* @return static
|
||||
*/
|
||||
public function makeRequest(): ?array
|
||||
protected function makeRequest(): static
|
||||
{
|
||||
try {
|
||||
$query = '
|
||||
query($userName: String) {
|
||||
User(name: $userName) {
|
||||
id
|
||||
favourites {
|
||||
anime {
|
||||
nodes {
|
||||
@@ -88,14 +104,14 @@ class AnilistExternalEntryAction extends BaseExternalEntryAction
|
||||
'userName' => $this->getUsername(),
|
||||
];
|
||||
|
||||
$response = Http::post('https://graphql.anilist.co', [
|
||||
$this->response = Http::post('https://graphql.anilist.co', [
|
||||
'query' => $query,
|
||||
'variables' => $variables,
|
||||
])
|
||||
->throw()
|
||||
->json();
|
||||
|
||||
return $response;
|
||||
return $this;
|
||||
|
||||
} catch (RequestException $e) {
|
||||
Log::error($e->getMessage());
|
||||
|
||||
+60
-9
@@ -8,8 +8,12 @@ use App\Actions\Models\List\ExternalProfile\ExternalEntry\BaseExternalEntryToken
|
||||
use App\Enums\Models\List\ExternalEntryWatchStatus;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
use App\Models\Wiki\ExternalResource;
|
||||
use Exception;
|
||||
use Firebase\JWT\JWT;
|
||||
use Firebase\JWT\Key;
|
||||
use Illuminate\Http\Client\RequestException;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
@@ -26,9 +30,12 @@ class AnilistExternalEntryTokenAction extends BaseExternalEntryTokenAction
|
||||
public function getEntries(): array
|
||||
{
|
||||
$entries = [];
|
||||
$response = $this->makeRequest();
|
||||
|
||||
if ($response !== null) {
|
||||
if ($this->response === null) {
|
||||
$this->makeRequest();
|
||||
}
|
||||
|
||||
if ($response = $this->response) {
|
||||
$lists = Arr::where(Arr::get($response, 'data.MediaListCollection.lists'), fn ($value) => $value['isCustomList'] === false);
|
||||
|
||||
foreach ($lists as $list) {
|
||||
@@ -47,17 +54,61 @@ class AnilistExternalEntryTokenAction extends BaseExternalEntryTokenAction
|
||||
return $entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the username.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getUsername(): ?string
|
||||
{
|
||||
if ($this->response === null) {
|
||||
$this->makeRequest();
|
||||
}
|
||||
|
||||
return Arr::get($this->response, 'data.Viewer.name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the id of the external user.
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getId(): ?int
|
||||
{
|
||||
if ($this->id !== null) {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
// TODO: This should be tested.
|
||||
try {
|
||||
$decoded = JWT::decode($this->getToken(), new Key(Config::get('services.anilist.client_secret'), 'HS256'));
|
||||
|
||||
$decodedArray = json_decode(json_encode($decoded), true);
|
||||
|
||||
$this->id = Arr::get($decodedArray, 'id');
|
||||
|
||||
return $this->id;
|
||||
} catch (Exception $e) {
|
||||
Log::error($e->getMessage());
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the request to the external api.
|
||||
*
|
||||
* @return array|null
|
||||
* @return static
|
||||
*/
|
||||
public function makeRequest(): ?array
|
||||
protected function makeRequest(): static
|
||||
{
|
||||
try {
|
||||
$query = '
|
||||
query($userName: String) {
|
||||
MediaListCollection(userName: $userName, type: ANIME) {
|
||||
query($userId: Int) {
|
||||
Viewer {
|
||||
name
|
||||
}
|
||||
MediaListCollection(userId: $userId, type: ANIME) {
|
||||
lists {
|
||||
name
|
||||
status
|
||||
@@ -77,10 +128,10 @@ class AnilistExternalEntryTokenAction extends BaseExternalEntryTokenAction
|
||||
';
|
||||
|
||||
$variables = [
|
||||
'userName' => $this->getUsername(),
|
||||
'userId' => $this->getId(),
|
||||
];
|
||||
|
||||
$response = Http::withToken($this->getToken())
|
||||
$this->response = Http::withToken($this->getToken())
|
||||
->post('https://graphql.anilist.co', [
|
||||
'query' => $query,
|
||||
'variables' => $variables,
|
||||
@@ -88,7 +139,7 @@ class AnilistExternalEntryTokenAction extends BaseExternalEntryTokenAction
|
||||
->throw()
|
||||
->json();
|
||||
|
||||
return $response;
|
||||
return $this;
|
||||
|
||||
} catch (RequestException $e) {
|
||||
Log::error($e->getMessage());
|
||||
|
||||
@@ -11,5 +11,18 @@ use App\Models\List\External\ExternalToken;
|
||||
*/
|
||||
abstract class BaseExternalTokenAction
|
||||
{
|
||||
abstract public function store(): ExternalToken;
|
||||
/**
|
||||
* Create a new action instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the authorization code to get the tokens and store them.
|
||||
*
|
||||
* @param string $code
|
||||
* @return ExternalToken|null
|
||||
*/
|
||||
abstract public function store(string $code): ?ExternalToken;
|
||||
}
|
||||
|
||||
+35
-8
@@ -6,6 +6,11 @@ namespace App\Actions\Models\List\ExternalProfile\ExternalToken\Site;
|
||||
|
||||
use App\Actions\Models\List\ExternalProfile\ExternalToken\BaseExternalTokenAction;
|
||||
use App\Models\List\External\ExternalToken;
|
||||
use Illuminate\Http\Client\RequestException;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class AnilistExternalTokenAction.
|
||||
@@ -13,18 +18,40 @@ use App\Models\List\External\ExternalToken;
|
||||
class AnilistExternalTokenAction extends BaseExternalTokenAction
|
||||
{
|
||||
/**
|
||||
* Create a new action instance.
|
||||
* Use the authorization code to get the tokens and store them.
|
||||
*
|
||||
* @param string $code
|
||||
* @return ExternalToken|null
|
||||
*/
|
||||
public function __construct(protected string $code)
|
||||
public function store(string $code): ?ExternalToken
|
||||
{
|
||||
}
|
||||
try {
|
||||
$response = Http::acceptJson()
|
||||
->asForm()
|
||||
->post('https://anilist.co/api/v2/oauth/token', [
|
||||
'grant_type' => 'authorization_code',
|
||||
'client_id' => Config::get('services.anilist.client_id'),
|
||||
'client_secret' => Config::get('services.anilist.client_secret'),
|
||||
'redirect_uri' => Config::get('services.anilist.redirect_uri'),
|
||||
'code' => $code,
|
||||
])
|
||||
->throw()
|
||||
->json();
|
||||
|
||||
public function store(): ExternalToken
|
||||
{
|
||||
// TODO: Make a request to the AniList API to get the access and the refresh tokens
|
||||
// and return the external token created.
|
||||
return new ExternalToken();
|
||||
$token = Arr::get($response, 'access_token');
|
||||
|
||||
if ($token !== null) {
|
||||
return ExternalToken::query()->create([
|
||||
ExternalToken::ATTRIBUTE_ACCESS_TOKEN => $token,
|
||||
]);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
} catch (RequestException $e) {
|
||||
Log::error($e->getMessage());
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Models\List\ExternalProfile;
|
||||
|
||||
use App\Actions\Http\Api\StoreAction;
|
||||
use App\Actions\Models\List\BaseStoreExternalProfileAction;
|
||||
use App\Actions\Models\List\ExternalProfile\ExternalEntry\BaseExternalEntryTokenAction;
|
||||
use App\Actions\Models\List\ExternalProfile\ExternalEntry\Site\AnilistExternalEntryTokenAction;
|
||||
use App\Enums\Models\List\ExternalProfileSite;
|
||||
use App\Enums\Models\List\ExternalProfileVisibility;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
use App\Models\List\External\ExternalToken;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use Error;
|
||||
use Exception;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class StoreExternalProfileTokenAction.
|
||||
*/
|
||||
class StoreExternalProfileTokenAction extends BaseStoreExternalProfileAction
|
||||
{
|
||||
protected Collection $resources;
|
||||
|
||||
/**
|
||||
* Find or store external profile and its entries given determined external token.
|
||||
*
|
||||
* @param ExternalToken $token
|
||||
* @param array $parameters
|
||||
* @return ExternalProfile
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function findOrCreate(ExternalToken $token, array $parameters): ExternalProfile
|
||||
{
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
|
||||
$site = ExternalProfileSite::fromLocalizedName(Arr::get($parameters, 'site'));
|
||||
|
||||
$action = $this->getActionClass($site, $token);
|
||||
|
||||
if ($action === null) {
|
||||
throw new Error("Undefined action for site {$site->localize()}"); // TODO: check if it is working
|
||||
}
|
||||
|
||||
$userId = $action->getId();
|
||||
|
||||
// TODO: if the profile already exists, the list should be synced.
|
||||
$profile = $this->searchForUserId($userId, $site, $action, $parameters);
|
||||
|
||||
$entries = $action->getEntries();
|
||||
|
||||
$this->preloadResources($site, $entries);
|
||||
|
||||
$token->externalprofile()->associate($profile);
|
||||
|
||||
$externalEntries = [];
|
||||
foreach ($entries as $entry) {
|
||||
$externalId = Arr::get($entry, 'external_id');
|
||||
|
||||
foreach ($this->getAnimesByExternalId($externalId) as $anime) {
|
||||
$externalEntries[] = [
|
||||
ExternalEntry::ATTRIBUTE_SCORE => Arr::get($entry, ExternalEntry::ATTRIBUTE_SCORE),
|
||||
ExternalEntry::ATTRIBUTE_IS_FAVORITE => Arr::get($entry, ExternalEntry::ATTRIBUTE_IS_FAVORITE),
|
||||
ExternalEntry::ATTRIBUTE_WATCH_STATUS => Arr::get($entry, ExternalEntry::ATTRIBUTE_WATCH_STATUS),
|
||||
ExternalEntry::ATTRIBUTE_ANIME => $anime->getKey(),
|
||||
ExternalEntry::ATTRIBUTE_PROFILE => $profile->getKey(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
ExternalEntry::insert($externalEntries);
|
||||
|
||||
DB::commit();
|
||||
|
||||
return $profile;
|
||||
|
||||
} catch (Exception $e) {
|
||||
Log::error($e->getMessage());
|
||||
|
||||
DB::rollBack();
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find or create the profile for a userId and site.
|
||||
*
|
||||
* @param int $userId
|
||||
* @param ExternalProfileSite $site
|
||||
* @param BaseExternalEntryTokenAction $action
|
||||
* @param array $parameters
|
||||
* @return ExternalProfile|null
|
||||
*/
|
||||
protected function searchForUserId(int $userId, ExternalProfileSite $site, BaseExternalEntryTokenAction $action, array $parameters): ?ExternalProfile
|
||||
{
|
||||
$claimedProfile = ExternalProfile::query()
|
||||
->where(ExternalProfile::ATTRIBUTE_EXTERNAL_USER_ID, $userId)
|
||||
->where(ExternalProfile::ATTRIBUTE_SITE, $site->value)
|
||||
->whereHas(ExternalProfile::RELATION_USER)
|
||||
->first();
|
||||
|
||||
if ($claimedProfile instanceof ExternalProfile) {
|
||||
return $claimedProfile;
|
||||
}
|
||||
|
||||
$unclaimedProfile = ExternalProfile::query()
|
||||
->where(ExternalProfile::ATTRIBUTE_EXTERNAL_USER_ID, $userId)
|
||||
->where(ExternalProfile::ATTRIBUTE_SITE, $site->value)
|
||||
->whereDoesntHave(ExternalProfile::RELATION_USER)
|
||||
->first();
|
||||
|
||||
if ($unclaimedProfile instanceof ExternalProfile) {
|
||||
$unclaimedProfile->update([
|
||||
ExternalProfile::ATTRIBUTE_USER => Arr::get($parameters, ExternalProfile::ATTRIBUTE_USER),
|
||||
ExternalProfile::ATTRIBUTE_NAME => $action->getUsername(),
|
||||
ExternalProfile::ATTRIBUTE_VISIBILITY => ExternalProfileVisibility::PRIVATE->value,
|
||||
]);
|
||||
|
||||
return $unclaimedProfile;
|
||||
}
|
||||
|
||||
$storeAction = new StoreAction();
|
||||
|
||||
$profile = $storeAction->store(ExternalProfile::query(), [
|
||||
ExternalProfile::ATTRIBUTE_EXTERNAL_USER_ID => $userId,
|
||||
ExternalProfile::ATTRIBUTE_USER => Arr::get($parameters, ExternalProfile::ATTRIBUTE_USER),
|
||||
ExternalProfile::ATTRIBUTE_NAME => $action->getUsername(),
|
||||
ExternalProfile::ATTRIBUTE_SITE => $site->value,
|
||||
ExternalProfile::ATTRIBUTE_VISIBILITY => ExternalProfileVisibility::PRIVATE->value,
|
||||
]);
|
||||
|
||||
if ($profile instanceof ExternalProfile) {
|
||||
return $profile;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mapping for the entries token class.
|
||||
*
|
||||
* @param ExternalProfileSite $site
|
||||
* @param ExternalToken $token
|
||||
* @return BaseExternalEntryTokenAction|null
|
||||
*/
|
||||
protected function getActionClass(ExternalProfileSite $site, ExternalToken $token): ?BaseExternalEntryTokenAction
|
||||
{
|
||||
return match ($site) {
|
||||
ExternalProfileSite::ANILIST => new AnilistExternalEntryTokenAction($token),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
+22
-46
@@ -5,48 +5,50 @@ declare(strict_types=1);
|
||||
namespace App\Actions\Models\List\ExternalProfile;
|
||||
|
||||
use App\Actions\Http\Api\StoreAction;
|
||||
use App\Actions\Models\List\BaseStoreExternalProfileAction;
|
||||
use App\Actions\Models\List\ExternalProfile\ExternalEntry\BaseExternalEntryAction;
|
||||
use App\Actions\Models\List\ExternalProfile\ExternalEntry\Site\AnilistExternalEntryAction;
|
||||
use App\Enums\Models\List\ExternalProfileSite;
|
||||
use App\Enums\Models\List\ExternalProfileVisibility;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use App\Models\Wiki\Anime;
|
||||
use App\Models\Wiki\ExternalResource;
|
||||
use Error;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class StoreExternalProfileAction.
|
||||
* Class StoreExternalProfileUsernameAction.
|
||||
*/
|
||||
class StoreExternalProfileAction
|
||||
class StoreExternalProfileUsernameAction extends BaseStoreExternalProfileAction
|
||||
{
|
||||
protected Collection $resources;
|
||||
|
||||
/**
|
||||
* Store external profile and its entries.
|
||||
* Find or store an external profile and its entries given determined username.
|
||||
*
|
||||
* @param Builder $builder
|
||||
* @param array $profileParameters
|
||||
* @return Model
|
||||
* @return ExternalProfile
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function store(Builder $builder, array $profileParameters): Model
|
||||
public function findOrCreate(Builder $builder, array $profileParameters): ExternalProfile
|
||||
{
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
|
||||
$storeAction = new StoreAction();
|
||||
|
||||
$profileSite = ExternalProfileSite::fromLocalizedName(Arr::get($profileParameters, 'site'));
|
||||
|
||||
$findProfile = ExternalProfile::query()
|
||||
->where(ExternalProfile::ATTRIBUTE_NAME, Arr::get($profileParameters, 'name'))
|
||||
->where(ExternalProfile::ATTRIBUTE_SITE, $profileSite->value)
|
||||
->first();
|
||||
|
||||
if ($findProfile instanceof ExternalProfile) {
|
||||
return $findProfile;
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
$action = $this->getActionClass($profileSite, $profileParameters);
|
||||
|
||||
if ($action === null) {
|
||||
@@ -57,7 +59,11 @@ class StoreExternalProfileAction
|
||||
|
||||
$this->preloadResources($profileSite, $entries);
|
||||
|
||||
$storeAction = new StoreAction();
|
||||
|
||||
/** @var ExternalProfile $profile */
|
||||
$profile = $storeAction->store($builder, [
|
||||
ExternalProfile::ATTRIBUTE_EXTERNAL_USER_ID => $action->getId(),
|
||||
ExternalProfile::ATTRIBUTE_NAME => Arr::get($profileParameters, ExternalProfile::ATTRIBUTE_NAME),
|
||||
ExternalProfile::ATTRIBUTE_SITE => $profileSite->value,
|
||||
ExternalProfile::ATTRIBUTE_VISIBILITY => ExternalProfileVisibility::fromLocalizedName(Arr::get($profileParameters, ExternalProfile::ATTRIBUTE_VISIBILITY))->value,
|
||||
@@ -106,34 +112,4 @@ class StoreExternalProfileAction
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Preload the resources for performance proposals.
|
||||
*
|
||||
* @param ExternalProfileSite $profileSite
|
||||
* @param array $entries
|
||||
* @return void
|
||||
*/
|
||||
protected function preloadResources(ExternalProfileSite $profileSite, array $entries): void
|
||||
{
|
||||
$externalResources = ExternalResource::query()
|
||||
->where(ExternalResource::ATTRIBUTE_SITE, $profileSite->getResourceSite()->value)
|
||||
->whereIn(ExternalResource::ATTRIBUTE_EXTERNAL_ID, Arr::pluck($entries, 'external_id'))
|
||||
->with(ExternalResource::RELATION_ANIME)
|
||||
->get()
|
||||
->mapWithKeys(fn (ExternalResource $resource) => [$resource->external_id => $resource->anime]);
|
||||
|
||||
$this->resources = $externalResources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the animes by the external id.
|
||||
*
|
||||
* @param int $externalId
|
||||
* @return Collection<int, Anime>
|
||||
*/
|
||||
protected function getAnimesByExternalId(int $externalId): Collection
|
||||
{
|
||||
return $this->resources[$externalId] ?? new Collection();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,9 @@ namespace App\Actions\Models\List\ExternalProfile;
|
||||
use App\Actions\Models\List\ExternalProfile\ExternalToken\BaseExternalTokenAction;
|
||||
use App\Actions\Models\List\ExternalProfile\ExternalToken\Site\AnilistExternalTokenAction;
|
||||
use App\Enums\Models\List\ExternalProfileSite;
|
||||
use App\Models\List\External\ExternalToken;
|
||||
use Error;
|
||||
use Exception;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
/**
|
||||
@@ -14,29 +17,41 @@ use Illuminate\Support\Arr;
|
||||
*/
|
||||
class StoreExternalTokenAction
|
||||
{
|
||||
public function store($query)
|
||||
/**
|
||||
* Store the token given the query of the callback URL.
|
||||
*
|
||||
* @param array $query
|
||||
* @return ExternalToken|null
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function store(array $query): ?ExternalToken
|
||||
{
|
||||
$profileSite = ExternalProfileSite::fromLocalizedName(Arr::get($query, 'site'));
|
||||
$code = Arr::get($query, 'code');
|
||||
$site = Arr::get($query, 'site');
|
||||
$profileSite = ExternalProfileSite::fromLocalizedName($site);
|
||||
|
||||
$action = $this->getActionClass($profileSite, $code);
|
||||
$action = $this->getActionClass($profileSite);
|
||||
|
||||
$externalToken = $action->store();
|
||||
if ($action === null) {
|
||||
throw new Error("Undefined callback URL for site {$site}");
|
||||
}
|
||||
|
||||
$externalToken = $action->store(Arr::get($query, 'code'));
|
||||
|
||||
return $externalToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mapping for the token class.
|
||||
*
|
||||
* @param ExternalProfileSite $site
|
||||
* @param string $code
|
||||
* @return BaseExternalTokenAction|null
|
||||
*/
|
||||
protected function getActionClass(ExternalProfileSite $site, string $code): ?BaseExternalTokenAction
|
||||
protected function getActionClass(ExternalProfileSite $site): ?BaseExternalTokenAction
|
||||
{
|
||||
return match ($site) {
|
||||
ExternalProfileSite::ANILIST => new AnilistExternalTokenAction($code),
|
||||
ExternalProfileSite::ANILIST => new AnilistExternalTokenAction(),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ class MalAnimeApiAction extends ApiAction
|
||||
$resource = $resources->firstWhere(ExternalResource::ATTRIBUTE_SITE, ResourceSite::MAL->value);
|
||||
|
||||
if ($resource instanceof ExternalResource) {
|
||||
$response = Http::withHeaders(['X-MAL-CLIENT-ID' => Config::get('services.mal.client')])
|
||||
$response = Http::withHeaders(['X-MAL-CLIENT-ID' => Config::get('services.mal.client_id')])
|
||||
->get("https://api.myanimelist.net/v2/anime/$resource->external_id", [
|
||||
'fields' => 'studios',
|
||||
])
|
||||
|
||||
@@ -8,6 +8,7 @@ use App\Console\Commands\Storage\Admin\DocumentDumpCommand;
|
||||
use App\Console\Commands\Storage\Admin\DumpPruneCommand;
|
||||
use App\Console\Commands\Storage\Admin\WikiDumpCommand;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use BezhanSalleh\FilamentExceptions\Models\Exception;
|
||||
use Illuminate\Auth\Console\ClearResetsCommand;
|
||||
use Illuminate\Cache\Console\PruneStaleTagsCommand;
|
||||
|
||||
+3
-3
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\TableActions\Models\List\ExternalProfile;
|
||||
|
||||
use App\Actions\Models\List\ExternalProfile\StoreExternalProfileAction;
|
||||
use App\Actions\Models\List\ExternalProfile\StoreExternalProfileUsernameAction;
|
||||
use App\Enums\Models\List\ExternalProfileSite;
|
||||
use App\Enums\Models\List\ExternalProfileVisibility;
|
||||
use App\Filament\Components\Fields\Select;
|
||||
@@ -47,9 +47,9 @@ class CreateExternalProfileTableAction extends BaseTableAction
|
||||
$site = Arr::get($fields, ExternalProfile::ATTRIBUTE_SITE);
|
||||
$visibility = Arr::get($fields, ExternalProfile::ATTRIBUTE_VISIBILITY);
|
||||
|
||||
$action = new StoreExternalProfileAction();
|
||||
$action = new StoreExternalProfileUsernameAction();
|
||||
|
||||
$action->store(ExternalProfile::query(), [
|
||||
$action->findOrCreate(ExternalProfile::query(), [
|
||||
ExternalProfile::ATTRIBUTE_USER => Filament::auth()->id(),
|
||||
ExternalProfile::ATTRIBUTE_NAME => $name,
|
||||
ExternalProfile::ATTRIBUTE_SITE => ExternalProfileSite::from(intval($site))->localize(),
|
||||
|
||||
+34
-8
@@ -4,12 +4,18 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Api\List\External;
|
||||
|
||||
use App\Actions\Models\List\ExternalProfile\StoreExternalProfileTokenAction;
|
||||
use App\Actions\Models\List\ExternalProfile\StoreExternalTokenAction;
|
||||
use App\Features\AllowExternalProfileManagement;
|
||||
use App\Http\Api\Query\Query;
|
||||
use App\Http\Controllers\Api\BaseController;
|
||||
use App\Http\Requests\Api\IndexRequest;
|
||||
use App\Models\List\External\ExternalToken;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Pennant\Middleware\EnsureFeaturesAreActive;
|
||||
|
||||
@@ -30,24 +36,44 @@ class ExternalTokenCallbackController extends BaseController
|
||||
->append(AllowExternalProfileManagement::class)
|
||||
->__toString();
|
||||
|
||||
$this->middleware($isExternalProfileManagementAllowed)->except(['index', 'show']);
|
||||
$this->middleware($isExternalProfileManagementAllowed);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the redirect URL which is set in the external provider.
|
||||
*
|
||||
* @param IndexRequest $request
|
||||
* @return RedirectResponse|JsonResponse
|
||||
*/
|
||||
public function index(IndexRequest $request)
|
||||
public function index(IndexRequest $request): RedirectResponse|JsonResponse
|
||||
{
|
||||
//$query = new Query($request->validated());
|
||||
$validated = array_merge(
|
||||
$request->validated(),
|
||||
[ExternalProfile::ATTRIBUTE_USER => Auth::id()]
|
||||
);
|
||||
|
||||
//$action = new StoreExternalTokenAction();
|
||||
$action = new StoreExternalTokenAction();
|
||||
|
||||
//$action->store($query); // This stores the external token.
|
||||
$externalToken = $action->store($validated);
|
||||
|
||||
// TODO: We should find or create a profile with the entries.
|
||||
if ($externalToken === null) {
|
||||
return new JsonResponse([
|
||||
'error' => 'invalid code',
|
||||
], 400);
|
||||
}
|
||||
|
||||
// TODO: Then, the user should be redirect to the client page, e.g. /external/{site}/{profile}.
|
||||
$profileAction = new StoreExternalProfileTokenAction();
|
||||
|
||||
$profile = $profileAction->findOrCreate($externalToken, $validated);
|
||||
|
||||
// https://animethemes.moe/external/{mal|anilist}/{profile_name}
|
||||
$clientUrl = Str::of(Config::get('wiki.external_profile'))
|
||||
->append('/')
|
||||
->append(Str::lower($profile->site->name))
|
||||
->append('/')
|
||||
->append($profile->getName())
|
||||
->__toString();
|
||||
|
||||
return Redirect::to($clientUrl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use App\Actions\Http\Api\IndexAction;
|
||||
use App\Actions\Http\Api\RestoreAction;
|
||||
use App\Actions\Http\Api\ShowAction;
|
||||
use App\Actions\Http\Api\UpdateAction;
|
||||
use App\Actions\Models\List\ExternalProfile\StoreExternalProfileAction;
|
||||
use App\Actions\Models\List\ExternalProfile\StoreExternalProfileUsernameAction;
|
||||
use App\Enums\Models\List\ExternalProfileVisibility;
|
||||
use App\Features\AllowExternalProfileManagement;
|
||||
use App\Http\Api\Query\Query;
|
||||
@@ -91,12 +91,12 @@ class ExternalProfileController extends BaseController
|
||||
* Store a newly created resource.
|
||||
*
|
||||
* @param StoreRequest $request
|
||||
* @param StoreExternalProfileAction $action
|
||||
* @param StoreExternalProfileUsernameAction $action
|
||||
* @return ExternalProfileResource
|
||||
*/
|
||||
public function store(StoreRequest $request, StoreExternalProfileAction $action): ExternalProfileResource
|
||||
public function store(StoreRequest $request, StoreExternalProfileUsernameAction $action): ExternalProfileResource
|
||||
{
|
||||
$externalprofile = $action->store(ExternalProfile::query(), $request->validated());
|
||||
$externalprofile = $action->findOrCreate(ExternalProfile::query(), $request->validated());
|
||||
|
||||
return new ExternalProfileResource($externalprofile, new Query());
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Models\List;
|
||||
|
||||
use App\Enums\Http\Api\Filter\ComparisonOperator;
|
||||
use App\Enums\Models\List\ExternalProfileSite;
|
||||
use App\Enums\Models\List\ExternalProfileVisibility;
|
||||
use App\Events\List\ExternalProfile\ExternalProfileCreated;
|
||||
@@ -16,11 +17,13 @@ use App\Models\List\External\ExternalEntry;
|
||||
use App\Models\List\External\ExternalToken;
|
||||
use Database\Factories\List\ExternalProfileFactory;
|
||||
use Elastic\ScoutDriverPlus\Searchable;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Date;
|
||||
|
||||
/**
|
||||
* Class ExternalProfile.
|
||||
@@ -28,6 +31,7 @@ use Illuminate\Support\Collection;
|
||||
* @property int $profile_id
|
||||
* @property Collection<int, ExternalEntry> $externalentries
|
||||
* @property ExternalToken|null $externaltoken
|
||||
* @property int|null $external_user_id
|
||||
* @property string $name
|
||||
* @property ExternalProfileSite $site
|
||||
* @property Carbon|null $synced_at
|
||||
@@ -44,6 +48,7 @@ class ExternalProfile extends BaseModel
|
||||
final public const TABLE = 'external_profiles';
|
||||
|
||||
final public const ATTRIBUTE_ID = 'profile_id';
|
||||
final public const ATTRIBUTE_EXTERNAL_USER_ID = 'external_user_id';
|
||||
final public const ATTRIBUTE_NAME = 'name';
|
||||
final public const ATTRIBUTE_SITE = 'site';
|
||||
final public const ATTRIBUTE_VISIBILITY = 'visibility';
|
||||
@@ -61,6 +66,7 @@ class ExternalProfile extends BaseModel
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
ExternalProfile::ATTRIBUTE_EXTERNAL_USER_ID,
|
||||
ExternalProfile::ATTRIBUTE_NAME,
|
||||
ExternalProfile::ATTRIBUTE_SITE,
|
||||
ExternalProfile::ATTRIBUTE_SYNCED_AT,
|
||||
@@ -200,4 +206,20 @@ class ExternalProfile extends BaseModel
|
||||
{
|
||||
return $this->hasOne(ExternalToken::class, ExternalToken::ATTRIBUTE_PROFILE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the prunable model query.
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
public function prunable(): Builder
|
||||
{
|
||||
return static::query()
|
||||
->whereDoesntHave(ExternalProfile::RELATION_USER)
|
||||
->where(
|
||||
BaseModel::ATTRIBUTE_CREATED_AT,
|
||||
ComparisonOperator::LTE->value,
|
||||
Date::now()->subWeek()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
"fakerphp/faker": "^1.21",
|
||||
"filament/filament": "3.2.106",
|
||||
"filament/forms": "3.2.106",
|
||||
"firebase/php-jwt": "^6.10",
|
||||
"flowframe/laravel-trend": "*",
|
||||
"guzzlehttp/guzzle": "^7.5",
|
||||
"laravel-notification-channels/discord": "^1.4",
|
||||
|
||||
Generated
+64
-1
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "a7f56b90543d589c1141e6bca68e58c5",
|
||||
"content-hash": "4a53b034ee5565ea66e4046e539c2fd2",
|
||||
"packages": [
|
||||
{
|
||||
"name": "anourvalar/eloquent-serialize",
|
||||
@@ -2444,6 +2444,69 @@
|
||||
},
|
||||
"time": "2024-07-31T11:53:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "firebase/php-jwt",
|
||||
"version": "v6.10.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/firebase/php-jwt.git",
|
||||
"reference": "500501c2ce893c824c801da135d02661199f60c5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/500501c2ce893c824c801da135d02661199f60c5",
|
||||
"reference": "500501c2ce893c824c801da135d02661199f60c5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"guzzlehttp/guzzle": "^7.4",
|
||||
"phpspec/prophecy-phpunit": "^2.0",
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"psr/cache": "^2.0||^3.0",
|
||||
"psr/http-client": "^1.0",
|
||||
"psr/http-factory": "^1.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-sodium": "Support EdDSA (Ed25519) signatures",
|
||||
"paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Firebase\\JWT\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Neuman Vong",
|
||||
"email": "neuman+pear@twilio.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Anant Narayanan",
|
||||
"email": "anant@php.net",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
|
||||
"homepage": "https://github.com/firebase/php-jwt",
|
||||
"keywords": [
|
||||
"jwt",
|
||||
"php"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/firebase/php-jwt/issues",
|
||||
"source": "https://github.com/firebase/php-jwt/tree/v6.10.1"
|
||||
},
|
||||
"time": "2024-05-18T18:05:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "flowframe/laravel-trend",
|
||||
"version": "v0.2.0",
|
||||
|
||||
+12
-4
@@ -41,11 +41,19 @@ return [
|
||||
'admin_discord_channel' => env('ADMIN_DISCORD_CHANNEL'),
|
||||
],
|
||||
|
||||
'mal' => [
|
||||
'client' => env('MAL_CLIENT_ID'),
|
||||
],
|
||||
|
||||
'openai' => [
|
||||
'token' => env('OPENAI_BEARER_TOKEN'),
|
||||
],
|
||||
|
||||
'anilist' => [
|
||||
'client_id' => env('ANILIST_CLIENT_ID'),
|
||||
'client_secret' => env('ANILIST_CLIENT_SECRET'),
|
||||
'redirect_uri' => env('ANILIST_REDIRECT_URI'),
|
||||
],
|
||||
|
||||
'mal' => [
|
||||
'client_id' => env('MAL_CLIENT_ID'),
|
||||
'client_secret' => env('MAL_CLIENT_SECRET'),
|
||||
'redirect_uri' => env('MAL_REDIRECT_URI'),
|
||||
],
|
||||
];
|
||||
|
||||
@@ -16,4 +16,6 @@ return [
|
||||
'login' => env('WIKI_LOGIN'),
|
||||
|
||||
'reset_password' => env('WIKI_RESET_PASSWORD'),
|
||||
|
||||
'external_profile' => env('WIKI_EXTERNAL_PROFILE'),
|
||||
];
|
||||
|
||||
+12
@@ -19,6 +19,12 @@ return new class extends Migration
|
||||
$table->timestamp(ExternalProfile::ATTRIBUTE_SYNCED_AT, 6)->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
if (! Schema::hasColumn(ExternalProfile::TABLE, ExternalProfile::ATTRIBUTE_EXTERNAL_USER_ID)) {
|
||||
Schema::table(ExternalProfile::TABLE, function (Blueprint $table) {
|
||||
$table->integer(ExternalProfile::ATTRIBUTE_EXTERNAL_USER_ID)->nullable();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -31,5 +37,11 @@ return new class extends Migration
|
||||
$table->dropColumn(ExternalProfile::ATTRIBUTE_SYNCED_AT);
|
||||
});
|
||||
}
|
||||
|
||||
if (Schema::hasColumn(ExternalProfile::TABLE, ExternalProfile::ATTRIBUTE_EXTERNAL_USER_ID)) {
|
||||
Schema::table(ExternalProfile::TABLE, function (Blueprint $table) {
|
||||
$table->dropColumn(ExternalProfile::ATTRIBUTE_EXTERNAL_USER_ID);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user