diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 58a0c7be..80926dcf 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -22,7 +22,7 @@ jobs:
- name: Setup node
uses: actions/setup-node@v1
with:
- node-version: 16
+ node-version: 18
- name: Install dependencies
run: yarn
@@ -31,4 +31,6 @@ jobs:
run: yarn lint
- name: Build project
+ env:
+ TURSO_DATABASE_URL: ${{ secrets.TURSO_DATABASE_URL }}
run: yarn build
diff --git a/.gitignore b/.gitignore
index 849425fe..f5337031 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,3 +31,4 @@ yarn-error.log*
# turbo
.turbo
+.vercel
diff --git a/apps/web/next.config.js b/apps/web/next.config.js
index 339eba75..3e92a147 100644
--- a/apps/web/next.config.js
+++ b/apps/web/next.config.js
@@ -11,10 +11,10 @@ const withPWA = require('next-pwa')({
module.exports = withPWA({
reactStrictMode: true,
- compiler: {
- removeConsole: process.env.NODE_ENV === 'production',
- },
- transpilePackages: ['@umamin/db', '@umamin/generated'],
+ // compiler: {
+ // removeConsole: process.env.NODE_ENV === 'production',
+ // },
+ transpilePackages: ['@umamin/generated'],
modularizeImports: {
'date-fns': {
transform: 'date-fns/{{member}}',
diff --git a/apps/web/package.json b/apps/web/package.json
index 21caf04f..aa923a33 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -8,7 +8,8 @@
"start": "next start",
"lint": "next lint",
"clean": "rimraf .next/ .turbo/ node_modules/",
- "clean:cache": "rimraf .next/ .turbo/"
+ "clean:cache": "rimraf .next/ .turbo/",
+ "postinstall": "prisma generate"
},
"dependencies": {
"@apollo/server": "^4.9.4",
@@ -16,9 +17,11 @@
"@as-integrations/next": "^2.0.2",
"@hcaptcha/react-hcaptcha": "^1.4.4",
"@headlessui/react": "^1.7.15",
+ "@libsql/client": "^0.6.0",
"@next-auth/prisma-adapter": "^1.0.5",
+ "@prisma/adapter-libsql": "^5.12.1",
+ "@prisma/client": "^5.12.1",
"@tanstack/react-query": "^5.4.3",
- "@umamin/db": "*",
"@umamin/generated": "*",
"@vercel/og": "^0.5.20",
"bcrypt": "^5.1.0",
@@ -34,7 +37,7 @@
"lru-cache": "^10.0.0",
"nanoid": "^4.0.0",
"next": "13.4.12",
- "next-auth": "^4.18.8",
+ "next-auth": "4.24.6",
"next-pwa": "^5.6.0",
"next-seo": "^5.4.0",
"nprogress": "^0.2.0",
@@ -74,6 +77,7 @@
"postcss": "^8.4.14",
"prettier": "^2.7.1",
"prettier-plugin-tailwindcss": "^0.1.11",
+ "prisma": "^5.12.1",
"tailwindcss": "^3.1.4",
"typescript": "4.7.4"
}
diff --git a/apps/web/prisma/dev.db b/apps/web/prisma/dev.db
new file mode 100644
index 00000000..c5600955
Binary files /dev/null and b/apps/web/prisma/dev.db differ
diff --git a/apps/web/prisma/dev.db-journal b/apps/web/prisma/dev.db-journal
new file mode 100644
index 00000000..6e9f97fd
Binary files /dev/null and b/apps/web/prisma/dev.db-journal differ
diff --git a/apps/web/prisma/migrations/20240407070104_init/migration.sql b/apps/web/prisma/migrations/20240407070104_init/migration.sql
new file mode 100644
index 00000000..dbca6d49
--- /dev/null
+++ b/apps/web/prisma/migrations/20240407070104_init/migration.sql
@@ -0,0 +1,90 @@
+-- CreateTable
+CREATE TABLE "Account" (
+ "id" TEXT NOT NULL PRIMARY KEY,
+ "userId" TEXT NOT NULL,
+ "type" TEXT NOT NULL,
+ "provider" TEXT NOT NULL,
+ "providerAccountId" TEXT NOT NULL,
+ "refresh_token" TEXT,
+ "access_token" TEXT,
+ "expires_at" INTEGER,
+ "token_type" TEXT,
+ "scope" TEXT,
+ "id_token" TEXT,
+ "session_state" TEXT
+);
+
+-- CreateTable
+CREATE TABLE "User" (
+ "id" TEXT NOT NULL PRIMARY KEY,
+ "name" TEXT,
+ "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "email" TEXT,
+ "emailVerified" DATETIME,
+ "username" TEXT,
+ "password" TEXT,
+ "image" TEXT,
+ "message" TEXT NOT NULL DEFAULT 'Send me an anonymous message!'
+);
+
+-- CreateTable
+CREATE TABLE "VerificationToken" (
+ "identifier" TEXT NOT NULL,
+ "token" TEXT NOT NULL,
+ "expires" DATETIME NOT NULL
+);
+
+-- CreateTable
+CREATE TABLE "Message" (
+ "id" TEXT NOT NULL PRIMARY KEY,
+ "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" DATETIME NOT NULL,
+ "content" TEXT NOT NULL,
+ "receiverMsg" TEXT NOT NULL,
+ "isOpened" BOOLEAN NOT NULL DEFAULT false,
+ "reply" TEXT,
+ "clue" TEXT,
+ "senderId" TEXT,
+ "receiverUsername" TEXT,
+ "receiverId" TEXT NOT NULL
+);
+
+-- CreateTable
+CREATE TABLE "GlobalMessage" (
+ "id" TEXT NOT NULL PRIMARY KEY,
+ "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" DATETIME NOT NULL,
+ "content" TEXT NOT NULL,
+ "isAnonymous" BOOLEAN NOT NULL DEFAULT true,
+ "userId" TEXT
+);
+
+-- CreateIndex
+CREATE INDEX "Account_userId_idx" ON "Account"("userId");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "Account"("provider", "providerAccountId");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "User_username_key" ON "User"("username");
+
+-- CreateIndex
+CREATE INDEX "User_email_username_idx" ON "User"("email", "username");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "VerificationToken_token_key" ON "VerificationToken"("token");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "VerificationToken_identifier_token_key" ON "VerificationToken"("identifier", "token");
+
+-- CreateIndex
+CREATE INDEX "Message_senderId_createdAt_idx" ON "Message"("senderId", "createdAt" DESC);
+
+-- CreateIndex
+CREATE INDEX "Message_receiverId_createdAt_idx" ON "Message"("receiverId", "createdAt" DESC);
+
+-- CreateIndex
+CREATE INDEX "GlobalMessage_userId_updatedAt_idx" ON "GlobalMessage"("userId", "updatedAt" DESC);
diff --git a/apps/web/prisma/migrations/20240408052932_add_global_msg_index/migration.sql b/apps/web/prisma/migrations/20240408052932_add_global_msg_index/migration.sql
new file mode 100644
index 00000000..0c99db31
--- /dev/null
+++ b/apps/web/prisma/migrations/20240408052932_add_global_msg_index/migration.sql
@@ -0,0 +1,8 @@
+-- DropIndex
+DROP INDEX "GlobalMessage_userId_updatedAt_idx";
+
+-- CreateIndex
+CREATE INDEX "GlobalMessage_userId_idx" ON "GlobalMessage"("userId");
+
+-- CreateIndex
+CREATE INDEX "GlobalMessage_updatedAt_idx" ON "GlobalMessage"("updatedAt" DESC);
diff --git a/apps/web/prisma/migrations/migration_lock.toml b/apps/web/prisma/migrations/migration_lock.toml
new file mode 100644
index 00000000..e5e5c470
--- /dev/null
+++ b/apps/web/prisma/migrations/migration_lock.toml
@@ -0,0 +1,3 @@
+# Please do not edit this file manually
+# It should be added in your version-control system (i.e. Git)
+provider = "sqlite"
\ No newline at end of file
diff --git a/apps/web/prisma/schema.prisma b/apps/web/prisma/schema.prisma
new file mode 100644
index 00000000..5444d152
--- /dev/null
+++ b/apps/web/prisma/schema.prisma
@@ -0,0 +1,94 @@
+// This is your Prisma schema file,
+// learn more about it in the docs: https://pris.ly/d/prisma-schema
+
+generator client {
+ provider = "prisma-client-js"
+ previewFeatures = ["driverAdapters"]
+}
+
+datasource db {
+ provider = "sqlite"
+ url = "file:./dev.db"
+ relationMode = "prisma"
+}
+
+model Account {
+ id String @id @default(cuid())
+ userId String
+ type String
+ provider String
+ providerAccountId String
+ refresh_token String?
+ access_token String?
+ expires_at Int?
+ token_type String?
+ scope String?
+ id_token String?
+ session_state String?
+
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
+
+ @@unique([provider, providerAccountId])
+ @@index([userId])
+}
+
+model User {
+ id String @id @default(cuid())
+ name String?
+ createdAt DateTime @default(now())
+ email String? @unique
+ emailVerified DateTime?
+ username String? @unique
+ password String?
+ image String?
+ message String @default("Send me an anonymous message!")
+
+ accounts Account[]
+ sentMessages Message[] @relation("sentMessages")
+ sentGlobalMessages GlobalMessage[] @relation("sentGlobalMessages")
+ receivedMessages Message[] @relation("receivedMessages")
+
+ @@index([email, username])
+}
+
+model VerificationToken {
+ identifier String
+ token String @unique
+ expires DateTime
+
+ @@unique([identifier, token])
+}
+
+model Message {
+ id String @id @default(cuid())
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ content String
+ receiverMsg String
+ isOpened Boolean @default(false)
+ reply String?
+ clue String?
+
+ senderId String?
+ sender User? @relation(name: "sentMessages", fields: [senderId], references: [id])
+ receiverUsername String?
+ receiverId String
+ receiver User @relation(name: "receivedMessages", fields: [receiverId], references: [id], onDelete: Cascade)
+
+ @@index([senderId, createdAt(sort: Desc)])
+ @@index([receiverId, createdAt(sort: Desc)])
+}
+
+model GlobalMessage {
+ id String @id @default(cuid())
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ content String
+ isAnonymous Boolean @default(true)
+
+ userId String?
+ user User? @relation(name: "sentGlobalMessages", fields: [userId], references: [id], onDelete: Cascade)
+
+ @@index([userId])
+ @@index([updatedAt(sort: Desc)])
+}
diff --git a/apps/web/src/components/Create.tsx b/apps/web/src/components/Create.tsx
index 561f80cd..a29f5180 100644
--- a/apps/web/src/components/Create.tsx
+++ b/apps/web/src/components/Create.tsx
@@ -12,26 +12,28 @@ const AdContainer = dynamic(() => import('@/components/AdContainer'), {
});
export const Create = () => {
- const { refetchUser } = useInboxContext();
+ const { user, refetchUser } = useInboxContext();
const { mutate } = useMutation({ mutationFn: editUsername });
const [username, setUsername] = useState('');
const handleSubmit: React.FormEventHandler = (e) => {
e.preventDefault();
- mutate(
- { username },
- {
- onSuccess: (data) => {
- if (data.editUsername.error) {
- toast.error(data.editUsername.error);
- return;
- }
-
- refetchUser();
- },
- }
- );
+ if (user?.id) {
+ mutate(
+ { userId: user.id, username },
+ {
+ onSuccess: (data) => {
+ if (data.editUsername.error) {
+ toast.error(data.editUsername.error);
+ return;
+ }
+
+ refetchUser();
+ },
+ }
+ );
+ }
setUsername('');
};
diff --git a/apps/web/src/components/Dialog/SendGlobal.tsx b/apps/web/src/components/Dialog/SendGlobal.tsx
index 95a9f1f3..19a5ec15 100644
--- a/apps/web/src/components/Dialog/SendGlobal.tsx
+++ b/apps/web/src/components/Dialog/SendGlobal.tsx
@@ -29,24 +29,27 @@ export const SendGlobalModal = ({
const handleSend: React.FormEventHandler = (e) => {
e.preventDefault();
- mutate(
- { input: { content: message, isAnonymous } },
- {
- onSuccess: (data) => {
- if (data.sendGlobalMessage.error) {
- toast.error(data.sendGlobalMessage.error);
- return;
- }
- setMessageData(data.sendGlobalMessage.data);
- toast.success('Message posted');
+ if (user?.id) {
+ mutate(
+ { input: { userId: user.id, content: message, isAnonymous } },
+ {
+ onSuccess: (data) => {
+ if (data.sendGlobalMessage.error) {
+ toast.error(data.sendGlobalMessage.error);
+ return;
+ }
- setTimeout(() => {
- setMessage('');
- }, 500);
- },
- }
- );
+ setMessageData(data.sendGlobalMessage.data);
+ toast.success('Message posted');
+
+ setTimeout(() => {
+ setMessage('');
+ }, 500);
+ },
+ }
+ );
+ }
setIsOpen(false);
};
@@ -58,12 +61,12 @@ export const SendGlobalModal = ({
className='msg-card flex flex-col space-y-4 p-6'
>
-
+
{user?.username}
@@ -84,7 +87,7 @@ export const SendGlobalModal = ({
/>
-