update backend for appleauth

This commit is contained in:
Gabriel Brown 2024-10-09 10:27:12 -05:00
parent cbdca78f18
commit 171de4104d
16 changed files with 57 additions and 632 deletions

View File

@ -1,33 +0,0 @@
"use server";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { changePassword } from "~/server/functions";
import { middleware } from "~/middleware";
type Data = {
userId: number;
oldPassword: string;
newPassword: string;
};
export const POST = async (request: NextRequest) => {
const middlewareResponse = await middleware(request);
if (middlewareResponse) return middlewareResponse;
try {
const { userId, oldPassword, newPassword } = await request.json() as Data;
console.log("Received request:", { userId, oldPassword, newPassword });
console.log("Changing password for user:", userId);
if (oldPassword === newPassword) {
return NextResponse.json(
{ message: "New password cannot be the same as the old password" },
{ status: 400 }
);
}
await changePassword(userId, oldPassword, newPassword);
return NextResponse.json({ message: "Password changed successfully" });
} catch (error) {
console.error("Error in changePassword:", error);
return NextResponse.json({ message: "Error changing password" }, { status: 500 });
}
};

View File

@ -5,12 +5,11 @@ import { createUser } from "~/server/functions";
import { middleware } from "~/middleware"; import { middleware } from "~/middleware";
type CreateUserRequest = { type CreateUserRequest = {
username: string; appleId: string;
email: string; appleEmail: string;
passwordHash: string; fullName: string;
name: string; pushToken: string;
pfpURL?: string; pfpURL?: string;
pushToken?: string;
}; };
export async function POST(request: NextRequest) { export async function POST(request: NextRequest) {
@ -18,16 +17,15 @@ export async function POST(request: NextRequest) {
if (middlewareResponse) return middlewareResponse; if (middlewareResponse) return middlewareResponse;
try { try {
const { const {
username, appleId,
email, appleEmail,
passwordHash, fullName,
name, pushToken,
pfpURL = "", pfpURL = "",
pushToken = ""
} = await request.json() as CreateUserRequest; } = await request.json() as CreateUserRequest;
// Validate required fields // Validate required fields
if (!username || !email || !passwordHash || !name) { if (!appleId || !appleEmail || !fullName || !pushToken) {
return NextResponse.json( return NextResponse.json(
{ message: "Missing required fields" }, { message: "Missing required fields" },
{ status: 400 } { status: 400 }
@ -35,12 +33,11 @@ export async function POST(request: NextRequest) {
} }
const user = await createUser( const user = await createUser(
username, appleId,
email, appleEmail,
passwordHash, fullName,
name, pushToken,
pfpURL, pfpURL
pushToken
); );
return NextResponse.json(user, { status: 201 }); return NextResponse.json(user, { status: 201 });

View File

@ -1,7 +1,7 @@
"use server"; "use server";
import { NextResponse } from "next/server"; import { NextResponse } from "next/server";
import type { NextRequest } from "next/server"; import type { NextRequest } from "next/server";
import { getUserByUsername } from "~/server/functions"; import { getUserByAppleEmail } from "~/server/functions";
import { middleware } from "~/middleware"; import { middleware } from "~/middleware";
export const GET = async (request: NextRequest) => { export const GET = async (request: NextRequest) => {
@ -9,8 +9,8 @@ export const GET = async (request: NextRequest) => {
if (middlewareResponse) return middlewareResponse; if (middlewareResponse) return middlewareResponse;
try { try {
const url = new URL(request.url); const url = new URL(request.url);
const username = url.searchParams.get("username") ?? "2"; const appleEmail = url.searchParams.get("appleEmail") ?? "";
const user = await getUserByUsername(username); const user = await getUserByAppleEmail(appleEmail);
return NextResponse.json(user); return NextResponse.json(user);
} catch (error) { } catch (error) {
console.error(error); console.error(error);

View File

@ -1,39 +0,0 @@
"use server";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { login } from "~/server/functions";
import { middleware } from "~/middleware";
type LoginRequest = {
username: string;
password: string;
}
export async function POST(request: NextRequest) {
const middlewareResponse = await middleware(request);
if (middlewareResponse) return middlewareResponse;
try {
const { username, password } = await request.json() as LoginRequest;
console.log("Received request:", { username, password });
if (!username || !password) {
return NextResponse.json({ message: "Missing required fields" }, { status: 400 });
}
console.log("Logging in user:", username);
const result = await login(username, password);
if (result) {
console.log("User logged in successfully");
return NextResponse.json({ result }, { status: 200 });
} else {
throw new Error("Failed to log in user");
}
} catch (error) {
console.error("Error in login:", error);
if (error instanceof Error) {
return NextResponse.json({ message: error.message }, { status: 400 });
}
return NextResponse.json({ message: "Error logging in user" }, { status: 500 });
}
}

View File

@ -1,25 +0,0 @@
"use server";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { logout } from "~/server/functions";
import { middleware } from "~/middleware";
export const POST = async (request: NextRequest) => {
const middlewareResponse = await middleware(request);
if (middlewareResponse) return middlewareResponse;
try {
const { refreshToken } = await request.json() as { refreshToken: string };
if (!refreshToken)
return NextResponse.json({ message: "Refresh token is required" }, { status: 400 });
await logout(refreshToken);
return NextResponse.json({ message: "Logged out successfully" });
} catch (error) {
console.error('Logout error:', error);
if (error instanceof Error)
return NextResponse.json({ message: error.message }, { status: 400 });
else
return NextResponse.json({ message: "Unknown error occurred" }, { status: 500 });
}
}

View File

@ -1,22 +0,0 @@
"use server";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { refreshToken } from "~/server/functions";
import { middleware } from "~/middleware";
export async function POST(request: NextRequest) {
const middlewareResponse = await middleware(request);
if (middlewareResponse) return middlewareResponse;
try {
const { refreshToken: token } = await request.json() as { refreshToken: string };
if (!token)
return NextResponse.json({ message: "Refresh token is required" },{ status: 400 });
const tokens = await refreshToken(token);
return NextResponse.json(tokens);
} catch (error) {
if (error instanceof Error)
return NextResponse.json({ message: error.message }, { status: 400 });
else
return NextResponse.json({ message: "Unknown error occurred" }, { status: 500 });
}
}

View File

@ -4,7 +4,7 @@ import type { NextRequest } from "next/server";
import { updateUserPFP } from "~/server/functions"; import { updateUserPFP } from "~/server/functions";
import { middleware } from "~/middleware"; import { middleware } from "~/middleware";
interface UpdatePfpRequest { type UpdatePfpRequest = {
userId: number; userId: number;
pfpURL: string; pfpURL: string;
} }

View File

@ -4,7 +4,7 @@ import type { NextRequest } from "next/server";
import { updateUserPushToken } from "~/server/functions"; import { updateUserPushToken } from "~/server/functions";
import { middleware } from "~/middleware"; import { middleware } from "~/middleware";
interface UpdatePushTokenRequest { type UpdatePushTokenRequest = {
userId: number; userId: number;
pushToken: string; pushToken: string;
} }

View File

@ -1,87 +0,0 @@
"use client";
import React, { useState } from 'react';
export default function ChangePasswordPage() {
const [userId, setUserId] = useState("");
const [oldPassword, setOldPassword] = useState("");
const [newPassword, setNewPassword] = useState("");
const [result, setResult] = useState<string | null>(null);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setResult(null);
try {
const response = await fetch('/api/users/changePassword', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': process.env.NEXT_PUBLIC_API_KEY ?? '',
},
body: JSON.stringify({
userId,
oldPassword,
newPassword
})
});
const data = await response.json() as {message: string}
setResult(JSON.stringify(data, null, 2));
} catch (error) {
console.error('Error:', error);
setResult('An error occurred');
}
};
return (
<main className="flex min-h-screen flex-col items-center justify-center
bg-gradient-to-b from-pink-500 to-orange-400 text-white cursor-pointer">
<div className="p-4">
<h1 className="text-2xl mb-4">Test Change Password</h1>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label htmlFor="userId" className="block">User ID:</label>
<input
type="text"
id="userId"
value={userId}
onChange={(e) => setUserId(e.target.value)}
className="border p-2 w-full bg-black"
required
/>
</div>
<div>
<label htmlFor="oldPassword" className="block">Old Password:</label>
<input
type="text"
id="oldPassword"
value={oldPassword}
onChange={(e) => setOldPassword(e.target.value)}
className="border p-2 w-full bg-black"
required
/>
</div>
<div>
<label htmlFor="newPassword" className="block">New Password:</label>
<input
type="text"
id="newPassword"
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
className="border p-2 w-full bg-black"
required
/>
</div>
<button type='submit' className='bg-blue-500 p-2 rounded'>
Change Password
</button>
</form>
{result && (
<div className='mt-4 p-2 rounded bg-black'>
<pre>{result}</pre>
</div>
)}
</div>
</main>
);
};

View File

@ -3,12 +3,11 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
export default function CreateUserPage() { export default function CreateUserPage() {
const [username, setUsername] = useState(""); const [appleId, setAppleId] = useState("");
const [email, setEmail] = useState(""); const [appleEmail, setAppleEmail] = useState("");
const [passwordHash, setPasswordHash] = useState(""); const [fullName, setFullName] = useState("");
const [name, setName] = useState("");
const [pfpURL, setPfpURL] = useState("");
const [pushToken, setPushToken] = useState(""); const [pushToken, setPushToken] = useState("");
const [pfpURL, setPfpURL] = useState("");
const [result, setResult] = useState<string | null>(null); const [result, setResult] = useState<string | null>(null);
const handleSubmit = async (e: React.FormEvent) => { const handleSubmit = async (e: React.FormEvent) => {
@ -23,10 +22,9 @@ export default function CreateUserPage() {
'x-api-key': process.env.NEXT_PUBLIC_API_KEY ?? '', 'x-api-key': process.env.NEXT_PUBLIC_API_KEY ?? '',
}, },
body: JSON.stringify({ body: JSON.stringify({
username, appleId,
email, appleEmail,
passwordHash, fullName,
name,
pfpURL, pfpURL,
pushToken pushToken
}) })
@ -46,45 +44,34 @@ export default function CreateUserPage() {
<h1 className="text-2xl mb-4">Test Create User</h1> <h1 className="text-2xl mb-4">Test Create User</h1>
<form onSubmit={handleSubmit} className="space-y-4"> <form onSubmit={handleSubmit} className="space-y-4">
<div> <div>
<label htmlFor="username" className="block">Username:</label> <label htmlFor="appleId" className="block">Username:</label>
<input <input
type="text" type="text"
id="username" id="appleId"
value={username} value={appleId}
onChange={(e) => setUsername(e.target.value)} onChange={(e) => setAppleId(e.target.value)}
className="border p-2 w-full bg-black" className="border p-2 w-full bg-black"
required required
/> />
</div> </div>
<div> <div>
<label htmlFor="email" className="block">Email:</label> <label htmlFor="appleEmail" className="block">Email:</label>
<input <input
type="text" type="text"
id="email" id="appleEmail"
value={email} value={appleEmail}
onChange={(e) => setEmail(e.target.value)} onChange={(e) => setAppleEmail(e.target.value)}
className="border p-2 w-full bg-black" className="border p-2 w-full bg-black"
required required
/> />
</div> </div>
<div> <div>
<label htmlFor="passwordHash" className="block">Password:</label> <label htmlFor="fullName" className="block">Name:</label>
<input <input
type="text" type="text"
id="passwordHash" id="fullName"
value={passwordHash} value={fullName}
onChange={(e) => setPasswordHash(e.target.value)} onChange={(e) => setFullName(e.target.value)}
className="border p-2 w-full bg-black"
required
/>
</div>
<div>
<label htmlFor="name" className="block">Name:</label>
<input
type="text"
id="name"
value={name}
onChange={(e) => setName(e.target.value)}
className="border p-2 w-full bg-black" className="border p-2 w-full bg-black"
required required
/> />

View File

@ -3,7 +3,7 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
export default function GetUserByUsernamePage() { export default function GetUserByUsernamePage() {
const [userName, setUserName] = useState(''); const [appleEmail, setAppleEmail] = useState('');
const [result, setResult] = useState<string | null>(null); const [result, setResult] = useState<string | null>(null);
const handleSubmit = async (e: React.FormEvent) => { const handleSubmit = async (e: React.FormEvent) => {
@ -11,7 +11,7 @@ export default function GetUserByUsernamePage() {
setResult(null); setResult(null);
try { try {
const response = await fetch(`/api/users/getUserByUsername?username=${userName}`, { const response = await fetch(`/api/users/getUserByAppleEmail?appleEmail=${appleEmail}`, {
method: 'GET', method: 'GET',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@ -37,8 +37,8 @@ export default function GetUserByUsernamePage() {
<input <input
type="text" type="text"
id="userId" id="userId"
value={userName} value={appleEmail}
onChange={(e) => setUserName(e.target.value)} onChange={(e) => setAppleEmail(e.target.value)}
className="border p-2 w-full bg-black" className="border p-2 w-full bg-black"
required required
/> />

View File

@ -1,74 +0,0 @@
'use client';
import React, { useState } from 'react';
export default function LoginTestPage() {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [result, setResult] = useState<string | null>(null);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setResult(null);
try {
const response = await fetch('/api/users/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': process.env.NEXT_PUBLIC_API_KEY ?? '',
},
body: JSON.stringify({
username,
password
})
});
const data = await response.json() as {message: string}
setResult(JSON.stringify(data, null, 2));
} catch (error) {
console.error('Error:', error);
setResult('An error occurred');
}
};
return (
<main className="flex min-h-screen flex-col items-center justify-center
bg-gradient-to-b from-pink-500 to-orange-400 text-white cursor-pointer">
<div className="p-4">
<h1 className="text-2xl mb-4">Test Login</h1>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label htmlFor="username" className="block">Username:</label>
<input
type="text"
id="username"
value={username}
onChange={(e) => setUsername(e.target.value)}
className="border p-2 w-full bg-black"
required
/>
</div>
<div>
<label htmlFor="password" className="block">Password:</label>
<input
type="text"
id="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="border p-2 w-full bg-black"
required
/>
</div>
<button type='submit' className='bg-blue-500 p-2 rounded'>
Login
</button>
</form>
{result && (
<div className='mt-4 p-2 rounded bg-black'>
<pre>{result}</pre>
</div>
)}
</div>
</main>
);
};

View File

@ -1,60 +0,0 @@
'use client';
import React, { useState } from 'react';
export default function TestLogout() {
const [refreshToken, setRefreshToken] = useState('');
const [result, setResult] = useState<string | null>(null);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setResult(null);
try {
const response = await fetch('/api/users/logout', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': process.env.NEXT_PUBLIC_API_KEY ?? '',
},
body: JSON.stringify({
refreshToken
})
});
const data = await response.json() as {message: string}
setResult(JSON.stringify(data, null, 2));
} catch (error) {
console.error('Error:', error);
setResult('An error occurred');
}
};
return (
<main className="flex min-h-screen flex-col items-center justify-center
bg-gradient-to-b from-pink-500 to-orange-400 text-white cursor-pointer">
<div className="p-4">
<h1 className="text-2xl mb-4">Test Logout</h1>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label htmlFor="refreshToken" className="block">Token:</label>
<input
type="text"
id="refreshToken"
value={refreshToken}
onChange={(e) => setRefreshToken(e.target.value)}
className="border p-2 w-full bg-black"
required
/>
</div>
<button type='submit' className='bg-blue-500 p-2 rounded'>
Logout
</button>
</form>
{result && (
<div className='mt-4 p-2 rounded bg-black'>
<pre>{result}</pre>
</div>
)}
</div>
</main>
);
};

View File

@ -1,60 +0,0 @@
'use client';
import React, { useState } from 'react';
export default function TestRefreshToken() {
const [refreshToken, setRefreshToken] = useState('');
const [result, setResult] = useState<string | null>(null);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setResult(null);
try {
const response = await fetch('/api/users/refreshToken', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': process.env.NEXT_PUBLIC_API_KEY ?? '',
},
body: JSON.stringify({
refreshToken
})
});
const data = await response.json() as {message: string}
setResult(JSON.stringify(data, null, 2));
} catch (error) {
console.error('Error:', error);
setResult('An error occurred');
}
};
return (
<main className="flex min-h-screen flex-col items-center justify-center
bg-gradient-to-b from-pink-500 to-orange-400 text-white cursor-pointer">
<div className="p-4">
<h1 className="text-2xl mb-4">Test Refresh Token</h1>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label htmlFor="refreshToken" className="block">Token:</label>
<input
type="text"
id="refreshToken"
value={refreshToken}
onChange={(e) => setRefreshToken(e.target.value)}
className="border p-2 w-full bg-black"
required
/>
</div>
<button type='submit' className='bg-blue-500 p-2 rounded'>
Refresh Token
</button>
</form>
{result && (
<div className='mt-4 p-2 rounded bg-black'>
<pre>{result}</pre>
</div>
)}
</div>
</main>
);
};

View File

@ -19,14 +19,11 @@ export const users = createTable(
"user", "user",
{ {
id: serial("id").primaryKey(), id: serial("id").primaryKey(),
username: varchar("username", { length: 50 }).unique().notNull(), appleId: varchar("apple_id", { length: 255 }).unique(),
email: varchar("email", { length: 255 }).unique().notNull(), appleEmail: varchar("apple_email", { length: 255 }).unique().notNull(),
passwordHash: varchar("password_hash", {length: 255}).notNull(), fullName: varchar("full_name", { length: 100 }),
name: varchar("name", { length: 100 }),
pfpURL: varchar("pfp_url", { length: 255 }), pfpURL: varchar("pfp_url", { length: 255 }),
pushToken: varchar("pushToken", { length: 255 }), pushToken: varchar("pushToken", { length: 255 }),
refreshToken: varchar("refreshToken", { length: 255 }),
lastLogin: timestamp("last_login", { withTimezone: true }),
createdAt: timestamp("created_at", { withTimezone: true }) createdAt: timestamp("created_at", { withTimezone: true })
.default(sql`CURRENT_TIMESTAMP`) .default(sql`CURRENT_TIMESTAMP`)
.notNull(), .notNull(),

View File

@ -2,9 +2,9 @@ import 'server-only';
import { db } from '~/server/db'; import { db } from '~/server/db';
import * as schema from '~/server/db/schema'; import * as schema from '~/server/db/schema';
import { eq, and, or, sql } from 'drizzle-orm'; import { eq, and, or, sql } from 'drizzle-orm';
import { pgEnum } from 'drizzle-orm/pg-core';
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import { inArray } from 'drizzle-orm/sql'; //import { pgEnum } from 'drizzle-orm/pg-core';
//import { inArray } from 'drizzle-orm/sql';
// --- Helper Functions --- // // --- Helper Functions --- //
@ -63,48 +63,7 @@ export const ensureRelationshipExistsByRelationshipId = async (relationshipId: n
* Handles both directions of a relationship (userId => partnerId or partnerId => userId). * Handles both directions of a relationship (userId => partnerId or partnerId => userId).
* Optionally checks relationship status. * Optionally checks relationship status.
*/ */
export const ensureRelationshipExistsOld = async (userId: number, partnerId: number, status?: 'pending' | 'accepted') => { export const ensureRelationshipExistsByUserIds = async (userId: number, partnerId: number) => {
try {
// Ensure bidirectional relationship (user1 <-> user2 or user2 <-> user1)
const relationship = await db.select({
relationshipId: schema.userRelationships.relationshipId,
status: schema.relationships.status,
})
.from(schema.userRelationships)
.leftJoin(
schema.relationships,
eq(schema.userRelationships.relationshipId, schema.relationships.id)
)
.where(or(
and(
eq(schema.userRelationships.userId, userId),
eq(schema.userRelationships.userId, partnerId) // Check if userId -> partnerId
),
and(
eq(schema.userRelationships.userId, partnerId),
eq(schema.userRelationships.userId, userId) // Check if partnerId -> userId (reverse relationship)
)
));
if (!relationship.length) {
throw new Error('Relationship does not exist');
}
if (status && relationship[0]?.status !== status) {
throw new Error(`Relationship is not in ${status} status`);
}
return relationship[0];
} catch (error) {
if (error instanceof Error) {
throw new Error(`Error checking relationship: ${error.message}`);
} else {
throw new Error("Unknown error occurred while checking relationship");
}
}
};
export const ensureRelationshipExists = async (userId: number, partnerId: number) => {
try { try {
// Ensure bidirectional relationship (user1 <-> user2 or user2 <-> user1) // Ensure bidirectional relationship (user1 <-> user2 or user2 <-> user1)
const relationship = await db.select({ const relationship = await db.select({
@ -160,10 +119,10 @@ export const getUserById = async (userId: number) => {
} }
}; };
export const getUserByUsername = async (username: string) => { export const getUserByAppleEmail = async (appleEmail: string) => {
try { try {
const user = await db.select().from(schema.users) const user = await db.select().from(schema.users)
.where(eq(schema.users.username, username)); .where(eq(schema.users.appleEmail, appleEmail));
if (user.length === 0) throw new Error('User not found'); if (user.length === 0) throw new Error('User not found');
return user[0]; return user[0];
@ -177,25 +136,24 @@ export const getUserByUsername = async (username: string) => {
}; };
export const createUser = async ( export const createUser = async (
username: string, email: string, passwordHash: string, appleId: string, appleEmail: string,
name: string, pfpURL = "", pushToken = "" fullName: string, pfpURL = "", pushToken = ""
) => { ) => {
try { try {
if (!username || !email || !passwordHash || !name) { if (!appleId || !appleEmail || !fullName || !pushToken) {
throw new Error("Error: All required fields must be filled"); throw new Error("Error: All required fields must be filled");
} }
// Check if username or email is already taken // Check if username or email is already taken
const existingUser = await db.select().from(schema.users) const existingUser = await db.select().from(schema.users)
.where(or(eq(schema.users.username, username), eq(schema.users.email, email))); .where(or(eq(schema.users.appleId, appleId), eq(schema.users.appleEmail, appleEmail)));
if (existingUser.length > 0) { if (existingUser.length > 0) {
throw new Error("Username or email is already in use"); throw new Error("Username or email is already in use");
} }
const newUser = await db.insert(schema.users).values({ const newUser = await db.insert(schema.users).values({
username, email, passwordHash, name, pfpURL, pushToken, appleId, appleEmail, fullName, pfpURL, pushToken
lastLogin: new Date(),
}).returning(); }).returning();
return newUser; return newUser;
@ -208,37 +166,6 @@ export const createUser = async (
} }
}; };
export const changePassword = async (
userId: number, oldPasswordHash: string, newPasswordHash: string
) => {
try {
// Ensure all arguments are provided
if (!oldPasswordHash || !newPasswordHash) throw new Error("Password fields are required");
if (oldPasswordHash === newPasswordHash)
throw new Error("New password cannot be the same as the old password");
const user = await ensureUserExists(userId);
// Validate old password
if (user?.passwordHash !== oldPasswordHash) {
throw new Error("Old password does not match");
}
// Update with the new password hash
await db.update(schema.users)
.set({ passwordHash: newPasswordHash })
.where(eq(schema.users.id, userId));
return { success: true };
} catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to change password: ${error.message}`);
} else {
throw new Error("Unknown error occurred while changing password");
}
}
};
export const updateUserPFP = async (userId: number, pfpURL: string) => { export const updateUserPFP = async (userId: number, pfpURL: string) => {
try { try {
await db.update(schema.users) await db.update(schema.users)
@ -271,88 +198,6 @@ export const updateUserPushToken = async (userId: number, pushToken: string) =>
} }
}; };
export const login = async (username: string, passwordHash: string) => {
try {
const user = await getUserByUsername(username);
if (user?.passwordHash !== passwordHash) {
throw new Error("Invalid password");
}
const accessToken = jwt.sign({ userId: user.id }, process.env.JWT_SECRET!,
{expiresIn: '15m' });
const refreshToken = jwt.sign({ userId: user.id }, process.env.JWT_REFRESH_SECRET!,
{expiresIn: '7d' });
// Update last login timestamp
await db.update(schema.users)
.set({ lastLogin: new Date() })
.where(eq(schema.users.id, user.id));
await db.update(schema.users)
.set({ refreshToken: refreshToken })
.where(eq(schema.users.id, user.id));
return { user, accessToken, refreshToken };
} catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to Log in: ${error.message}`);
} else {
throw new Error("Unknown error occurred while logging in");
}
}
};
export const refreshToken = async (refreshToken: string) => {
try {
const decoded = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET!) as { userId: number };
if (!decoded.userId)
throw new Error("Invalid refresh token");
const user = await getUserById(decoded.userId);
if (!user || user.refreshToken !== refreshToken)
throw new Error("Invalid refresh token");
const newAccessToken = jwt.sign({ userId: user.id }, process.env.JWT_SECRET!,
{expiresIn: '15m' });
const newRefreshToken = jwt.sign({ userId: user.id }, process.env.JWT_REFRESH_SECRET!,
{expiresIn: '7d' });
await db.update(schema.users)
.set({ refreshToken: newRefreshToken })
.where(eq(schema.users.id, user.id));
return { accessToken: newAccessToken, refreshToken: newRefreshToken };
} catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to refresh token: ${error.message}`);
} else {
throw new Error("Unknown error occurred while refreshing token");
}
}
};
export const logout = async (refreshToken: string) => {
try {
const user = await db.select()
.from(schema.users)
.where(eq(schema.users.refreshToken, refreshToken))
.limit(1);
if (user.length === 0 || !user[0]?.id) {
throw new Error('No user found with this refresh token');
}
await db.update(schema.users)
.set({ refreshToken: null })
.where(eq(schema.users.id, user[0].id));
return { success: true };
} catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to logout: ${error.message}`);
} else {
throw new Error("Unknown error occurred while logging out");
}
}
};
// --- Relationship Management Functions --- // // --- Relationship Management Functions --- //
export const createRelationshipRequest = async (requestorId: number, requestedId: number) => { export const createRelationshipRequest = async (requestorId: number, requestedId: number) => {
@ -463,7 +308,7 @@ export const sendMessage = async (
await ensureUserExists(senderId); await ensureUserExists(senderId);
await ensureUserExists(receiverId); await ensureUserExists(receiverId);
await ensureRelationshipExists(senderId, receiverId); await ensureRelationshipExistsByUserIds(senderId, receiverId);
// Insert the new message // Insert the new message
const message = await db.insert(schema.messages).values({ const message = await db.insert(schema.messages).values({
@ -481,7 +326,6 @@ export const sendMessage = async (
type: mediaType, type: mediaType,
}); });
} }
return message; return message;
} catch (error) { } catch (error) {
if (error instanceof Error) { if (error instanceof Error) {
@ -495,7 +339,7 @@ export const sendMessage = async (
export const fetchMessages = async (userId: number, partnerId: number) => { export const fetchMessages = async (userId: number, partnerId: number) => {
try { try {
await ensureUserExists(userId); await ensureUserExists(userId);
await ensureRelationshipExists(userId, partnerId); await ensureRelationshipExistsByUserIds(userId, partnerId);
const messages = await db.select().from(schema.messages) const messages = await db.select().from(schema.messages)
.where(or( .where(or(