Implement Auth.
This commit is contained in:
parent
3c9a23481e
commit
6414307462
Binary file not shown.
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 4.1 KiB |
BIN
public/images/tech_tracker_logo.png
Normal file
BIN
public/images/tech_tracker_logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 386 KiB |
@ -1,19 +1,20 @@
|
|||||||
import "~/styles/globals.css";
|
import "~/styles/globals.css";
|
||||||
import { Inter as FontSans } from "next/font/google";
|
import { Inter as FontSans } from "next/font/google";
|
||||||
import { cn } from "~/lib/utils";
|
import { cn } from "~/lib/utils";
|
||||||
|
import { SessionProvider } from "next-auth/react";
|
||||||
|
|
||||||
|
import { type Metadata } from "next";
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Tech Tracker",
|
||||||
|
description: "App used by COG IT employees to update their status throughout the day.",
|
||||||
|
icons: [{ rel: "icon", url: "/favicon.ico" }],
|
||||||
|
};
|
||||||
|
|
||||||
const fontSans = FontSans({
|
const fontSans = FontSans({
|
||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
variable: "--font-sans",
|
variable: "--font-sans",
|
||||||
});
|
});
|
||||||
|
|
||||||
import { type Metadata } from "next";
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: "Create T3 App",
|
|
||||||
description: "Generated by create-t3-app",
|
|
||||||
icons: [{ rel: "icon", url: "/favicon.ico" }],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
children,
|
children,
|
||||||
}: Readonly<{ children: React.ReactNode }>) {
|
}: Readonly<{ children: React.ReactNode }>) {
|
||||||
@ -22,10 +23,11 @@ export default function RootLayout({
|
|||||||
<body
|
<body
|
||||||
className={cn(
|
className={cn(
|
||||||
"min-h-screen bg-background font-sans antialiased",
|
"min-h-screen bg-background font-sans antialiased",
|
||||||
fontSans.variable
|
fontSans.variable)}
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
{children}
|
<SessionProvider>
|
||||||
|
{children}
|
||||||
|
</SessionProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
@ -1,37 +1,21 @@
|
|||||||
import Link from "next/link";
|
import { auth } from "~/auth";
|
||||||
|
import { db } from "~/server/db";
|
||||||
|
import No_Session from "~/components/auth/No_Session";
|
||||||
|
|
||||||
export default function HomePage() {
|
export default async function HomePage() {
|
||||||
return (
|
const user = await auth();
|
||||||
<main className="flex min-h-screen flex-col items-center justify-center bg-gradient-to-b from-[#2e026d] to-[#15162c] text-white">
|
const employees = await db.query.users.findMany({
|
||||||
<div className="container flex flex-col items-center justify-center gap-12 px-4 py-16">
|
orderBy: (model, {desc}) => desc(model.id),
|
||||||
<h1 className="text-5xl font-extrabold tracking-tight text-white sm:text-[5rem]">
|
});
|
||||||
Create <span className="text-[hsl(280,100%,70%)]">T3</span> App
|
if (!user) {
|
||||||
</h1>
|
return (
|
||||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:gap-8">
|
<No_Session />
|
||||||
<Link
|
|
||||||
className="flex max-w-xs flex-col gap-4 rounded-xl bg-white/10 p-4 text-white hover:bg-white/20"
|
|
||||||
href="https://create.t3.gg/en/usage/first-steps"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<h3 className="text-2xl font-bold">First Steps →</h3>
|
|
||||||
<div className="text-lg">
|
|
||||||
Just the basics - Everything you need to know to set up your
|
|
||||||
database and authentication.
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
className="flex max-w-xs flex-col gap-4 rounded-xl bg-white/10 p-4 text-white hover:bg-white/20"
|
|
||||||
href="https://create.t3.gg/en/introduction"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<h3 className="text-2xl font-bold">Documentation →</h3>
|
|
||||||
<div className="text-lg">
|
|
||||||
Learn more about Create T3 App, the libraries it uses, and how to
|
|
||||||
deploy it.
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<main className="min-h-screen bg-gradient-to-b from-[#111111] to-[#111325] text-white">
|
||||||
|
<h1 className="text-2xl font-semibold">Welcome!</h1>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
10
src/components/auth/No_Session.tsx
Normal file
10
src/components/auth/No_Session.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import Sign_In from "./Sign_In";
|
||||||
|
|
||||||
|
export default function No_Session() {
|
||||||
|
return (
|
||||||
|
<div className="w-2/3 mx-auto text-center pt-10">
|
||||||
|
<h1 className="text-2xl font-semibold">Unauthorized. Please sign in first.</h1>
|
||||||
|
< Sign_In />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
13
src/components/auth/Sign_In.tsx
Normal file
13
src/components/auth/Sign_In.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { signIn } from "~/auth";
|
||||||
|
|
||||||
|
export default async function Sign_In() {
|
||||||
|
return (
|
||||||
|
<form className="w-full"
|
||||||
|
action={async () => {
|
||||||
|
"use server";
|
||||||
|
await signIn("microsoft-entra-id");
|
||||||
|
}}>
|
||||||
|
<button type="submit" className="w-full">Sign In</button>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
14
src/env.js
14
src/env.js
@ -16,9 +16,9 @@ export const env = createEnv({
|
|||||||
process.env.NODE_ENV === "production"
|
process.env.NODE_ENV === "production"
|
||||||
? z.string()
|
? z.string()
|
||||||
: z.string().optional(),
|
: z.string().optional(),
|
||||||
AUTH_MICROSOFT_ENTRA_ID: z.string(),
|
AUTH_MICROSOFT_ENTRA_ID_ID: z.string(),
|
||||||
AUTH_MICROSOFT_ENTRA_SECRET: z.string(),
|
AUTH_MICROSOFT_ENTRA_ID_SECRET: z.string(),
|
||||||
AUTH_MICROSOFT_ENTRA_TENANT_ID: z.string(),
|
AUTH_MICROSOFT_ENTRA_ID_TENANT_ID: z.string(),
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,10 +38,10 @@ export const env = createEnv({
|
|||||||
DATABASE_URL: process.env.DATABASE_URL,
|
DATABASE_URL: process.env.DATABASE_URL,
|
||||||
NODE_ENV: process.env.NODE_ENV,
|
NODE_ENV: process.env.NODE_ENV,
|
||||||
API_KEY: process.env.API_KEY,
|
API_KEY: process.env.API_KEY,
|
||||||
AUTH_SECRET: process.env.AUTH_SECRET,
|
AUTH_SECRET: process.env.NEXTAUTH_SECRET,
|
||||||
AUTH_MICROSOFT_ENTRA_ID: process.env.AUTH_MICROSOFT_ENTRA_ID,
|
AUTH_MICROSOFT_ENTRA_ID_ID: process.env.AUTH_MICROSOFT_ENTRA_ID_ID,
|
||||||
AUTH_MICROSOFT_ENTRA_SECRET: process.env.AUTH_MICROSOFT_ENTRA_SECRET,
|
AUTH_MICROSOFT_ENTRA_ID_SECRET: process.env.AUTH_MICROSOFT_ENTRA_ID_SECRET,
|
||||||
AUTH_MICROSOFT_ENTRA_TENANT_ID: process.env.AUTH_MICROSOFT_ENTRA_TENANT_ID,
|
AUTH_MICROSOFT_ENTRA_ID_TENANT_ID: process.env.AUTH_MICROSOFT_ENTRA_ID_TENANT_ID,
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially
|
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially
|
||||||
|
@ -1,34 +1,32 @@
|
|||||||
// Example model schema from the Drizzle docs
|
|
||||||
// https://orm.drizzle.team/docs/sql-schema-declaration
|
// https://orm.drizzle.team/docs/sql-schema-declaration
|
||||||
|
|
||||||
import { sql } from "drizzle-orm";
|
import { sql } from "drizzle-orm";
|
||||||
import {
|
import {
|
||||||
bigint,
|
bigint,
|
||||||
index,
|
|
||||||
mysqlTableCreator,
|
mysqlTableCreator,
|
||||||
timestamp,
|
timestamp,
|
||||||
varchar,
|
varchar,
|
||||||
} from "drizzle-orm/mysql-core";
|
} from "drizzle-orm/mysql-core";
|
||||||
|
|
||||||
/**
|
export const createTable = mysqlTableCreator((name) => `${name}`);
|
||||||
* This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same
|
|
||||||
* database instance for multiple projects.
|
|
||||||
*
|
|
||||||
* @see https://orm.drizzle.team/docs/goodies#multi-project-schema
|
|
||||||
*/
|
|
||||||
export const createTable = mysqlTableCreator((name) => `tech_tracker_web_${name}`);
|
|
||||||
|
|
||||||
export const posts = createTable(
|
export const users = createTable(
|
||||||
"post",
|
"users",
|
||||||
{
|
{
|
||||||
id: bigint("id", { mode: "number" }).primaryKey().autoincrement(),
|
id: bigint("id", {mode: "number"}).primaryKey().autoincrement(),
|
||||||
name: varchar("name", { length: 256 }),
|
name: varchar("name", { length: 256 }),
|
||||||
createdAt: timestamp("created_at")
|
status: varchar("status", { length: 256 }),
|
||||||
|
updatedAt: timestamp("updated_at")
|
||||||
.default(sql`CURRENT_TIMESTAMP`)
|
.default(sql`CURRENT_TIMESTAMP`)
|
||||||
.notNull(),
|
.notNull(),
|
||||||
updatedAt: timestamp("updated_at").onUpdateNow(),
|
|
||||||
},
|
},
|
||||||
(example) => ({
|
);
|
||||||
nameIndex: index("name_idx").on(example.name),
|
|
||||||
})
|
export const history = createTable(
|
||||||
|
"history",
|
||||||
|
{
|
||||||
|
id: bigint("id", {mode: "number"}).primaryKey().autoincrement(),
|
||||||
|
user_id: bigint("user_id", {mode: "number"}).references(() => users.id),
|
||||||
|
status: varchar("status", { length: 256 }),
|
||||||
|
updatedAt: timestamp("updated_at").notNull(),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
@ -4,6 +4,33 @@
|
|||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
:root {
|
:root {
|
||||||
|
--background: 240 10% 3.9%;
|
||||||
|
--foreground: 0 0% 98%;
|
||||||
|
--card: 240 10% 3.9%;
|
||||||
|
--card-foreground: 0 0% 98%;
|
||||||
|
--popover: 240 10% 3.9%;
|
||||||
|
--popover-foreground: 0 0% 98%;
|
||||||
|
--primary: 0 0% 98%;
|
||||||
|
--primary-foreground: 240 5.9% 10%;
|
||||||
|
--secondary: 240 3.7% 15.9%;
|
||||||
|
--secondary-foreground: 0 0% 98%;
|
||||||
|
--muted: 240 3.7% 15.9%;
|
||||||
|
--muted-foreground: 240 5% 64.9%;
|
||||||
|
--accent: 240 3.7% 15.9%;
|
||||||
|
--accent-foreground: 0 0% 98%;
|
||||||
|
--destructive: 0 62.8% 30.6%;
|
||||||
|
--destructive-foreground: 0 0% 98%;
|
||||||
|
--border: 240 3.7% 15.9%;
|
||||||
|
--input: 240 3.7% 15.9%;
|
||||||
|
--ring: 240 4.9% 83.9%;
|
||||||
|
--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%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
--background: 0 0% 100%;
|
--background: 0 0% 100%;
|
||||||
--foreground: 240 10% 3.9%;
|
--foreground: 240 10% 3.9%;
|
||||||
--card: 0 0% 100%;
|
--card: 0 0% 100%;
|
||||||
@ -30,33 +57,6 @@
|
|||||||
--chart-4: 43 74% 66%;
|
--chart-4: 43 74% 66%;
|
||||||
--chart-5: 27 87% 67%;
|
--chart-5: 27 87% 67%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
|
||||||
--background: 240 10% 3.9%;
|
|
||||||
--foreground: 0 0% 98%;
|
|
||||||
--card: 240 10% 3.9%;
|
|
||||||
--card-foreground: 0 0% 98%;
|
|
||||||
--popover: 240 10% 3.9%;
|
|
||||||
--popover-foreground: 0 0% 98%;
|
|
||||||
--primary: 0 0% 98%;
|
|
||||||
--primary-foreground: 240 5.9% 10%;
|
|
||||||
--secondary: 240 3.7% 15.9%;
|
|
||||||
--secondary-foreground: 0 0% 98%;
|
|
||||||
--muted: 240 3.7% 15.9%;
|
|
||||||
--muted-foreground: 240 5% 64.9%;
|
|
||||||
--accent: 240 3.7% 15.9%;
|
|
||||||
--accent-foreground: 0 0% 98%;
|
|
||||||
--destructive: 0 62.8% 30.6%;
|
|
||||||
--destructive-foreground: 0 0% 98%;
|
|
||||||
--border: 240 3.7% 15.9%;
|
|
||||||
--input: 240 3.7% 15.9%;
|
|
||||||
--ring: 240 4.9% 83.9%;
|
|
||||||
--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%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
|
Loading…
Reference in New Issue
Block a user