mirror of
https://github.com/koel/koel
synced 2024-11-27 22:40:26 +00:00
feat: QR for one time tokens
This commit is contained in:
parent
1cb89b3a5f
commit
9d2a3afca9
14 changed files with 367 additions and 45 deletions
17
app/Http/Controllers/API/GetOneTimeTokenController.php
Normal file
17
app/Http/Controllers/API/GetOneTimeTokenController.php
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use App\Services\AuthenticationService;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
|
||||
class GetOneTimeTokenController extends Controller
|
||||
{
|
||||
/** @var User $user */
|
||||
public function __invoke(AuthenticationService $auth, Authenticatable $user)
|
||||
{
|
||||
return response()->json(['token' => $auth->generateOneTimeToken($user)]);
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ use App\Values\CompositeToken;
|
|||
use Illuminate\Auth\Events\PasswordReset;
|
||||
use Illuminate\Auth\Passwords\PasswordBroker;
|
||||
use Illuminate\Hashing\HashManager;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
|
||||
class AuthenticationService
|
||||
|
@ -70,4 +71,12 @@ class AuthenticationService
|
|||
|
||||
return $status === Password::PASSWORD_RESET;
|
||||
}
|
||||
|
||||
public function generateOneTimeToken(User $user): string
|
||||
{
|
||||
$token = bin2hex(random_bytes(16));
|
||||
Cache::set("one-time-token.$user->id", $token, 60 * 10);
|
||||
|
||||
return $token;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
"@typescript-eslint/parser": "^4.11.1",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"@vueuse/core": "^10.9.0",
|
||||
"@vueuse/integrations": "^10.9.0",
|
||||
"add": "^2.0.6",
|
||||
"cross-env": "^7.0.3",
|
||||
"cypress": "^9.5.4",
|
||||
|
@ -71,6 +72,7 @@
|
|||
"laravel-vite-plugin": "^1.0.2",
|
||||
"lint-staged": "^10.3.0",
|
||||
"lucide-vue-next": "^0.363.0",
|
||||
"qrcode": "^1",
|
||||
"sass": "^1.72.0",
|
||||
"start-server-and-test": "^2.0.3",
|
||||
"typescript": "^4.8.4",
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
<template>
|
||||
<div class="integrations">
|
||||
<article>
|
||||
<SpotifyIntegration />
|
||||
</article>
|
||||
<article>
|
||||
<LastfmIntegration />
|
||||
</article>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import LastfmIntegration from '@/components/profile-preferences/LastfmIntegration.vue'
|
||||
import SpotifyIntegration from '@/components/profile-preferences/SpotifyIntegration.vue'
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.integrations {
|
||||
> article + article {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
:deep(h3) {
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,11 +1,11 @@
|
|||
<template>
|
||||
<section class="text-secondary">
|
||||
<h1>
|
||||
<h3>
|
||||
<span class="lastfm-icon">
|
||||
<Icon :icon="faLastfm" />
|
||||
</span>
|
||||
Last.fm Integration
|
||||
</h1>
|
||||
</h3>
|
||||
|
||||
<div v-if="useLastfm" data-testid="lastfm-integrated">
|
||||
<p>Last.fm integration is enabled. Koel will attempt to retrieve album and artist information from Last.fm.</p>
|
||||
|
|
|
@ -131,12 +131,6 @@ const update = async () => {
|
|||
|
||||
<style lang="scss" scoped>
|
||||
form {
|
||||
width: 66%;
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
<template>
|
||||
<article class="text-secondary">
|
||||
Instead of using a password, you can scan the QR code below to log in to
|
||||
<a href="https://koel.dev/#mobile" target="_blank" class="text-highlight">Koel Player</a>
|
||||
on your mobile device.<br>
|
||||
The QR code will refresh every 10 minutes.
|
||||
<img :src="qrCodeUrl" alt="QR Code" width="192" height="192">
|
||||
</article>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import { useQRCode } from '@vueuse/integrations/useQRCode'
|
||||
import { authService } from '@/services'
|
||||
import { base64Encode } from '@/utils'
|
||||
|
||||
const qrCodeData = ref('')
|
||||
const oneTimeToken = ref('')
|
||||
|
||||
watch(oneTimeToken, () => {
|
||||
qrCodeData.value = base64Encode(JSON.stringify({
|
||||
token: oneTimeToken.value,
|
||||
url: window.BASE_URL
|
||||
}))
|
||||
})
|
||||
|
||||
const qrCodeUrl = useQRCode(qrCodeData, {
|
||||
width: window.devicePixelRatio === 1 ? 196 : 384,
|
||||
height: window.devicePixelRatio === 1 ? 196 : 384
|
||||
})
|
||||
|
||||
const resetOneTimeToken = async () => (oneTimeToken.value = await authService.getOneTimeToken())
|
||||
|
||||
onMounted(() => {
|
||||
window.setInterval(resetOneTimeToken, 60 * 10 * 1000)
|
||||
resetOneTimeToken()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
img {
|
||||
margin-top: 1.5rem;
|
||||
display: block;
|
||||
border-radius: 8px;
|
||||
}
|
||||
</style>
|
|
@ -1,11 +1,11 @@
|
|||
<template>
|
||||
<section class="text-secondary">
|
||||
<h1>
|
||||
<h3>
|
||||
<span class="spotify-icon">
|
||||
<Icon :icon="faSpotify" />
|
||||
</span>
|
||||
Spotify Integration
|
||||
</h1>
|
||||
</h3>
|
||||
|
||||
<div v-if="useSpotify">
|
||||
<p>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<template>
|
||||
<section>
|
||||
<h1>Theme</h1>
|
||||
<ul class="themes">
|
||||
<li v-for="theme in themes" :key="theme.id" data-testid="theme-card">
|
||||
<ThemeCard :key="theme.id" :theme="theme" @selected="setTheme" />
|
||||
|
@ -24,11 +23,15 @@ const setTheme = (theme: Theme) => themeStore.setTheme(theme)
|
|||
.themes {
|
||||
display: grid;
|
||||
grid-auto-rows: 8rem;
|
||||
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
||||
grid-template-columns: auto auto auto;
|
||||
grid-gap: .75rem 1rem;
|
||||
|
||||
@media only screen and (max-width: 667px) {
|
||||
grid-template-columns: 1fr;
|
||||
@media only screen and (max-width: 768px) {
|
||||
grid-template-columns: auto auto;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 480px) {
|
||||
grid-template-columns: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -3,35 +3,121 @@
|
|||
<ScreenHeader>Profile & Preferences</ScreenHeader>
|
||||
|
||||
<div class="main-scroll-wrap">
|
||||
<ProfileForm />
|
||||
<ThemeList />
|
||||
<PreferencesForm />
|
||||
<SpotifyIntegration />
|
||||
<LastfmIntegration />
|
||||
<main class="tabs">
|
||||
<div class="clear" role="tablist">
|
||||
<button
|
||||
role="tab"
|
||||
:aria-selected="currentTab === 'profile'"
|
||||
aria-controls="profilePaneProfile"
|
||||
@click.prevent="currentTab = 'profile'"
|
||||
>
|
||||
Profile
|
||||
</button>
|
||||
<button
|
||||
role="tab"
|
||||
:aria-selected="currentTab === 'preferences'"
|
||||
aria-controls="profilePanePreferences"
|
||||
@click.prevent="currentTab = 'preferences'"
|
||||
>
|
||||
Preferences
|
||||
</button>
|
||||
<button
|
||||
role="tab"
|
||||
:aria-selected="currentTab === 'themes'"
|
||||
aria-controls="profilePaneThemes"
|
||||
@click.prevent="currentTab = 'themes'"
|
||||
>
|
||||
Themes
|
||||
</button>
|
||||
<button
|
||||
role="tab"
|
||||
:aria-selected="currentTab === 'integrations'"
|
||||
aria-controls="profilePaneIntegrations"
|
||||
@click.prevent="currentTab = 'integrations'"
|
||||
>
|
||||
Integrations
|
||||
</button>
|
||||
<button
|
||||
role="tab"
|
||||
:aria-selected="currentTab === 'qr'"
|
||||
aria-controls="profilePaneQr"
|
||||
@click.prevent="currentTab = 'qr'"
|
||||
>
|
||||
<QrCodeIcon :size="16" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="panes">
|
||||
<div
|
||||
v-show="currentTab === 'profile'"
|
||||
id="profilePaneProfile"
|
||||
role="tabpanel"
|
||||
aria-labelledby="profilePaneProfile"
|
||||
tabindex="0"
|
||||
>
|
||||
<ProfileForm />
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="currentTab === 'preferences'"
|
||||
id="profilePanePreferences"
|
||||
role="tabpanel"
|
||||
aria-labelledby="profilePanePreferences"
|
||||
tabindex="0"
|
||||
>
|
||||
<PreferencesForm />
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="currentTab === 'themes'"
|
||||
id="profilePaneThemes"
|
||||
role="tabpanel"
|
||||
tabindex="0"
|
||||
aria-labelledby="profilePaneThemes"
|
||||
>
|
||||
<ThemeList />
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="currentTab === 'integrations'"
|
||||
id="profilePaneIntegrations"
|
||||
role="tabpanel"
|
||||
tabindex="0"
|
||||
aria-labelledby="profilePaneIntegrations"
|
||||
>
|
||||
<Integrations />
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="currentTab === 'qr'"
|
||||
id="profilePaneQr"
|
||||
role="tabpanel"
|
||||
aria-labelledby="profilePaneQr"
|
||||
tabindex="0"
|
||||
>
|
||||
<QRLogin />
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from 'vue'
|
||||
import { QrCodeIcon } from 'lucide-vue-next'
|
||||
import { useLocalStorage } from '@/composables'
|
||||
|
||||
import ScreenHeader from '@/components/ui/ScreenHeader.vue'
|
||||
import ProfileForm from '@/components/profile-preferences/ProfileForm.vue'
|
||||
import SpotifyIntegration from '@/components/profile-preferences/SpotifyIntegration.vue'
|
||||
import LastfmIntegration from '@/components/profile-preferences/LastfmIntegration.vue'
|
||||
import PreferencesForm from '@/components/profile-preferences/PreferencesForm.vue'
|
||||
import ThemeList from '@/components/profile-preferences/ThemeList.vue'
|
||||
import Integrations from '@/components/profile-preferences/Integrations.vue'
|
||||
import QRLogin from '@/components/profile-preferences/QRLogin.vue'
|
||||
|
||||
const { get, set } = useLocalStorage()
|
||||
|
||||
const currentTab = ref(get<'profile' | 'preferences' | 'themes' | 'integrations' | 'qr'>('profileScreenTab', 'profile'))
|
||||
|
||||
watch(currentTab, tab => set('profileScreenTab', tab))
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
#profileWrapper {
|
||||
.main-scroll-wrap > * + * {
|
||||
margin-top: 1.75rem;
|
||||
padding-top: 1.75rem;
|
||||
border-top: 1px solid var(--color-background-secondary);
|
||||
}
|
||||
|
||||
.main-scroll-wrap h1 {
|
||||
font-size: 1.85rem;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -61,5 +61,7 @@ export const authService = {
|
|||
|
||||
resetPassword: async (email: string, password: string, token: string) => {
|
||||
await http.post('reset-password', { email, password, token })
|
||||
}
|
||||
},
|
||||
|
||||
getOneTimeToken: async () => (await http.get<{ token: string }>('one-time-token')).token
|
||||
}
|
||||
|
|
|
@ -180,13 +180,15 @@ label {
|
|||
|
||||
[role=tablist] {
|
||||
border-bottom: 2px solid rgba(255, 255, 255, .1);
|
||||
padding: 0 1.25rem;
|
||||
display: flex;
|
||||
gap: 0.3rem;
|
||||
|
||||
[role=tab] {
|
||||
position: relative;
|
||||
padding: .7rem 1.3rem;
|
||||
margin-right: 0.2rem;
|
||||
border-radius: 0;
|
||||
opacity: .5;
|
||||
border-radius: 4px 4px 0 0;
|
||||
opacity: .7;
|
||||
background: rgba(255, 255, 255, .05);
|
||||
text-transform: uppercase;
|
||||
color: var(--color-text-secondary);
|
||||
|
@ -196,11 +198,8 @@ label {
|
|||
background: rgba(255, 255, 255, .1);
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
margin-left: 1.25rem;
|
||||
}
|
||||
|
||||
&[aria-selected=true] {
|
||||
transition: none;
|
||||
color: var(--color-text-primary);
|
||||
background: rgba(255, 255, 255, .1);
|
||||
opacity: 1;
|
||||
|
|
|
@ -23,6 +23,7 @@ use App\Http\Controllers\API\FetchSongsForQueueController;
|
|||
use App\Http\Controllers\API\ForgotPasswordController;
|
||||
use App\Http\Controllers\API\GenreController;
|
||||
use App\Http\Controllers\API\GenreSongController;
|
||||
use App\Http\Controllers\API\GetOneTimeTokenController;
|
||||
use App\Http\Controllers\API\LambdaSongController as S3SongController;
|
||||
use App\Http\Controllers\API\LikeMultipleSongsController;
|
||||
use App\Http\Controllers\API\MovePlaylistSongsController;
|
||||
|
@ -73,6 +74,7 @@ Route::prefix('api')->middleware('api')->group(static function (): void {
|
|||
Route::post('invitations/accept', [UserInvitationController::class, 'accept']);
|
||||
|
||||
Route::middleware('auth')->group(static function (): void {
|
||||
Route::get('one-time-token', GetOneTimeTokenController::class);
|
||||
Route::post('broadcasting/auth', static function (Request $request) {
|
||||
$pusher = new Pusher(
|
||||
config('broadcasting.connections.pusher.key'),
|
||||
|
|
135
yarn.lock
135
yarn.lock
|
@ -1069,7 +1069,7 @@
|
|||
"@vueuse/shared" "10.9.0"
|
||||
vue-demi ">=0.14.7"
|
||||
|
||||
"@vueuse/integrations@^10.7.2":
|
||||
"@vueuse/integrations@^10.7.2", "@vueuse/integrations@^10.9.0":
|
||||
version "10.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@vueuse/integrations/-/integrations-10.9.0.tgz#2b1a9556215ad3c1f96d39cbfbef102cf6e0ec05"
|
||||
integrity sha512-acK+A01AYdWSvL4BZmCoJAcyHJ6EqhmkQEXbQLwev1MY7NBnS+hcEMx/BzVoR9zKI+UqEPMD9u6PsyAuiTRT4Q==
|
||||
|
@ -1442,6 +1442,11 @@ callsites@^3.0.0:
|
|||
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
||||
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
||||
|
||||
camelcase@^5.0.0:
|
||||
version "5.3.1"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
|
||||
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
|
||||
|
||||
caseless@~0.12.0:
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
|
||||
|
@ -1546,6 +1551,15 @@ cli-truncate@^2.1.0:
|
|||
slice-ansi "^3.0.0"
|
||||
string-width "^4.2.0"
|
||||
|
||||
cliui@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
|
||||
integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==
|
||||
dependencies:
|
||||
string-width "^4.2.0"
|
||||
strip-ansi "^6.0.0"
|
||||
wrap-ansi "^6.2.0"
|
||||
|
||||
color-convert@^1.9.0:
|
||||
version "1.9.3"
|
||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
||||
|
@ -1788,6 +1802,11 @@ debug@^3.1.0, debug@^3.2.7:
|
|||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
decamelize@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||
integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==
|
||||
|
||||
decimal.js@^10.3.1:
|
||||
version "10.4.1"
|
||||
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.1.tgz#be75eeac4a2281aace80c1a8753587c27ef053e7"
|
||||
|
@ -1844,6 +1863,11 @@ delayed-stream@~1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
|
||||
|
||||
dijkstrajs@^1.0.1:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/dijkstrajs/-/dijkstrajs-1.0.3.tgz#4c8dbdea1f0f6478bff94d9c49c784d623e4fc23"
|
||||
integrity sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==
|
||||
|
||||
dir-glob@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
|
||||
|
@ -1910,6 +1934,11 @@ emoji-regex@^8.0.0:
|
|||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
|
||||
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
|
||||
|
||||
encode-utf8@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/encode-utf8/-/encode-utf8-1.0.3.tgz#f30fdd31da07fb596f281beb2f6b027851994cda"
|
||||
integrity sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==
|
||||
|
||||
end-of-stream@^1.1.0:
|
||||
version "1.4.4"
|
||||
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
|
||||
|
@ -2554,6 +2583,14 @@ fill-range@^7.0.1:
|
|||
dependencies:
|
||||
to-regex-range "^5.0.1"
|
||||
|
||||
find-up@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
|
||||
integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
|
||||
dependencies:
|
||||
locate-path "^5.0.0"
|
||||
path-exists "^4.0.0"
|
||||
|
||||
find-up@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
|
||||
|
@ -2679,6 +2716,11 @@ functions-have-names@^1.2.2:
|
|||
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
|
||||
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
|
||||
|
||||
get-caller-file@^2.0.1:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
||||
|
||||
get-func-name@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41"
|
||||
|
@ -3480,6 +3522,13 @@ local-storage@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/local-storage/-/local-storage-2.0.0.tgz#748b7d041b197f46f3ec7393640851c175b64db8"
|
||||
integrity sha512-/0sRoeijw7yr/igbVVygDuq6dlYCmtsuTmmpnweVlVtl/s10pf5BCq8LWBxW/AMyFJ3MhMUuggMZiYlx6qr9tw==
|
||||
|
||||
locate-path@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
|
||||
integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
|
||||
dependencies:
|
||||
p-locate "^4.1.0"
|
||||
|
||||
locate-path@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
|
||||
|
@ -3801,6 +3850,13 @@ ospath@^1.2.2:
|
|||
resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b"
|
||||
integrity sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==
|
||||
|
||||
p-limit@^2.2.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
|
||||
integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
|
||||
dependencies:
|
||||
p-try "^2.0.0"
|
||||
|
||||
p-limit@^3.0.2:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
|
||||
|
@ -3808,6 +3864,13 @@ p-limit@^3.0.2:
|
|||
dependencies:
|
||||
yocto-queue "^0.1.0"
|
||||
|
||||
p-locate@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
|
||||
integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
|
||||
dependencies:
|
||||
p-limit "^2.2.0"
|
||||
|
||||
p-locate@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
|
||||
|
@ -3822,6 +3885,11 @@ p-map@^4.0.0:
|
|||
dependencies:
|
||||
aggregate-error "^3.0.0"
|
||||
|
||||
p-try@^2.0.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
|
||||
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
|
||||
|
||||
parent-module@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
|
||||
|
@ -3930,6 +3998,11 @@ plyr@1.5.x:
|
|||
resolved "https://registry.yarnpkg.com/plyr/-/plyr-1.5.20.tgz#e97d620cb6c37735d3c0fffef7afa9f2ed82a854"
|
||||
integrity sha512-YwI0jNfRrWz+t3b28rrOhm1VpzmNl8bCsFGQ/wJaU/8utyrFNX9AEZi2GAvSO5ctODLKt424RQs2CQ1SY+YZfg==
|
||||
|
||||
pngjs@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-5.0.0.tgz#e79dd2b215767fd9c04561c01236df960bce7fbb"
|
||||
integrity sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==
|
||||
|
||||
postcss-selector-parser@^6.0.9:
|
||||
version "6.0.10"
|
||||
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d"
|
||||
|
@ -4049,6 +4122,16 @@ pusher-js@^4.1.0:
|
|||
tweetnacl-util "^0.15.0"
|
||||
xmlhttprequest "^1.8.0"
|
||||
|
||||
qrcode@^1:
|
||||
version "1.5.3"
|
||||
resolved "https://registry.yarnpkg.com/qrcode/-/qrcode-1.5.3.tgz#03afa80912c0dccf12bc93f615a535aad1066170"
|
||||
integrity sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==
|
||||
dependencies:
|
||||
dijkstrajs "^1.0.1"
|
||||
encode-utf8 "^1.0.3"
|
||||
pngjs "^5.0.0"
|
||||
yargs "^15.3.1"
|
||||
|
||||
qs@~6.5.2:
|
||||
version "6.5.3"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad"
|
||||
|
@ -4102,6 +4185,16 @@ request-progress@^3.0.0:
|
|||
dependencies:
|
||||
throttleit "^1.0.0"
|
||||
|
||||
require-directory@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
|
||||
integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
|
||||
|
||||
require-main-filename@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
|
||||
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
|
||||
|
||||
requires-port@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
||||
|
@ -4268,6 +4361,11 @@ semver@^7.3.2, semver@^7.3.5, semver@^7.3.7:
|
|||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
set-blocking@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
|
||||
integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==
|
||||
|
||||
shebang-command@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
|
||||
|
@ -4950,6 +5048,11 @@ which-collection@^1.0.1:
|
|||
is-weakmap "^2.0.1"
|
||||
is-weakset "^2.0.1"
|
||||
|
||||
which-module@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409"
|
||||
integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==
|
||||
|
||||
which-pm-runs@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.1.0.tgz#35ccf7b1a0fce87bd8b92a478c9d045785d3bf35"
|
||||
|
@ -5022,6 +5125,11 @@ xmlhttprequest@^1.8.0:
|
|||
resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc"
|
||||
integrity sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA==
|
||||
|
||||
y18n@^4.0.0:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
|
||||
integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==
|
||||
|
||||
yallist@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
|
||||
|
@ -5037,6 +5145,31 @@ yaml@^1.10.0:
|
|||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
|
||||
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
|
||||
|
||||
yargs-parser@^18.1.2:
|
||||
version "18.1.3"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
|
||||
integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
|
||||
dependencies:
|
||||
camelcase "^5.0.0"
|
||||
decamelize "^1.2.0"
|
||||
|
||||
yargs@^15.3.1:
|
||||
version "15.4.1"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
|
||||
integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==
|
||||
dependencies:
|
||||
cliui "^6.0.0"
|
||||
decamelize "^1.2.0"
|
||||
find-up "^4.1.0"
|
||||
get-caller-file "^2.0.1"
|
||||
require-directory "^2.1.1"
|
||||
require-main-filename "^2.0.0"
|
||||
set-blocking "^2.0.0"
|
||||
string-width "^4.2.0"
|
||||
which-module "^2.0.0"
|
||||
y18n "^4.0.0"
|
||||
yargs-parser "^18.1.2"
|
||||
|
||||
yarn@^1.22.22:
|
||||
version "1.22.22"
|
||||
resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.22.tgz#ac34549e6aa8e7ead463a7407e1c7390f61a6610"
|
||||
|
|
Loading…
Reference in a new issue