mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
feat: adding basic end-to-end testing and ci workflow (#225)
* feat: add end-to-end testing * chore: fix bug in github action. --------- Co-authored-by: charlesgauthereau <charles.gauthereau@soluce-technologies.com> Co-authored-by: killianlarcher <killian.larcher@soluce-technologies.com>
This commit is contained in:
co-authored by
charlesgauthereau
killianlarcher
parent
260b3ba1f9
commit
67246efafc
@@ -6,7 +6,6 @@ DATABASE_URL=postgresql://devuser:changeme@localhost:5433/devdb?schema=public
|
|||||||
|
|
||||||
# Project
|
# Project
|
||||||
PROJECT_NAME="Portabase"
|
PROJECT_NAME="Portabase"
|
||||||
PROJECT_DESCRIPTION="Portabase is a powerful database manager"
|
|
||||||
PROJECT_URL=http://localhost:8887
|
PROJECT_URL=http://localhost:8887
|
||||||
PROJECT_SECRET=
|
PROJECT_SECRET=
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
name: End-to-end testing
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
cache: 'pnpm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Build Docker image
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./docker/dockerfile/Dockerfile
|
||||||
|
push: false
|
||||||
|
load: true
|
||||||
|
tags: portabase/portabase:test
|
||||||
|
|
||||||
|
- name: Run app container
|
||||||
|
run: |
|
||||||
|
docker run -d --name myapp \
|
||||||
|
-p 8887:80 \
|
||||||
|
-e NODE_ENV=production \
|
||||||
|
-e PROJECT_URL=http://localhost:8887 \
|
||||||
|
-e PROJECT_SECRET=80af5e4c875dfded3a6ba511c6ed8b0160cad723f848ee0851403ed6141f6ba1 \
|
||||||
|
portabase/portabase:test
|
||||||
|
|
||||||
|
- name: Wait for app port to be listening
|
||||||
|
run: |
|
||||||
|
for i in {1..40}; do
|
||||||
|
|
||||||
|
if curl -fsS http://localhost:8887/ >/dev/null; then
|
||||||
|
echo "App is responding on port 8887"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "Waiting for app readiness... ($i/40)"
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
echo "Timeout: app never became ready"
|
||||||
|
docker logs myapp
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
- name: Install Playwright browsers
|
||||||
|
run: pnpm exec playwright install --with-deps chromium firefox webkit
|
||||||
|
|
||||||
|
- name: Run Playwright tests
|
||||||
|
run: pnpm exec playwright test --project chromium
|
||||||
|
env:
|
||||||
|
PROJECT_URL: http://localhost:8887
|
||||||
|
|
||||||
|
- name: Upload report if failed
|
||||||
|
if: failure()
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: playwright-report
|
||||||
|
path: playwright-report/
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
- name: Cleanup
|
||||||
|
if: always()
|
||||||
|
run: docker stop myapp && docker rm myapp || true
|
||||||
@@ -47,3 +47,10 @@ private/keys/*
|
|||||||
seeds/pocket-id/*.db
|
seeds/pocket-id/*.db
|
||||||
|
|
||||||
certificates
|
certificates
|
||||||
|
|
||||||
|
# Playwright
|
||||||
|
/test-results/
|
||||||
|
/playwright-report/
|
||||||
|
/blob-report/
|
||||||
|
/playwright/.cache/
|
||||||
|
/playwright/.auth/
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ import { author, geistMono, poppins } from "@/fonts/fonts";
|
|||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { Providers } from "./providers";
|
import { Providers } from "./providers";
|
||||||
|
|
||||||
const title = process.env.PROJECT_NAME ?? "App Title";
|
const title = process.env.PROJECT_NAME ?? "Portabase";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: {
|
title: {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ services:
|
|||||||
# context: .
|
# context: .
|
||||||
# dockerfile: docker/dockerfile/Dockerfile
|
# dockerfile: docker/dockerfile/Dockerfile
|
||||||
# target: prod
|
# target: prod
|
||||||
image: portabase/portabase:1.4.0
|
image: portabase/portabase:1.7.1
|
||||||
ports:
|
ports:
|
||||||
- '8887:80'
|
- '8887:80'
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
import {test, expect, Page} from '@playwright/test';
|
||||||
|
|
||||||
|
|
||||||
|
type UserCredentials = {
|
||||||
|
username: string
|
||||||
|
email: string
|
||||||
|
password: string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const users: Record<string, UserCredentials> = {
|
||||||
|
admin: {username: 'Admin', email: 'admin@example.com', password: 'testPASS123456!'},
|
||||||
|
normal: {username: 'John Doe', email: 'john.doe@example.com', password: 'testPASS123456!'},
|
||||||
|
}
|
||||||
|
|
||||||
|
async function register(page: Page, name: string, email: string, password: string, confirmPassword: string) {
|
||||||
|
await page.locator('input[name="name"]').fill(name)
|
||||||
|
await page.locator('input[name="email"]').fill(email)
|
||||||
|
await page.locator('input[name="password"]').fill(password)
|
||||||
|
await page.locator('input[name="confirmPassword"]').fill(confirmPassword)
|
||||||
|
|
||||||
|
await page.click('button[type="submit"]')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function login(page: Page, email: string, password: string) {
|
||||||
|
await page.locator('input[name="email"]').fill(email)
|
||||||
|
await page.locator('input[name="password"]').fill(password)
|
||||||
|
|
||||||
|
await page.locator('button:has-text("Login")').click()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function logout() {
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
test.describe.serial('User signup and login flows', () => {
|
||||||
|
|
||||||
|
test('Redirect to login if not connected', async ({page}) => {
|
||||||
|
await page.goto('/dashboard/projects');
|
||||||
|
await expect(page).toHaveURL("login?redirect=%2Fdashboard%2Fprojects");
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Password too short', async ({page}) => {
|
||||||
|
await page.goto('')
|
||||||
|
await page.click('text=Sign up')
|
||||||
|
await expect(page).toHaveURL('/register')
|
||||||
|
|
||||||
|
let password = '123456'
|
||||||
|
await register(page, users["admin"].username, users["admin"].email, password, password)
|
||||||
|
|
||||||
|
const toast = page.locator('text=Must have at least 8 character')
|
||||||
|
await expect(toast).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Password too simple', async ({page}) => {
|
||||||
|
await page.goto('')
|
||||||
|
await page.click('text=Sign up')
|
||||||
|
await expect(page).toHaveURL('/register')
|
||||||
|
|
||||||
|
let password = '12345678'
|
||||||
|
await register(page, users["admin"].username, users["admin"].email, password, password)
|
||||||
|
|
||||||
|
const toast = page.locator('text=Your password must contain at least one uppercase letter, one lowercase letter, one number, and one special character.')
|
||||||
|
await expect(toast).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Password and confirm password mismatch', async ({page}) => {
|
||||||
|
await page.goto('/')
|
||||||
|
await page.click('text=Sign up')
|
||||||
|
await expect(page).toHaveURL('/register')
|
||||||
|
|
||||||
|
let password = 'testPASS123456!'
|
||||||
|
let confirmPassword = 'testPASS123456!!'
|
||||||
|
await register(page, users["admin"].username, users["admin"].email, password, confirmPassword)
|
||||||
|
|
||||||
|
const toast = page.locator('text=The passwords did not match')
|
||||||
|
await expect(toast).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Successful register for admin', async ({page}) => {
|
||||||
|
await page.goto('/')
|
||||||
|
|
||||||
|
await page.click('text=Sign up')
|
||||||
|
await expect(page).toHaveURL('/register')
|
||||||
|
|
||||||
|
await register(page, users["admin"].username, users["admin"].email, users["admin"].password, users["admin"].password)
|
||||||
|
|
||||||
|
await expect(page).toHaveURL('/login')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('User already exists.', async ({page}) => {
|
||||||
|
await page.goto('/')
|
||||||
|
|
||||||
|
await page.click('text=Sign up')
|
||||||
|
await expect(page).toHaveURL('/register')
|
||||||
|
|
||||||
|
await register(page, users["admin"].username, users["admin"].email, users["admin"].password, users["admin"].password)
|
||||||
|
|
||||||
|
const toast = page.locator('text=User already exists. Use another email.')
|
||||||
|
await expect(toast).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Successful register for normal', async ({page}) => {
|
||||||
|
await page.goto('/')
|
||||||
|
|
||||||
|
await page.click('text=Sign up')
|
||||||
|
await expect(page).toHaveURL('/register')
|
||||||
|
|
||||||
|
await register(page, users["normal"].username, users["normal"].email, users["normal"].password, users["normal"].password)
|
||||||
|
|
||||||
|
await expect(page).toHaveURL('login')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Failed login because account not active', async ({page}) => {
|
||||||
|
await page.goto('/login')
|
||||||
|
await login(page, users["normal"].email, users["normal"].password)
|
||||||
|
|
||||||
|
const toast = page.locator('text=Your account is not active.')
|
||||||
|
await expect(toast).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Successful login', async ({page}) => {
|
||||||
|
await page.goto('login')
|
||||||
|
await login(page, users["admin"].email, users["admin"].password)
|
||||||
|
|
||||||
|
await expect(page).toHaveURL('/dashboard/home')
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// test('Successful logout', async ({page}) => {
|
||||||
|
// await logout()
|
||||||
|
// await expect(page).toHaveURL('/login')
|
||||||
|
// })
|
||||||
@@ -103,6 +103,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@iconify/react": "^6.0.2",
|
"@iconify/react": "^6.0.2",
|
||||||
|
"@playwright/test": "1.58.2",
|
||||||
"@react-email/preview-server": "4.3.2",
|
"@react-email/preview-server": "4.3.2",
|
||||||
"@react-email/render": "^2.0.4",
|
"@react-email/render": "^2.0.4",
|
||||||
"@release-it/bumper": "^7.0.5",
|
"@release-it/bumper": "^7.0.5",
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
import { defineConfig, devices } from '@playwright/test';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read environment variables from file.
|
||||||
|
* https://github.com/motdotla/dotenv
|
||||||
|
*/
|
||||||
|
// import dotenv from 'dotenv';
|
||||||
|
// import path from 'path';
|
||||||
|
// dotenv.config({ path: path.resolve(__dirname, '.env') });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See https://playwright.dev/docs/test-configuration.
|
||||||
|
*/
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: './e2e',
|
||||||
|
/* Run tests in files in parallel */
|
||||||
|
fullyParallel: true,
|
||||||
|
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||||
|
forbidOnly: !!process.env.CI,
|
||||||
|
/* Retry on CI only */
|
||||||
|
retries: process.env.CI ? 2 : 0,
|
||||||
|
/* Opt out of parallel tests on CI. */
|
||||||
|
workers: process.env.CI ? 1 : undefined,
|
||||||
|
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||||
|
reporter: 'html',
|
||||||
|
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||||
|
use: {
|
||||||
|
/* Base URL to use in actions like `await page.goto('')`. */
|
||||||
|
baseURL: process.env.PROJECT_URL,
|
||||||
|
|
||||||
|
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||||
|
trace: 'on-first-retry',
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Configure projects for major browsers */
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: 'chromium',
|
||||||
|
use: { ...devices['Desktop Chrome'] },
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'firefox',
|
||||||
|
use: { ...devices['Desktop Firefox'] },
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'webkit',
|
||||||
|
use: { ...devices['Desktop Safari'] },
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Test against mobile viewports. */
|
||||||
|
{
|
||||||
|
name: 'Mobile Chrome',
|
||||||
|
use: { ...devices['Pixel 5'] },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Mobile Safari',
|
||||||
|
use: { ...devices['iPhone 12'] },
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Test against branded browsers. */
|
||||||
|
// {
|
||||||
|
// name: 'Microsoft Edge',
|
||||||
|
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: 'Google Chrome',
|
||||||
|
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
|
||||||
|
/* Run your local dev server before starting the tests */
|
||||||
|
// webServer: {
|
||||||
|
// command: 'docker compose up -d && make up',
|
||||||
|
// url: 'http://localhost:8887',
|
||||||
|
// reuseExistingServer: !process.env.CI,
|
||||||
|
// },
|
||||||
|
});
|
||||||
Generated
+58
-18
@@ -10,10 +10,10 @@ importers:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@better-auth/passkey':
|
'@better-auth/passkey':
|
||||||
specifier: ^1.4.19
|
specifier: ^1.4.19
|
||||||
version: 1.4.19(@better-auth/core@1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@3.25.76))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0))(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-auth@1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(better-call@1.1.8(zod@3.25.76))(nanostores@1.1.0)
|
version: 1.4.19(@better-auth/core@1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@3.25.76))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0))(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-auth@1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(better-call@1.1.8(zod@3.25.76))(nanostores@1.1.0)
|
||||||
'@better-auth/sso':
|
'@better-auth/sso':
|
||||||
specifier: ^1.4.19
|
specifier: ^1.4.19
|
||||||
version: 1.4.19(@better-auth/utils@0.3.0)(better-auth@1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(better-call@1.1.8(zod@3.25.76))
|
version: 1.4.19(@better-auth/utils@0.3.0)(better-auth@1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(better-call@1.1.8(zod@3.25.76))
|
||||||
'@hookform/resolvers':
|
'@hookform/resolvers':
|
||||||
specifier: ^5.2.2
|
specifier: ^5.2.2
|
||||||
version: 5.2.2(react-hook-form@7.71.2(react@19.2.4))
|
version: 5.2.2(react-hook-form@7.71.2(react@19.2.4))
|
||||||
@@ -130,7 +130,7 @@ importers:
|
|||||||
version: 6.0.0
|
version: 6.0.0
|
||||||
better-auth:
|
better-auth:
|
||||||
specifier: 1.4.18
|
specifier: 1.4.18
|
||||||
version: 1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
version: 1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
class-variance-authority:
|
class-variance-authority:
|
||||||
specifier: ^0.7.1
|
specifier: ^0.7.1
|
||||||
version: 0.7.1
|
version: 0.7.1
|
||||||
@@ -175,10 +175,10 @@ importers:
|
|||||||
version: 12.34.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
version: 12.34.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
next:
|
next:
|
||||||
specifier: 16.1.5
|
specifier: 16.1.5
|
||||||
version: 16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
version: 16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
next-safe-action:
|
next-safe-action:
|
||||||
specifier: ^7.10.8
|
specifier: ^7.10.8
|
||||||
version: 7.10.8(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@3.25.76)
|
version: 7.10.8(next@16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@3.25.76)
|
||||||
next-themes:
|
next-themes:
|
||||||
specifier: ^0.4.6
|
specifier: ^0.4.6
|
||||||
version: 0.4.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
version: 0.4.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
@@ -267,9 +267,12 @@ importers:
|
|||||||
'@iconify/react':
|
'@iconify/react':
|
||||||
specifier: ^6.0.2
|
specifier: ^6.0.2
|
||||||
version: 6.0.2(react@19.2.4)
|
version: 6.0.2(react@19.2.4)
|
||||||
|
'@playwright/test':
|
||||||
|
specifier: 1.58.2
|
||||||
|
version: 1.58.2
|
||||||
'@react-email/preview-server':
|
'@react-email/preview-server':
|
||||||
specifier: 4.3.2
|
specifier: 4.3.2
|
||||||
version: 4.3.2(postcss@8.5.6)
|
version: 4.3.2(@playwright/test@1.58.2)(postcss@8.5.6)
|
||||||
'@react-email/render':
|
'@react-email/render':
|
||||||
specifier: ^2.0.4
|
specifier: ^2.0.4
|
||||||
version: 2.0.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
version: 2.0.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
@@ -1952,6 +1955,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
|
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
|
'@playwright/test@1.58.2':
|
||||||
|
resolution: {integrity: sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
'@prisma/client@6.7.0':
|
'@prisma/client@6.7.0':
|
||||||
resolution: {integrity: sha512-+k61zZn1XHjbZul8q6TdQLpuI/cvyfil87zqK2zpreNIXyXtpUv3+H/oM69hcsFcZXaokHJIzPAt5Z8C8eK2QA==}
|
resolution: {integrity: sha512-+k61zZn1XHjbZul8q6TdQLpuI/cvyfil87zqK2zpreNIXyXtpUv3+H/oM69hcsFcZXaokHJIzPAt5Z8C8eK2QA==}
|
||||||
engines: {node: '>=18.18'}
|
engines: {node: '>=18.18'}
|
||||||
@@ -4909,6 +4917,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==}
|
resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==}
|
||||||
engines: {node: '>=14.14'}
|
engines: {node: '>=14.14'}
|
||||||
|
|
||||||
|
fsevents@2.3.2:
|
||||||
|
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
|
||||||
|
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
fsevents@2.3.3:
|
fsevents@2.3.3:
|
||||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||||
@@ -6215,6 +6228,16 @@ packages:
|
|||||||
pkg-types@2.3.0:
|
pkg-types@2.3.0:
|
||||||
resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==}
|
resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==}
|
||||||
|
|
||||||
|
playwright-core@1.58.2:
|
||||||
|
resolution: {integrity: sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
playwright@1.58.2:
|
||||||
|
resolution: {integrity: sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
pluralize@8.0.0:
|
pluralize@8.0.0:
|
||||||
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
|
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -7695,23 +7718,23 @@ snapshots:
|
|||||||
nanostores: 1.1.0
|
nanostores: 1.1.0
|
||||||
zod: 4.3.6
|
zod: 4.3.6
|
||||||
|
|
||||||
'@better-auth/passkey@1.4.19(@better-auth/core@1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@3.25.76))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0))(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-auth@1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(better-call@1.1.8(zod@3.25.76))(nanostores@1.1.0)':
|
'@better-auth/passkey@1.4.19(@better-auth/core@1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@3.25.76))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0))(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-auth@1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(better-call@1.1.8(zod@3.25.76))(nanostores@1.1.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@better-auth/core': 1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@3.25.76))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0)
|
'@better-auth/core': 1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@3.25.76))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0)
|
||||||
'@better-auth/utils': 0.3.0
|
'@better-auth/utils': 0.3.0
|
||||||
'@better-fetch/fetch': 1.1.21
|
'@better-fetch/fetch': 1.1.21
|
||||||
'@simplewebauthn/browser': 13.2.2
|
'@simplewebauthn/browser': 13.2.2
|
||||||
'@simplewebauthn/server': 13.2.3
|
'@simplewebauthn/server': 13.2.3
|
||||||
better-auth: 1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
better-auth: 1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
better-call: 1.1.8(zod@3.25.76)
|
better-call: 1.1.8(zod@3.25.76)
|
||||||
nanostores: 1.1.0
|
nanostores: 1.1.0
|
||||||
zod: 4.3.6
|
zod: 4.3.6
|
||||||
|
|
||||||
'@better-auth/sso@1.4.19(@better-auth/utils@0.3.0)(better-auth@1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(better-call@1.1.8(zod@3.25.76))':
|
'@better-auth/sso@1.4.19(@better-auth/utils@0.3.0)(better-auth@1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(better-call@1.1.8(zod@3.25.76))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@better-auth/utils': 0.3.0
|
'@better-auth/utils': 0.3.0
|
||||||
'@better-fetch/fetch': 1.1.21
|
'@better-fetch/fetch': 1.1.21
|
||||||
better-auth: 1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
better-auth: 1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
better-call: 1.1.8(zod@3.25.76)
|
better-call: 1.1.8(zod@3.25.76)
|
||||||
fast-xml-parser: 5.3.9
|
fast-xml-parser: 5.3.9
|
||||||
jose: 6.1.3
|
jose: 6.1.3
|
||||||
@@ -8807,6 +8830,10 @@ snapshots:
|
|||||||
'@pkgjs/parseargs@0.11.0':
|
'@pkgjs/parseargs@0.11.0':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@playwright/test@1.58.2':
|
||||||
|
dependencies:
|
||||||
|
playwright: 1.58.2
|
||||||
|
|
||||||
'@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3)':
|
'@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3)':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
prisma: 6.7.0(typescript@5.9.3)
|
prisma: 6.7.0(typescript@5.9.3)
|
||||||
@@ -10186,7 +10213,7 @@ snapshots:
|
|||||||
md-to-react-email: 5.0.5(react@19.2.4)
|
md-to-react-email: 5.0.5(react@19.2.4)
|
||||||
react: 19.2.4
|
react: 19.2.4
|
||||||
|
|
||||||
'@react-email/preview-server@4.3.2(postcss@8.5.6)':
|
'@react-email/preview-server@4.3.2(@playwright/test@1.58.2)(postcss@8.5.6)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.26.10
|
'@babel/core': 7.26.10
|
||||||
'@babel/parser': 7.27.0
|
'@babel/parser': 7.27.0
|
||||||
@@ -10212,7 +10239,7 @@ snapshots:
|
|||||||
json5: 2.2.3
|
json5: 2.2.3
|
||||||
log-symbols: 4.1.0
|
log-symbols: 4.1.0
|
||||||
module-punycode: punycode@2.3.1
|
module-punycode: punycode@2.3.1
|
||||||
next: 15.5.2(@babel/core@7.26.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
next: 15.5.2(@babel/core@7.26.10)(@playwright/test@1.58.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
node-html-parser: 7.0.1
|
node-html-parser: 7.0.1
|
||||||
ora: 5.4.1
|
ora: 5.4.1
|
||||||
pretty-bytes: 6.1.1
|
pretty-bytes: 6.1.1
|
||||||
@@ -11187,7 +11214,7 @@ snapshots:
|
|||||||
|
|
||||||
before-after-hook@4.0.0: {}
|
before-after-hook@4.0.0: {}
|
||||||
|
|
||||||
better-auth@1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
better-auth@1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@better-auth/core': 1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@3.25.76))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0)
|
'@better-auth/core': 1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@3.25.76))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0)
|
||||||
'@better-auth/telemetry': 1.4.18(@better-auth/core@1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@3.25.76))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0))
|
'@better-auth/telemetry': 1.4.18(@better-auth/core@1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@3.25.76))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0))
|
||||||
@@ -11205,7 +11232,7 @@ snapshots:
|
|||||||
'@prisma/client': 6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3)
|
'@prisma/client': 6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3)
|
||||||
drizzle-kit: 0.31.9
|
drizzle-kit: 0.31.9
|
||||||
drizzle-orm: 0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))
|
drizzle-orm: 0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))
|
||||||
next: 16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
next: 16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
pg: 8.18.0
|
pg: 8.18.0
|
||||||
prisma: 6.7.0(typescript@5.9.3)
|
prisma: 6.7.0(typescript@5.9.3)
|
||||||
react: 19.2.4
|
react: 19.2.4
|
||||||
@@ -12564,6 +12591,9 @@ snapshots:
|
|||||||
jsonfile: 6.2.0
|
jsonfile: 6.2.0
|
||||||
universalify: 2.0.1
|
universalify: 2.0.1
|
||||||
|
|
||||||
|
fsevents@2.3.2:
|
||||||
|
optional: true
|
||||||
|
|
||||||
fsevents@2.3.3:
|
fsevents@2.3.3:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -13437,9 +13467,9 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
type-fest: 2.19.0
|
type-fest: 2.19.0
|
||||||
|
|
||||||
next-safe-action@7.10.8(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@3.25.76):
|
next-safe-action@7.10.8(next@16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@3.25.76):
|
||||||
dependencies:
|
dependencies:
|
||||||
next: 16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
next: 16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
react: 19.2.4
|
react: 19.2.4
|
||||||
react-dom: 19.2.4(react@19.2.4)
|
react-dom: 19.2.4(react@19.2.4)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
@@ -13450,7 +13480,7 @@ snapshots:
|
|||||||
react: 19.2.4
|
react: 19.2.4
|
||||||
react-dom: 19.2.4(react@19.2.4)
|
react-dom: 19.2.4(react@19.2.4)
|
||||||
|
|
||||||
next@15.5.2(@babel/core@7.26.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
next@15.5.2(@babel/core@7.26.10)(@playwright/test@1.58.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@next/env': 15.5.2
|
'@next/env': 15.5.2
|
||||||
'@swc/helpers': 0.5.15
|
'@swc/helpers': 0.5.15
|
||||||
@@ -13468,12 +13498,13 @@ snapshots:
|
|||||||
'@next/swc-linux-x64-musl': 15.5.2
|
'@next/swc-linux-x64-musl': 15.5.2
|
||||||
'@next/swc-win32-arm64-msvc': 15.5.2
|
'@next/swc-win32-arm64-msvc': 15.5.2
|
||||||
'@next/swc-win32-x64-msvc': 15.5.2
|
'@next/swc-win32-x64-msvc': 15.5.2
|
||||||
|
'@playwright/test': 1.58.2
|
||||||
sharp: 0.34.5
|
sharp: 0.34.5
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@babel/core'
|
- '@babel/core'
|
||||||
- babel-plugin-macros
|
- babel-plugin-macros
|
||||||
|
|
||||||
next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
next@16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@next/env': 16.1.5
|
'@next/env': 16.1.5
|
||||||
'@swc/helpers': 0.5.15
|
'@swc/helpers': 0.5.15
|
||||||
@@ -13492,6 +13523,7 @@ snapshots:
|
|||||||
'@next/swc-linux-x64-musl': 16.1.5
|
'@next/swc-linux-x64-musl': 16.1.5
|
||||||
'@next/swc-win32-arm64-msvc': 16.1.5
|
'@next/swc-win32-arm64-msvc': 16.1.5
|
||||||
'@next/swc-win32-x64-msvc': 16.1.5
|
'@next/swc-win32-x64-msvc': 16.1.5
|
||||||
|
'@playwright/test': 1.58.2
|
||||||
sharp: 0.34.5
|
sharp: 0.34.5
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@babel/core'
|
- '@babel/core'
|
||||||
@@ -13860,6 +13892,14 @@ snapshots:
|
|||||||
exsolve: 1.0.8
|
exsolve: 1.0.8
|
||||||
pathe: 2.0.3
|
pathe: 2.0.3
|
||||||
|
|
||||||
|
playwright-core@1.58.2: {}
|
||||||
|
|
||||||
|
playwright@1.58.2:
|
||||||
|
dependencies:
|
||||||
|
playwright-core: 1.58.2
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents: 2.3.2
|
||||||
|
|
||||||
pluralize@8.0.0: {}
|
pluralize@8.0.0: {}
|
||||||
|
|
||||||
possible-typed-array-names@1.1.0: {}
|
possible-typed-array-names@1.1.0: {}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
+45
-58
@@ -1,45 +1,28 @@
|
|||||||
import { betterAuth } from "better-auth";
|
import {betterAuth} from "better-auth";
|
||||||
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
import {drizzleAdapter} from "better-auth/adapters/drizzle";
|
||||||
import * as drizzleDb from "@/db";
|
import * as drizzleDb from "@/db";
|
||||||
import { db } from "@/db";
|
import {db} from "@/db";
|
||||||
import { env } from "@/env.mjs";
|
import {env} from "@/env.mjs";
|
||||||
import { nextCookies } from "better-auth/next-js";
|
import {nextCookies} from "better-auth/next-js";
|
||||||
import {
|
import {admin as adminPlugin, openAPI, Organization, organization, twoFactor} from "better-auth/plugins";
|
||||||
admin as adminPlugin,
|
import {ac, admin, orgAdmin, orgMember, orgOwner, pending, superadmin, user} from "@/lib/auth/permissions";
|
||||||
openAPI,
|
import {headers} from "next/headers";
|
||||||
Organization,
|
import {count, eq} from "drizzle-orm";
|
||||||
organization,
|
import {MemberWithUser, OrganizationWithMembersAndUsers,} from "@/db/schema/03_organization";
|
||||||
twoFactor,
|
import {sendEmail} from "@/lib/email";
|
||||||
} from "better-auth/plugins";
|
import {render} from "@react-email/render";
|
||||||
import {
|
import {withUpdatedAt} from "@/db/utils";
|
||||||
ac,
|
|
||||||
admin,
|
|
||||||
orgAdmin,
|
|
||||||
orgMember,
|
|
||||||
orgOwner,
|
|
||||||
pending,
|
|
||||||
superadmin,
|
|
||||||
user,
|
|
||||||
} from "@/lib/auth/permissions";
|
|
||||||
import { headers } from "next/headers";
|
|
||||||
import { count, eq } from "drizzle-orm";
|
|
||||||
import {
|
|
||||||
MemberWithUser,
|
|
||||||
OrganizationWithMembersAndUsers,
|
|
||||||
} from "@/db/schema/03_organization";
|
|
||||||
import { sendEmail } from "@/lib/email";
|
|
||||||
import { render } from "@react-email/render";
|
|
||||||
import { withUpdatedAt } from "@/db/utils";
|
|
||||||
import EmailVerification from "@/components/emails/auth/email-verification";
|
import EmailVerification from "@/components/emails/auth/email-verification";
|
||||||
import EmailForgotPassword from "@/components/emails/auth/email-forgot-password";
|
import EmailForgotPassword from "@/components/emails/auth/email-forgot-password";
|
||||||
import { getDeviceDetails } from "@/utils/detection";
|
import {getDeviceDetails} from "@/utils/detection";
|
||||||
import EmailNewLogin from "@/components/emails/auth/email-new-login";
|
import EmailNewLogin from "@/components/emails/auth/email-new-login";
|
||||||
import { sso } from "@better-auth/sso";
|
import {sso} from "@better-auth/sso";
|
||||||
import { SUPPORTED_PROVIDERS } from "@/lib/auth/config";
|
import {SUPPORTED_PROVIDERS} from "@/lib/auth/config";
|
||||||
import { passkey } from "@better-auth/passkey";
|
import {passkey} from "@better-auth/passkey";
|
||||||
import { getOidcProviders } from "./oidc";
|
import {getOidcProviders} from "./oidc";
|
||||||
import { APIError } from "better-auth/api";
|
import {APIError} from "better-auth/api";
|
||||||
import { getOAuthProviders } from "./oauth";
|
import {getOAuthProviders} from "./oauth";
|
||||||
|
|
||||||
|
|
||||||
const oidcProviders = getOidcProviders();
|
const oidcProviders = getOidcProviders();
|
||||||
|
|
||||||
@@ -60,7 +43,7 @@ export const auth = betterAuth({
|
|||||||
emailAndPassword: {
|
emailAndPassword: {
|
||||||
enabled: env.AUTH_EMAIL_PASSWORD_ENABLED === "true",
|
enabled: env.AUTH_EMAIL_PASSWORD_ENABLED === "true",
|
||||||
requireEmailVerification: false,
|
requireEmailVerification: false,
|
||||||
sendResetPassword: async ({ user, token }, request) => {
|
sendResetPassword: async ({user, token}, request) => {
|
||||||
if (env.AUTH_EMAIL_PASSWORD_ENABLED !== "true") {
|
if (env.AUTH_EMAIL_PASSWORD_ENABLED !== "true") {
|
||||||
throw new APIError("FORBIDDEN", {
|
throw new APIError("FORBIDDEN", {
|
||||||
message: "Password reset is disabled.",
|
message: "Password reset is disabled.",
|
||||||
@@ -90,7 +73,7 @@ export const auth = betterAuth({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
emailVerification: {
|
emailVerification: {
|
||||||
async sendVerificationEmail({ user, token, url }) {
|
async sendVerificationEmail({user, token, url}) {
|
||||||
await sendEmail({
|
await sendEmail({
|
||||||
to: user.email,
|
to: user.email,
|
||||||
subject: "Portabase Email Verification",
|
subject: "Portabase Email Verification",
|
||||||
@@ -129,7 +112,7 @@ export const auth = betterAuth({
|
|||||||
clientId: provider.client,
|
clientId: provider.client,
|
||||||
clientSecret: provider.secret,
|
clientSecret: provider.secret,
|
||||||
...(provider.id === "apple" && provider.appleBundleIdentifier
|
...(provider.id === "apple" && provider.appleBundleIdentifier
|
||||||
? { appBundleIdentifier: provider.appleBundleIdentifier }
|
? {appBundleIdentifier: provider.appleBundleIdentifier}
|
||||||
: {}),
|
: {}),
|
||||||
};
|
};
|
||||||
return acc;
|
return acc;
|
||||||
@@ -168,7 +151,7 @@ export const auth = betterAuth({
|
|||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
issuer: p.issuerUrl,
|
issuer: p.issuerUrl,
|
||||||
})),
|
})),
|
||||||
provisionUser: async ({ user: usr, userInfo, provider }) => {
|
provisionUser: async ({user: usr, userInfo, provider}) => {
|
||||||
const existingUser = await db.query.user.findFirst({
|
const existingUser = await db.query.user.findFirst({
|
||||||
where: eq(drizzleDb.schemas.user.email, usr.email),
|
where: eq(drizzleDb.schemas.user.email, usr.email),
|
||||||
});
|
});
|
||||||
@@ -212,7 +195,7 @@ export const auth = betterAuth({
|
|||||||
|
|
||||||
if (hasAccess) {
|
if (hasAccess) {
|
||||||
const userCount = (
|
const userCount = (
|
||||||
await db.select({ count: count() }).from(drizzleDb.schemas.user)
|
await db.select({count: count()}).from(drizzleDb.schemas.user)
|
||||||
)[0].count;
|
)[0].count;
|
||||||
const isSuperadmin = userCount === 0;
|
const isSuperadmin = userCount === 0;
|
||||||
|
|
||||||
@@ -240,7 +223,7 @@ export const auth = betterAuth({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const userCount = (
|
const userCount = (
|
||||||
await db.select({ count: count() }).from(drizzleDb.schemas.user)
|
await db.select({count: count()}).from(drizzleDb.schemas.user)
|
||||||
)[0].count;
|
)[0].count;
|
||||||
if (
|
if (
|
||||||
userCount === 0 &&
|
userCount === 0 &&
|
||||||
@@ -252,7 +235,7 @@ export const auth = betterAuth({
|
|||||||
if (existingUser) {
|
if (existingUser) {
|
||||||
await db
|
await db
|
||||||
.update(drizzleDb.schemas.user)
|
.update(drizzleDb.schemas.user)
|
||||||
.set({ role: roleToAssign, emailVerified: true })
|
.set({role: roleToAssign, emailVerified: true})
|
||||||
.where(eq(drizzleDb.schemas.user.id, existingUser.id));
|
.where(eq(drizzleDb.schemas.user.id, existingUser.id));
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
@@ -367,7 +350,7 @@ export const auth = betterAuth({
|
|||||||
create: {
|
create: {
|
||||||
async before(user, context) {
|
async before(user, context) {
|
||||||
const userCount = (
|
const userCount = (
|
||||||
await db.select({ count: count() }).from(drizzleDb.schemas.user)
|
await db.select({count: count()}).from(drizzleDb.schemas.user)
|
||||||
)[0].count;
|
)[0].count;
|
||||||
|
|
||||||
if (env.AUTH_SIGNUP_ENABLED !== "true" && userCount > 0) {
|
if (env.AUTH_SIGNUP_ENABLED !== "true" && userCount > 0) {
|
||||||
@@ -387,7 +370,7 @@ export const auth = betterAuth({
|
|||||||
},
|
},
|
||||||
async after(user, context) {
|
async after(user, context) {
|
||||||
const userCount = (
|
const userCount = (
|
||||||
await db.select({ count: count() }).from(drizzleDb.schemas.user)
|
await db.select({count: count()}).from(drizzleDb.schemas.user)
|
||||||
)[0].count;
|
)[0].count;
|
||||||
const role = userCount === 0 ? "owner" : "admin";
|
const role = userCount === 0 ? "owner" : "admin";
|
||||||
|
|
||||||
@@ -590,14 +573,15 @@ export const getSession = async () => {
|
|||||||
|
|
||||||
export const revokeSession = async (e: string) => {
|
export const revokeSession = async (e: string) => {
|
||||||
try {
|
try {
|
||||||
const { status } = await auth.api.revokeSession({
|
const {status} = await auth.api.revokeSession({
|
||||||
body: {
|
body: {
|
||||||
token: e,
|
token: e,
|
||||||
},
|
},
|
||||||
headers: await headers(),
|
headers: await headers(),
|
||||||
});
|
});
|
||||||
return status;
|
return status;
|
||||||
} catch (e) {}
|
} catch (e) {
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAccounts = async () => {
|
export const getAccounts = async () => {
|
||||||
@@ -608,7 +592,7 @@ export const getAccounts = async () => {
|
|||||||
|
|
||||||
export const unlinkAccount = async (provider: string, account: string) => {
|
export const unlinkAccount = async (provider: string, account: string) => {
|
||||||
try {
|
try {
|
||||||
const { status } = await auth.api.unlinkAccount({
|
const {status} = await auth.api.unlinkAccount({
|
||||||
body: {
|
body: {
|
||||||
providerId: provider,
|
providerId: provider,
|
||||||
accountId: account,
|
accountId: account,
|
||||||
@@ -617,27 +601,28 @@ export const unlinkAccount = async (provider: string, account: string) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
} catch (e) {}
|
} catch (e) {
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getOrganization = async ({
|
export const getOrganization = async ({
|
||||||
organizationId,
|
organizationId,
|
||||||
organizationSlug,
|
organizationSlug,
|
||||||
}: {
|
}: {
|
||||||
organizationId?: string;
|
organizationId?: string;
|
||||||
organizationSlug?: string;
|
organizationSlug?: string;
|
||||||
} = {}): Promise<OrganizationWithMembersAndUsers | null> => {
|
} = {}): Promise<OrganizationWithMembersAndUsers | null> => {
|
||||||
const query =
|
const query =
|
||||||
organizationId != null
|
organizationId != null
|
||||||
? { organizationId }
|
? {organizationId}
|
||||||
: organizationSlug != null
|
: organizationSlug != null
|
||||||
? { organizationSlug }
|
? {organizationSlug}
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await auth.api.getFullOrganization({
|
const response = await auth.api.getFullOrganization({
|
||||||
headers: await headers(),
|
headers: await headers(),
|
||||||
...(query ? { query } : {}),
|
...(query ? {query} : {}),
|
||||||
});
|
});
|
||||||
|
|
||||||
return response as OrganizationWithMembersAndUsers;
|
return response as OrganizationWithMembersAndUsers;
|
||||||
@@ -751,7 +736,7 @@ export const deleteOrganization = async (organizationId: string) => {
|
|||||||
|
|
||||||
export const checkSlugOrganization = async (slug: string) => {
|
export const checkSlugOrganization = async (slug: string) => {
|
||||||
try {
|
try {
|
||||||
const { status } = await auth.api.checkOrganizationSlug({
|
const {status} = await auth.api.checkOrganizationSlug({
|
||||||
headers: await headers(),
|
headers: await headers(),
|
||||||
body: {
|
body: {
|
||||||
slug,
|
slug,
|
||||||
@@ -759,7 +744,8 @@ export const checkSlugOrganization = async (slug: string) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
} catch {}
|
} catch {
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getActiveMember = async () => {
|
export const getActiveMember = async () => {
|
||||||
@@ -782,5 +768,6 @@ export const setActiveOrganization = async (slug: string) => {
|
|||||||
organizationSlug: slug,
|
organizationSlug: slug,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch {}
|
} catch {
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user