Compare commits
18 Commits
85fb966491
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| e530f23103 | |||
| 9dd25fa545 | |||
| cfcb55dc33 | |||
| 54fefdd927 | |||
| fe525afd9f | |||
| e5bbecb689 | |||
| 2f4583f423 | |||
| ee69746ac7 | |||
| db084a579f | |||
| 02a6bb6025 | |||
| d9955a64d4 | |||
| 5892921a7c | |||
| 470efe6956 | |||
| 9fc830a98b | |||
| 29b395d695 | |||
| b548676fd3 | |||
| 2191edf6bf | |||
| f2ba1e3565 |
@@ -2,5 +2,8 @@ export default defineAppConfig({
|
||||
ui: {
|
||||
primary: 'sky',
|
||||
gray: 'cool',
|
||||
container: {
|
||||
constrained: 'max-w-full',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import type { NavItem } from '@nuxt/content/dist/runtime/types'
|
||||
|
||||
const navigation = inject<Ref<NavItem[]>>('navigation')
|
||||
|
||||
const links = [{
|
||||
label: 'Documentation',
|
||||
icon: 'i-heroicons-book-open',
|
||||
to: '/getting-started',
|
||||
to: 'https://ui.nuxt.com/getting-started',
|
||||
}, {
|
||||
label: 'Pro',
|
||||
icon: 'i-heroicons-square-3-stack-3d',
|
||||
to: '/pro',
|
||||
to: 'https://ui.nuxt.com/pro',
|
||||
}, {
|
||||
label: 'Releases',
|
||||
icon: 'i-heroicons-rocket-launch',
|
||||
@@ -27,12 +23,11 @@ const links = [{
|
||||
|
||||
<template #right>
|
||||
<UColorModeButton />
|
||||
|
||||
<UButton to="https://github.com/nuxt/ui" target="_blank" icon="i-simple-icons-github" color="gray" variant="ghost" />
|
||||
<UserDropdown />
|
||||
</template>
|
||||
|
||||
<template #panel>
|
||||
<UNavigationTree :links="mapContentNavigation(navigation)" />
|
||||
<UNavigationTree :links="links" />
|
||||
</template>
|
||||
</UHeader>
|
||||
</template>
|
||||
|
||||
@@ -6,19 +6,19 @@ const links = [
|
||||
children: [
|
||||
{
|
||||
label: 'Overview',
|
||||
to: '/partner/overview',
|
||||
to: '/login',
|
||||
icon: 'i-heroicons-eye',
|
||||
},
|
||||
{
|
||||
label: 'Add Company',
|
||||
to: '/pro/components/docs/docs-search',
|
||||
to: '/login',
|
||||
icon: 'i-heroicons-plus-circle',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'People',
|
||||
to: '/pro/components/docs/docs-search-button',
|
||||
to: '/login',
|
||||
icon: 'i-heroicons-user-group',
|
||||
},
|
||||
]
|
||||
|
||||
22
components/UserDropdown.vue
Normal file
22
components/UserDropdown.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
const links = [{
|
||||
label: 'Logout',
|
||||
to: '/logout',
|
||||
icon: 'i-heroicons-arrow-left-on-rectangle',
|
||||
}]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UPopover>
|
||||
<UButton icon="i-heroicons-user-solid" color="gray" variant="ghost" />
|
||||
<template #panel>
|
||||
<div class="p-4">
|
||||
<UNavigationLinks :links="links" />
|
||||
</div>
|
||||
</template>
|
||||
</UPopover>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,6 +1,17 @@
|
||||
export interface User {
|
||||
id: number
|
||||
name: string
|
||||
email: string
|
||||
email_verified_at: string | null
|
||||
password?: string
|
||||
remember_token?: string | null
|
||||
roles: Role[]
|
||||
created_at: string | null
|
||||
updated_at: string | null
|
||||
}
|
||||
|
||||
export interface Role {
|
||||
name: string
|
||||
email?: string
|
||||
}
|
||||
|
||||
export interface LoginCredentials {
|
||||
@@ -44,7 +55,9 @@ export function useAuth<T = User>() {
|
||||
}
|
||||
|
||||
async function login(credentials: LoginCredentials) {
|
||||
if (isLoggedIn.value) { return }
|
||||
if (isLoggedIn.value) {
|
||||
return
|
||||
}
|
||||
|
||||
await $larafetch('/login', { method: 'post', body: credentials })
|
||||
await refresh()
|
||||
@@ -65,11 +78,16 @@ export function useAuth<T = User>() {
|
||||
}
|
||||
|
||||
async function logout() {
|
||||
if (!isLoggedIn.value) { return }
|
||||
if (!isLoggedIn.value) {
|
||||
return
|
||||
}
|
||||
|
||||
await $larafetch('/logout', { method: 'post' })
|
||||
user.value = null
|
||||
|
||||
const csrf_cookie = useCookie('XSRF-TOKEN')
|
||||
csrf_cookie.value = null
|
||||
|
||||
await router.push('/login')
|
||||
}
|
||||
|
||||
@@ -105,8 +123,12 @@ export async function fetchCurrentUser<T = User>() {
|
||||
return await $larafetch<T>('/api/user')
|
||||
}
|
||||
catch (error: any) {
|
||||
if ([401, 419].includes(error?.response?.status)) { return null }
|
||||
if (error?.response?.status === undefined) { return null }
|
||||
if ([401, 419].includes(error?.response?.status)) {
|
||||
return null
|
||||
}
|
||||
if (error?.response?.status === undefined) {
|
||||
return null
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
11
composables/useRoles.ts
Normal file
11
composables/useRoles.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export function useRoles() {
|
||||
const user = useUser()
|
||||
|
||||
function hasRole(roleName: string) {
|
||||
return user.value?.roles?.some(role => role.name === roleName) ?? false
|
||||
}
|
||||
|
||||
return {
|
||||
hasRole,
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,9 @@ export function useSubmit<T>(
|
||||
validationErrors.value = []
|
||||
}
|
||||
|
||||
if (e.response?.status !== 422) { throw e }
|
||||
if (e.response?.status !== 422) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
finally {
|
||||
inProgress.value = false
|
||||
|
||||
13
error.vue
Normal file
13
error.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import type { NuxtError } from '#app'
|
||||
|
||||
defineProps<{
|
||||
error: NuxtError
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NuxtLayout name="auth">
|
||||
<UPageError :error="error" />
|
||||
</NuxtLayout>
|
||||
</template>
|
||||
@@ -1,17 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<Header />
|
||||
<UPage>
|
||||
<template #left>
|
||||
<UAside class="lg:static">
|
||||
<Navigation />
|
||||
</UAside>
|
||||
</template>
|
||||
<UPageBody>
|
||||
<UContainer>
|
||||
<slot />
|
||||
</UContainer>
|
||||
</UPageBody>
|
||||
</UPage>
|
||||
</div>
|
||||
</template>
|
||||
5
layouts/auth.vue
Normal file
5
layouts/auth.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,5 +1,17 @@
|
||||
<template>
|
||||
<div>
|
||||
<slot />
|
||||
<div>
|
||||
<Header />
|
||||
<UPage>
|
||||
<template #left>
|
||||
<UAside class="lg:static">
|
||||
<Navigation />
|
||||
</UAside>
|
||||
</template>
|
||||
<UPageBody>
|
||||
<UContainer>
|
||||
<slot />
|
||||
</UContainer>
|
||||
</UPageBody>
|
||||
</UPage>
|
||||
</div>
|
||||
</template>
|
||||
11
middleware/admin.ts
Normal file
11
middleware/admin.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export default defineNuxtRouteMiddleware(async () => {
|
||||
const { hasRole } = useRoles()
|
||||
const requiredRole = 'super-admin' // Define the role required for this route
|
||||
|
||||
if (!hasRole(requiredRole)) {
|
||||
return abortNavigation({
|
||||
message: 'You are not authorized to access this page',
|
||||
statusCode: 403,
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,4 +1,6 @@
|
||||
export default defineNuxtRouteMiddleware(async () => {
|
||||
const user = useUser();
|
||||
if (!user.value) return navigateTo("/login", { replace: true });
|
||||
});
|
||||
const user = useUser()
|
||||
if (!user.value) {
|
||||
return navigateTo('/login', { replace: true })
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
export default defineNuxtRouteMiddleware(async () => {
|
||||
const user = useUser();
|
||||
if (user.value) return navigateTo("/", { replace: true });
|
||||
});
|
||||
const user = useUser()
|
||||
if (user.value) {
|
||||
return navigateTo('/', { replace: true })
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
export default defineNuxtRouteMiddleware(() => {
|
||||
const user = useUser();
|
||||
const user = useUser()
|
||||
|
||||
if (!user.value) return navigateTo("/login");
|
||||
if (!user.value) {
|
||||
return navigateTo('/login')
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
if (user.value.email_verified_at || user.value.is_verified)
|
||||
return navigateTo("/");
|
||||
});
|
||||
if (user.value.email_verified_at) {
|
||||
return navigateTo('/')
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
export default defineNuxtRouteMiddleware(() => {
|
||||
const user = useUser();
|
||||
const user = useUser()
|
||||
|
||||
if (!user.value) return navigateTo("/login");
|
||||
if (!user.value) {
|
||||
return navigateTo('/login')
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
if (!(user.value.email_verified_at || user.value.is_verified))
|
||||
return navigateTo("/verify-email");
|
||||
});
|
||||
if (!(user.value.email_verified_at)) {
|
||||
return navigateTo('/verify-email')
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||
// https://nuxt.com/docs/api/configuration/nbun uxt-config
|
||||
export default defineNuxtConfig({
|
||||
extends: ['@nuxt/ui-pro'],
|
||||
modules: ['@nuxt/ui'],
|
||||
|
||||
18
package.json
18
package.json
@@ -12,19 +12,17 @@
|
||||
"postinstall": "nuxt prepare"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^1.1.0",
|
||||
"@iconify-json/heroicons": "^1.1.13",
|
||||
"@iconify-json/logos": "^1.1.37",
|
||||
"@iconify-json/simple-icons": "^1.1.76",
|
||||
"@antfu/eslint-config": "^2.8.0",
|
||||
"@iconify-json/heroicons": "^1.1.20",
|
||||
"@nuxt/devtools": "latest",
|
||||
"@nuxt/ui-pro": "^0.4.2",
|
||||
"eslint": "^8.53.0",
|
||||
"eslint-plugin-tailwindcss": "^3.13.0",
|
||||
"@nuxt/ui-pro": "^1.0.2",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-tailwindcss": "^3.14.2",
|
||||
"laravel-echo": "^1.15.3",
|
||||
"nuxt": "^3.8.1",
|
||||
"nuxt": "^3.10.3",
|
||||
"pusher-js": "^8.3.0",
|
||||
"typescript": "^5.2.2",
|
||||
"vue": "^3.3.8",
|
||||
"typescript": "^5.3.3",
|
||||
"vue": "^3.4.19",
|
||||
"vue-router": "^4.2.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({ middleware: ['guest'] })
|
||||
definePageMeta({ middleware: ['guest'], layout: 'auth' })
|
||||
|
||||
const router = useRouter()
|
||||
const { forgotPassword } = useAuth()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({ middleware: ['auth'], layout: 'app' })
|
||||
definePageMeta({ middleware: ['auth'] })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({ middleware: ['guest'] })
|
||||
definePageMeta({ middleware: ['guest'], layout: 'auth' })
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
8
pages/logout/index.vue
Normal file
8
pages/logout/index.vue
Normal file
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
definePageMeta({ middleware: ['guest'], layout: 'auth' })
|
||||
const { logout } = useAuth()
|
||||
logout()
|
||||
</script>
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({ middleware: ['guest'] })
|
||||
definePageMeta({ middleware: ['guest'], layout: 'auth' })
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
@@ -4,7 +4,9 @@ export default defineNuxtPlugin(async () => {
|
||||
const user = useUser()
|
||||
|
||||
// Skip if already initialized on server
|
||||
if (user.value !== undefined) { return }
|
||||
if (user.value !== undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
user.value = await fetchCurrentUser()
|
||||
})
|
||||
|
||||
@@ -2,7 +2,9 @@ import { FetchError } from 'ofetch'
|
||||
|
||||
export default defineNuxtPlugin(async (nuxtApp) => {
|
||||
nuxtApp.hook('vue:error', (error) => {
|
||||
if (!(error instanceof FetchError)) { throw error }
|
||||
if (!(error instanceof FetchError)) {
|
||||
throw error
|
||||
}
|
||||
|
||||
const status = error.response?.status ?? -1
|
||||
|
||||
|
||||
8980
pnpm-lock.yaml
generated
Normal file
8980
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@ export const $larafetch = $fetch.create({
|
||||
async onRequest({ options }) {
|
||||
const { backendUrl, frontendUrl } = useRuntimeConfig().public
|
||||
const event = process.nitro ? useEvent() : null
|
||||
|
||||
let token = event
|
||||
? parseCookies(event)[CSRF_COOKIE]
|
||||
: useCookie(CSRF_COOKIE).value
|
||||
@@ -46,17 +47,23 @@ export const $larafetch = $fetch.create({
|
||||
},
|
||||
async onResponseError({ response }) {
|
||||
const status = response.status
|
||||
|
||||
if (status === 419) {
|
||||
await initCsrf(true)
|
||||
}
|
||||
if ([500].includes(status)) {
|
||||
console.error('[Laravel Error]', response.statusText, response._data)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
async function initCsrf() {
|
||||
async function initCsrf(forceRefresh = false) {
|
||||
const { backendUrl } = useRuntimeConfig().public
|
||||
const existingToken = useCookie(CSRF_COOKIE).value
|
||||
|
||||
if (existingToken) { return existingToken }
|
||||
if (existingToken && !forceRefresh) {
|
||||
return existingToken
|
||||
}
|
||||
|
||||
await $fetch('/sanctum/csrf-cookie', {
|
||||
baseURL: backendUrl,
|
||||
|
||||
Reference in New Issue
Block a user