Finish front end tests
This commit is contained in:
parent
a2bb8023c9
commit
9f7d142ff4
0
.env.example
Normal file → Executable file
0
.env.example
Normal file → Executable file
0
.eslintrc.cjs
Normal file → Executable file
0
.eslintrc.cjs
Normal file → Executable file
0
.gitignore
vendored
Normal file → Executable file
0
.gitignore
vendored
Normal file → Executable file
0
drizzle.config.ts
Normal file → Executable file
0
drizzle.config.ts
Normal file → Executable file
0
next.config.js
Normal file → Executable file
0
next.config.js
Normal file → Executable file
0
package.json
Normal file → Executable file
0
package.json
Normal file → Executable file
0
pnpm-lock.yaml
generated
Normal file → Executable file
0
pnpm-lock.yaml
generated
Normal file → Executable file
0
postcss.config.cjs
Normal file → Executable file
0
postcss.config.cjs
Normal file → Executable file
0
prettier.config.js
Normal file → Executable file
0
prettier.config.js
Normal file → Executable file
0
public/favicon.png
Normal file → Executable file
0
public/favicon.png
Normal file → Executable file
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
@ -1,27 +0,0 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import type { NextRequest } from 'next/server';
|
||||
import { updateUserPushToken } from '~/server/functions';
|
||||
import { middleware } from "~/middleware";
|
||||
|
||||
type Data = {
|
||||
userId: string;
|
||||
pushToken: string;
|
||||
};
|
||||
|
||||
export const POST = async (request: NextRequest) => {
|
||||
const middlewareResponse = await middleware(request);
|
||||
if (middlewareResponse) return middlewareResponse;
|
||||
try {
|
||||
const { userId, pushToken } = await request.json() as Data;
|
||||
console.log('Received request:', { userId, pushToken });
|
||||
|
||||
console.log('Updating push token for user:', userId);
|
||||
await updateUserPushToken(parseInt(userId), pushToken);
|
||||
|
||||
console.log('Push token updated successfully');
|
||||
return NextResponse.json({ message: "Push token updated successfully" });
|
||||
} catch (error) {
|
||||
console.error('Error in updatePushToken:', error);
|
||||
return NextResponse.json({ message: "Error updating push token" }, { status: 500 });
|
||||
}
|
||||
};
|
@ -18,6 +18,12 @@ export const POST = async (request: NextRequest) => {
|
||||
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) {
|
||||
|
@ -3,27 +3,20 @@ import { NextResponse } from "next/server";
|
||||
import type { NextRequest } from "next/server";
|
||||
import { logout } from "~/server/functions";
|
||||
import { middleware } from "~/middleware";
|
||||
import jwt from "jsonwebtoken";
|
||||
|
||||
export const POST = async (request: NextRequest) => {
|
||||
const middlewareResponse = await middleware(request);
|
||||
if (middlewareResponse) return middlewareResponse;
|
||||
|
||||
try {
|
||||
const { token } = await request.json() as { token: string };
|
||||
if (!token)
|
||||
return NextResponse.json({ message: "Token is required" },{ status: 400 });
|
||||
const { refreshToken } = await request.json() as { refreshToken: string };
|
||||
if (!refreshToken)
|
||||
return NextResponse.json({ message: "Refresh token is required" }, { status: 400 });
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET!) as { userId: number };
|
||||
if (!decoded.userId)
|
||||
throw new Error("Invalid token");
|
||||
await logout(decoded.userId);
|
||||
await logout(refreshToken);
|
||||
return NextResponse.json({ message: "Logged out successfully" });
|
||||
} catch (jwtError) {
|
||||
return NextResponse.json({ message: "Invalid token", error: jwtError }, { status: 400 });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Logout error:', error);
|
||||
if (error instanceof Error)
|
||||
return NextResponse.json({ message: error.message }, { status: 400 });
|
||||
else
|
||||
|
0
src/app/layout.tsx
Normal file → Executable file
0
src/app/layout.tsx
Normal file → Executable file
0
src/app/page.tsx
Normal file → Executable file
0
src/app/page.tsx
Normal file → Executable file
@ -24,7 +24,7 @@ export default function TestCreateRequestPage() {
|
||||
}),
|
||||
});
|
||||
const data = await response.json() as {message: string};
|
||||
setResult(JSON.stringify(data));
|
||||
setResult(JSON.stringify(data, null, 2));
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
setResult('An error occurred');
|
||||
|
61
src/app/relationships/deleteRelationship/page.tsx
Normal file
61
src/app/relationships/deleteRelationship/page.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
export default function DeleteRelationshipPage() {
|
||||
const [relationshipId, setRelationshipId] = useState("");
|
||||
const [result, setResult] = useState<string | null>(null);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setResult(null);
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/relationships/deleteRelationship', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': process.env.NEXT_PUBLIC_API_KEY ?? '',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
relationshipId
|
||||
})
|
||||
});
|
||||
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 Send Message</h1>
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<label htmlFor="relationshipId" className="block">Relationship ID:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="relationshipId"
|
||||
value={relationshipId}
|
||||
onChange={(e) => setRelationshipId(e.target.value)}
|
||||
className="border p-2 w-full bg-black"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<button type='submit' className='bg-blue-500 p-2 rounded'>
|
||||
Delete Relationship
|
||||
</button>
|
||||
</form>
|
||||
{result && (
|
||||
<div className='mt-4 p-2 rounded bg-black'>
|
||||
<pre>{result}</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
};
|
79
src/app/relationships/updateRequest/page.tsx
Normal file
79
src/app/relationships/updateRequest/page.tsx
Normal file
@ -0,0 +1,79 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
type status = 'pending' | 'accepted' | 'rejected';
|
||||
|
||||
export default function UpdateRequestPage() {
|
||||
const [relationshipId, setRelationshipId] = useState("");
|
||||
const [status, setStatus] = useState("");
|
||||
const [result, setResult] = useState<string | null>(null);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setResult(null);
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/relationships/updateRequest', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': process.env.NEXT_PUBLIC_API_KEY ?? '',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
relationshipId,
|
||||
status
|
||||
})
|
||||
});
|
||||
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 Send Message</h1>
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<label htmlFor="relationshipId" className="block">Relationship ID:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="relationshipId"
|
||||
value={relationshipId}
|
||||
onChange={(e) => setRelationshipId(e.target.value)}
|
||||
className="border p-2 w-full bg-black"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="status" className="block">Status:</label>
|
||||
<select
|
||||
id="status"
|
||||
value={status}
|
||||
onChange={(e) => setStatus(e.target.value as status)}
|
||||
className="border p-2 w-full bg-black"
|
||||
required
|
||||
>
|
||||
<option value="pending">Pending</option>
|
||||
<option value="accepted">Accepted</option>
|
||||
<option value="rejected">Rejected</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type='submit' className='bg-blue-500 p-2 rounded'>
|
||||
Update Request
|
||||
</button>
|
||||
</form>
|
||||
{result && (
|
||||
<div className='mt-4 p-2 rounded bg-black'>
|
||||
<pre>{result}</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
};
|
87
src/app/users/changePassword/page.tsx
Normal file
87
src/app/users/changePassword/page.tsx
Normal file
@ -0,0 +1,87 @@
|
||||
"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>
|
||||
);
|
||||
};
|
124
src/app/users/createUser/page.tsx
Normal file
124
src/app/users/createUser/page.tsx
Normal file
@ -0,0 +1,124 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
export default function CreateUserPage() {
|
||||
const [username, setUsername] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [passwordHash, setPasswordHash] = useState("");
|
||||
const [name, setName] = useState("");
|
||||
const [pfpURL, setPfpURL] = useState("");
|
||||
const [pushToken, setPushToken] = 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/createUser', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': process.env.NEXT_PUBLIC_API_KEY ?? '',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username,
|
||||
email,
|
||||
passwordHash,
|
||||
name,
|
||||
pfpURL,
|
||||
pushToken
|
||||
})
|
||||
});
|
||||
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 Create User</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="email" className="block">Email:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className="border p-2 w-full bg-black"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="passwordHash" className="block">Password:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="passwordHash"
|
||||
value={passwordHash}
|
||||
onChange={(e) => setPasswordHash(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"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="pfpURL" className="block">Pfp URL:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="pfpURL"
|
||||
value={pfpURL}
|
||||
onChange={(e) => setPfpURL(e.target.value)}
|
||||
className="border p-2 w-full bg-black"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="pushToken" className="block">Push Token:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="pushToken"
|
||||
value={pushToken}
|
||||
onChange={(e) => setPushToken(e.target.value)}
|
||||
className="border p-2 w-full bg-black"
|
||||
/>
|
||||
</div>
|
||||
<button type='submit' className='bg-blue-500 p-2 rounded'>
|
||||
Create User
|
||||
</button>
|
||||
</form>
|
||||
{result && (
|
||||
<div className='mt-4 p-2 rounded bg-black'>
|
||||
<pre>{result}</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
};
|
58
src/app/users/getUserByID/page.tsx
Normal file
58
src/app/users/getUserByID/page.tsx
Normal file
@ -0,0 +1,58 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
export default function GetUserByIDPage() {
|
||||
const [userId, setUserId] = 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/getUserByID?userId=${userId}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': process.env.NEXT_PUBLIC_API_KEY ?? '',
|
||||
},
|
||||
});
|
||||
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 Get User By ID</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>
|
||||
<button type='submit' className='bg-blue-500 p-2 rounded'>
|
||||
Get User By ID
|
||||
</button>
|
||||
</form>
|
||||
{result && (
|
||||
<div className='mt-4 p-2 rounded bg-black'>
|
||||
<pre>{result}</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
};
|
58
src/app/users/getUserByUsername/page.tsx
Normal file
58
src/app/users/getUserByUsername/page.tsx
Normal file
@ -0,0 +1,58 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
export default function GetUserByUsernamePage() {
|
||||
const [userName, setUserName] = 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/getUserByUsername?username=${userName}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': process.env.NEXT_PUBLIC_API_KEY ?? '',
|
||||
},
|
||||
});
|
||||
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 Get User By Username</h1>
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<label htmlFor="userId" className="block">User ID:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="userId"
|
||||
value={userName}
|
||||
onChange={(e) => setUserName(e.target.value)}
|
||||
className="border p-2 w-full bg-black"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<button type='submit' className='bg-blue-500 p-2 rounded'>
|
||||
Get User By ID
|
||||
</button>
|
||||
</form>
|
||||
{result && (
|
||||
<div className='mt-4 p-2 rounded bg-black'>
|
||||
<pre>{result}</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
};
|
74
src/app/users/login/page.tsx
Normal file
74
src/app/users/login/page.tsx
Normal file
@ -0,0 +1,74 @@
|
||||
'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>
|
||||
);
|
||||
};
|
60
src/app/users/logout/page.tsx
Normal file
60
src/app/users/logout/page.tsx
Normal file
@ -0,0 +1,60 @@
|
||||
'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>
|
||||
);
|
||||
};
|
60
src/app/users/refreshToken/page.tsx
Normal file
60
src/app/users/refreshToken/page.tsx
Normal file
@ -0,0 +1,60 @@
|
||||
'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>
|
||||
);
|
||||
};
|
73
src/app/users/updatePFP/page.tsx
Normal file
73
src/app/users/updatePFP/page.tsx
Normal file
@ -0,0 +1,73 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
export default function TestUpdatePFP() {
|
||||
const [userId, setUserId] = useState('');
|
||||
const [pfpURL, setPfpURL] = 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/updatePFP', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': process.env.NEXT_PUBLIC_API_KEY ?? '',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
userId,
|
||||
pfpURL
|
||||
})
|
||||
});
|
||||
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 Update PFP</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="pfpURL" className="block">PFP URL:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="pfpURL"
|
||||
value={pfpURL}
|
||||
onChange={(e) => setPfpURL(e.target.value)}
|
||||
className="border p-2 w-full bg-black"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<button type='submit' className='bg-blue-500 p-2 rounded'>
|
||||
Update PFP
|
||||
</button>
|
||||
</form>
|
||||
{result && (
|
||||
<div className='mt-4 p-2 rounded bg-black'>
|
||||
<pre>{result}</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
73
src/app/users/updatePushToken/page.tsx
Normal file
73
src/app/users/updatePushToken/page.tsx
Normal file
@ -0,0 +1,73 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
export default function TestUpdatePushToken() {
|
||||
const [userId, setUserId] = useState('');
|
||||
const [pushToken, setPushToken] = 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/updatePushToken', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': process.env.NEXT_PUBLIC_API_KEY ?? '',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
userId,
|
||||
pushToken
|
||||
})
|
||||
});
|
||||
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 Update Push Token</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="pushToken" className="block">Push Token:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="pushToken"
|
||||
value={pushToken}
|
||||
onChange={(e) => setPushToken(e.target.value)}
|
||||
className="border p-2 w-full bg-black"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<button type='submit' className='bg-blue-500 p-2 rounded'>
|
||||
Update Push Token
|
||||
</button>
|
||||
</form>
|
||||
{result && (
|
||||
<div className='mt-4 p-2 rounded bg-black'>
|
||||
<pre>{result}</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
};
|
33
src/env.js
Normal file → Executable file
33
src/env.js
Normal file → Executable file
@ -2,45 +2,32 @@ import { createEnv } from "@t3-oss/env-nextjs";
|
||||
import { z } from "zod";
|
||||
|
||||
export const env = createEnv({
|
||||
/**
|
||||
* Specify your server-side environment variables schema here. This way you can ensure the app
|
||||
* isn't built with invalid env vars.
|
||||
*/
|
||||
|
||||
server: {
|
||||
DATABASE_URL: z.string().url(),
|
||||
API_KEY: z.string(),
|
||||
NODE_ENV: z
|
||||
.enum(["development", "test", "production"])
|
||||
.default("development"),
|
||||
JWT_SECRET: z.string(),
|
||||
JWT_REFRESH_SECRET: z.string(),
|
||||
SKIP_ENV_VALIDATION: z.boolean().optional(),
|
||||
},
|
||||
|
||||
/**
|
||||
* Specify your client-side environment variables schema here. This way you can ensure the app
|
||||
* isn't built with invalid env vars. To expose them to the client, prefix them with
|
||||
* `NEXT_PUBLIC_`.
|
||||
*/
|
||||
client: {
|
||||
// NEXT_PUBLIC_CLIENTVAR: z.string(),
|
||||
NEXT_PUBLIC_API_KEY: z.string(),
|
||||
},
|
||||
|
||||
/**
|
||||
* You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g.
|
||||
* middlewares) or client-side so we need to destruct manually.
|
||||
*/
|
||||
runtimeEnv: {
|
||||
DATABASE_URL: process.env.DATABASE_URL,
|
||||
API_KEY: process.env.API_KEY,
|
||||
NODE_ENV: process.env.NODE_ENV,
|
||||
// NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR,
|
||||
JWT_SECRET: process.env.JWT_SECRET,
|
||||
JWT_REFRESH_SECRET: process.env.JWT_REFRESH_SECRET,
|
||||
NEXT_PUBLIC_API_KEY: process.env.NEXT_PUBLIC_API_KEY,
|
||||
SKIP_ENV_VALIDATION: process.env.SKIP_ENV_VALIDATION,
|
||||
},
|
||||
/**
|
||||
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially
|
||||
* useful for Docker builds.
|
||||
*/
|
||||
|
||||
skipValidation: !!process.env.SKIP_ENV_VALIDATION,
|
||||
/**
|
||||
* Makes it so that empty strings are treated as undefined. `SOME_VAR: z.string()` and
|
||||
* `SOME_VAR=''` will throw an error.
|
||||
*/
|
||||
emptyStringAsUndefined: true,
|
||||
});
|
||||
|
0
src/server/db/index.ts
Normal file → Executable file
0
src/server/db/index.ts
Normal file → Executable file
0
src/server/db/schema.ts
Normal file → Executable file
0
src/server/db/schema.ts
Normal file → Executable file
25
src/server/functions.ts
Normal file → Executable file
25
src/server/functions.ts
Normal file → Executable file
@ -214,6 +214,8 @@ export const changePassword = async (
|
||||
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);
|
||||
|
||||
@ -286,6 +288,10 @@ export const login = async (username: string, passwordHash: string) => {
|
||||
.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) {
|
||||
@ -322,11 +328,21 @@ export const refreshToken = async (refreshToken: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const logout = async (userId: number) => {
|
||||
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({ lastLogin: null })
|
||||
.where(eq(schema.users.id, userId));
|
||||
.set({ refreshToken: null })
|
||||
.where(eq(schema.users.id, user[0].id));
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
@ -417,6 +433,9 @@ export const updateRelationshipRequest = async (relationshipId: number, status:
|
||||
export const deleteRelationship = async (relationshipId: number) => {
|
||||
try {
|
||||
await db.transaction(async (trx) => {
|
||||
await trx.delete(schema.countdowns)
|
||||
.where(eq(schema.countdowns.relationshipId, relationshipId));
|
||||
|
||||
await trx.delete(schema.userRelationships)
|
||||
.where(eq(schema.userRelationships.relationshipId, relationshipId));
|
||||
|
||||
|
0
src/styles/globals.css
Normal file → Executable file
0
src/styles/globals.css
Normal file → Executable file
0
tailwind.config.ts
Normal file → Executable file
0
tailwind.config.ts
Normal file → Executable file
0
tsconfig.json
Normal file → Executable file
0
tsconfig.json
Normal file → Executable file
Loading…
Reference in New Issue
Block a user