schema mostly done. functions mostly done. working on APIs now
This commit is contained in:
parent
2a5acf9325
commit
edffe130a5
6681
package-lock.json
generated
Normal file
6681
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,22 +0,0 @@
|
||||
'use server';
|
||||
import { NextResponse } from 'next/server';
|
||||
import { getMessage } from '~/server/functions';
|
||||
|
||||
export const GET = async (request: Request) => {
|
||||
try {
|
||||
const url = new URL(request.url);
|
||||
const apiKey = url.searchParams.get('apiKey');
|
||||
if (apiKey !== process.env.API_KEY) {
|
||||
console.log('Invalid API Key');
|
||||
return NextResponse.json({ message: "Invalid API Key" }, { status: 401 });
|
||||
} else {
|
||||
const userId = url.searchParams.get('userId') ?? '2';
|
||||
const message = await getMessage(parseInt(userId));
|
||||
return NextResponse.json(message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return NextResponse.json({ message: "Error" }, { status: 500 });
|
||||
}
|
||||
};
|
||||
// localhost:3000/api/getMessage?apiKey=I_Love_Madeline&userId=2
|
@ -1,21 +0,0 @@
|
||||
'use server';
|
||||
import { NextResponse } from 'next/server';
|
||||
import { getUsers } from '~/server/functions';
|
||||
|
||||
export const GET = async (request: Request) => {
|
||||
try {
|
||||
const url = new URL(request.url);
|
||||
const apiKey = url.searchParams.get('apiKey');
|
||||
if (apiKey !== process.env.API_KEY) {
|
||||
console.log('Invalid API Key');
|
||||
return NextResponse.json({ message: "Invalid API Key" }, { status: 401 });
|
||||
} else {
|
||||
const users = await getUsers();
|
||||
return NextResponse.json(users);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return NextResponse.json({ message: "Error" }, { status: 500 });
|
||||
}
|
||||
};
|
||||
// localhost:3000/api/getUsers?apiKey=I_Love_Madeline
|
0
src/app/api/messages/fetchMessages/route.ts
Normal file
0
src/app/api/messages/fetchMessages/route.ts
Normal file
39
src/app/api/messages/sendMessage/route.ts
Normal file
39
src/app/api/messages/sendMessage/route.ts
Normal file
@ -0,0 +1,39 @@
|
||||
"use server";
|
||||
import { NextResponse } from "next/server";
|
||||
import type { NextRequest } from "next/server";
|
||||
import { sendMessage } from "~/server/functions";
|
||||
|
||||
type sendMessageRequest = {
|
||||
senderId: number;
|
||||
receiverId: number;
|
||||
content: string;
|
||||
mediaUrl?: string;
|
||||
mediaType?: "text" | "image" | "video" | "audio" | "file" | "link";
|
||||
};
|
||||
|
||||
export const POST = async (request: NextRequest) => {
|
||||
try {
|
||||
const url = new URL(request.url);
|
||||
const apiKey = url.searchParams.get("apiKey");
|
||||
if (apiKey !== process.env.API_KEY) {
|
||||
console.log("Invalid API Key");
|
||||
return NextResponse.json({ message: "Invalid API Key" }, { status: 401 });
|
||||
}
|
||||
const body = await request.json() as sendMessageRequest;
|
||||
const {
|
||||
senderId,
|
||||
receiverId,
|
||||
content,
|
||||
mediaUrl = "",
|
||||
mediaType = "text",
|
||||
} = body;
|
||||
if (!senderId || !receiverId || !content) {
|
||||
return NextResponse.json({ message: "Missing required fields" }, { status: 400 });
|
||||
}
|
||||
const message = await sendMessage(senderId, receiverId, content, mediaUrl, mediaType);
|
||||
return NextResponse.json(message, { status: 201 });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return NextResponse.json({ message: "Error" }, { status: 500 });
|
||||
}
|
||||
};
|
41
src/app/api/relationships/createRequest/route.ts
Normal file
41
src/app/api/relationships/createRequest/route.ts
Normal file
@ -0,0 +1,41 @@
|
||||
"use server";
|
||||
import { NextResponse } from "next/server";
|
||||
import { createRelationshipRequest } from "~/server/functions";
|
||||
|
||||
interface CreateRequestRequest {
|
||||
apiKey: string;
|
||||
userId: number;
|
||||
targetUserId: number;
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const { apiKey, userId, targetUserId } = await request.json() as CreateRequestRequest;
|
||||
console.log("Received request:", { apiKey, userId, targetUserId });
|
||||
|
||||
if (apiKey !== process.env.API_KEY) {
|
||||
console.log("Invalid API Key");
|
||||
return NextResponse.json({ message: "Invalid API Key" }, { status: 401 });
|
||||
}
|
||||
|
||||
if (!userId || !targetUserId) {
|
||||
return NextResponse.json({ message: "Missing required fields" }, { status: 400 });
|
||||
}
|
||||
|
||||
console.log("Creating relationship request for user:", userId);
|
||||
const result = await createRelationshipRequest(userId, targetUserId);
|
||||
|
||||
if (result) {
|
||||
console.log("Relationship request created successfully");
|
||||
return NextResponse.json({ result }, { status: 200 });
|
||||
} else {
|
||||
throw new Error("Failed to create relationship request");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error in createRequest:", error);
|
||||
if (error instanceof Error) {
|
||||
return NextResponse.json({ message: error.message }, { status: 400 });
|
||||
}
|
||||
return NextResponse.json({ message: "Error creating relationship request" }, { status: 500 });
|
||||
}
|
||||
}
|
40
src/app/api/relationships/deleteRelationship/route.ts
Normal file
40
src/app/api/relationships/deleteRelationship/route.ts
Normal file
@ -0,0 +1,40 @@
|
||||
"use server";
|
||||
import { NextResponse } from "next/server";
|
||||
import { deleteRelationship } from "~/server/functions";
|
||||
|
||||
interface DeleteRelationshipRequest {
|
||||
apiKey: string;
|
||||
relationshipId: number;
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const { apiKey, relationshipId } = await request.json() as DeleteRelationshipRequest;
|
||||
console.log("Received request:", { apiKey, relationshipId });
|
||||
|
||||
if (apiKey !== process.env.API_KEY) {
|
||||
console.log("Invalid API Key");
|
||||
return NextResponse.json({ message: "Invalid API Key" }, { status: 401 });
|
||||
}
|
||||
|
||||
if (!relationshipId) {
|
||||
return NextResponse.json({ message: "Missing required fields" }, { status: 400 });
|
||||
}
|
||||
|
||||
console.log("Deleting relationship:", relationshipId);
|
||||
const result = await deleteRelationship(relationshipId);
|
||||
|
||||
if (result.success) {
|
||||
console.log("Relationship deleted successfully");
|
||||
return NextResponse.json({ result }, { status: 200 });
|
||||
} else {
|
||||
throw new Error("Failed to delete relationship");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error in deleteRelationship:", error);
|
||||
if (error instanceof Error) {
|
||||
return NextResponse.json({ message: error.message }, { status: 400 });
|
||||
}
|
||||
return NextResponse.json({ message: "Error deleting relationship" }, { status: 500 });
|
||||
}
|
||||
}
|
41
src/app/api/relationships/updateRequest/route.ts
Normal file
41
src/app/api/relationships/updateRequest/route.ts
Normal file
@ -0,0 +1,41 @@
|
||||
"use server";
|
||||
import { NextResponse } from "next/server";
|
||||
import { updateRelationshipRequest } from "~/server/functions";
|
||||
|
||||
interface UpdateRequestRequest {
|
||||
apiKey: string;
|
||||
relationshipId: number;
|
||||
status: 'accepted' | 'rejected';
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const { apiKey, relationshipId, status } = await request.json() as UpdateRequestRequest;
|
||||
console.log("Received request:", { apiKey, relationshipId, status });
|
||||
|
||||
if (apiKey !== process.env.API_KEY) {
|
||||
console.log("Invalid API Key");
|
||||
return NextResponse.json({ message: "Invalid API Key" }, { status: 401 });
|
||||
}
|
||||
|
||||
if (!relationshipId || !status) {
|
||||
return NextResponse.json({ message: "Missing required fields" }, { status: 400 });
|
||||
}
|
||||
|
||||
console.log("Updating relationship request:", relationshipId);
|
||||
const result = await updateRelationshipRequest(relationshipId, status);
|
||||
|
||||
if (result.success) {
|
||||
console.log("Relationship request updated successfully");
|
||||
return NextResponse.json({ result }, { status: 200 });
|
||||
} else {
|
||||
throw new Error("Failed to update relationship request");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error in updateRequest:", error);
|
||||
if (error instanceof Error) {
|
||||
return NextResponse.json({ message: error.message }, { status: 400 });
|
||||
}
|
||||
return NextResponse.json({ message: "Error updating relationship request" }, { status: 500 });
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
"use server";
|
||||
import { NextResponse } from "next/server";
|
||||
import type { NextRequest } from "next/server";
|
||||
import { setCountdown } from "~/server/functions";
|
||||
|
||||
export const POST = async (request: NextRequest) => {
|
||||
try {
|
||||
const url = new URL(request.url);
|
||||
const apiKey = url.searchParams.get("apiKey");
|
||||
if (apiKey !== process.env.API_KEY) {
|
||||
console.log("Invalid API Key");
|
||||
return NextResponse.json({ message: "Invalid API Key" }, { status: 401 });
|
||||
} else {
|
||||
const countdown = url.searchParams.get("countdown") ?? "2023-01-01T00:00:00.000Z";
|
||||
await setCountdown(new Date(countdown));
|
||||
return NextResponse.json({ message: "Countdown set successfully" });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return NextResponse.json({ message: "Error" }, { status: 500 });
|
||||
}
|
||||
};
|
||||
// localhost:3000/api/setCountdown?apiKey=I_Love_Madeline&countdown=2024-09-20T12:00:00.000Z
|
31
src/app/api/users/changePassword/route.ts
Normal file
31
src/app/api/users/changePassword/route.ts
Normal file
@ -0,0 +1,31 @@
|
||||
"use server";
|
||||
import { NextResponse } from "next/server";
|
||||
import { changePassword } from "~/server/functions";
|
||||
|
||||
type Data = {
|
||||
apiKey: string;
|
||||
userId: number;
|
||||
oldPassword: string;
|
||||
newPassword: string;
|
||||
};
|
||||
|
||||
export const POST = async (request: Request) => {
|
||||
try {
|
||||
const { apiKey, userId, oldPassword, newPassword } = await request.json() as Data;
|
||||
console.log("Received request:", { apiKey, userId, oldPassword, newPassword });
|
||||
|
||||
if (apiKey !== process.env.API_KEY) {
|
||||
console.log("Invalid API Key");
|
||||
return NextResponse.json({ message: "Invalid API Key" }, { status: 401 });
|
||||
}
|
||||
|
||||
console.log("Changing password for user:", userId);
|
||||
await changePassword(userId, oldPassword, newPassword);
|
||||
|
||||
console.log("Password changed successfully");
|
||||
return NextResponse.json({ message: "Password changed successfully" });
|
||||
} catch (error) {
|
||||
console.error("Error in changePassword:", error);
|
||||
return NextResponse.json({ message: "Error changing password" }, { status: 500 });
|
||||
}
|
||||
};
|
65
src/app/api/users/createUser/route.ts
Normal file
65
src/app/api/users/createUser/route.ts
Normal file
@ -0,0 +1,65 @@
|
||||
"use server";
|
||||
import { NextResponse } from "next/server";
|
||||
import { createUser } from "~/server/functions";
|
||||
|
||||
type CreateUserRequest = {
|
||||
username: string;
|
||||
email: string;
|
||||
passwordHash: string;
|
||||
name: string;
|
||||
pfpURL?: string;
|
||||
pushToken?: string;
|
||||
};
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const url = new URL(request.url);
|
||||
const apiKey = url.searchParams.get("apiKey");
|
||||
|
||||
if (apiKey !== process.env.API_KEY) {
|
||||
console.log("Invalid API Key");
|
||||
return NextResponse.json({ message: "Invalid API Key" }, { status: 401 });
|
||||
}
|
||||
|
||||
// Parse the request body instead of using URL parameters for POST requests
|
||||
const body = await request.json() as CreateUserRequest;
|
||||
|
||||
// Destructure the body with default values for optional fields
|
||||
const {
|
||||
username,
|
||||
email,
|
||||
passwordHash,
|
||||
name,
|
||||
pfpURL = "",
|
||||
pushToken = ""
|
||||
} = body;
|
||||
|
||||
// Validate required fields
|
||||
if (!username || !email || !passwordHash || !name) {
|
||||
return NextResponse.json(
|
||||
{ message: "Missing required fields" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const user = await createUser(
|
||||
username,
|
||||
email,
|
||||
passwordHash,
|
||||
name,
|
||||
pfpURL,
|
||||
pushToken
|
||||
);
|
||||
|
||||
return NextResponse.json(user, { status: 201 });
|
||||
} catch (error) {
|
||||
console.error("Error creating user:", error);
|
||||
if (error instanceof Error) {
|
||||
return NextResponse.json({ message: error.message }, { status: 400 });
|
||||
}
|
||||
return NextResponse.json(
|
||||
{ message: "An unexpected error occurred" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
11
src/app/api/setMessage/route.ts → src/app/api/users/getUserByID/route.ts
Executable file → Normal file
11
src/app/api/setMessage/route.ts → src/app/api/users/getUserByID/route.ts
Executable file → Normal file
@ -1,9 +1,8 @@
|
||||
"use server";
|
||||
import { NextResponse } from "next/server";
|
||||
import type { NextRequest } from "next/server";
|
||||
import { setMessage } from "~/server/functions";
|
||||
import { getUserById } from "~/server/functions";
|
||||
|
||||
export const POST = async (request: NextRequest) => {
|
||||
export const GET = async (request: Request) => {
|
||||
try {
|
||||
const url = new URL(request.url);
|
||||
const apiKey = url.searchParams.get("apiKey");
|
||||
@ -12,13 +11,11 @@ export const POST = async (request: NextRequest) => {
|
||||
return NextResponse.json({ message: "Invalid API Key" }, { status: 401 });
|
||||
} else {
|
||||
const userId = url.searchParams.get("userId") ?? "2";
|
||||
const message = url.searchParams.get("message") ?? "Test";
|
||||
await setMessage(parseInt(userId), message);
|
||||
return NextResponse.json({ message: "Message set successfully" });
|
||||
const user = await getUserById(parseInt(userId));
|
||||
return NextResponse.json(user);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return NextResponse.json({ message: "Error" }, { status: 500 });
|
||||
}
|
||||
};
|
||||
// localhost:3000/api/setMessage?apiKey=I_Love_Madeline&userId=2&message=HelloWorld
|
16
src/app/api/getCountdown/route.ts → src/app/api/users/getUserByUsername/route.ts
Executable file → Normal file
16
src/app/api/getCountdown/route.ts → src/app/api/users/getUserByUsername/route.ts
Executable file → Normal file
@ -1,21 +1,21 @@
|
||||
'use server';
|
||||
import { NextResponse } from 'next/server';
|
||||
import { getCountdown } from '~/server/functions';
|
||||
"use server";
|
||||
import { NextResponse } from "next/server";
|
||||
import { getUserByUsername } from "~/server/functions";
|
||||
|
||||
export const GET = async (request: Request) => {
|
||||
try {
|
||||
const url = new URL(request.url);
|
||||
const apiKey = url.searchParams.get('apiKey');
|
||||
const apiKey = url.searchParams.get("apiKey");
|
||||
if (apiKey !== process.env.API_KEY) {
|
||||
console.log('Invalid API Key');
|
||||
console.log("Invalid API Key");
|
||||
return NextResponse.json({ message: "Invalid API Key" }, { status: 401 });
|
||||
} else {
|
||||
const countdown = await getCountdown();
|
||||
return NextResponse.json(countdown);
|
||||
const username = url.searchParams.get("username") ?? "2";
|
||||
const user = await getUserByUsername(username);
|
||||
return NextResponse.json(user);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return NextResponse.json({ message: "Error" }, { status: 500 });
|
||||
}
|
||||
};
|
||||
// localhost:3000/api/getCountdown?apiKey=I_Love_Madeline
|
41
src/app/api/users/login/route.ts
Normal file
41
src/app/api/users/login/route.ts
Normal file
@ -0,0 +1,41 @@
|
||||
"use server";
|
||||
import { NextResponse } from "next/server";
|
||||
import { userLogin } from "~/server/functions";
|
||||
|
||||
interface LoginRequest {
|
||||
apiKey: string;
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const { apiKey, username, password } = await request.json() as LoginRequest;
|
||||
console.log("Received request:", { apiKey, username, password });
|
||||
|
||||
if (apiKey !== process.env.API_KEY) {
|
||||
console.log("Invalid API Key");
|
||||
return NextResponse.json({ message: "Invalid API Key" }, { status: 401 });
|
||||
}
|
||||
|
||||
if (!username || !password) {
|
||||
return NextResponse.json({ message: "Missing required fields" }, { status: 400 });
|
||||
}
|
||||
|
||||
console.log("Logging in user:", username);
|
||||
const result = await userLogin(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 });
|
||||
}
|
||||
}
|
41
src/app/api/users/updatePFP/route.ts
Normal file
41
src/app/api/users/updatePFP/route.ts
Normal file
@ -0,0 +1,41 @@
|
||||
"use server";
|
||||
import { NextResponse } from "next/server";
|
||||
import { updateUserPFP } from "~/server/functions";
|
||||
|
||||
interface UpdatePfpRequest {
|
||||
apiKey: string;
|
||||
userId: number;
|
||||
pfpURL: string;
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const { apiKey, userId, pfpURL } = await request.json() as UpdatePfpRequest;
|
||||
console.log("Received request:", { apiKey, userId, pfpURL });
|
||||
|
||||
if (apiKey !== process.env.API_KEY) {
|
||||
console.log("Invalid API Key");
|
||||
return NextResponse.json({ message: "Invalid API Key" }, { status: 401 });
|
||||
}
|
||||
|
||||
if (!userId || !pfpURL) {
|
||||
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 });
|
||||
}
|
||||
}
|
36
src/app/api/users/updatePushToken/route.ts
Normal file
36
src/app/api/users/updatePushToken/route.ts
Normal file
@ -0,0 +1,36 @@
|
||||
"use server";
|
||||
import { NextResponse } from "next/server";
|
||||
import { updateUserPushToken } from "~/server/functions";
|
||||
|
||||
interface UpdatePushTokenRequest {
|
||||
apiKey: string;
|
||||
userId: number;
|
||||
pushToken: string;
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const { apiKey, userId, pushToken } = await request.json() as UpdatePushTokenRequest;
|
||||
console.log("Received request:", { apiKey, userId, pushToken });
|
||||
if (apiKey !== process.env.API_KEY) {
|
||||
console.log("Invalid API Key");
|
||||
return NextResponse.json({ message: "Invalid API Key" }, { status: 401 });
|
||||
}
|
||||
if (!userId || !pushToken)
|
||||
return NextResponse.json({ message: "Missing required fields" }, { status: 400 });
|
||||
console.log("Updating push token for user:", userId);
|
||||
const result = await updateUserPushToken(userId, pushToken);
|
||||
if (result.success) {
|
||||
console.log("Push token updated successfully");
|
||||
return NextResponse.json({ message: "Push token updated successfully" });
|
||||
} else {
|
||||
throw new Error("Failed to update push token");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error in updatePushToken:", error);
|
||||
if (error instanceof Error) {
|
||||
return NextResponse.json({ message: error.message }, { status: 400 });
|
||||
}
|
||||
return NextResponse.json({ message: "Error updating push token" }, { status: 500 });
|
||||
}
|
||||
}
|
@ -4,27 +4,91 @@ import {
|
||||
serial,
|
||||
timestamp,
|
||||
varchar,
|
||||
text,
|
||||
integer,
|
||||
boolean,
|
||||
pgEnum
|
||||
} from "drizzle-orm/pg-core";
|
||||
|
||||
export const createTable = pgTableCreator((name) => `${name}`);
|
||||
|
||||
export const statusEnum = pgEnum("status", ["pending", "accepted", "rejected"]);
|
||||
export const messageTypeEnum = pgEnum("message_types", ["text", "image", "video", "audio", "file", "link"]);
|
||||
|
||||
export const users = createTable(
|
||||
"user",
|
||||
{
|
||||
id: serial("id").primaryKey(),
|
||||
name: varchar("name", { length: 256 }),
|
||||
message: varchar("message", { length: 256 }),
|
||||
username: varchar("username", { length: 50 }).unique().notNull(),
|
||||
email: varchar("email", { length: 255 }).unique().notNull(),
|
||||
passwordHash: varchar("password_hash", {length: 255}).notNull(),
|
||||
name: varchar("name", { length: 100 }),
|
||||
pfpURL: varchar("pfp_url", { length: 255 }),
|
||||
pushToken: varchar("pushToken", { length: 256 }),
|
||||
lastLogin: timestamp("last_login", { withTimezone: true }),
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.default(sql`CURRENT_TIMESTAMP`)
|
||||
.notNull(),
|
||||
},
|
||||
);
|
||||
|
||||
export const countdown = createTable(
|
||||
export const relationships = createTable(
|
||||
"relationship",
|
||||
{
|
||||
id: serial("id").primaryKey(),
|
||||
title: varchar("title", { length: 100 }).default("Relationship").notNull(),
|
||||
status: statusEnum("status").default("pending").notNull(),
|
||||
relationshipStartDate: timestamp("relationship_start_date", { withTimezone: true })
|
||||
.default(sql`CURRENT_TIMESTAMP`).notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.default(sql`CURRENT_TIMESTAMP`).notNull(),
|
||||
},
|
||||
);
|
||||
|
||||
export const userRelationships = createTable(
|
||||
"user_relationship",
|
||||
{
|
||||
id: serial("id").primaryKey(),
|
||||
userId: integer("user_id").references(() => users.id).notNull(),
|
||||
relationshipId: integer("relationship_id").references(() => relationships.id).notNull(),
|
||||
},
|
||||
);
|
||||
|
||||
export const messages = createTable(
|
||||
"message",
|
||||
{
|
||||
id: serial("id").primaryKey(),
|
||||
senderId: integer("sender_id").references(() => users.id).notNull(),
|
||||
receiverId: integer("receiver_id").references(() => users.id).notNull(),
|
||||
content: text("content").notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.default(sql`CURRENT_TIMESTAMP`)
|
||||
.notNull(),
|
||||
isRead: boolean("is_read").default(false).notNull(),
|
||||
},
|
||||
);
|
||||
|
||||
export const messageMedia = createTable(
|
||||
"message_media",
|
||||
{
|
||||
id: serial("id").primaryKey(),
|
||||
messageId: integer("message_id").references(() => messages.id).notNull(),
|
||||
type: messageTypeEnum("type").notNull(),
|
||||
mediaUrl: varchar("url", { length: 255 }).notNull(),
|
||||
},
|
||||
);
|
||||
|
||||
export const countdowns = createTable(
|
||||
"countdown",
|
||||
{
|
||||
id: serial("id").primaryKey(),
|
||||
relationshipId: integer("relationship_id").references(() => relationships.id).notNull(),
|
||||
title: varchar("title", { length: 100 })
|
||||
.default("Countdown to Next Visit")
|
||||
.notNull(),
|
||||
date: timestamp("date", { withTimezone: true }),
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.default(sql`CURRENT_TIMESTAMP`)
|
||||
.notNull(),
|
||||
},
|
||||
);
|
||||
|
@ -1,130 +1,417 @@
|
||||
import 'server-only';
|
||||
import { db } from '~/server/db';
|
||||
import * as schema from '~/server/db/schema';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { eq, and, or } from 'drizzle-orm';
|
||||
import { pgEnum } from 'drizzle-orm/pg-core';
|
||||
|
||||
export const getUsers = async () => {
|
||||
try {
|
||||
const result = await db.select({
|
||||
id: schema.users.id,
|
||||
name: schema.users.name,
|
||||
message: schema.users.message,
|
||||
}).from(schema.users);
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error("Error fetching users", error);
|
||||
throw new Error("Failed to fetch users");
|
||||
}
|
||||
};
|
||||
// --- Helper Functions --- //
|
||||
|
||||
export const getUser = async (userId: number) => {
|
||||
/**
|
||||
* Ensure the user exists based on their userId.
|
||||
* Throws an error if the user doesn't exist.
|
||||
*/
|
||||
export const ensureUserExists = async (userId: number) => {
|
||||
try {
|
||||
const result = await db.select({
|
||||
id: schema.users.id,
|
||||
name: schema.users.name,
|
||||
message: schema.users.message,
|
||||
pushToken: schema.users.pushToken,
|
||||
}).from(schema.users)
|
||||
const user = await db.select().from(schema.users)
|
||||
.where(eq(schema.users.id, userId));
|
||||
return result;
|
||||
|
||||
if (user.length === 0) {
|
||||
throw new Error('User does not exist');
|
||||
}
|
||||
|
||||
return user[0]; // Return user for further use if needed
|
||||
} catch (error) {
|
||||
console.error("Error fetching user", error);
|
||||
throw new Error("Failed to fetch user");
|
||||
if (error instanceof Error) {
|
||||
throw new Error(`Error checking user: ${error.message}`);
|
||||
} else {
|
||||
throw new Error("Unknown error occurred while checking user");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const getMessage = async (userId: number) => {
|
||||
/**
|
||||
* 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') => {
|
||||
try {
|
||||
let message = 1;
|
||||
if (userId === 1)
|
||||
message = 2;
|
||||
const result = await db.select({
|
||||
receivedMessage: schema.users.message,
|
||||
}).from(schema.users)
|
||||
.where(eq(schema.users.id, message))
|
||||
return result;
|
||||
// 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) {
|
||||
console.error("Error fetching message", error);
|
||||
throw new Error("Failed to fetch message");
|
||||
if (error instanceof Error) {
|
||||
throw new Error(`Error checking relationship: ${error.message}`);
|
||||
} else {
|
||||
throw new Error("Unknown error occurred while checking relationship");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const setMessage = async (userId: number, message: string) => {
|
||||
// --- User Management Functions --- //
|
||||
|
||||
export const getUserById = async (userId: number) => {
|
||||
try {
|
||||
return await ensureUserExists(userId);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
throw new Error(`Failed to fetch user by id: ${error.message}`);
|
||||
} else {
|
||||
throw new Error("Unknown error occurred while fetching user by id");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const getUserByUsername = async (username: string) => {
|
||||
try {
|
||||
const user = await db.select().from(schema.users)
|
||||
.where(eq(schema.users.username, username));
|
||||
|
||||
if (user.length === 0) throw new Error('User not found');
|
||||
return user[0];
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
throw new Error(`Failed to fetch user by username: ${error.message}`);
|
||||
} else {
|
||||
throw new Error("Unknown error occurred while fetching user by username");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const createUser = async (
|
||||
username: string, email: string, passwordHash: string,
|
||||
name: string, pfpURL = "", pushToken = ""
|
||||
) => {
|
||||
try {
|
||||
if (!username || !email || !passwordHash || !name) {
|
||||
throw new Error("Error: All required fields must be filled");
|
||||
}
|
||||
|
||||
// Check if username or email is already taken
|
||||
const existingUser = await db.select().from(schema.users)
|
||||
.where(or(eq(schema.users.username, username), eq(schema.users.email, email)));
|
||||
|
||||
if (existingUser.length > 0) {
|
||||
throw new Error("Username or email is already in use");
|
||||
}
|
||||
|
||||
const newUser = await db.insert(schema.users).values({
|
||||
username, email, passwordHash, name, pfpURL, pushToken,
|
||||
lastLogin: new Date(),
|
||||
}).returning();
|
||||
|
||||
return newUser;
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
throw new Error(`Failed to create new user: ${error.message}`);
|
||||
} else {
|
||||
throw new Error("Unknown error occurred while creating new user");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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");
|
||||
|
||||
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) => {
|
||||
try {
|
||||
await db.update(schema.users)
|
||||
.set({ message: message })
|
||||
.set({ pfpURL })
|
||||
.where(eq(schema.users.id, userId));
|
||||
const otherUserId = userId === 1 ? 2 : 1;
|
||||
const otherUser = await getUser(otherUserId);
|
||||
|
||||
if (otherUser?.[0]?.pushToken) {
|
||||
await sendPushNotification(otherUser[0].pushToken, message);
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
throw new Error(`Failed to update profile: ${error.message}`);
|
||||
} else {
|
||||
console.log(`Other user with id ${otherUserId} does not have a push token`);
|
||||
throw new Error("Unknown error occurred while updating profile");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error setting message", error);
|
||||
throw new Error("Failed to set message");
|
||||
}
|
||||
};
|
||||
|
||||
export const getCountdown = async () => {
|
||||
try {
|
||||
const result = await db.select({
|
||||
countdown: schema.countdown.date,
|
||||
}).from(schema.countdown)
|
||||
.where(eq(schema.countdown.id, 1))
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error("Error fetching countdown", error);
|
||||
throw new Error("Failed to fetch countdown");
|
||||
}
|
||||
};
|
||||
|
||||
export const setCountdown = async (date: Date) => {
|
||||
try {
|
||||
await db.update(schema.countdown)
|
||||
.set({ date: date })
|
||||
.where(eq(schema.countdown.id, 1));
|
||||
} catch (error) {
|
||||
console.error("Error setting countdown", error);
|
||||
throw new Error("Failed to set countdown");
|
||||
}
|
||||
};
|
||||
|
||||
export const updateUserPushToken = async (userId: number, pushToken: string) => {
|
||||
try {
|
||||
await db.update(schema.users)
|
||||
.set({ pushToken: pushToken })
|
||||
.set({ pushToken })
|
||||
.where(eq(schema.users.id, userId));
|
||||
console.log(`Push token updated for userId ${userId}`);
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error("Error updating push token", error);
|
||||
throw new Error("Failed to update push token");
|
||||
if (error instanceof Error) {
|
||||
throw new Error(`Failed to update profile: ${error.message}`);
|
||||
} else {
|
||||
throw new Error("Unknown error occurred while updating profile");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const sendPushNotification = async (expoPushToken: string, message: string) => {
|
||||
const notificationMessage = {
|
||||
to: expoPushToken,
|
||||
sound: 'default',
|
||||
title: `New message!`,
|
||||
body: message,
|
||||
data: { message }, // Extra data you might want to send
|
||||
};
|
||||
|
||||
export const userLogin = async (username: string, passwordHash: string) => {
|
||||
try {
|
||||
await fetch('https://exp.host/--/api/v2/push/send', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Accept-Encoding': 'gzip, deflate',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(notificationMessage),
|
||||
});
|
||||
console.log('Push notification sent successfully');
|
||||
const user = await getUserByUsername(username);
|
||||
if (user?.passwordHash !== passwordHash) {
|
||||
throw new Error("Invalid password");
|
||||
}
|
||||
|
||||
// Update last login timestamp
|
||||
await db.update(schema.users)
|
||||
.set({ lastLogin: new Date() })
|
||||
.where(eq(schema.users.id, user.id));
|
||||
|
||||
return user;
|
||||
} catch (error) {
|
||||
console.error('Error sending push notification', error);
|
||||
if (error instanceof Error) {
|
||||
throw new Error(`Failed to Log in: ${error.message}`);
|
||||
} else {
|
||||
throw new Error("Unknown error occurred while logging in");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// --- Relationship Management Functions --- //
|
||||
|
||||
export const createRelationshipRequest = async (requestorId: number, requestedId: number) => {
|
||||
try {
|
||||
await ensureUserExists(requestorId);
|
||||
await ensureUserExists(requestedId);
|
||||
|
||||
// Check if a relationship exists in either direction
|
||||
const existingRelationship = await db.select()
|
||||
.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
|
||||
));
|
||||
|
||||
if (existingRelationship.length && existingRelationship[0]?.relationship?.status !== 'rejected') {
|
||||
throw new Error('A relationship already exists or is pending between these users');
|
||||
}
|
||||
|
||||
// Create new relationship entry
|
||||
const newRelationship = await db.insert(schema.relationships).values({
|
||||
status: "pending",
|
||||
}).returning();
|
||||
|
||||
// Check for successful insertion
|
||||
if (!newRelationship.length || !newRelationship[0]?.id)
|
||||
throw new Error("Failed to create relationship request");
|
||||
|
||||
// Add both users to user_relationships table
|
||||
await db.insert(schema.userRelationships).values([
|
||||
{ userId: requestorId, relationshipId: newRelationship[0]?.id },
|
||||
{ userId: requestedId, relationshipId: newRelationship[0].id },
|
||||
]);
|
||||
|
||||
return newRelationship;
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
throw new Error(`Failed to create relationship request: ${error.message}`);
|
||||
} else {
|
||||
throw new Error("Unknown error occurred while creating relationship request");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export const updateRelationshipRequest = async (relationshipId: number, status: 'accepted' | 'rejected') => {
|
||||
try {
|
||||
const relationship = await db.select().from(schema.relationships)
|
||||
.where(eq(schema.relationships.id, relationshipId));
|
||||
|
||||
if (!relationship.length) {
|
||||
throw new Error("Relationship request not found");
|
||||
}
|
||||
|
||||
await db.update(schema.relationships)
|
||||
.set({ status })
|
||||
.where(eq(schema.relationships.id, relationshipId));
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
throw new Error(`Failed to update relationship request: ${error.message}`);
|
||||
} else {
|
||||
throw new Error("Unknown error occurred while updating relationship request");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteRelationship = async (relationshipId: number) => {
|
||||
try {
|
||||
await db.transaction(async (trx) => {
|
||||
await trx.delete(schema.userRelationships)
|
||||
.where(eq(schema.userRelationships.relationshipId, relationshipId));
|
||||
|
||||
await trx.delete(schema.relationships)
|
||||
.where(eq(schema.relationships.id, relationshipId));
|
||||
});
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
throw new Error(`Failed to delete the relationship: ${error.message}`);
|
||||
} else {
|
||||
throw new Error("Unknown error occurred while deleting the relationship");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// --- Message Management Functions --- //
|
||||
type MediaType = "text" | "image" | "video" | "audio" | "file" | "link";
|
||||
export const sendMessage = async (
|
||||
senderId: number, receiverId: number, content: string, mediaUrl?: string, mediaType?: MediaType
|
||||
) => {
|
||||
try {
|
||||
// Ensure both sender and receiver exist
|
||||
await ensureUserExists(senderId);
|
||||
await ensureUserExists(receiverId);
|
||||
|
||||
// Ensure the relationship exists (no need to use returned 'relationshipId' for now)
|
||||
await ensureRelationshipExists(senderId, receiverId, "accepted");
|
||||
|
||||
// Insert the new message
|
||||
const message = await db.insert(schema.messages).values({
|
||||
senderId, receiverId, content, createdAt: new Date(),
|
||||
}).returning();
|
||||
|
||||
// Check for successful insertion of the message
|
||||
if (!message.length || !message[0]?.id)
|
||||
throw new Error("Failed to send message");
|
||||
|
||||
if (mediaUrl && mediaType) {
|
||||
await db.insert(schema.messageMedia).values({
|
||||
messageId: message[0].id,
|
||||
mediaUrl,
|
||||
type: mediaType,
|
||||
});
|
||||
}
|
||||
|
||||
return message;
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
throw new Error(`Failed to send message: ${error.message}`);
|
||||
} else {
|
||||
throw new Error("Unknown error occurred while sending message");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchMessages = async (userId: number, partnerId: number) => {
|
||||
try {
|
||||
await ensureUserExists(userId);
|
||||
await ensureRelationshipExists(userId, partnerId, "accepted");
|
||||
|
||||
const messages = await db.select().from(schema.messages)
|
||||
.where(or(
|
||||
and(eq(schema.messages.senderId, userId), eq(schema.messages.receiverId, partnerId)),
|
||||
and(eq(schema.messages.senderId, partnerId), eq(schema.messages.receiverId, userId))
|
||||
));
|
||||
|
||||
return messages;
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
throw new Error(`Failed to retrieve messages: ${error.message}`);
|
||||
} else {
|
||||
throw new Error("Unknown error occurred while retrieving messages");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// --- Countdown Management Functions --- //
|
||||
|
||||
export const createOrUpdateCountdown = async (relationshipId: number, title: string, date: Date) => {
|
||||
try {
|
||||
// Check if countdown already exists
|
||||
const countdown = await db.select().from(schema.countdowns)
|
||||
.where(eq(schema.countdowns.relationshipId, relationshipId));
|
||||
|
||||
// Update existing or insert countdown
|
||||
if (countdown.length) {
|
||||
await db.update(schema.countdowns)
|
||||
.set({ title, date })
|
||||
.where(eq(schema.countdowns.relationshipId, relationshipId));
|
||||
} else {
|
||||
await db.insert(schema.countdowns).values({
|
||||
relationshipId, title, date, createdAt: new Date(),
|
||||
});
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
throw new Error(`Failed to create/update countdown: ${error.message}`);
|
||||
} else {
|
||||
throw new Error("Unknown error occurred while creating/updating countdown");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const getCountdownByRelationship = async (relationshipId: number) => {
|
||||
try {
|
||||
const countdown = await db.select().from(schema.countdowns)
|
||||
.where(eq(schema.countdowns.relationshipId, relationshipId));
|
||||
|
||||
if (!countdown.length) throw new Error("No countdown found");
|
||||
|
||||
return countdown[0];
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
throw new Error(`Failed to retrieve countdown: ${error.message}`);
|
||||
} else {
|
||||
throw new Error("Unknown error occurred while retrieving countdown");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user