update apis. a complete mess now. will refactor or maybe even rewrite later

This commit is contained in:
Gabriel Brown 2024-10-11 15:52:25 -05:00
parent 59a399b16e
commit 06c151ba9f
11 changed files with 262 additions and 64 deletions

View File

@ -17,6 +17,7 @@
}, },
"dependencies": { "dependencies": {
"@t3-oss/env-nextjs": "^0.10.1", "@t3-oss/env-nextjs": "^0.10.1",
"@types/formidable": "^3.4.5",
"drizzle-orm": "^0.33.0", "drizzle-orm": "^0.33.0",
"geist": "^1.3.1", "geist": "^1.3.1",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",

10
pnpm-lock.yaml generated
View File

@ -11,6 +11,9 @@ importers:
'@t3-oss/env-nextjs': '@t3-oss/env-nextjs':
specifier: ^0.10.1 specifier: ^0.10.1
version: 0.10.1(typescript@5.6.2)(zod@3.23.8) version: 0.10.1(typescript@5.6.2)(zod@3.23.8)
'@types/formidable':
specifier: ^3.4.5
version: 3.4.5
drizzle-orm: drizzle-orm:
specifier: ^0.33.0 specifier: ^0.33.0
version: 0.33.0(@types/react@18.3.11)(postgres@3.4.4)(react@18.3.1) version: 0.33.0(@types/react@18.3.11)(postgres@3.4.4)(react@18.3.1)
@ -541,6 +544,9 @@ packages:
'@types/estree@1.0.6': '@types/estree@1.0.6':
resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
'@types/formidable@3.4.5':
resolution: {integrity: sha512-s7YPsNVfnsng5L8sKnG/Gbb2tiwwJTY1conOkJzTMRvJAlLFW1nEua+ADsJQu8N1c0oTHx9+d5nqg10WuT9gHQ==}
'@types/json-schema@7.0.15': '@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
@ -2413,6 +2419,10 @@ snapshots:
'@types/estree@1.0.6': {} '@types/estree@1.0.6': {}
'@types/formidable@3.4.5':
dependencies:
'@types/node': 20.16.10
'@types/json-schema@7.0.15': {} '@types/json-schema@7.0.15': {}
'@types/json5@0.0.29': {} '@types/json5@0.0.29': {}

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,27 @@
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { getPartnerByAppleId, checkRelationshipStatusByAppleId } from "~/server/functions";
import { middleware } from "~/middleware";
export const GET = async (request: NextRequest) => {
const middlewareResponse = await middleware(request);
if (middlewareResponse) return middlewareResponse;
const appleId = request.nextUrl.searchParams.get('appleId');
if (!appleId) {
return NextResponse.json({ message: "Missing required fields" }, { status: 400 });
}
try {
const relationship = await checkRelationshipStatusByAppleId(appleId);
const partner = await getPartnerByAppleId(appleId);
return NextResponse.json({ relationship, partner });
} catch (error) {
if (error instanceof Error) {
return NextResponse.json({ message: error.message }, { status: 404 });
}
return NextResponse.json({ message: "Error fetching relationship status" }, { status: 500 });
}
};

View File

@ -1,38 +0,0 @@
"use server";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { updateUserPFP } from "~/server/functions";
import { middleware } from "~/middleware";
type UpdatePfpRequest = {
userId: number;
pfpURL: string;
}
export async function POST(request: NextRequest) {
const middlewareResponse = await middleware(request);
if (middlewareResponse) return middlewareResponse;
try {
const { userId, pfpURL } = await request.json() as UpdatePfpRequest;
console.log("Received request:", { userId, pfpURL });
if (!userId || !pfpURL || isNaN(userId))
return NextResponse.json({ message: "Missing required fields" }, { status: 400 });
console.log("Updating profile picture for user:", userId);
const result = await updateUserPFP(userId, pfpURL);
if (result.success) {
console.log("Profile picture updated successfully");
return NextResponse.json({ message: "Profile picture updated successfully" });
} else {
throw new Error("Failed to update profile picture");
}
} catch (error) {
console.error("Error in updatePFP:", error);
if (error instanceof Error) {
return NextResponse.json({ message: error.message }, { status: 400 });
}
return NextResponse.json({ message: "Error updating profile picture" }, { status: 500 });
}
}

View File

@ -0,0 +1,92 @@
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { updateUserPfpUrl, getUserPfpUrl } from "~/server/functions";
import { middleware } from "~/middleware";
import fs from "fs/promises";
import path from "path";
const UPLOAD_DIR = path.resolve(process.cwd(), "public/uploads/profile-pictures");
export async function POST(request: NextRequest) {
console.log("Received POST request to /api/users/updatePfp");
// Apply middleware
const middlewareResponse = await middleware(request);
if (middlewareResponse) {
console.log("Middleware rejected the request");
return middlewareResponse;
}
try {
console.log("Parsing form data...");
const formData = await request.formData();
console.log("Form data parsed successfully");
const file = formData.get('file') as File | null;
const appleId = formData.get('appleId') as string | null;
console.log("Received file:", file ? "Yes" : "No");
console.log("Received appleId:", appleId);
if (!file || !appleId) {
console.log("File or appleId missing");
return NextResponse.json({ message: "File or appleId missing." }, { status: 400 });
}
// Validate file type
console.log("File type:", file.type);
if (!file.type.startsWith('image/')) {
console.log("Invalid file type");
return NextResponse.json({ message: "Invalid file type. Only images are allowed." }, { status: 400 });
}
// Ensure upload directory exists
console.log("Creating upload directory if it doesn't exist");
await fs.mkdir(UPLOAD_DIR, { recursive: true });
// Generate a unique filename
const fileExtension = path.extname(file.name);
const fileName = `${appleId}_${Date.now()}${fileExtension}`;
const filePath = path.join(UPLOAD_DIR, fileName);
console.log("Generated file path:", filePath);
// Write file to disk
console.log("Writing file to disk...");
const buffer = Buffer.from(await file.arrayBuffer());
await fs.writeFile(filePath, buffer);
console.log("File written successfully");
// Get the relative URL for the uploaded file
const pfpURL = `/uploads/profile-pictures/${fileName}`;
console.log("New pfpURL:", pfpURL);
// Get old pfpURL and remove the old file if it exists
console.log("Fetching old pfpURL...");
const oldPfpURL = await getUserPfpUrl(appleId);
if (oldPfpURL) {
console.log("Old pfpURL found:", oldPfpURL);
const oldFilePath = path.join(process.cwd(), 'public', oldPfpURL);
console.log("Attempting to delete old file:", oldFilePath);
await fs.unlink(oldFilePath).catch((err) => {
console.log("Error deleting old file:", err.message);
});
} else {
console.log("No old pfpURL found");
}
// Update the user's pfpURL in the database
console.log("Updating user's pfpURL in the database...");
await updateUserPfpUrl(appleId, pfpURL);
console.log("Database updated successfully");
console.log("Sending successful response");
return NextResponse.json({ message: "Profile picture updated successfully", pfpURL });
} catch (error) {
console.error("Error in updatePFP:", error);
if (error instanceof Error) {
console.error("Error details:", error.message);
return NextResponse.json({ message: error.message }, { status: 500 });
}
return NextResponse.json({ message: "Error updating profile picture" }, { status: 500 });
}
}

View File

@ -3,33 +3,45 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
export default function TestUpdatePFP() { export default function TestUpdatePFP() {
const [userId, setUserId] = useState(''); const [appleId, setAppleId] = useState('');
const [pfpURL, setPfpURL] = useState(''); const [file, setFile] = useState<File | null>(null);
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) => {
e.preventDefault(); e.preventDefault();
setResult(null); setResult(null);
if (!file) {
setResult('Please select a file');
return;
}
try { try {
const response = await fetch('/api/users/updatePFP', { const formData = new FormData();
formData.append('appleId', appleId);
formData.append('file', file);
const response = await fetch('/api/users/updatePfp', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json',
'x-api-key': process.env.NEXT_PUBLIC_API_KEY ?? '', 'x-api-key': process.env.NEXT_PUBLIC_API_KEY ?? '',
}, },
body: JSON.stringify({ body: formData
userId,
pfpURL
})
}); });
const data = await response.json() as {message: string} const data = await response.json();
setResult(JSON.stringify(data, null, 2)); setResult(JSON.stringify(data, null, 2));
} catch (error) { } catch (error) {
console.error('Error:', error); console.error('Error:', error);
setResult('An error occurred'); setResult('An error occurred');
} }
}; };
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files.length > 0) {
setFile(e.target.files[0]);
}
};
return ( return (
<main className="flex min-h-screen flex-col items-center justify-center <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"> bg-gradient-to-b from-pink-500 to-orange-400 text-white cursor-pointer">
@ -37,24 +49,24 @@ export default function TestUpdatePFP() {
<h1 className="text-2xl mb-4">Test Update PFP</h1> <h1 className="text-2xl mb-4">Test Update PFP</h1>
<form onSubmit={handleSubmit} className="space-y-4"> <form onSubmit={handleSubmit} className="space-y-4">
<div> <div>
<label htmlFor="userId" className="block">User ID:</label> <label htmlFor="appleId" className="block">Apple ID:</label>
<input <input
type="text" type="text"
id="userId" id="appleId"
value={userId} value={appleId}
onChange={(e) => setUserId(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="pfpURL" className="block">PFP URL:</label> <label htmlFor="profilepic" className="block">Profile Picture</label>
<input <input
type="text" type="file"
id="pfpURL" id="profilepic"
value={pfpURL} onChange={handleFileChange}
onChange={(e) => setPfpURL(e.target.value)}
className="border p-2 w-full bg-black" className="border p-2 w-full bg-black"
accept="image/*"
required required
/> />
</div> </div>

View File

@ -9,9 +9,10 @@ export const env = createEnv({
NODE_ENV: z NODE_ENV: z
.enum(["development", "test", "production"]) .enum(["development", "test", "production"])
.default("development"), .default("development"),
JWT_SECRET: z.string(), //JWT_SECRET: z.string(),
JWT_REFRESH_SECRET: z.string(), //JWT_REFRESH_SECRET: z.string(),
SKIP_ENV_VALIDATION: z.boolean().optional(), SKIP_ENV_VALIDATION: z.boolean().optional(),
ROOT_PATH: z.string(),
}, },
client: { client: {
@ -22,10 +23,11 @@ export const env = createEnv({
DATABASE_URL: process.env.DATABASE_URL, DATABASE_URL: process.env.DATABASE_URL,
API_KEY: process.env.API_KEY, API_KEY: process.env.API_KEY,
NODE_ENV: process.env.NODE_ENV, NODE_ENV: process.env.NODE_ENV,
JWT_SECRET: process.env.JWT_SECRET, //JWT_SECRET: process.env.JWT_SECRET,
JWT_REFRESH_SECRET: process.env.JWT_REFRESH_SECRET, //JWT_REFRESH_SECRET: process.env.JWT_REFRESH_SECRET,
NEXT_PUBLIC_API_KEY: process.env.NEXT_PUBLIC_API_KEY, NEXT_PUBLIC_API_KEY: process.env.NEXT_PUBLIC_API_KEY,
SKIP_ENV_VALIDATION: process.env.SKIP_ENV_VALIDATION, SKIP_ENV_VALIDATION: process.env.SKIP_ENV_VALIDATION,
ROOT_PATH: process.env.ROOT_PATH,
}, },
skipValidation: !!process.env.SKIP_ENV_VALIDATION, skipValidation: !!process.env.SKIP_ENV_VALIDATION,

View File

@ -1,7 +1,7 @@
import 'server-only'; 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, not } from 'drizzle-orm';
// --- Helper Functions --- // // --- Helper Functions --- //
@ -20,6 +20,21 @@ export const ensureUserExists = async (userId: number) => {
} }
}; };
export const ensureUserExistsByAppleId = async (appleId: string) => {
try {
const user = await db.select().from(schema.users)
.where(eq(schema.users.appleId, appleId));
return (user.length > 0) ? user[0] : null;
} catch (error) {
if (error instanceof Error) {
throw new Error(`Error checking user: ${error.message}`);
} else {
throw new Error("Unknown error occurred while checking user");
}
}
};
export const ensureRelationshipExistsByRelationshipId = async (relationshipId: number) => { export const ensureRelationshipExistsByRelationshipId = async (relationshipId: number) => {
try { try {
const relationship = await db.select({ const relationship = await db.select({
@ -168,12 +183,11 @@ export const createUser = async (
} }
}; };
export const updateUserPFP = async (userId: number, pfpURL: string) => { export const updateUserPfpUrl = async (appleId: string, pfpURL: string) => {
try { try {
await db.update(schema.users) await db.update(schema.users)
.set({ pfpURL }) .set({ pfpURL })
.where(eq(schema.users.id, userId)); .where(eq(schema.users.appleId, appleId));
return { success: true }; return { success: true };
} catch (error) { } catch (error) {
if (error instanceof Error) { if (error instanceof Error) {
@ -184,6 +198,20 @@ export const updateUserPFP = async (userId: number, pfpURL: string) => {
} }
}; };
export const getUserPfpUrl = async (appleId: string) => {
try {
const user = await db.select().from(schema.users)
.where(eq(schema.users.appleId, appleId));
return (user.length > 0) ? user[0]?.pfpURL : null;
} catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to fetch user by appleId: ${error.message}`);
} else {
throw new Error("Unknown error occurred while fetching user by appleId");
}
}
};
export const updateUserPushToken = async (userId: number, pushToken: string) => { export const updateUserPushToken = async (userId: number, pushToken: string) => {
try { try {
await db.update(schema.users) await db.update(schema.users)
@ -218,6 +246,67 @@ export const updateUserPushTokenByAppleId = async (appleId: string, pushToken: s
// --- Relationship Management Functions --- // // --- Relationship Management Functions --- //
export const checkRelationshipStatusByAppleId = async (appleId: string) => {
try {
await ensureUserExistsByAppleId(appleId);
const user = await getUserByAppleId(appleId);
if (!user) throw new Error("User not found");
// Check and see if this user is in any relationships
const relationships = await db.select().from(schema.userRelationships)
.where(eq(schema.userRelationships.userId, user.id));
if (!relationships.length) throw new Error("No relationships found for this user");
// User can only be in one relationship at a time
const relationship = relationships[0];
if (!relationship) throw new Error("No relationship found for this user");
// Check if the relationship is accepted
const relationshipStatus = await db.select().from(schema.relationships)
.where(eq(schema.relationships.id, relationship.relationshipId));
if (!relationshipStatus.length) throw new Error("Relationship not found");
if (!relationshipStatus[0]) throw new Error("Relationship not found");
if (relationshipStatus[0].status !== "accepted") throw new Error("Relationship not accepted");
else return relationshipStatus[0];
} catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to check relationship status: ${error.message}`);
} else {
throw new Error("Unknown error occurred while checking relationship status");
}
}
};
export const getPartnerByAppleId = async (appleId: string) => {
try {
const user = await getUserByAppleId(appleId);
if (!user) throw new Error("User not found");
const relationship = await checkRelationshipStatusByAppleId(appleId);
if (!relationship) throw new Error("User is not in a relationship");
const partnerRelation = await db.select()
.from(schema.userRelationships)
.where(
and(
eq(schema.userRelationships.relationshipId, relationship.id),
not(eq(schema.userRelationships.userId, user.id))
)
);
if (!partnerRelation.length) throw new Error("Partner not found");
if (!partnerRelation[0]) throw new Error("Partner not found");
const partner = await getUserById(partnerRelation[0].userId);
if (!partner) throw new Error("Partner not found");
return partner;
} catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to get partner: ${error.message}`);
} else {
throw new Error("Unknown error occurred while getting partner");
}
}
};
export const createRelationshipRequest = async (requestorId: number, requestedId: number) => { export const createRelationshipRequest = async (requestorId: number, requestedId: number) => {
try { try {
await ensureUserExists(requestorId); await ensureUserExists(requestorId);

3
undotree_2 Normal file
View File

@ -0,0 +1,3 @@
" Press ? for help.
* >0< (Original)