mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-08 00:02:15 +01:00
remove jetstream inertia properties; remove unused ApiTokenManager;
This commit is contained in:
committed by
Constantin Graf
parent
56ea0ed90e
commit
bffd0773be
@@ -9,12 +9,10 @@ use App\Models\User;
|
||||
use App\Service\PermissionStore;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\MessageBag;
|
||||
use Inertia\Inertia;
|
||||
use Laravel\Fortify\Features;
|
||||
use Laravel\Jetstream\Jetstream;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class ShareInertiaData
|
||||
@@ -27,25 +25,6 @@ class ShareInertiaData
|
||||
/** @var PermissionStore $permissions */
|
||||
$permissions = app(PermissionStore::class);
|
||||
Inertia::share([
|
||||
'jetstream' => function () use ($request) {
|
||||
/** @var User|null $user */
|
||||
$user = $request->user();
|
||||
|
||||
return [
|
||||
'canCreateTeams' => $user !== null &&
|
||||
Jetstream::userHasTeamFeatures($user) &&
|
||||
Gate::forUser($user)->check('create', Jetstream::newTeamModel()),
|
||||
'canManageTwoFactorAuthentication' => Features::canManageTwoFactorAuthentication(),
|
||||
'canUpdatePassword' => Features::enabled(Features::updatePasswords()),
|
||||
'canUpdateProfileInformation' => Features::canUpdateProfileInformation(),
|
||||
'hasEmailVerification' => Features::enabled(Features::emailVerification()),
|
||||
'hasAccountDeletionFeatures' => Jetstream::hasAccountDeletionFeatures(),
|
||||
'hasApiFeatures' => Jetstream::hasApiFeatures(),
|
||||
'hasTeamFeatures' => Jetstream::hasTeamFeatures(),
|
||||
'hasTermsAndPrivacyPolicyFeature' => Jetstream::hasTermsAndPrivacyPolicyFeature(),
|
||||
'managesProfilePhotos' => Jetstream::managesProfilePhotos(),
|
||||
];
|
||||
},
|
||||
'auth' => [
|
||||
'permissions' => $request->user() !== null && $request->user()->currentTeam !== null ? $permissions->getPermissions($request->user()->currentTeam) : [],
|
||||
'user' => function () use ($request): array {
|
||||
|
||||
@@ -20,12 +20,6 @@ import {
|
||||
} from '@/packages/ui/src';
|
||||
|
||||
const page = usePage<{
|
||||
jetstream: {
|
||||
canCreateTeams: boolean;
|
||||
hasTeamFeatures: boolean;
|
||||
managesProfilePhotos: boolean;
|
||||
hasApiFeatures: boolean;
|
||||
};
|
||||
auth: {
|
||||
user: User & {
|
||||
all_teams: Organization[];
|
||||
@@ -39,7 +33,7 @@ const switchToTeam = (organization: Organization) => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenu v-if="page.props.jetstream.hasTeamFeatures">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
class="flex w-full text-left hover:bg-white/10 focus-visible:ring-2 focus-visible:ring-ring cursor-pointer transition pl-2 py-1 rounded w-full items-center justify-between"
|
||||
as-child>
|
||||
@@ -78,7 +72,7 @@ const switchToTeam = (organization: Organization) => {
|
||||
<Link href="/billing" class="inline-flex items-center w-full"> Billing </Link>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem v-if="page.props.jetstream.canCreateTeams" as-child>
|
||||
<DropdownMenuItem as-child>
|
||||
<Link
|
||||
:href="route('teams.create')"
|
||||
class="inline-flex items-center gap-2.5 w-full">
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
} from '@/packages/ui/src';
|
||||
import {
|
||||
UserCircleIcon,
|
||||
KeyIcon,
|
||||
ArrowLeftOnRectangleIcon,
|
||||
ChatBubbleLeftRightIcon,
|
||||
} from '@heroicons/vue/24/solid';
|
||||
@@ -19,12 +18,6 @@ import { openFeedback } from '@/utils/feedback';
|
||||
const page = usePage<{
|
||||
has_services_extension?: boolean;
|
||||
has_billing_extension?: boolean;
|
||||
jetstream: {
|
||||
canCreateTeams: boolean;
|
||||
hasTeamFeatures: boolean;
|
||||
managesProfilePhotos: boolean;
|
||||
hasApiFeatures: boolean;
|
||||
};
|
||||
auth: {
|
||||
user: User & {
|
||||
all_teams: Organization[];
|
||||
@@ -61,15 +54,6 @@ const logout = () => {
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem v-if="page.props.jetstream.hasApiFeatures" as-child>
|
||||
<Link
|
||||
:href="route('api-tokens.index')"
|
||||
class="inline-flex items-center gap-2.5 w-full">
|
||||
<KeyIcon class="w-5 h-5 text-icon-default" />
|
||||
<span>API Tokens</span>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem v-if="page.props.has_services_extension" as-child>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import ApiTokenManager from '@/Pages/API/Partials/ApiTokenManager.vue';
|
||||
import AppLayout from '@/Layouts/AppLayout.vue';
|
||||
import type { Token } from '@/types/jetstream';
|
||||
|
||||
defineProps<{
|
||||
tokens: Token[];
|
||||
availablePermissions: string[];
|
||||
defaultPermissions: string[];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AppLayout title="API Tokens">
|
||||
<template #header>
|
||||
<h2 class="font-semibold text-xl text-text-primary leading-tight">API Tokens</h2>
|
||||
</template>
|
||||
|
||||
<div>
|
||||
<div class="max-w-7xl mx-auto py-10 sm:px-6 lg:px-8">
|
||||
<ApiTokenManager
|
||||
:tokens="tokens"
|
||||
:available-permissions="availablePermissions"
|
||||
:default-permissions="defaultPermissions" />
|
||||
</div>
|
||||
</div>
|
||||
</AppLayout>
|
||||
</template>
|
||||
@@ -1,263 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useForm, usePage } from '@inertiajs/vue3';
|
||||
import ActionMessage from '@/Components/ActionMessage.vue';
|
||||
import ActionSection from '@/Components/ActionSection.vue';
|
||||
import Checkbox from '@/packages/ui/src/Input/Checkbox.vue';
|
||||
import ConfirmationModal from '@/Components/ConfirmationModal.vue';
|
||||
import DangerButton from '@/packages/ui/src/Buttons/DangerButton.vue';
|
||||
import DialogModal from '@/packages/ui/src/DialogModal.vue';
|
||||
import FormSection from '@/Components/FormSection.vue';
|
||||
import { Field, FieldLabel, FieldError } from '@/packages/ui/src/field';
|
||||
import PrimaryButton from '@/packages/ui/src/Buttons/PrimaryButton.vue';
|
||||
import SecondaryButton from '@/packages/ui/src/Buttons/SecondaryButton.vue';
|
||||
import SectionBorder from '@/Components/SectionBorder.vue';
|
||||
import TextInput from '@/packages/ui/src/Input/TextInput.vue';
|
||||
import type { Token } from '@/types/jetstream';
|
||||
|
||||
const props = defineProps<{
|
||||
tokens: Token[];
|
||||
availablePermissions: string[];
|
||||
defaultPermissions: string[];
|
||||
}>();
|
||||
|
||||
const createApiTokenForm = useForm({
|
||||
name: '',
|
||||
permissions: props.defaultPermissions,
|
||||
});
|
||||
|
||||
const page = usePage<{
|
||||
jetstream: {
|
||||
flash: {
|
||||
token: string;
|
||||
};
|
||||
};
|
||||
}>();
|
||||
|
||||
const updateApiTokenForm = useForm<{
|
||||
permissions: string[];
|
||||
}>({
|
||||
permissions: [],
|
||||
});
|
||||
|
||||
const deleteApiTokenForm = useForm({});
|
||||
|
||||
const displayingToken = ref(false);
|
||||
const managingPermissionsFor = ref<Token | null>(null);
|
||||
const apiTokenBeingDeleted = ref<Token | null>(null);
|
||||
|
||||
const createApiToken = () => {
|
||||
createApiTokenForm.post(route('api-tokens.store'), {
|
||||
preserveScroll: true,
|
||||
onSuccess: () => {
|
||||
displayingToken.value = true;
|
||||
createApiTokenForm.reset();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const manageApiTokenPermissions = (token: Token) => {
|
||||
updateApiTokenForm.permissions = token.abilities;
|
||||
managingPermissionsFor.value = token;
|
||||
};
|
||||
|
||||
const updateApiToken = () => {
|
||||
updateApiTokenForm.put(route('api-tokens.update', managingPermissionsFor.value?.id), {
|
||||
preserveScroll: true,
|
||||
preserveState: true,
|
||||
onSuccess: () => (managingPermissionsFor.value = null),
|
||||
});
|
||||
};
|
||||
|
||||
const confirmApiTokenDeletion = (token: Token) => {
|
||||
apiTokenBeingDeleted.value = token;
|
||||
};
|
||||
|
||||
const deleteApiToken = () => {
|
||||
deleteApiTokenForm.delete(route('api-tokens.destroy', apiTokenBeingDeleted.value?.id), {
|
||||
preserveScroll: true,
|
||||
preserveState: true,
|
||||
onSuccess: () => (apiTokenBeingDeleted.value = null),
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<!-- Generate API Token -->
|
||||
<FormSection @submitted="createApiToken">
|
||||
<template #title> Create API Token </template>
|
||||
|
||||
<template #description>
|
||||
API tokens allow third-party services to authenticate with our application on your
|
||||
behalf.
|
||||
</template>
|
||||
|
||||
<template #form>
|
||||
<!-- Token Name -->
|
||||
<Field class="col-span-6 sm:col-span-4">
|
||||
<FieldLabel for="name">Name</FieldLabel>
|
||||
<TextInput
|
||||
id="name"
|
||||
v-model="createApiTokenForm.name"
|
||||
type="text"
|
||||
class="block w-full"
|
||||
autofocus />
|
||||
<FieldError v-if="createApiTokenForm.errors.name">{{
|
||||
createApiTokenForm.errors.name
|
||||
}}</FieldError>
|
||||
</Field>
|
||||
|
||||
<!-- Token Permissions -->
|
||||
<div v-if="availablePermissions.length > 0" class="col-span-6">
|
||||
<FieldLabel for="permissions">Permissions</FieldLabel>
|
||||
|
||||
<div class="mt-2 grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div v-for="permission in availablePermissions" :key="permission">
|
||||
<label class="flex items-center">
|
||||
<Checkbox
|
||||
v-model:checked="createApiTokenForm.permissions"
|
||||
:value="permission" />
|
||||
<span class="ms-2 text-sm text-text-secondary">{{
|
||||
permission
|
||||
}}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
<ActionMessage :on="createApiTokenForm.recentlySuccessful" class="me-3">
|
||||
Created.
|
||||
</ActionMessage>
|
||||
|
||||
<PrimaryButton
|
||||
:class="{ 'opacity-25': createApiTokenForm.processing }"
|
||||
:disabled="createApiTokenForm.processing">
|
||||
Create
|
||||
</PrimaryButton>
|
||||
</template>
|
||||
</FormSection>
|
||||
|
||||
<div v-if="tokens.length > 0">
|
||||
<SectionBorder />
|
||||
|
||||
<!-- Manage API Tokens -->
|
||||
<div class="mt-10 sm:mt-0">
|
||||
<ActionSection>
|
||||
<template #title> Manage API Tokens </template>
|
||||
|
||||
<template #description>
|
||||
You may delete any of your existing tokens if they are no longer needed.
|
||||
</template>
|
||||
|
||||
<!-- API Token List -->
|
||||
<template #content>
|
||||
<div class="space-y-6">
|
||||
<div
|
||||
v-for="token in tokens"
|
||||
:key="token.id"
|
||||
class="flex items-center justify-between">
|
||||
<div class="break-all text-text-primary">
|
||||
{{ token.name }}
|
||||
</div>
|
||||
|
||||
<div class="flex items-center ms-2">
|
||||
<div v-if="token.last_used_ago" class="text-sm text-gray-400">
|
||||
Last used {{ token.last_used_ago }}
|
||||
</div>
|
||||
|
||||
<button
|
||||
v-if="availablePermissions.length > 0"
|
||||
class="cursor-pointer ms-6 text-sm text-gray-400 underline"
|
||||
@click="manageApiTokenPermissions(token)">
|
||||
Permissions
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="cursor-pointer ms-6 text-sm text-red-500"
|
||||
@click="confirmApiTokenDeletion(token)">
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ActionSection>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Token Value Modal -->
|
||||
<DialogModal :show="displayingToken" @close="displayingToken = false">
|
||||
<template #title> API Token </template>
|
||||
|
||||
<template #content>
|
||||
<div>
|
||||
Please copy your new API token. For your security, it won't be shown again.
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="page.props.jetstream.flash.token"
|
||||
class="mt-4 bg-card-backgroundpx-4 py-2 rounded font-mono text-sm text-gray-500 break-all">
|
||||
{{ page.props.jetstream.flash.token }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<SecondaryButton @click="displayingToken = false"> Close </SecondaryButton>
|
||||
</template>
|
||||
</DialogModal>
|
||||
|
||||
<!-- API Token Permissions Modal -->
|
||||
<DialogModal :show="managingPermissionsFor != null" @close="managingPermissionsFor = null">
|
||||
<template #title> API Token Permissions </template>
|
||||
|
||||
<template #content>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div v-for="permission in availablePermissions" :key="permission">
|
||||
<label class="flex items-center">
|
||||
<Checkbox
|
||||
v-model:checked="updateApiTokenForm.permissions"
|
||||
:value="permission" />
|
||||
<span class="ms-2 text-sm text-muted">{{ permission }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<SecondaryButton @click="managingPermissionsFor = null"> Cancel </SecondaryButton>
|
||||
|
||||
<PrimaryButton
|
||||
class="ms-3"
|
||||
:class="{ 'opacity-25': updateApiTokenForm.processing }"
|
||||
:disabled="updateApiTokenForm.processing"
|
||||
@click="updateApiToken">
|
||||
Save
|
||||
</PrimaryButton>
|
||||
</template>
|
||||
</DialogModal>
|
||||
|
||||
<!-- Delete Token Confirmation Modal -->
|
||||
<ConfirmationModal
|
||||
:show="apiTokenBeingDeleted != null"
|
||||
@close="apiTokenBeingDeleted = null">
|
||||
<template #title> Delete API Token </template>
|
||||
|
||||
<template #content> Are you sure you would like to delete this API token? </template>
|
||||
|
||||
<template #footer>
|
||||
<SecondaryButton @click="apiTokenBeingDeleted = null"> Cancel </SecondaryButton>
|
||||
|
||||
<DangerButton
|
||||
class="ms-3"
|
||||
:class="{ 'opacity-25': deleteApiTokenForm.processing }"
|
||||
:disabled="deleteApiTokenForm.processing"
|
||||
@click="deleteApiToken">
|
||||
Delete
|
||||
</DangerButton>
|
||||
</template>
|
||||
</ConfirmationModal>
|
||||
</div>
|
||||
</template>
|
||||
@@ -30,9 +30,6 @@ const page = usePage<{
|
||||
terms_url: string | null;
|
||||
privacy_policy_url: string | null;
|
||||
newsletter_consent: boolean;
|
||||
jetstream: {
|
||||
hasTermsAndPrivacyPolicyFeature: boolean;
|
||||
};
|
||||
flash: {
|
||||
message: string;
|
||||
};
|
||||
@@ -117,11 +114,7 @@ const page = usePage<{
|
||||
</Field>
|
||||
|
||||
<div
|
||||
v-if="
|
||||
page.props.jetstream.hasTermsAndPrivacyPolicyFeature &&
|
||||
page.props.terms_url !== null &&
|
||||
page.props.privacy_policy_url !== null
|
||||
"
|
||||
v-if="page.props.terms_url !== null && page.props.privacy_policy_url !== null"
|
||||
class="mt-4">
|
||||
<Field orientation="horizontal">
|
||||
<Checkbox id="terms" v-model:checked="form.terms" name="terms" />
|
||||
|
||||
@@ -176,7 +176,6 @@ onBeforeUnmount(() => {
|
||||
});
|
||||
|
||||
const page = usePage<{
|
||||
jetstream: { managesProfilePhotos: boolean };
|
||||
timezones: Record<string, string>;
|
||||
weekdays: Record<string, string>;
|
||||
}>();
|
||||
@@ -192,7 +191,7 @@ const page = usePage<{
|
||||
|
||||
<template #form>
|
||||
<!-- Profile Photo -->
|
||||
<div v-if="page.props.jetstream.managesProfilePhotos" class="col-span-6 sm:col-span-4">
|
||||
<div class="col-span-6 sm:col-span-4">
|
||||
<input
|
||||
id="photo"
|
||||
ref="photoInput"
|
||||
|
||||
@@ -6,8 +6,6 @@ import SectionBorder from '@/Components/SectionBorder.vue';
|
||||
import TwoFactorAuthenticationForm from '@/Pages/Profile/Partials/TwoFactorAuthenticationForm.vue';
|
||||
import UpdatePasswordForm from '@/Pages/Profile/Partials/UpdatePasswordForm.vue';
|
||||
import UpdateProfileInformationForm from '@/Pages/Profile/Partials/UpdateProfileInformationForm.vue';
|
||||
import { usePage } from '@inertiajs/vue3';
|
||||
import type { User } from '@/types/models';
|
||||
import type { Session } from '@/types/jetstream';
|
||||
import ApiTokensForm from '@/Pages/Profile/Partials/ApiTokensForm.vue';
|
||||
import ThemeForm from '@/Pages/Profile/Partials/ThemeForm.vue';
|
||||
@@ -16,18 +14,6 @@ defineProps<{
|
||||
confirmsTwoFactorAuthentication: boolean;
|
||||
sessions: Session[];
|
||||
}>();
|
||||
|
||||
const page = usePage<{
|
||||
jetstream: {
|
||||
canUpdateProfileInformation: boolean;
|
||||
canUpdatePassword: boolean;
|
||||
canManageTwoFactorAuthentication: boolean;
|
||||
hasAccountDeletionFeatures: boolean;
|
||||
};
|
||||
auth: {
|
||||
user: User;
|
||||
};
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -38,7 +24,7 @@ const page = usePage<{
|
||||
|
||||
<div>
|
||||
<div class="max-w-7xl mx-auto py-10 sm:px-6 lg:px-8">
|
||||
<div v-if="page.props.jetstream.canUpdateProfileInformation">
|
||||
<div>
|
||||
<UpdateProfileInformationForm />
|
||||
|
||||
<SectionBorder />
|
||||
@@ -50,13 +36,13 @@ const page = usePage<{
|
||||
<SectionBorder />
|
||||
</div>
|
||||
|
||||
<div v-if="page.props.jetstream.canUpdatePassword">
|
||||
<div>
|
||||
<UpdatePasswordForm class="mt-10 sm:mt-0" />
|
||||
|
||||
<SectionBorder />
|
||||
</div>
|
||||
|
||||
<div v-if="page.props.jetstream.canManageTwoFactorAuthentication">
|
||||
<div>
|
||||
<TwoFactorAuthenticationForm
|
||||
:requires-confirmation="confirmsTwoFactorAuthentication"
|
||||
class="mt-10 sm:mt-0" />
|
||||
@@ -69,11 +55,9 @@ const page = usePage<{
|
||||
|
||||
<ApiTokensForm></ApiTokensForm>
|
||||
|
||||
<template v-if="page.props.jetstream.hasAccountDeletionFeatures">
|
||||
<SectionBorder />
|
||||
<SectionBorder />
|
||||
|
||||
<DeleteUserForm class="mt-10 sm:mt-0" />
|
||||
</template>
|
||||
<DeleteUserForm class="mt-10 sm:mt-0" />
|
||||
</div>
|
||||
</div>
|
||||
</AppLayout>
|
||||
|
||||
3
resources/js/types/inertia.d.ts
vendored
3
resources/js/types/inertia.d.ts
vendored
@@ -9,9 +9,6 @@ declare global {
|
||||
created_at: Date;
|
||||
updated_at: Date;
|
||||
};
|
||||
jetstream: {
|
||||
[key: string]: boolean;
|
||||
};
|
||||
errorBags: unknown;
|
||||
errors: unknown;
|
||||
}
|
||||
|
||||
@@ -33,10 +33,3 @@ export interface Role {
|
||||
export type JetstreamUser = User & {
|
||||
two_factor_enabled: boolean;
|
||||
};
|
||||
export interface Token {
|
||||
name: string;
|
||||
token: string;
|
||||
abilities: string[];
|
||||
id: string;
|
||||
last_used_ago: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user