added demo app, renamed nextjs folder to dev, added docusaurus to turbo

This commit is contained in:
Zai Shi 2024-02-28 18:08:23 +01:00
parent d37c3a206e
commit a5933c43de
46 changed files with 350 additions and 1 deletions

26
apps/demo/package.json Normal file
View File

@ -0,0 +1,26 @@
{
"name": "demo-app",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
"typecheck": "tsc --noEmit",
"clean": "rm -rf .next",
"dev": "next dev --port 8103",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"next": "14.0.4",
"next-themes": "^0.2.1",
"react": "^18",
"react-dom": "^18",
"stack": "workspace:*",
"stack-shared": "workspace:*"
},
"devDependencies": {
"@types/react": "^18.2.23",
"@types/react-dom": "^18.2.8"
}
}

4
apps/dev/.env Normal file
View File

@ -0,0 +1,4 @@
NEXT_PUBLIC_STACK_URL=# enter your stack endpoint here, e.g. http://localhost:8101
NEXT_PUBLIC_STACK_PROJECT_ID=# enter your stack project id here
NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY=# enter your stack publishable client key here
STACK_SECRET_SERVER_KEY=# enter your stack secret server key here

22
apps/dev/.eslintrc.js Normal file
View File

@ -0,0 +1,22 @@
module.exports = {
"extends": [
"../../eslint-configs/defaults.js",
"../../eslint-configs/next.js",
],
"ignorePatterns": ['/*', '!/src'],
rules: {
"import/order": [
1,
{
groups: [
"external",
"builtin",
"internal",
"sibling",
"parent",
"index",
],
},
],
},
};

7
apps/dev/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
node_modules/
/test-results/
/playwright-report/
/playwright/.cache/
dbschema/edgeql-js
*.tsbuildinfo

4
apps/dev/.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,4 @@
{
"typescript.tsdk": "../../../node_modules/.pnpm/typescript@4.9.4/node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}

5
apps/dev/next-env.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

7
apps/dev/next.config.js Normal file
View File

@ -0,0 +1,7 @@
/** @type {import("next").NextConfig} */
module.exports = {
webpack(config) {
config.experiments = { ...config.experiments, topLevelAwait: true }
return config
},
}

View File

@ -0,0 +1,6 @@
import { StackHandler } from "stack";
import { stackServerApp } from "src/stack";
export default function Handler(props) {
return <StackHandler app={stackServerApp} {...props} />;
}

View File

@ -0,0 +1,25 @@
import { StackProvider } from "stack";
import { stackServerApp } from "src/stack";
import Provider from "src/components/Provider";
export default function RootLayout({
children,
}: {
children: React.ReactNode,
}) {
return (
<html lang="en" suppressHydrationWarning>
<head />
<body>
<StackProvider
app={stackServerApp}
>
<Provider>
{children}
</Provider>
</StackProvider>
</body>
</html>
);
}

43
apps/dev/src/app/page.tsx Normal file
View File

@ -0,0 +1,43 @@
import Link from 'next/link';
import { stackServerApp } from 'src/stack';
import ColorModeButton from 'src/components/ColorThemeButton';
import SignOutButton from 'src/components/SignOutButton';
import UserInfo from 'src/components/UserInfo';
import UserInfoClient from 'src/components/UserInfoClient';
export default async function Page() {
return (
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
<div>
<div>Server:</div>
<UserInfo />
<div style={{ marginBottom: '1rem' }}/>
<div>Client:</div>
<UserInfoClient />
</div>
<div style={{ marginBottom: '1rem' }}/>
<Link href="/handler/signin">
Sign in
</Link>
<Link href="/handler/signup">
Sign up
</Link>
<div style={{ marginBottom: '1rem' }}/>
<SignOutButton />
<Link href={stackServerApp.urls['signOut']}>
Sign out (server)
</Link>
<div style={{ marginBottom: '1rem' }}/>
<ColorModeButton />
<div style={{ marginBottom: '1rem' }}/>
<Link href="/protected-client">
Protected client
</Link>
<Link href="/protected-server">
Protected server
</Link>
</div>
);
}

View File

@ -0,0 +1,7 @@
'use client';
import { useUser } from "stack";
export default function ProtectedPage() {
useUser({ or: 'redirect' });
return <div>This is protected. You can see this because you are signed in</div>;
}

View File

@ -0,0 +1,6 @@
import { stackServerApp } from "src/stack";
export default async function ProtectedPage() {
await stackServerApp.getUser({ or: 'redirect' });
return <div>This is protected. You can see this because you are signed in</div>;
}

View File

@ -0,0 +1,36 @@
/* eslint-disable @typescript-eslint/no-misused-promises */
'use client';
import { useStackApp, validateEmail } from "stack";
import { useState } from "react";
export default function CustomCredentialSignIn() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const app = useStackApp();
const onSubmit = async () => {
if (!validateEmail(email)) {
setError('Please enter a valid email');
return;
}
if (!password) {
setError('Please enter your password');
return;
}
const errorCode = await app.signInWithCredential({ email, password, redirectUrl: app.urls.userHome });
// It is better to handle each error code separately, but for simplicity, we will just show the error code directly
if (errorCode) {
setError(errorCode);
}
};
return (
<div>
{error}
<input type='email' placeholder="email@example.com" value={email} onChange={(e) => setEmail(e.target.value)} />
<input type='password' placeholder="password" value={password} onChange={(e) => setPassword(e.target.value)} />
<button onClick={onSubmit}>Sign In</button>
</div>
);
}

View File

@ -0,0 +1,12 @@
/* eslint-disable @typescript-eslint/no-misused-promises */
'use client';
import { useStackApp } from "stack";
export default function CustomOAuthSignIn() {
const app = useStackApp();
return <div>
<h1>My Custom Sign In page</h1>
<button onClick={async () => await app.signInWithOauth('google')}>Sign In with Google</button>
</div>;
}

View File

@ -0,0 +1,10 @@
import { SignIn } from "stack";
import { stackServerApp } from "src/stack";
import CustomCredentialSignIn from "./custom-credential";
import CustomOAuthSignIn from "./custom-oauth";
export default function Page() {
return <SignIn fullPage redirectUrl={stackServerApp.urls.home} />;
// return <CustomCredentialSignIn />;
// return <CustomOAuthSignIn />;
}

View File

@ -0,0 +1,24 @@
'use client';
import { useTheme } from "next-themes";
import { useEffect, useState } from "react";
export default function ColorModeButton() {
const [mounted, setMounted] = useState(false);
const { theme, setTheme } = useTheme();
// useEffect only runs on the client, so now we can safely show the UI
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return null;
}
return (
<header>
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle {theme === 'light' ? 'Dark' : 'Light'}
</button>
</header>
);
}

View File

@ -0,0 +1,10 @@
'use client';
import { ThemeProvider } from 'next-themes';
export default function Provider({ children }) {
return (
<ThemeProvider>
{children}
</ThemeProvider>
);
}

View File

@ -0,0 +1,9 @@
'use client';
import { useUser, useStackApp } from "stack";
import { runAsynchronously } from "stack-shared/dist/utils/promises";
export default function SignOutButton() {
const user = useUser();
return (<button onClick={() => runAsynchronously(user?.signOut())}>Sign out</button>);
}

View File

@ -0,0 +1,19 @@
import { stackServerApp } from "src/stack";
export default async function UserInfo() {
const user = await stackServerApp.getUser();
const serverUser = await stackServerApp.getServerUser();
return <div>
{user ? (
<div>User Status: Logged in as {user.displayName ?? user.primaryEmail}</div>
) : (
<div>User Status: Not signed in</div>
)}
{serverUser ? (
<div>ServerUser Status: Logged in as {serverUser.displayName ?? serverUser.primaryEmail}</div>
) : (
<div>ServerUser Status: Not signed in</div>
)}
</div>;
}

View File

@ -0,0 +1,17 @@
'use client';
import { useUser } from "stack";
export default function UserInfoClient() {
const user = useUser();
return (
<div>
{user ? (
<div>User Status: Logged in as {user.displayName ?? user.primaryEmail}</div>
) : (
<div>User Status: Not signed in</div>
)}
</div>
);
}

11
apps/dev/src/stack.tsx Normal file
View File

@ -0,0 +1,11 @@
import "server-only";
import { StackServerApp } from "stack";
export const stackServerApp = new StackServerApp({
baseUrl: "http://localhost:8101",
tokenStore: "nextjs-cookie",
urls: {
signIn: "/signin",
}
});

38
apps/dev/tsconfig.json Normal file
View File

@ -0,0 +1,38 @@
{
"compilerOptions": {
"target": "esnext",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"incremental": true,
"jsx": "preserve",
"baseUrl": ".",
"plugins": [
{
"name": "next"
}
],
"strictNullChecks": true
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": [
"node_modules",
]
}

View File

@ -4,7 +4,7 @@
"private": true,
"scripts": {
"docusaurus": "docusaurus",
"dev": "pnpm start",
"dev": "docusaurus start --port 8104 --no-open",
"start": "docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",

View File

@ -1,3 +1,4 @@
packages:
- packages/*
- apps/*
- docs