diff --git a/drizzle/0000_warm_edwin_jarvis.sql b/drizzle/0000_warm_edwin_jarvis.sql new file mode 100644 index 0000000..09b3813 --- /dev/null +++ b/drizzle/0000_warm_edwin_jarvis.sql @@ -0,0 +1,20 @@ +CREATE OR REPLACE FUNCTION notify_new_message() +RETURNS trigger LANGUAGE plpgsql AS $$ +BEGIN + PERFORM pg_notify( + 'new_message', + json_build_object( + 'id', NEW.id, + 'text', NEW.text, + 'senderId', NEW.senderId, + 'receiverId', NEW.receiverId, + 'createdAt', NEW.createdAt + )::text + ); + RETURN NEW; +END; +$$; + +CREATE TRIGGER message_insert_trigger +AFTER INSERT ON messages +FOR EACH ROW EXECUTE FUNCTION notify_new_message(); diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json new file mode 100644 index 0000000..944a48e --- /dev/null +++ b/drizzle/meta/0000_snapshot.json @@ -0,0 +1,15 @@ +{ + "id": "ba7dd70a-8dd6-41be-b1d2-158ab83ba830", + "prevId": "00000000-0000-0000-0000-000000000000", + "version": "7", + "dialect": "postgresql", + "tables": {}, + "enums": {}, + "schemas": {}, + "sequences": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json new file mode 100644 index 0000000..8ca6fba --- /dev/null +++ b/drizzle/meta/_journal.json @@ -0,0 +1,13 @@ +{ + "version": "7", + "dialect": "postgresql", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1729627211020, + "tag": "0000_warm_edwin_jarvis", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/package.json b/package.json index e83ae79..0d6f7a2 100755 --- a/package.json +++ b/package.json @@ -12,8 +12,8 @@ "dev": "next dev", "lint": "next lint", "start": "next start", - "go": "pnpm dev", - "go:prod": "pnpm build && pnpm start" + "go": "next dev", + "go:prod": "next build && next start" }, "dependencies": { "@t3-oss/env-nextjs": "^0.10.1", diff --git a/public/favicon.ico b/public/favicon.ico deleted file mode 100755 index 60c702a..0000000 Binary files a/public/favicon.ico and /dev/null differ diff --git a/public/favicon.png b/public/favicon.png new file mode 100755 index 0000000..3d18e56 Binary files /dev/null and b/public/favicon.png differ diff --git a/src/app/api/messages/get/route.ts b/src/app/api/messages/get/route.ts new file mode 100644 index 0000000..70c63a0 --- /dev/null +++ b/src/app/api/messages/get/route.ts @@ -0,0 +1,41 @@ +'use server'; +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; +import { getMessages } from '~/server/functions'; +import { middleware } from '~/middleware'; +import type { Message } from '~/server/types'; + +export const GET = async (request: NextRequest) => { + const middlewareResponse = await middleware(request); + if (middlewareResponse) return middlewareResponse; + try { + const url = new URL(request.url); + const userId = Number.parseInt(url.searchParams.get('userId') ?? ''); + const limit = Number.parseInt(url.searchParams.get('limit') ?? ''); + const offset = Number.parseInt(url.searchParams.get('offset') ?? '0'); + if (!userId || isNaN(userId) || !limit || isNaN(limit) || isNaN(offset)) { + console.log('userId: ', userId, 'limit: ', limit, 'offset: ', offset); + return NextResponse.json( + { message: 'Missing userId, limit, or offset' }, { status: 400 } + ); + } + const messages: Message[] | undefined = await getMessages(userId, limit, offset); + if (messages === undefined) { + return NextResponse.json( + { message: 'No messages found' }, { status: 404 } + ); + } + return NextResponse.json(messages); + } catch (error) { + console.error('Error getting messages:', error); + if (error instanceof Error) { + return NextResponse.json( + { message: error.message }, { status: 500 } + ); + } else { + return NextResponse.json( + { message: 'Unknown error occurred' }, { status: 500 } + ); + } + } +}; diff --git a/src/app/api/messages/send/route.ts b/src/app/api/messages/send/route.ts new file mode 100644 index 0000000..5e5323d --- /dev/null +++ b/src/app/api/messages/send/route.ts @@ -0,0 +1,41 @@ +'use server' +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; +import { sendMessage } from '~/server/functions'; +import { middleware } from '~/middleware'; +import type { Message } from '~/server/types'; + +export const POST = async (request: NextRequest) => { + const middlewareResponse = await middleware(request); + if (middlewareResponse) return middlewareResponse; + try { + const body = await request.json() as { + message: Message; + }; + const { message } = body; + if (!message) { + console.log('Message:', message); + return NextResponse.json( + { message: 'Missing message' }, { status: 400 } + ); + } + const newMessage: Message | null = await sendMessage(message); + if (!newMessage) { + return NextResponse.json( + { message: 'Error sending message' }, { status: 500 } + ); + } + return NextResponse.json(newMessage); + } catch (error) { + console.error('Error sending message:', error); + if (error instanceof Error) { + return NextResponse.json( + { message: error.message }, { status: 500 } + ); + } else { + return NextResponse.json( + { message: 'Unknown error occurred' }, { status: 500 } + ); + } + } +}; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 42ed47c..d797777 100755 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -4,9 +4,9 @@ import { GeistSans } from "geist/font/sans"; import { type Metadata } from "next"; export const metadata: Metadata = { - title: "Create T3 App", - description: "Generated by create-t3-app", - icons: [{ rel: "icon", url: "/favicon.ico" }], + title: "Is Madeline the Cutest?", + description: "Answering the easiest question in the world!", + icons: [{ rel: "icon", url: "/favicon.png" }], }; export default function RootLayout({ diff --git a/src/app/page.tsx b/src/app/page.tsx index 773fef1..54a891a 100755 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,37 +1,35 @@ -import Link from "next/link"; +"use client"; +import { useState, useEffect } from "react"; + +const interestingYes = () => { + const yesArray = [ + "Absolutely, yes.", + "Without a doubt.", + "Of course.", + "Definitely.", + "Obviously!", + "Certainly!", + "Positively.", + "100%", + ]; + return yesArray[Math.floor(Math.random() * yesArray.length)]; +} export default function HomePage() { + const [currentText, setCurrentText] = useState(""); + useEffect(() => { + setCurrentText(interestingYes() ?? "Absolutely, yes."); + }, []); + const handleClick = () => { + setCurrentText(interestingYes() ?? "Absolutely, yes."); + }; return ( -
-
-

- Create T3 App -

-
- -

First Steps →

-
- Just the basics - Everything you need to know to set up your - database and authentication. -
- - -

Documentation →

-
- Learn more about Create T3 App, the libraries it uses, and how to - deploy it. -
- -
-
+
+

+ {currentText} +

); } diff --git a/src/server/db/schema.ts b/src/server/db/schema.ts index a0cf622..25fa70e 100755 --- a/src/server/db/schema.ts +++ b/src/server/db/schema.ts @@ -1,4 +1,5 @@ // https://orm.drizzle.team/docs/sql-schema-declaration +import { db } from '~/server/db'; import { sql } from "drizzle-orm"; import { boolean, @@ -161,3 +162,4 @@ export const quickReplyOptions = pgTable( quickReplyIdIndex: index('qr_options_quick_reply_id_idx').on(table.quickReplyId), }) ); + diff --git a/src/server/functions.ts b/src/server/functions.ts index fce176b..d5348b8 100755 --- a/src/server/functions.ts +++ b/src/server/functions.ts @@ -1,7 +1,7 @@ import 'server-only'; import { db } from '~/server/db'; import * as schema from '~/server/db/schema'; -import { eq, and, or, like, not } from 'drizzle-orm'; +import { eq, and, or, like, not, desc, sql } from 'drizzle-orm'; import { User, Relationship, UserRelationship, @@ -227,7 +227,7 @@ export const updateRelationshipStatus = async ( } const relationship = relationships[0] as Relationship; // Everything above is just getting info - if (userId === relationship.requestorId) { + if (userId === relationship.requestorId && status === 'accepted') { throw new Error('The requestor cannot accept the relationship they requested.'); } if (status === 'accepted') { @@ -255,7 +255,7 @@ export const updateRelationshipStatus = async ( return relationshipData; } else if (status === 'rejected') { await db.delete(schema.userRelationships) - .where(eq(schema.userRelationships.id, userRelationship.id)); + .where(eq(schema.userRelationships.relationshipId, relationship.id)); await db.delete(schema.relationships) .where(eq(schema.relationships.id, relationship.id)); return null; @@ -325,3 +325,36 @@ export const createRelationshipRequest = async (userId: number, partnerId: numbe } }; +export const getMessages = async (userId: number, limit: number, offset: number) => { + try { + const messages = await db.select().from(schema.messages) + .where(or( + eq(schema.messages.senderId, userId), + eq(schema.messages.receiverId, userId) + )) + .limit(limit) + .offset(offset) + .orderBy(desc(schema.messages.createdAt), desc(schema.messages.id)); + return messages as Message[]; + } catch (error) { + console.error('Error getting messages:', error); + throw error; + } +}; + +export const sendMessage = async (message: Message) => { + try { + const [newMessage] = await db.insert(schema.messages).values({ + senderId: message.senderId, + receiverId: message.receiverId, + text: message.text, + hasLocation: message.hasLocation, + hasMedia: message.hasMedia, + hasQuickReply: message.hasQuickReply, + }).returning(); + return newMessage as Message; + } catch (error) { + console.error('Error sending message:', error); + throw error; + } +};