Start making front end web pages to test every single API.
This commit is contained in:
parent
d7d3c63d7f
commit
a2bb8023c9
@ -16,7 +16,11 @@ export const GET = async (request: NextRequest) => {
|
||||
const messages = await fetchMessages(parseInt(userId), parseInt(partnerId));
|
||||
return NextResponse.json(messages);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return NextResponse.json({ message: "Error" }, { status: 500 });
|
||||
console.error('Detailed error:', error);
|
||||
if (error instanceof Error) {
|
||||
return NextResponse.json({ message: `Error: ${error.message}` }, { status: 500 });
|
||||
} else {
|
||||
return NextResponse.json({ message: "Unknown error occurred" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
87
src/app/countdown/createOrUpdateCountdown/page.tsx
Normal file
87
src/app/countdown/createOrUpdateCountdown/page.tsx
Normal file
@ -0,0 +1,87 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
export default function TestCreateOrUpdateCountdown() {
|
||||
const [relationshipId, setRelationshipId] = useState('');
|
||||
const [title, setTitle] = useState('');
|
||||
const [date, setDate] = useState('');
|
||||
const [result, setResult] = useState<string | null>(null);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setResult(null);
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/countdown/createOrUpdateCountdown', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': process.env.NEXT_PUBLIC_API_KEY ?? '',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
relationshipId,
|
||||
title,
|
||||
date,
|
||||
}),
|
||||
});
|
||||
const data = await response.json() as { message: string };
|
||||
setResult(data.message);
|
||||
} 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 or Update Countdown</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="title" className="block">Title:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="title"
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
className="border p-2 w-full bg-black"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="date" className="block">Date:</label>
|
||||
<input
|
||||
type="datetime-local"
|
||||
id="date"
|
||||
value={date}
|
||||
onChange={(e) => setDate(e.target.value)}
|
||||
className="border p-2 w-full bg-black"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<button type="submit" className="bg-blue-500 text-white p-2 rounded">
|
||||
Submit
|
||||
</button>
|
||||
</form>
|
||||
{result && (
|
||||
<div className="mt-4 p-2 rounded bg-black">
|
||||
Result: {result}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
57
src/app/countdown/getCountdownByRelationship/page.tsx
Normal file
57
src/app/countdown/getCountdownByRelationship/page.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
export default function TestGetCountdownByRelationship() {
|
||||
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/countdown/getCountdownByRelationship?relationshipId=${relationshipId}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'x-api-key': process.env.NEXT_PUBLIC_API_KEY ?? '',
|
||||
},
|
||||
});
|
||||
const data = await response.json() as { countdown: 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 Countdown By Relationship</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 text-white p-2 rounded">
|
||||
Submit
|
||||
</button>
|
||||
</form>
|
||||
{result && (
|
||||
<div className="mt-4 p-2 rounded bg-black">
|
||||
<pre>{result}</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
};
|
69
src/app/messages/fetchMessages/page.tsx
Normal file
69
src/app/messages/fetchMessages/page.tsx
Normal file
@ -0,0 +1,69 @@
|
||||
"use client";
|
||||
import React, { useState } from "react";
|
||||
|
||||
export default function TestFetchMessages() {
|
||||
const [userId, setUserId] = useState("");
|
||||
const [partnerId, setPartnerId] = useState("");
|
||||
const [result, setResult] = useState<string | null>(null);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setResult(null);
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/messages/fetchMessages?userId=${userId}&partnerId=${partnerId}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"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 Fetch Messages</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="partnerId" className="block">Partner ID:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="partnerId"
|
||||
value={partnerId}
|
||||
onChange={(e) => setPartnerId(e.target.value)}
|
||||
className="border p-2 w-full bg-black"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<button type="submit" className="bg-blue-500 text-white p-2 rounded">
|
||||
Submit
|
||||
</button>
|
||||
</form>
|
||||
{result && (
|
||||
<div className="mt-4 p-2 rounded bg-black">
|
||||
<pre>{result}</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
};
|
121
src/app/messages/sendMessage/page.tsx
Normal file
121
src/app/messages/sendMessage/page.tsx
Normal file
@ -0,0 +1,121 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
type MediaType = 'text' | 'image' | 'video' | 'audio' | 'file' | 'link';
|
||||
|
||||
export default function TestSendMessage() {
|
||||
const [senderId, setSenderId] = useState('');
|
||||
const [receiverId, setReceiverId] = useState('');
|
||||
const [content, setContent] = useState('');
|
||||
const [mediaType, setMediaType] = useState<MediaType | ''>('');
|
||||
const [mediaUrl, setMediaUrl] = useState('');
|
||||
const [result, setResult] = useState<string | null>(null);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setResult(null);
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/messages/sendMessage', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': process.env.NEXT_PUBLIC_API_KEY ?? '',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
senderId,
|
||||
receiverId,
|
||||
content,
|
||||
...(mediaType && mediaUrl && { mediaType, mediaUrl }),
|
||||
}),
|
||||
});
|
||||
|
||||
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="senderId" className="block">Sender ID:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="senderId"
|
||||
value={senderId}
|
||||
onChange={(e) => setSenderId(e.target.value)}
|
||||
className="border p-2 w-full bg-black"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="receiverId" className="block">Receiver ID:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="receiverId"
|
||||
value={receiverId}
|
||||
onChange={(e) => setReceiverId(e.target.value)}
|
||||
className="border p-2 w-full bg-black"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="content" className="block">Content:</label>
|
||||
<textarea
|
||||
id="content"
|
||||
value={content}
|
||||
onChange={(e) => setContent(e.target.value)}
|
||||
className="border p-2 w-full bg-black"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="mediaType" className="block">Media Type:</label>
|
||||
<select
|
||||
id="mediaType"
|
||||
value={mediaType}
|
||||
onChange={(e) => setMediaType(e.target.value as MediaType)}
|
||||
className="border p-2 w-full bg-black"
|
||||
>
|
||||
<option value="">None</option>
|
||||
<option value="image">Image</option>
|
||||
<option value="video">Video</option>
|
||||
<option value="audio">Audio</option>
|
||||
<option value="file">File</option>
|
||||
<option value="link">Link</option>
|
||||
</select>
|
||||
</div>
|
||||
{mediaType && (
|
||||
<div>
|
||||
<label htmlFor="mediaUrl" className="block">Media URL:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="mediaUrl"
|
||||
value={mediaUrl}
|
||||
onChange={(e) => setMediaUrl(e.target.value)}
|
||||
className="border p-2 w-full bg-black"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<button type="submit" className="bg-blue-500 text-white p-2 rounded">
|
||||
Send Message
|
||||
</button>
|
||||
</form>
|
||||
{result && (
|
||||
<div className="mt-4 p-2 rounded bg-black">
|
||||
<pre>{result}</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
75
src/app/relationships/createRequest/page.tsx
Normal file
75
src/app/relationships/createRequest/page.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
"use client";
|
||||
|
||||
import React, {useState} from "react";
|
||||
|
||||
export default function TestCreateRequestPage() {
|
||||
const [userId, setUserId] = useState('');
|
||||
const [targetUserId, setTargetUserId] = 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/createRequest', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': process.env.NEXT_PUBLIC_API_KEY ?? '',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
userId,
|
||||
targetUserId,
|
||||
}),
|
||||
});
|
||||
const data = await response.json() as {message: string};
|
||||
setResult(JSON.stringify(data));
|
||||
} 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">Create Relationship Request</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="targetUserId" className="block">Target User ID:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="targetUserId"
|
||||
value={targetUserId}
|
||||
onChange={(e) => setTargetUserId(e.target.value)}
|
||||
className="border p-2 w-full bg-black"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<button type="submit" className="bg-blue-500 p-2 rounded">
|
||||
Submit
|
||||
</button>
|
||||
</form>
|
||||
{result && (
|
||||
<div className="mt-4 p-2 rounded bg-black">
|
||||
Result: {result}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
@ -1,9 +1,10 @@
|
||||
import 'server-only';
|
||||
import { db } from '~/server/db';
|
||||
import * as schema from '~/server/db/schema';
|
||||
import { eq, and, or } from 'drizzle-orm';
|
||||
import { eq, and, or, sql } from 'drizzle-orm';
|
||||
import { pgEnum } from 'drizzle-orm/pg-core';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { inArray } from 'drizzle-orm/sql';
|
||||
|
||||
// --- Helper Functions --- //
|
||||
|
||||
@ -30,12 +31,39 @@ export const ensureUserExists = async (userId: number) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const ensureRelationshipExistsByRelationshipId = async (relationshipId: number) => {
|
||||
try {
|
||||
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(eq(schema.userRelationships.relationshipId, relationshipId));
|
||||
|
||||
if (relationship.length === 0) {
|
||||
throw new Error("Relationship not found");
|
||||
}
|
||||
|
||||
if (relationship[0]?.status !== 'accepted') {
|
||||
throw new Error("Relationship not accepted");
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Ensure relationship exists between a user and a partner.
|
||||
* Handles both directions of a relationship (userId => partnerId or partnerId => userId).
|
||||
* Optionally checks relationship status.
|
||||
*/
|
||||
export const ensureRelationshipExists = async (userId: number, partnerId: number, status?: 'pending' | 'accepted') => {
|
||||
export const ensureRelationshipExistsOld = async (userId: number, partnerId: number, status?: 'pending' | 'accepted') => {
|
||||
try {
|
||||
// Ensure bidirectional relationship (user1 <-> user2 or user2 <-> user1)
|
||||
const relationship = await db.select({
|
||||
@ -76,6 +104,48 @@ export const ensureRelationshipExists = async (userId: number, partnerId: number
|
||||
}
|
||||
};
|
||||
|
||||
export const ensureRelationshipExists = 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(
|
||||
and(
|
||||
eq(schema.userRelationships.relationshipId,
|
||||
db.select({ relationshipId: schema.userRelationships.relationshipId })
|
||||
.from(schema.userRelationships)
|
||||
.where(eq(schema.userRelationships.userId, partnerId))
|
||||
.limit(1)
|
||||
),
|
||||
eq(schema.userRelationships.userId, userId)
|
||||
)
|
||||
);
|
||||
|
||||
if (!relationship.length) {
|
||||
throw new Error('Relationship does not exist');
|
||||
}
|
||||
|
||||
if (relationship[0]?.status !== "accepted") {
|
||||
throw new Error(`Relationship is not accepted`);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// --- User Management Functions --- //
|
||||
|
||||
export const getUserById = async (userId: number) => {
|
||||
@ -275,15 +345,24 @@ export const createRelationshipRequest = async (requestorId: number, requestedId
|
||||
await ensureUserExists(requestedId);
|
||||
|
||||
// Check if a relationship exists in either direction
|
||||
const existingRelationship = await db.select()
|
||||
const existingRelationship = 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, requestorId), eq(schema.userRelationships.userId, requestedId)), // userId -> requestedId
|
||||
and(eq(schema.userRelationships.userId, requestedId), eq(schema.userRelationships.userId, requestorId)) // requestedId -> userId
|
||||
));
|
||||
.innerJoin(
|
||||
schema.relationships,
|
||||
eq(schema.userRelationships.relationshipId, schema.relationships.id)
|
||||
)
|
||||
.where(
|
||||
or(
|
||||
eq(schema.userRelationships.userId, requestorId),
|
||||
eq(schema.userRelationships.userId, requestedId)
|
||||
)
|
||||
).limit(1);
|
||||
|
||||
if (existingRelationship.length && existingRelationship[0]?.relationship?.status !== 'rejected') {
|
||||
if (existingRelationship.length > 0) {
|
||||
throw new Error('A relationship already exists or is pending between these users');
|
||||
}
|
||||
|
||||
@ -312,7 +391,6 @@ export const createRelationshipRequest = async (requestorId: number, requestedId
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export const updateRelationshipRequest = async (relationshipId: number, status: 'accepted' | 'rejected') => {
|
||||
try {
|
||||
const relationship = await db.select().from(schema.relationships)
|
||||
@ -366,8 +444,7 @@ export const sendMessage = async (
|
||||
await ensureUserExists(senderId);
|
||||
await ensureUserExists(receiverId);
|
||||
|
||||
// Ensure the relationship exists (no need to use returned 'relationshipId' for now)
|
||||
await ensureRelationshipExists(senderId, receiverId, "accepted");
|
||||
await ensureRelationshipExists(senderId, receiverId);
|
||||
|
||||
// Insert the new message
|
||||
const message = await db.insert(schema.messages).values({
|
||||
@ -378,7 +455,7 @@ export const sendMessage = async (
|
||||
if (!message.length || !message[0]?.id)
|
||||
throw new Error("Failed to send message");
|
||||
|
||||
if (mediaUrl && mediaType) {
|
||||
if (mediaUrl && mediaType && mediaUrl.length > 0 && mediaType !== "text") {
|
||||
await db.insert(schema.messageMedia).values({
|
||||
messageId: message[0].id,
|
||||
mediaUrl,
|
||||
@ -399,7 +476,7 @@ export const sendMessage = async (
|
||||
export const fetchMessages = async (userId: number, partnerId: number) => {
|
||||
try {
|
||||
await ensureUserExists(userId);
|
||||
await ensureRelationshipExists(userId, partnerId, "accepted");
|
||||
await ensureRelationshipExists(userId, partnerId);
|
||||
|
||||
const messages = await db.select().from(schema.messages)
|
||||
.where(or(
|
||||
|
Loading…
Reference in New Issue
Block a user