Merge branch 'canary' into feat/stripe-add-cancel-at
@@ -0,0 +1,325 @@
|
||||
name: Cherry-pick to Main
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Run every 5 minutes
|
||||
- cron: '*/5 * * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
cherry-pick:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'better-auth/better-auth'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.PAT_TOKEN }}
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
- name: Find PRs with merge-to-main label
|
||||
id: find-prs
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
// Get all closed PRs targeting canary branch
|
||||
const prs = await github.rest.pulls.list({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'closed',
|
||||
base: 'canary',
|
||||
per_page: 100,
|
||||
sort: 'updated',
|
||||
direction: 'desc'
|
||||
});
|
||||
|
||||
// Filter for merged PRs with merge-to-main label
|
||||
const prsToProcess = prs.data.filter(pr =>
|
||||
pr.merged_at &&
|
||||
pr.labels.some(label => label.name === 'merge-to-main')
|
||||
);
|
||||
|
||||
console.log(`Found ${prsToProcess.length} PRs with merge-to-main label`);
|
||||
|
||||
if (prsToProcess.length === 0) {
|
||||
console.log('No PRs to process');
|
||||
core.setOutput('has_prs', 'false');
|
||||
return;
|
||||
}
|
||||
|
||||
core.setOutput('has_prs', 'true');
|
||||
core.setOutput('prs_to_process', JSON.stringify(prsToProcess.map(pr => ({
|
||||
number: pr.number,
|
||||
merge_commit_sha: pr.merge_commit_sha,
|
||||
title: pr.title
|
||||
}))));
|
||||
|
||||
- name: Cherry-pick to main
|
||||
id: cherry-pick
|
||||
if: steps.find-prs.outputs.has_prs == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.PAT_TOKEN }}
|
||||
run: |
|
||||
# Fetch all branches
|
||||
git fetch origin main
|
||||
git fetch origin canary
|
||||
|
||||
# Checkout main branch
|
||||
git checkout -B main origin/main
|
||||
|
||||
set +e # Don't exit on error
|
||||
|
||||
echo "Processing PRs with merge-to-main label..."
|
||||
echo '${{ steps.find-prs.outputs.prs_to_process }}' > prs.json
|
||||
|
||||
success_prs=""
|
||||
conflict_prs=""
|
||||
skipped_prs=""
|
||||
success_count=0
|
||||
conflict_count=0
|
||||
skipped_count=0
|
||||
|
||||
for row in $(jq -r '.[] | @base64' prs.json); do
|
||||
_jq() {
|
||||
echo ${row} | base64 --decode | jq -r ${1}
|
||||
}
|
||||
|
||||
PR_NUM=$(_jq '.number')
|
||||
MERGE_COMMIT=$(_jq '.merge_commit_sha')
|
||||
PR_TITLE=$(_jq '.title')
|
||||
|
||||
echo "----------------------------------------"
|
||||
echo "Processing PR #$PR_NUM: $PR_TITLE"
|
||||
echo "Merge commit: $MERGE_COMMIT"
|
||||
|
||||
# Check if commit already exists in main
|
||||
if git branch -r --contains "$MERGE_COMMIT" | grep -q "origin/main"; then
|
||||
echo "✓ PR #$PR_NUM already in main, will remove label"
|
||||
skipped_prs="$skipped_prs $PR_NUM"
|
||||
skipped_count=$((skipped_count + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
# Attempt cherry-pick
|
||||
if git cherry-pick -m 1 "$MERGE_COMMIT"; then
|
||||
echo "✅ Successfully cherry-picked PR #$PR_NUM"
|
||||
success_prs="$success_prs $PR_NUM"
|
||||
success_count=$((success_count + 1))
|
||||
else
|
||||
echo "❌ Conflict in PR #$PR_NUM"
|
||||
conflict_prs="$conflict_prs $PR_NUM"
|
||||
git cherry-pick --abort
|
||||
conflict_count=$((conflict_count + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
set -e # Re-enable exit on error
|
||||
|
||||
echo "----------------------------------------"
|
||||
echo "Summary:"
|
||||
echo " Success: $success_count"
|
||||
echo " Conflicts: $conflict_count"
|
||||
echo " Already in main: $skipped_count"
|
||||
|
||||
# Push all successful cherry-picks
|
||||
if [ $success_count -gt 0 ]; then
|
||||
git push origin main
|
||||
echo "✅ Pushed $success_count cherry-pick(s) to main"
|
||||
fi
|
||||
|
||||
# Save results for next steps
|
||||
echo "success_prs=$success_prs" >> $GITHUB_OUTPUT
|
||||
echo "conflict_prs=$conflict_prs" >> $GITHUB_OUTPUT
|
||||
echo "skipped_prs=$skipped_prs" >> $GITHUB_OUTPUT
|
||||
echo "success_count=$success_count" >> $GITHUB_OUTPUT
|
||||
echo "conflict_count=$conflict_count" >> $GITHUB_OUTPUT
|
||||
echo "skipped_count=$skipped_count" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Remove label and comment on successful PRs
|
||||
if: always() && steps.cherry-pick.outputs.success_prs != ''
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const successPrs = '${{ steps.cherry-pick.outputs.success_prs }}'.trim().split(' ').filter(Boolean);
|
||||
|
||||
for (const prNumber of successPrs) {
|
||||
console.log(`Processing successful PR #${prNumber}`);
|
||||
|
||||
// Remove merge-to-main label
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
name: 'merge-to-main'
|
||||
});
|
||||
console.log(`✅ Removed merge-to-main label from PR #${prNumber}`);
|
||||
} catch (error) {
|
||||
console.log(`⚠️ Could not remove merge-to-main label from PR #${prNumber}: ${error.message}`);
|
||||
}
|
||||
|
||||
// Remove merge-to-main-failure label if it exists
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
name: 'merge-to-main-failure'
|
||||
});
|
||||
console.log(`✅ Removed merge-to-main-failure label from PR #${prNumber}`);
|
||||
} catch (error) {
|
||||
// Ignore if label doesn't exist
|
||||
if (error.status !== 404) {
|
||||
console.log(`⚠️ Could not remove merge-to-main-failure label from PR #${prNumber}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Add success comment
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
body: '✅ Successfully cherry-picked to `main` branch!'
|
||||
});
|
||||
}
|
||||
|
||||
- name: Remove label from skipped PRs
|
||||
if: always() && steps.cherry-pick.outputs.skipped_prs != ''
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const skippedPrs = '${{ steps.cherry-pick.outputs.skipped_prs }}'.trim().split(' ').filter(Boolean);
|
||||
|
||||
for (const prNumber of skippedPrs) {
|
||||
console.log(`Processing skipped PR #${prNumber}`);
|
||||
|
||||
// Remove merge-to-main label
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
name: 'merge-to-main'
|
||||
});
|
||||
console.log(`✅ Removed merge-to-main label from PR #${prNumber}`);
|
||||
} catch (error) {
|
||||
console.log(`⚠️ Could not remove merge-to-main label from PR #${prNumber}: ${error.message}`);
|
||||
}
|
||||
|
||||
// Remove merge-to-main-failure label if it exists
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
name: 'merge-to-main-failure'
|
||||
});
|
||||
console.log(`✅ Removed merge-to-main-failure label from PR #${prNumber}`);
|
||||
} catch (error) {
|
||||
// Ignore if label doesn't exist
|
||||
if (error.status !== 404) {
|
||||
console.log(`⚠️ Could not remove merge-to-main-failure label from PR #${prNumber}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Add info comment
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
body: 'ℹ️ This commit already exists in the `main` branch. Removed `merge-to-main` label.'
|
||||
});
|
||||
}
|
||||
|
||||
- name: Comment on PRs with conflicts
|
||||
if: always() && steps.cherry-pick.outputs.conflict_prs != ''
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const conflictPrs = '${{ steps.cherry-pick.outputs.conflict_prs }}'.trim().split(' ').filter(Boolean);
|
||||
|
||||
for (const prNumber of conflictPrs) {
|
||||
console.log(`Checking PR #${prNumber} for existing conflict comment`);
|
||||
|
||||
// Check if we've already commented about conflicts
|
||||
const comments = await github.rest.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
});
|
||||
|
||||
const hasConflictComment = comments.data.some(comment =>
|
||||
comment.body.includes('Cherry-pick to `main` failed due to conflicts')
|
||||
);
|
||||
|
||||
if (hasConflictComment) {
|
||||
console.log(`⏭️ Skipping PR #${prNumber} - conflict comment already exists`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`Adding conflict comment and label to PR #${prNumber}`);
|
||||
|
||||
// Add merge-to-main-failure label
|
||||
try {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
labels: ['merge-to-main-failure']
|
||||
});
|
||||
console.log(`✅ Added merge-to-main-failure label to PR #${prNumber}`);
|
||||
} catch (error) {
|
||||
console.log(`⚠️ Could not add label to PR #${prNumber}: ${error.message}`);
|
||||
}
|
||||
|
||||
// Remove merge-to-main label to stop retry attempts
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
name: 'merge-to-main'
|
||||
});
|
||||
console.log(`✅ Removed merge-to-main label from PR #${prNumber}`);
|
||||
} catch (error) {
|
||||
console.log(`⚠️ Could not remove label from PR #${prNumber}: ${error.message}`);
|
||||
}
|
||||
|
||||
// Add conflict comment
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
body: `⚠️ **Cherry-pick to \`main\` failed due to conflicts!**\n\nPlease manually cherry-pick this commit to the \`main\` branch and resolve the conflicts.`
|
||||
});
|
||||
}
|
||||
|
||||
- name: Workflow Summary
|
||||
if: always() && steps.find-prs.outputs.has_prs == 'true'
|
||||
run: |
|
||||
echo "### Cherry-pick Summary" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- ✅ Success: ${{ steps.cherry-pick.outputs.success_count }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- ⚠️ Conflicts: ${{ steps.cherry-pick.outputs.conflict_count }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- ℹ️ Already in main: ${{ steps.cherry-pick.outputs.skipped_count }}" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
if [ -n "${{ steps.cherry-pick.outputs.success_prs }}" ]; then
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Successful PRs:** ${{ steps.cherry-pick.outputs.success_prs }}" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
if [ -n "${{ steps.cherry-pick.outputs.conflict_prs }}" ]; then
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**PRs with conflicts:** ${{ steps.cherry-pick.outputs.conflict_prs }}" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
@@ -39,7 +39,24 @@ When contributing to Better Auth:
|
||||
cd better-auth
|
||||
```
|
||||
3. Install Node.js (LTS version recommended)
|
||||
4. Install pnpm if you haven't already:
|
||||
|
||||
> **Note**: This project is configured to use [nvm](https://github.com/nvm-sh/nvm) to manage the local Node.js version, as such this is simplest way to get you up and running.
|
||||
|
||||
Once installed, use:
|
||||
|
||||
```bash
|
||||
$ nvm install
|
||||
$ nvm use
|
||||
```
|
||||
|
||||
Alternatively, see Node.js [installation](https://nodejs.org/en/download) for other supported methods.
|
||||
|
||||
4. Install `pnpm` if you haven't already:
|
||||
|
||||
> **Note:** This project is configured to manage [pnpm](https://pnpm.io/) via [corepack](https://github.com/nodejs/corepack). Once installed, upon usage you'll be prompted to install the correct pnpm version
|
||||
|
||||
Alternatively, use `npm` to install it:
|
||||
|
||||
```bash
|
||||
npm install -g pnpm
|
||||
```
|
||||
@@ -106,6 +123,9 @@ pnpm lint:fix
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
> Note: On MacOS, the **mssql** container will likely require Rosetta emulation and at least 2GB of RAM of allocated memory. See their [container requirements](https://learn.microsoft.com/en-us/sql/linux/quickstart-install-connect-docker?view=sql-server-ver17&tabs=cli&pivots=cs1-bash#prerequisites).
|
||||
|
||||
5. Run the test suite:
|
||||
```bash
|
||||
# Run all tests
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</h2>
|
||||
|
||||
<p align="center">
|
||||
The most comprehensive authentication library for TypeScript
|
||||
The most comprehensive authentication framework for TypeScript
|
||||
<br />
|
||||
<a href="https://better-auth.com"><strong>Learn more »</strong></a>
|
||||
<br />
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"enabled": true,
|
||||
"indentStyle": "tab"
|
||||
},
|
||||
"assist": { "actions": { "source": { "organizeImports": "off" } } },
|
||||
"assist": { "actions": { "source": { "organizeImports": "on" } } },
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
GITHUB_CLIENT_ID=
|
||||
GITHUB_CLIENT_SECRET=
|
||||
GOOGLE_CLIENT_ID=
|
||||
GOOGLE_CLIENT_SECRET=
|
||||
BETTER_AUTH_SECRET=
|
||||
DATABASE_URL=
|
||||
BETTER_AUTH_URL=http://localhost:8081
|
||||
@@ -1,3 +0,0 @@
|
||||
/// <reference types="expo/types" />
|
||||
|
||||
// NOTE: This file should not be edited and should be in your git ignore
|
||||
@@ -0,0 +1,7 @@
|
||||
GITHUB_CLIENT_ID=
|
||||
GITHUB_CLIENT_SECRET=
|
||||
GOOGLE_CLIENT_ID=
|
||||
GOOGLE_CLIENT_SECRET=
|
||||
BETTER_AUTH_SECRET=
|
||||
DATABASE_URL=postgres://user:password@localhost:5432/better_auth
|
||||
BETTER_AUTH_URL=http://localhost:8081
|
||||
|
Before Width: | Height: | Size: 155 KiB After Width: | Height: | Size: 155 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"name": "expo-example",
|
||||
"main": "index.ts",
|
||||
"name": "@better-auth/expo-example",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
@@ -15,7 +14,7 @@
|
||||
"dependencies": {
|
||||
"@better-auth/expo": "workspace:*",
|
||||
"@expo/metro-runtime": "^6.1.2",
|
||||
"@expo/vector-icons": "^15.0.2",
|
||||
"@expo/vector-icons": "^15.0.3",
|
||||
"@nanostores/react": "^1.0.0",
|
||||
"@react-native-async-storage/async-storage": "2.2.0",
|
||||
"@react-navigation/native": "^7.1.17",
|
||||
@@ -27,23 +26,25 @@
|
||||
"babel-plugin-transform-import-meta": "^2.2.1",
|
||||
"better-auth": "workspace:*",
|
||||
"better-sqlite3": "^11.6.0",
|
||||
"expo": "~54.0.10",
|
||||
"expo-constants": "~18.0.9",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"expo": "~54.0.21",
|
||||
"expo-constants": "~18.0.10",
|
||||
"expo-crypto": "^15.0.7",
|
||||
"expo-font": "~14.0.8",
|
||||
"expo-font": "~14.0.9",
|
||||
"expo-linking": "~8.0.8",
|
||||
"expo-router": "~6.0.8",
|
||||
"expo-router": "~6.0.14",
|
||||
"expo-secure-store": "~15.0.7",
|
||||
"expo-splash-screen": "~31.0.10",
|
||||
"expo-status-bar": "~3.0.8",
|
||||
"expo-system-ui": "~6.0.7",
|
||||
"expo-web-browser": "~15.0.7",
|
||||
"expo-system-ui": "~6.0.8",
|
||||
"expo-web-browser": "~15.0.8",
|
||||
"nanostores": "^0.11.3",
|
||||
"nativewind": "^4.1.23",
|
||||
"pg": "^8.13.1",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-native": "~0.81.4",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-native": "~0.81.5",
|
||||
"react-native-css-interop": "^0.2.1",
|
||||
"react-native-gesture-handler": "~2.28.0",
|
||||
"react-native-reanimated": "~4.1.2",
|
||||
@@ -52,6 +53,7 @@
|
||||
"react-native-svg": "^15.12.1",
|
||||
"react-native-web": "~0.21.1",
|
||||
"react-native-worklets": "^0.5.1",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwindcss": "^3.4.16"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -59,6 +61,6 @@
|
||||
"@babel/preset-env": "^7.26.0",
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@types/babel__core": "^7.20.5",
|
||||
"@types/react": "^19.2.2"
|
||||
"@types/react": "^19.1.17"
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Slot } from "expo-router";
|
||||
import "../global.css";
|
||||
import { ImageBackground, StyleSheet, View } from "react-native";
|
||||
import { SafeAreaProvider } from "react-native-safe-area-context";
|
||||
import { ImageBackground, View } from "react-native";
|
||||
import { StyleSheet } from "react-native";
|
||||
|
||||
export default function RootLayout() {
|
||||
return (
|
||||
@@ -1,13 +1,13 @@
|
||||
import Ionicons from "@expo/vector-icons/AntDesign";
|
||||
import { useStore } from "@nanostores/react";
|
||||
import { router } from "expo-router";
|
||||
import { useEffect } from "react";
|
||||
import { View } from "react-native";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardFooter, CardHeader } from "@/components/ui/card";
|
||||
import { Text } from "@/components/ui/text";
|
||||
import { authClient } from "@/lib/auth-client";
|
||||
import { View } from "react-native";
|
||||
import Ionicons from "@expo/vector-icons/AntDesign";
|
||||
import { router } from "expo-router";
|
||||
import { useEffect } from "react";
|
||||
import { useStore } from "@nanostores/react";
|
||||
|
||||
export default function Dashboard() {
|
||||
const { data: session, isPending } = useStore(authClient.useSession);
|
||||
@@ -1,3 +1,7 @@
|
||||
import Icons from "@expo/vector-icons/AntDesign";
|
||||
import { router } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
@@ -9,10 +13,6 @@ import {
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Text } from "@/components/ui/text";
|
||||
import { authClient } from "@/lib/auth-client";
|
||||
import { useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import Icons from "@expo/vector-icons/AntDesign";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function ForgetPassword() {
|
||||
const [email, setEmail] = useState("");
|
||||
@@ -1,14 +1,14 @@
|
||||
import Ionicons from "@expo/vector-icons/AntDesign";
|
||||
import { useStore } from "@nanostores/react";
|
||||
import { router, useNavigationContainerRef } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Image, View } from "react-native";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Text } from "@/components/ui/text";
|
||||
import { authClient } from "@/lib/auth-client";
|
||||
import { Image, View } from "react-native";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { useEffect, useState } from "react";
|
||||
import { router, useNavigationContainerRef } from "expo-router";
|
||||
import { useStore } from "@nanostores/react";
|
||||
|
||||
export default function Index() {
|
||||
const { data: isAuthenticated } = useStore(authClient.useSession);
|
||||
@@ -1,12 +1,11 @@
|
||||
import { useRouter } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { Image, KeyboardAvoidingView, View } from "react-native";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Text } from "@/components/ui/text";
|
||||
import { authClient } from "@/lib/auth-client";
|
||||
import { KeyboardAvoidingView, View } from "react-native";
|
||||
import { Image } from "react-native";
|
||||
import { useRouter } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function SignUp() {
|
||||
const router = useRouter();
|
||||
@@ -1,8 +1,8 @@
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
import * as React from "react";
|
||||
import { Pressable } from "react-native";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { TextClassContext } from "@/components/ui/text";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const buttonVariants = cva(
|
||||
"group flex items-center justify-center rounded-md web:ring-offset-background web:transition-colors web:focus-visible:outline-none web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2",
|
||||
@@ -16,7 +16,7 @@ const buttonVariants = cva(
|
||||
secondary: "bg-secondary web:hover:opacity-80 active:opacity-80",
|
||||
ghost:
|
||||
"web:hover:bg-accent web:hover:text-accent-foreground active:bg-accent",
|
||||
link: "web:underline-offset-4 web:hover:underline web:focus:underline ",
|
||||
link: "web:underline-offset-4 web:hover:underline web:focus:underline",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 px-4 py-2 native:h-12 native:px-5 native:py-3",
|
||||
@@ -1,8 +1,8 @@
|
||||
import { TextRef, ViewRef } from "@rn-primitives/types";
|
||||
import * as React from "react";
|
||||
import { Text, type TextProps, View, type ViewProps } from "react-native";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { TextClassContext } from "@/components/ui/text";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Card = React.forwardRef<ViewRef, ViewProps>(
|
||||
({ className, ...props }, ref) => (
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createAuthClient } from "better-auth/client";
|
||||
import { expoClient } from "@better-auth/expo/client";
|
||||
import { createAuthClient } from "better-auth/client";
|
||||
import * as SecureStore from "expo-secure-store";
|
||||
|
||||
export const authClient = createAuthClient({
|
||||
@@ -1,5 +1,5 @@
|
||||
import { betterAuth } from "better-auth";
|
||||
import { expo } from "@better-auth/expo";
|
||||
import { betterAuth } from "better-auth";
|
||||
import { Pool } from "pg";
|
||||
|
||||
export const auth = betterAuth({
|
||||
@@ -1,4 +1,5 @@
|
||||
import { X } from "lucide-react-native";
|
||||
import { iconWithClassName } from "./iconWithClassName";
|
||||
|
||||
iconWithClassName(X);
|
||||
export { X };
|
||||
@@ -1,5 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { AlertCircle, ArrowLeft, CheckCircle2 } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
@@ -13,9 +16,6 @@ import {
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { client } from "@/lib/auth-client";
|
||||
import { AlertCircle, ArrowLeft, CheckCircle2 } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function Component() {
|
||||
const [email, setEmail] = useState("");
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { AlertCircle } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
@@ -12,10 +16,6 @@ import {
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { PasswordInput } from "@/components/ui/password-input";
|
||||
import { client } from "@/lib/auth-client";
|
||||
import { AlertCircle } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
export default function ResetPassword() {
|
||||
const [password, setPassword] = useState("");
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useEffect } from "react";
|
||||
import { toast } from "sonner";
|
||||
import SignIn from "@/components/sign-in";
|
||||
import { SignUp } from "@/components/sign-up";
|
||||
import { Tabs } from "@/components/ui/tabs2";
|
||||
import { client } from "@/lib/auth-client";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useEffect } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { getCallbackURL } from "@/lib/shared";
|
||||
|
||||
export default function Page() {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { AlertCircle, CheckCircle2, Mail } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
@@ -11,9 +14,6 @@ import {
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { client } from "@/lib/auth-client";
|
||||
import { AlertCircle, CheckCircle2, Mail } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function Component() {
|
||||
const [otp, setOtp] = useState("");
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { AlertCircle, CheckCircle2 } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
@@ -12,9 +15,6 @@ import {
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { client } from "@/lib/auth-client";
|
||||
import { AlertCircle, CheckCircle2 } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function Component() {
|
||||
const [totpCode, setTotpCode] = useState("");
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import {
|
||||
Card,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
CardDescription,
|
||||
CardContent,
|
||||
CardFooter,
|
||||
} from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { AlertCircle } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
|
||||
export function InvitationError() {
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { CheckIcon, XIcon } from "lucide-react";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
@@ -9,9 +12,6 @@ import {
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { CheckIcon, XIcon } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { client, organization } from "@/lib/auth-client";
|
||||
import { InvitationError } from "./invitation-error";
|
||||
|
||||
@@ -1,9 +1,36 @@
|
||||
"use client";
|
||||
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { format } from "date-fns";
|
||||
import {
|
||||
Calendar as CalendarIcon,
|
||||
Loader2,
|
||||
Plus,
|
||||
RefreshCw,
|
||||
Trash,
|
||||
UserCircle,
|
||||
} from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { Toaster, toast } from "sonner";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Calendar } from "@/components/ui/calendar";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -19,35 +46,8 @@ import {
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { toast, Toaster } from "sonner";
|
||||
import { client } from "@/lib/auth-client";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useRouter } from "next/navigation";
|
||||
import {
|
||||
Loader2,
|
||||
Plus,
|
||||
Trash,
|
||||
RefreshCw,
|
||||
UserCircle,
|
||||
Calendar as CalendarIcon,
|
||||
} from "lucide-react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Calendar } from "@/components/ui/calendar";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { format } from "date-fns";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
|
||||
type User = {
|
||||
id: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { auth } from "@/lib/auth";
|
||||
import { toNextJsHandler } from "better-auth/next-js";
|
||||
import { auth } from "@/lib/auth";
|
||||
|
||||
export const { GET, POST } = toNextJsHandler(auth);
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { AlertCircle, Loader2 } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
@@ -11,9 +14,6 @@ import {
|
||||
} from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { AlertCircle, Loader2 } from "lucide-react";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
import { client } from "@/lib/auth-client";
|
||||
|
||||
export default function RegisterOAuthClient() {
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
"use client";
|
||||
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { useState, useTransition } from "react";
|
||||
import { signIn, client } from "@/lib/auth-client";
|
||||
import { toast } from "sonner";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
CardFooter,
|
||||
} from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { toast } from "sonner";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { client, signIn } from "@/lib/auth-client";
|
||||
|
||||
export default function ClientTest() {
|
||||
const [email, setEmail] = useState("");
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { ArrowUpFromLine, CreditCard, RefreshCcw } from "lucide-react";
|
||||
import { useId, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
@@ -11,9 +14,6 @@ import { Label } from "@/components/ui/label";
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||
import { client } from "@/lib/auth-client";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { ArrowUpFromLine, CreditCard, RefreshCcw } from "lucide-react";
|
||||
import { useId, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
function Component(props: { currentPlan?: string; isTrial?: boolean }) {
|
||||
const [selectedPlan, setSelectedPlan] = useState("plus");
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
"use client";
|
||||
|
||||
import { ChevronDownIcon, PlusIcon } from "@radix-ui/react-icons";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { Loader2, MailPlus } from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import { useEffect, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import CopyButton from "@/components/ui/copy-button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
@@ -34,13 +41,6 @@ import {
|
||||
useSession,
|
||||
} from "@/lib/auth-client";
|
||||
import { ActiveOrganization, Session } from "@/lib/auth-types";
|
||||
import { ChevronDownIcon, PlusIcon } from "@radix-ui/react-icons";
|
||||
import { Loader2, MailPlus } from "lucide-react";
|
||||
import { useState, useEffect } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import CopyButton from "@/components/ui/copy-button";
|
||||
import Image from "next/image";
|
||||
|
||||
export function OrganizationCard(props: {
|
||||
session: Session | null;
|
||||
@@ -82,7 +82,7 @@ export function OrganizationCard(props: {
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start">
|
||||
<DropdownMenuItem
|
||||
className=" py-1"
|
||||
className="py-1"
|
||||
onClick={async () => {
|
||||
organization.setActive({
|
||||
organizationId: null,
|
||||
@@ -94,7 +94,7 @@ export function OrganizationCard(props: {
|
||||
</DropdownMenuItem>
|
||||
{organizations.data?.map((org) => (
|
||||
<DropdownMenuItem
|
||||
className=" py-1"
|
||||
className="py-1"
|
||||
key={org.id}
|
||||
onClick={async () => {
|
||||
if (org.id === optimisticOrg?.id) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { auth } from "@/lib/auth";
|
||||
import { headers } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
import UserCard from "./user-card";
|
||||
import { OrganizationCard } from "./organization-card";
|
||||
import AccountSwitcher from "@/components/account-switch";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { OrganizationCard } from "./organization-card";
|
||||
import UserCard from "./user-card";
|
||||
|
||||
export default async function DashboardPage() {
|
||||
const [session, activeSessions, deviceSessions, organization, subscriptions] =
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import { Sparkles } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function UpgradeButton() {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
@@ -1,31 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { PasswordInput } from "@/components/ui/password-input";
|
||||
import { client, signOut, useSession } from "@/lib/auth-client";
|
||||
import { Session } from "@/lib/auth-types";
|
||||
import { Subscription } from "@better-auth/stripe";
|
||||
import { MobileIcon } from "@radix-ui/react-icons";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import {
|
||||
Edit,
|
||||
Fingerprint,
|
||||
@@ -43,8 +20,35 @@ import {
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState, useTransition } from "react";
|
||||
import QRCode from "react-qr-code";
|
||||
import { toast } from "sonner";
|
||||
import { UAParser } from "ua-parser-js";
|
||||
import { SubscriptionTierLabel } from "@/components/tier-labels";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import CopyButton from "@/components/ui/copy-button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { PasswordInput } from "@/components/ui/password-input";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
@@ -53,13 +57,9 @@ import {
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import QRCode from "react-qr-code";
|
||||
import CopyButton from "@/components/ui/copy-button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { SubscriptionTierLabel } from "@/components/tier-labels";
|
||||
import { client, signOut, useSession } from "@/lib/auth-client";
|
||||
import { Session } from "@/lib/auth-types";
|
||||
import { Component } from "./change-plan";
|
||||
import { Subscription } from "@better-auth/stripe";
|
||||
|
||||
export default function UserCard(props: {
|
||||
session: Session | null;
|
||||
@@ -212,7 +212,7 @@ export default function UserCard(props: {
|
||||
session.userAgent}
|
||||
, {new UAParser(session.userAgent || "").getBrowser().name}
|
||||
<button
|
||||
className="text-red-500 opacity-80 cursor-pointer text-xs border-muted-foreground border-red-600 underline "
|
||||
className="text-red-500 opacity-80 cursor-pointer text-xs border-muted-foreground border-red-600 underline"
|
||||
onClick={async () => {
|
||||
setIsTerminating(session.id);
|
||||
const res = await client.revokeSession({
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useTransition } from "react";
|
||||
import { Check, Loader2, X } from "lucide-react";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { client, useSession } from "@/lib/auth-client";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useState, useTransition } from "react";
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||
import { Loader2, Check, X } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { client, useSession } from "@/lib/auth-client";
|
||||
|
||||
export default function DeviceApprovalPage() {
|
||||
const router = useRouter();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { X } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card } from "@/components/ui/card";
|
||||
|
||||
export default function DeviceDeniedPage() {
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { auth } from "@/lib/auth";
|
||||
import { headers } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
import { auth } from "@/lib/auth";
|
||||
|
||||
export default async function DevicePage({
|
||||
children,
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useTransition } from "react";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { client } from "@/lib/auth-client";
|
||||
import { useState, useTransition } from "react";
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { client } from "@/lib/auth-client";
|
||||
|
||||
export default function DeviceAuthorizationPage() {
|
||||
const router = useRouter();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Check } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card } from "@/components/ui/card";
|
||||
|
||||
export default function DeviceSuccessPage() {
|
||||
return (
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
"use client";
|
||||
import React from "react";
|
||||
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import React from "react";
|
||||
import { Logo } from "@/components/logo";
|
||||
|
||||
export function Features() {
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col lg:flex-row bg-white dark:bg-black w-full gap-4 mx-auto px-8">
|
||||
<Card title="Better Auth" icon={<Logo className=" w-44" />}></Card>
|
||||
<Card title="Better Auth" icon={<Logo className="w-44" />}></Card>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import "./globals.css";
|
||||
import { Toaster } from "@/components/ui/sonner";
|
||||
import { ThemeProvider } from "@/components/theme-provider";
|
||||
import { GeistMono } from "geist/font/mono";
|
||||
import { GeistSans } from "geist/font/sans";
|
||||
import { ThemeProvider } from "@/components/theme-provider";
|
||||
import { Toaster } from "@/components/ui/sonner";
|
||||
import { Wrapper, WrapperWithQuery } from "@/components/wrapper";
|
||||
import { createMetadata } from "@/lib/metadata";
|
||||
|
||||
@@ -11,7 +11,7 @@ export const metadata = createMetadata({
|
||||
template: "%s | Better Auth",
|
||||
default: "Better Auth",
|
||||
},
|
||||
description: "The most comprehensive authentication library for typescript",
|
||||
description: "The most comprehensive authentication framework for TypeScript",
|
||||
metadataBase: new URL("https://demo.better-auth.com"),
|
||||
});
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { CardFooter } from "@/components/ui/card";
|
||||
import { client } from "@/lib/auth-client";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { CardFooter } from "@/components/ui/card";
|
||||
import { client } from "@/lib/auth-client";
|
||||
|
||||
export function ConsentBtns() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Metadata } from "next";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { headers } from "next/headers";
|
||||
import { ArrowLeftRight, ArrowUpRight, Mail, Users } from "lucide-react";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import { Logo } from "@/components/logo";
|
||||
import { Metadata } from "next";
|
||||
import { headers } from "next/headers";
|
||||
import Image from "next/image";
|
||||
import { Logo } from "@/components/logo";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { ConsentBtns } from "./concet-buttons";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SignInButton, SignInFallback } from "@/components/sign-in-btn";
|
||||
import { Suspense } from "react";
|
||||
import { SignInButton, SignInFallback } from "@/components/sign-in-btn";
|
||||
|
||||
const features = [
|
||||
{
|
||||
@@ -63,7 +63,7 @@ export default async function Home() {
|
||||
<div className="md:w-10/12 w-full flex flex-col gap-4">
|
||||
<div className="flex flex-col gap-3 pt-2 flex-wrap">
|
||||
<div className="border-y py-2 border-dotted bg-secondary/60 opacity-80">
|
||||
<div className="text-xs flex items-center gap-2 justify-center text-muted-foreground ">
|
||||
<div className="text-xs flex items-center gap-2 justify-center text-muted-foreground">
|
||||
<span className="text-center">
|
||||
All features on this demo are implemented with Better Auth
|
||||
without any custom backend code
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { ChevronDown, PlusCircle } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Command,
|
||||
CommandGroup,
|
||||
@@ -15,10 +12,13 @@ import {
|
||||
CommandList,
|
||||
CommandSeparator,
|
||||
} from "@/components/ui/command";
|
||||
import { ChevronDown, PlusCircle } from "lucide-react";
|
||||
import { Session } from "@/lib/auth-types";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { client, useSession } from "@/lib/auth-client";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { Session } from "@/lib/auth-types";
|
||||
|
||||
export default function AccountSwitcher({ sessions }: { sessions: Session[] }) {
|
||||
const { data: currentUser } = useSession();
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
"use client";
|
||||
|
||||
import NumberFlow from "@number-flow/react";
|
||||
import { CheckIcon } from "@radix-ui/react-icons";
|
||||
import confetti from "canvas-confetti";
|
||||
import { motion } from "framer-motion";
|
||||
import { Star } from "lucide-react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { Button, buttonVariants } from "@/components/ui/button";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { motion } from "framer-motion";
|
||||
import { Star } from "lucide-react";
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import confetti from "canvas-confetti";
|
||||
import NumberFlow from "@number-flow/react";
|
||||
import { CheckIcon } from "@radix-ui/react-icons";
|
||||
import { client } from "@/lib/auth-client";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
function useMediaQuery(query: string) {
|
||||
const [matches, setMatches] = useState(false);
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { client, signIn } from "@/lib/auth-client";
|
||||
import { Key, Loader2 } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { client, signIn } from "@/lib/auth-client";
|
||||
import { Button } from "./ui/button";
|
||||
import { Checkbox } from "./ui/checkbox";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -10,14 +16,8 @@ import {
|
||||
DialogTitle,
|
||||
} from "./ui/dialog";
|
||||
import { Input } from "./ui/input";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import { PasswordInput } from "./ui/password-input";
|
||||
import { Checkbox } from "./ui/checkbox";
|
||||
import { Button } from "./ui/button";
|
||||
import { Key, Loader2 } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import { Label } from "./ui/label";
|
||||
import { PasswordInput } from "./ui/password-input";
|
||||
|
||||
export function OneTap() {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
@@ -123,7 +123,7 @@ function SignInBox() {
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className=" gap-2"
|
||||
className="gap-2"
|
||||
onClick={async () => {
|
||||
await signIn.social({
|
||||
provider: "google",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Link from "next/link";
|
||||
import { Button } from "./ui/button";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { headers } from "next/headers";
|
||||
import Link from "next/link";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { Button } from "./ui/button";
|
||||
|
||||
export async function SignInButton() {
|
||||
const session = await auth.api.getSession({
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
"use client";
|
||||
|
||||
import { Key, Loader2 } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useState, useTransition } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { useState, useTransition } from "react";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { client, signIn } from "@/lib/auth-client";
|
||||
import Link from "next/link";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { toast } from "sonner";
|
||||
import { getCallbackURL } from "@/lib/shared";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export default function SignIn() {
|
||||
const [email, setEmail] = useState("");
|
||||
@@ -213,6 +213,29 @@ export default function SignIn() {
|
||||
<LastUsedIndicator />
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn("w-full gap-2 flex items-center relative")}
|
||||
onClick={async () => {
|
||||
await signIn.passkey({
|
||||
fetchOptions: {
|
||||
onSuccess() {
|
||||
toast.success("Successfully signed in");
|
||||
router.push(getCallbackURL(params));
|
||||
},
|
||||
onError(context) {
|
||||
toast.error(
|
||||
"Authentication failed: " + context.error.message,
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Key size={16} />
|
||||
<span>Sign in with Passkey</span>
|
||||
{client.isLastUsedLoginMethod("passkey") && <LastUsedIndicator />}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { Loader2, X } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useState, useTransition } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
@@ -11,12 +16,7 @@ import {
|
||||
} from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useState, useTransition } from "react";
|
||||
import { Loader2, X } from "lucide-react";
|
||||
import { signUp } from "@/lib/auth-client";
|
||||
import { toast } from "sonner";
|
||||
import { useSearchParams, useRouter } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import { getCallbackURL } from "@/lib/shared";
|
||||
|
||||
export function SignUp() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type React from "react";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
import type React from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const tierVariants = cva(
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as AccordionPrimitive from "@radix-ui/react-accordion";
|
||||
import { ChevronDownIcon } from "@radix-ui/react-icons";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import * as React from "react";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const AlertDialog = AlertDialogPrimitive.Root;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from "react";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from "react";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from "react";
|
||||
import { ChevronRightIcon, DotsHorizontalIcon } from "@radix-ui/react-icons";
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from "react";
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
|
||||
import * as React from "react";
|
||||
import { DayPicker } from "react-day-picker";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export type CalendarProps = React.ComponentProps<typeof DayPicker>;
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import { ArrowLeftIcon, ArrowRightIcon } from "@radix-ui/react-icons";
|
||||
import useEmblaCarousel, {
|
||||
type UseEmblaCarouselType,
|
||||
} from "embla-carousel-react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import * as React from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type CarouselApi = UseEmblaCarouselType[1];
|
||||
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
||||
import { CheckIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import { Command as CommandPrimitive } from "cmdk";
|
||||
import { SearchIcon } from "lucide-react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import * as React from "react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -12,6 +10,7 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
function Command({
|
||||
className,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";
|
||||
import {
|
||||
CheckIcon,
|
||||
ChevronRightIcon,
|
||||
DotFilledIcon,
|
||||
} from "@radix-ui/react-icons";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { Check, Copy } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Copy, Check } from "lucide-react";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
||||
import { XIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
||||
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as LabelPrimitive from "@radix-ui/react-label";
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
import * as React from "react";
|
||||
import {
|
||||
Controller,
|
||||
ControllerProps,
|
||||
@@ -11,9 +11,8 @@ import {
|
||||
FormProvider,
|
||||
useFormContext,
|
||||
} from "react-hook-form";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Form = FormProvider;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import { DashIcon } from "@radix-ui/react-icons";
|
||||
import { OTPInput, OTPInputContext } from "input-otp";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
|
||||