Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: set up database and auth #178

Merged
merged 9 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM node:20-alpine

WORKDIR /space

COPY . .

RUN cd app && apk add --no-cache make gcc g++ python3 py3-pip && \
npm install && \
npm rebuild bcrypt --build-from-source && \
apk del make gcc g++ python3 py3-pip

WORKDIR /space/app

ENTRYPOINT ["sh", "entrypoint.sh"]
5 changes: 4 additions & 1 deletion app/.env.example
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
DATABASE_URL=postgres://postgres:password@localhost:15432/spacedb
NEXT_PUBLIC_ENVIRONMENT=development
NEXT_PUBLIC_API_URL=https://localhost:8000/
NEXTAUTH_URL=https://localhost:3000
DATABASE_URL=postgres://postgres:password@localhost:15432/spacedb
2 changes: 2 additions & 0 deletions app/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts

certificates
148 changes: 148 additions & 0 deletions app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,154 @@ You can start editing the page by modifying `app/page.tsx`. The page auto-update

This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.

## Model

```mermaid
erDiagram
Account ||--o{ User : ""
Account {
String id PK
String userId FK
String type
String provider
String providerAccountId
Boolean ok
String state
String refresh_token
String access_token
Int expires_at
String token_type
String scope
String id_token
DateTime createdAt
DateTime updatedAt
String session_state
}

Session ||--o{ User : ""
Session {
String id PK
String sessionToken
String userId FK
DateTime expires
DateTime createdAt
DateTime updatedAt
}

User ||--|{ VerificationToken : ""
User ||--o{ Profile : ""
User ||--o{ DepartmentMembership : ""
User ||--o{ Opportunity : ""
User ||--o{ OpportunityParticipation : ""
User ||--o{ Review : ""
User ||--o{ Account : ""
User ||--o{ Session : ""
User {
String id PK
Int profileId FK
String email
String password
String first_name
String last_name
String image
UserPermission permission
DateTime emailVerified
DateTime createdAt
DateTime updatedAt
}

VerificationToken {
String id PK
String identifier
String token
DateTime expires
DateTime createdAt
DateTime updatedAt
}

Profile ||--|{ Contact : ""
Profile ||--o{ User : ""
Profile {
Int id PK
Int userId FK
DateTime birthday
String nationality
String description
String activity_status
String degree_level
String degree_name
Int degree_semester
DateTime degree_last_update
String university
String profile_picture
DateTime createdAt
DateTime updatedAt
}

Contact {
Int id PK
Int profileId FK
ContactType type
String username
DateTime createdAt
DateTime updatedAt
}

Department ||--|{ DepartmentMembership : ""
Department ||--|{ Opportunity : ""
Department {
Int id PK
String name
DepartmentType type
DateTime creation_date
DateTime createdAt
DateTime updatedAt
}

DepartmentMembership {
Int id PK
String userId FK
Int departmentId FK
DateTime membership_start
DateTime membership_end
DepartmentPosition department_position
DateTime createdAt
DateTime updatedAt
}

Opportunity ||--|{ Review : ""
Opportunity ||--|{ OpportunityParticipation : ""
Opportunity {
Int id PK
String creatorId FK
String title
String description
Int departmentId FK
DateTime opportunity_start
DateTime opportunity_end
DateTime createdAt
DateTime updatedAt
}

OpportunityParticipation {
String userId FK
Int opportunityId FK
OpportunityPermission permission
DateTime createdAt
DateTime updatedAt
}

Review {
Int id PK
Int opportunityId FK
Int assigneeId FK
String review_text
Json content
DateTime createdAt
DateTime updatedAt
}
```

## Learn More

To learn more about Next.js, take a look at the following resources:
Expand Down
109 changes: 109 additions & 0 deletions app/app/api/auth/[...nextauth]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { NextAuthOptions } from "next-auth";
import NextAuth from "next-auth/next";
import SlackProvider from "next-auth/providers/slack";
import { PrismaAdapter } from "@next-auth/prisma-adapter"
import prisma from "database/db";
import CredentialsProvider from "next-auth/providers/credentials";
import EmailProvider from "next-auth/providers/email";
import { compare } from "bcrypt";

export const authOptions: NextAuthOptions = {
session:{
strategy:"jwt",
maxAge: 30 * 24 * 60 * 60 //30 days
},
adapter: PrismaAdapter(prisma),
secret: process.env.NEXTAUTH_SECRET,
pages: {
signIn: '/auth',
/*
newUser: '/auth/signup',
signOut: '/auth/signout',
error: '/auth/error', // Error code passed in query string as ?error=
verifyRequest: '/auth/verify-request', // (used for check email message)
*/
//TODO: add signOut, error pages
},
providers: [
SlackProvider(
{
clientId: process.env.SLACK_CLIENT_ID,
clientSecret: process.env.SLACK_CLIENT_SECRET,
profile(profile, tokens) {
return {
id: profile["https://slack.com/user_id"] || profile.sub,
email: profile.email,
image: profile.picture,
first_name: profile.given_name,
permission: 'member',
last_name: profile.family_name,
emailVerified: profile.date_email_verified
};
},
},
),
//TODO: add email provider setup
CredentialsProvider({
name: "Credentials",
credentials: {
email: { label: "Email", type: "email", placeholder: "[email protected]" },
password: { label: "Password", type: "password" }
},
async authorize(credentials) {
if(!credentials?.email || !credentials?.password) {
return null;
}

const existingUser = await prisma.user.findUnique({
where: { email : credentials?.email }
});

if(!existingUser) {
return null;
}

const passwordMatch = await compare(credentials.password, existingUser.password);

if(!passwordMatch) {
return null;
}

return {
id: `${existingUser.id}`,
username: existingUser.first_name + "_" + existingUser.last_name, //do we really need this here?
email: existingUser.email
}
}
}),
// EmailProvider({
// server: process.env.EMAIL_SERVER,
// from: process.env.EMAIL_FROM
// }),
],
callbacks:{
async jwt({token,user}){
return{...token,...user}
},
async session({session,token}){
return {
...session,
user : {
...session.user,
id: token.id,
first_name: token.first_name,
permission: token.permission,
image: token.image,
}
}
// session.user.id = token.id
// session.user.first_name = token.first_name
// session.user.permission = token.permission
// session.user.image = token.image
// return session
}
}
}

const handler = NextAuth(authOptions)

export {handler as GET, handler as POST}
32 changes: 32 additions & 0 deletions app/app/api/auth/signup/signup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { NextResponse } from 'next/server';
import db from '../../../../database/db';
import { hash } from 'bcrypt';



export default async function POST(req, res) {
try {
const { email, password } = req.body;
console.log(email, password);
const existingUser = await db.user.findUnique({
where: { email : email }
});
if (!existingUser) {
return NextResponse.json({ error: 'User with this email already exists' }, { status: 409 });
}

const hashedPassword = await hash(password, 10);

// const newUser = await db.user.create({
// data: {
// email: email,
// password: hashedPassword
// //TODO: add other fields
// }
// });

return NextResponse.json({ message: 'User created' }, { status: 201 });
} catch (error) {
return NextResponse.json({ error: 'Something went wrong' }, { status: 500 });
}
}
Loading
Loading