feat(frontend): Get project running again after move to Mac Mini

This commit is contained in:
2025-05-31 10:14:36 +02:00
parent d553668893
commit 075847f0ee
10 changed files with 86 additions and 28 deletions

View File

@@ -7,8 +7,8 @@
BETTER_AUTH_URL=http://localhost:3000
BETTER_AUTH_SECRET=YOUR_SECRET
```
2. Generate database schema: `npx @better-auth/cli generate`
3. Migrate schema: `npx @better-auth/cli migrate`
2. Generate database schema: `pnpm dlx @better-auth/cli generate`
3. Migrate schema: `pnpm dlx @better-auth/cli migrate`
## Common errors
@@ -31,3 +31,9 @@ rm -fr node_modules; pnpm store prune
```
https://github.com/elizaOS/eliza/pull/665
### Unauthorized /token and /organization/list endpoints
User needs to be logged in to access these endpoints.
https://www.better-auth.com/docs/plugins/organization#accept-invitation

View File

@@ -6,6 +6,8 @@ create table "account" ("id" text not null primary key, "accountId" text not nul
create table "verification" ("id" text not null primary key, "identifier" text not null, "value" text not null, "expiresAt" date not null, "createdAt" date, "updatedAt" date);
create table "jwks" ("id" text not null primary key, "publicKey" text not null, "privateKey" text not null, "createdAt" date not null);
create table "organization" ("id" text not null primary key, "name" text not null, "slug" text not null unique, "logo" text, "createdAt" date not null, "metadata" text);
create table "member" ("id" text not null primary key, "organizationId" text not null references "organization" ("id"), "userId" text not null references "user" ("id"), "role" text not null, "createdAt" date not null);

View File

@@ -178,7 +178,7 @@ const items = computed<DropdownMenuItem[][]>(() => [
icon: 'i-lucide-log-out',
async onSelect(e: Event) {
e.preventDefault()
signOut()
await signOut({ redirectTo: '/' })
}
}
]

View File

@@ -1,11 +1,12 @@
// Copied from https://github.com/atinux/nuxthub-better-auth
import { defu } from 'defu'
import { createAuthClient } from 'better-auth/client'
import { createAuthClient } from 'better-auth/vue'
import type { InferSessionFromClient, InferUserFromClient, ClientOptions } from 'better-auth/client'
import { organizationClient, jwtClient } from 'better-auth/client/plugins'
import type { RouteLocationRaw } from 'vue-router'
import type { UserDto } from '~/.api-client'
import type { RouteLocationNormalizedLoaded } from '#vue-router'
interface RuntimeAuthConfig {
redirectUserTo: RouteLocationRaw | string
@@ -30,6 +31,7 @@ const selectedOrganization = ref<{
export function useAuth() {
const url = useRequestURL()
const route = useRoute()
const headers = import.meta.server ? useRequestHeaders() : undefined
const client = createAuthClient({
@@ -60,18 +62,32 @@ export function useAuth() {
user.value = data?.user || null
sessionFetching.value = false
// Fetch JWT - workaround for not working extraction of JWT out of session (https://github.com/better-auth/better-auth/issues/1835)
jwt.value = (await client.token()).data?.token ?? null
// Fetch organization
organizations.value = (await client.organization.list()).data ?? []
if (!selectedOrganization.value && organizations.value.length > 0) {
selectedOrganization.value = organizations.value[0]
// Only fetch JWT and organizations if we have a session and not on public routes
if (session.value && !isPublicRoute()) {
await fetchJwtAndOrganizations()
}
return data
}
async function fetchJwtAndOrganizations() {
// Fetch JWT
const tokenResult = await client.token()
jwt.value = tokenResult.data?.token ?? null
// Fetch organization
const orgResult = await client.organization.list({
fetchOptions: {
headers
}
})
organizations.value = orgResult.data ?? []
if (!selectedOrganization.value && organizations.value.length > 0) {
selectedOrganization.value = organizations.value[0]
}
}
watch(
() => selectedOrganization.value,
async (newValue) => {
@@ -90,6 +106,12 @@ export function useAuth() {
})
}
function isPublicRoute(routeToCheck?: RouteLocationNormalizedLoaded) {
const finalRoute = routeToCheck ?? route
const publicRoutes = ['/login', '/signup', '/accept-invitation']
return publicRoutes.some((path) => finalRoute.path.startsWith(path))
}
async function signOut({ redirectTo }: { redirectTo?: RouteLocationRaw } = {}) {
const res = await client.signOut()
if (res.error) {
@@ -99,7 +121,7 @@ export function useAuth() {
session.value = null
user.value = null
if (redirectTo) {
await navigateTo(redirectTo)
await navigateTo(redirectTo, { external: true })
}
return res
}
@@ -122,7 +144,9 @@ export function useAuth() {
selectedOrganization,
options,
fetchSession,
fetchJwtAndOrganizations,
client,
jwt
jwt,
isPublicRoute
}
}

View File

@@ -1,6 +1,7 @@
// Copied from https://github.com/atinux/nuxthub-better-auth
import { defu } from 'defu'
import type { RouteLocationNormalized } from '#vue-router'
type MiddlewareOptions =
| false
@@ -31,33 +32,44 @@ declare module 'vue-router' {
}
}
export default defineNuxtRouteMiddleware(async (to) => {
// If auth is disabled, skip middleware
export default defineNuxtRouteMiddleware(async (to: RouteLocationNormalized) => {
// 1. If auth is disabled, skip middleware
if (to.meta?.auth === false) {
console.log('[1] Auth middleware disabled for this route:', to.path)
return
}
const { loggedIn, options, fetchSession } = useAuth()
const { loggedIn, options, fetchSession, isPublicRoute } = useAuth()
const { only, redirectUserTo, redirectGuestTo } = defu(to.meta?.auth, options)
// If guest mode, redirect if authenticated
// 2. If guest mode, redirect if authenticated
if (only === 'guest' && loggedIn.value) {
// Avoid infinite redirect
console.log('[2] Guest mode: user is authenticated, redirecting to', redirectUserTo)
if (to.path === redirectUserTo) {
console.log('[2.1] Already at redirectUserTo:', redirectUserTo)
return
}
return navigateTo(redirectUserTo)
}
// If client-side, fetch session between each navigation
// 3. If client-side, fetch session between each navigation
if (import.meta.client) {
await fetchSession()
console.log('[3] Client-side navigation, fetching session')
try {
await fetchSession()
} catch (e) {
console.error(e)
}
}
// If not authenticated, redirect to home
// 4. If not authenticated, redirect to home or guest route
if (!loggedIn.value) {
// Avoid infinite redirect
if (to.path === redirectGuestTo) {
if (isPublicRoute(to)) {
console.log('[4] Not authenticated, but route is public:', to.path)
// Continue navigating to the public route
return
}
// No public route, redirect to guest route
console.log('[4.1] Not authenticated, redirecting to guest route:', redirectGuestTo)
return navigateTo(redirectGuestTo)
}
})

View File

@@ -7,13 +7,14 @@
"dev": "nuxt dev --port 3001 --host",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare && pnpm run fix:bettersqlite",
"postinstall": "nuxt prepare && pnpm run fix:bettersqlite && pnpm run api:generate",
"format": "prettier . --write",
"type-check": "nuxi typecheck",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"api:generate": "openapi-generator-cli generate -i ../legalconsenthub-backend/api/legalconsenthub.yml -g typescript-fetch -o .api-client",
"fix:bettersqlite": "cd node_modules/better-sqlite3 && pnpm dlx node-gyp rebuild && cd ../..",
"generate:betterauth": "pnpm dlx @better-auth/cli generate --config server/utils/auth.ts",
"migrate:betterauth": "pnpm dlx @better-auth/cli migrate --config server/utils/auth.ts"
},
"dependencies": {
@@ -35,5 +36,9 @@
"prettier": "3.5.1",
"typescript": "5.7.3",
"vue-tsc": "2.2.2"
},
"volta": {
"node": "22.16.0",
"pnpm": "10.11.0"
}
}

View File

@@ -0,0 +1,9 @@
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('vue:error', (error, instance, info) => {
console.error('Vue error:', error, 'Instance:', instance, 'Info:', info)
})
nuxtApp.hook('app:error', (error) => {
console.error('App error:', error)
})
})

View File

@@ -9,7 +9,7 @@ export const auth = betterAuth({
plugins: [
jwt({
jwt: {
issuer: 'http://192.168.178.105:3001',
issuer: 'http://192.168.178.114:3001',
expirationTime: '48h'
},
jwks: {
@@ -22,7 +22,7 @@ export const auth = betterAuth({
organization({
async sendInvitationEmail(data) {
console.log('Sending invitation email', data)
const inviteLink = `http://192.168.178.105:3001/accept-invitation/${data.id}`
const inviteLink = `http://192.168.178.114:3001/accept-invitation/${data.id}`
await resend.emails.send({
from: 'Acme <onboarding@resend.dev>',
to: data.email,