update backend for appleauth
This commit is contained in:
parent
cbdca78f18
commit
171de4104d
@ -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 });
|
|
||||||
}
|
|
||||||
};
|
|
@ -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 });
|
||||||
|
@ -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);
|
@ -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 });
|
|
||||||
}
|
|
||||||
}
|
|
@ -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 });
|
|
||||||
}
|
|
||||||
}
|
|
@ -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 });
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
@ -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
|
||||||
/>
|
/>
|
||||||
|
@ -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
|
||||||
/>
|
/>
|
@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
@ -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(),
|
||||||
|
@ -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(
|
||||||
|
Loading…
Reference in New Issue
Block a user