api and shadcn

This commit is contained in:
2025-02-14 12:29:20 +01:00
parent 63c47fb09e
commit f19a38ee0c
16 changed files with 591 additions and 202 deletions
+9
View File
@@ -0,0 +1,9 @@
'use client';
import * as React from 'react';
import { ThemeProvider as NextThemesProvider } from 'next-themes';
import { type ThemeProviderProps } from 'next-themes';
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}
+77 -3
View File
@@ -1,13 +1,87 @@
import { NextResponse, NextRequest } from 'next/server';
import { TwitterApi } from 'twitter-api-v2';
import config from '@/env';
import { PostRequest } from '@/platform';
import config from '@/app/api/env';
import { PostRequest } from '@/utils';
import { inspect } from 'util';
import database from '../database';
import { generateUrlSafeString } from '@/utils';
// Account creation
// Email doesn't exist
// Password is between 6-20 characters
export async function POST(req: NextRequest) {
const { email, password } = await req.json();
if (typeof email !== 'string' || typeof password !== 'string')
return NextResponse.json(
{
message:
"Invalid structure, body requires 'email: string' and 'password: string'",
},
{ status: 400 }
);
if (await database.users.countDocuments({ email }))
return NextResponse.json(
{
message: 'Email already registered',
},
{ status: 409 }
);
await database.users.insertOne({ email, password });
const response = NextResponse.json(
{
message: 'ok',
},
{ status: 200 }
);
response.cookies.set('session_id', generateUrlSafeString(16));
return;
}
// Account login
// Email exists
// Password is valid
export async function GET(req: NextRequest) {
const { email, password } = await req.json();
if (typeof email !== 'string' || typeof password !== 'string')
return NextResponse.json(
{
message:
"Invalid structure, body requires 'email: string' and 'password: string'",
},
{ status: 400 }
);
const user = await database.users.findOne({ email });
if (!user)
return NextResponse.json(
{
message: 'Invalid email',
},
{ status: 400 }
);
if (user.password !== password)
return NextResponse.json(
{
message: 'Invalid password',
},
{ status: 400 }
);
const sessionId = (await database.sessions.insertOne({ email })).insertedId;
return NextResponse.json(
{
message: 'ok, feature not available yet',
message: 'ok',
sessionId,
},
{ status: 200 }
);
+12
View File
@@ -0,0 +1,12 @@
import { MongoClient } from 'mongodb';
import config from './env';
import { generateUrlSafeString, Session, User } from '@/utils';
const client = new MongoClient(config.mongoUrl);
const db = client.db('klayrion');
export default {
users: db.collection<User>('users'),
sessions: db.collection<Session>('sessions'),
};
+14
View File
@@ -0,0 +1,14 @@
function error(name: string): string {
throw `'${name}' not found in '.env'`;
}
const config = {
twitter: {
key: process.env.TWITTER_KEY || error('TWITTER_KEY'),
secret: process.env.TWITTER_SECRET || error('TWITTER_SECRET'),
},
mongoUrl: process.env.MONGO_URL || error('MONGO_URL'),
};
export default config;
+2 -2
View File
@@ -1,7 +1,7 @@
import { NextResponse, NextRequest } from 'next/server';
import { TwitterApi } from 'twitter-api-v2';
import config from '@/env';
import { PostRequest } from '@/platform';
import config from '@/app/api/env';
import { PostRequest } from '@/utils';
import { inspect } from 'util';
export async function POST(req: NextRequest) {
+61 -10
View File
@@ -2,20 +2,71 @@
@tailwind components;
@tailwind utilities;
:root {
--background: #ffffff;
--foreground: #171717;
body {
font-family: Arial, Helvetica, sans-serif;
}
@media (prefers-color-scheme: dark) {
@layer base {
:root {
--background: #0a0a0a;
--foreground: #ededed;
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--card: 0 0% 100%;
--card-foreground: 0 0% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 3.9%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--muted: 0 0% 96.1%;
--muted-foreground: 0 0% 45.1%;
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--ring: 0 0% 3.9%;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
--radius: 0.5rem;
}
.dark {
--background: 0 0% 3.9%;
--foreground: 0 0% 98%;
--card: 0 0% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 0 0% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 0 0% 9%;
--secondary: 0 0% 14.9%;
--secondary-foreground: 0 0% 98%;
--muted: 0 0% 14.9%;
--muted-foreground: 0 0% 63.9%;
--accent: 0 0% 14.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 14.9%;
--input: 0 0% 14.9%;
--ring: 0 0% 83.1%;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
}
}
body {
color: var(--foreground);
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
+27 -26
View File
@@ -1,34 +1,35 @@
import type { Metadata } from "next";
import { Roboto, Roboto_Mono } from "next/font/google";
import "./globals.css";
const roboto = Roboto({
weight: "500",
subsets: ["latin"],
variable: "--font-roboto",
});
const robotoMono = Roboto_Mono({
weight: "500",
subsets: ["latin"],
variable: "--font-roboto-mono",
});
import type { Metadata } from 'next';
import { cn } from '@/lib/utils';
import './globals.css';
import { ThemeProvider } from './ThemeProvider';
export const metadata: Metadata = {
title: "Klayrion: The best AI social media scheduling tool",
description: "Generated by create next app",
title: 'Create Next App',
description: 'Generated by create next app',
};
export default function RootLayout({
children,
children,
}: Readonly<{
children: React.ReactNode;
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={`${roboto.variable} ${robotoMono.variable} antialiased`}>
{children}
</body>
</html>
);
return (
<html lang="en" suppressHydrationWarning>
<body
suppressHydrationWarning
className={cn(
'min-h-screen bg-background font-sans antialiased'
)}
>
<ThemeProvider
attribute="class"
defaultTheme="dark"
enableSystem
disableTransitionOnChange
>
{children}
</ThemeProvider>
</body>
</html>
);
}
+5
View File
@@ -0,0 +1,5 @@
'use client';
export default function Login() {
return <div className="bg-[#ccc] w-1/2 h-screen ml-auto"></div>;
}
+5
View File
@@ -0,0 +1,5 @@
'use client';
export default function SignUp() {
return <div className="bg-[#ccc] w-1/2 h-screen ml-auto"></div>;
}