Skip to content

Commit c9be1d8

Browse files
committed
feat(49): create authorization
1 parent c868590 commit c9be1d8

18 files changed

Lines changed: 348 additions & 32 deletions

File tree

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
},
2828
"dependencies": {
2929
"@prisma/client": "6.15.0",
30+
"bcrypt": "^6.0.0",
31+
"jose": "^6.1.0",
3032
"next": "15.5.2",
3133
"prisma": "^6.15.0",
3234
"react": "19.1.0",
@@ -36,6 +38,7 @@
3638
"devDependencies": {
3739
"@eslint/eslintrc": "^3",
3840
"@tailwindcss/postcss": "^4",
41+
"@types/bcrypt": "^6.0.0",
3942
"@types/node": "^22",
4043
"@types/react": "^19",
4144
"@types/react-dom": "^19",

pnpm-lock.yaml

Lines changed: 42 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/*
2+
Warnings:
3+
4+
- Added the required column `hashedPassword` to the `User` table without a default value. This is not possible if the table is not empty.
5+
6+
*/
7+
-- AlterTable
8+
ALTER TABLE "public"."User" ADD COLUMN "hashedPassword" TEXT NOT NULL;

prisma/schema.prisma

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,32 @@ datasource db {
99
}
1010

1111
model Organization {
12-
id String @id @unique @default(uuid())
13-
name String
14-
createdAt DateTime @default(now())
15-
updatedAt DateTime @updatedAt
16-
createdById String @unique
17-
createdBy User @relation("CreatedOrganization", fields: [createdById], references: [id])
18-
users User[] @relation("UserOrganization")
12+
id String @id @unique @default(uuid())
13+
name String
14+
createdAt DateTime @default(now())
15+
updatedAt DateTime @updatedAt
16+
createdById String @unique
17+
createdBy User @relation("CreatedOrganization", fields: [createdById], references: [id])
18+
users User[] @relation("UserOrganization")
1919
positions Position[]
2020
candidates Candidate[]
2121
AssessmentTemplate AssessmentTemplate[]
2222
TaskTemplate TaskTemplate[]
2323
}
2424

2525
model User {
26-
id String @id @unique @default(uuid())
27-
name String
28-
email String @unique
29-
orgId String?
30-
createdAt DateTime @default(now())
31-
updatedAt DateTime @updatedAt
32-
organization Organization? @relation("UserOrganization", fields: [orgId], references: [id])
26+
id String @id @unique @default(uuid())
27+
name String
28+
email String @unique
29+
orgId String?
30+
hashedPassword String
31+
createdAt DateTime @default(now())
32+
updatedAt DateTime @updatedAt
33+
organization Organization? @relation("UserOrganization", fields: [orgId], references: [id])
3334
createdOrganization Organization? @relation("CreatedOrganization")
34-
userRoles UserRole[]
35-
Review Review[]
36-
Comment Comment[]
35+
userRoles UserRole[]
36+
Review Review[]
37+
Comment Comment[]
3738
}
3839

3940
model Role {

prisma/seed.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ async function main() {
1010
update: {},
1111
create: {
1212
id: 'e99335bd-9dd7-4260-8977-2eeaa4df799c',
13+
hashedPassword: 'abcd',
1314
name: 'Admin User',
1415
email: 'admin@techcorp.com',
1516
},
@@ -20,6 +21,7 @@ async function main() {
2021
create: {
2122
id: '68992d1e-e119-4874-b768-bf685d10194e',
2223
name: 'John Doe',
24+
hashedPassword: 'abcd',
2325
email: 'john.doe@techcorp.com',
2426
},
2527
}),
@@ -29,6 +31,7 @@ async function main() {
2931
create: {
3032
id: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
3133
name: 'Jane Smith',
34+
hashedPassword: 'abcd',
3235
email: 'jane.smith@startupxyz.com',
3336
},
3437
}),
@@ -38,6 +41,7 @@ async function main() {
3841
create: {
3942
id: 'b2c3d4e5-f6g7-8901-bcde-f12345678901',
4043
name: 'Bob Wilson',
44+
hashedPassword: 'abcd',
4145
email: 'bob.wilson@enterprise.com',
4246
},
4347
}),
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function LoginPage() {
2+
return <div className="">login</div>;
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function SignupPage() {
2+
return <div className="">signup</div>;
3+
}

src/app/(web)/dashboard/page.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function DashboardPage() {
2+
return <div className=""></div>;
3+
}

src/app/api/auth/login/route.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { type NextRequest } from 'next/server';
2+
import { sargeApiError, sargeApiResponse } from '@/lib/responses';
3+
import { login } from '@/lib/auth/auth-service';
4+
import { InvalidInputError } from '@/lib/schemas/errors';
5+
6+
export async function POST(request: NextRequest) {
7+
try {
8+
const body = await request.json();
9+
10+
if (!body.email || !body.password) {
11+
return sargeApiError('Email and password are required', 400);
12+
}
13+
14+
const user = await login({
15+
email: body.email,
16+
password: body.password,
17+
});
18+
19+
return sargeApiResponse(user, 200);
20+
} catch (error) {
21+
if (error instanceof InvalidInputError) {
22+
return sargeApiError(error.message, 400);
23+
}
24+
25+
const message = error instanceof Error ? error.message : String(error);
26+
27+
if (message.includes('Invalid credentials')) {
28+
return sargeApiError('Invalid email or password', 401);
29+
}
30+
31+
if (message.includes('Login implementation needed')) {
32+
return sargeApiError('Authentication not yet configured', 501);
33+
}
34+
35+
return sargeApiError(message, 500);
36+
}
37+
}

src/app/api/auth/logout/route.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { type NextRequest } from 'next/server';
2+
import { sargeApiResponse } from '@/lib/responses';
3+
import { logout } from '@/lib/auth/auth-service';
4+
5+
export async function POST(_request: NextRequest) {
6+
try {
7+
await logout();
8+
return sargeApiResponse({ message: 'Logged out successfully' }, 200);
9+
} catch (error) {
10+
const message = error instanceof Error ? error.message : String(error);
11+
return sargeApiResponse({ message: 'Logged out', error: message }, 200);
12+
}
13+
}

0 commit comments

Comments
 (0)