From de51b1d2e3d53924830096bccec229517322e959 Mon Sep 17 00:00:00 2001 From: akashamba Date: Thu, 5 Jun 2025 02:58:43 -0400 Subject: [PATCH 1/6] better auth working but not fully integrated --- bun.lock | 54 ++++ drizzle.config.ts | 2 +- drizzle/0005_freezing_spyke.sql | 50 ++++ drizzle/meta/0005_snapshot.json | 405 +++++++++++++++++++++++++++++ drizzle/meta/_journal.json | 7 + package.json | 1 + src/app/api/auth/[...all]/route.ts | 4 + src/app/articles/old-page.tsx | 169 ++++++++++++ src/app/articles/page.tsx | 181 ++----------- src/lib/auth-client.ts | 5 + src/lib/auth.ts | 22 ++ src/server/db/auth-schema.ts | 47 ++++ 12 files changed, 792 insertions(+), 155 deletions(-) create mode 100644 drizzle/0005_freezing_spyke.sql create mode 100644 drizzle/meta/0005_snapshot.json create mode 100644 src/app/api/auth/[...all]/route.ts create mode 100644 src/app/articles/old-page.tsx create mode 100644 src/lib/auth-client.ts create mode 100644 src/lib/auth.ts create mode 100644 src/server/db/auth-schema.ts diff --git a/bun.lock b/bun.lock index 7110137..4c01583 100644 --- a/bun.lock +++ b/bun.lock @@ -6,6 +6,7 @@ "dependencies": { "@clerk/nextjs": "^6.20.2", "@neondatabase/serverless": "neondatabase/serverless", + "@radix-ui/react-alert-dialog": "^1.1.14", "@radix-ui/react-avatar": "^1.1.10", "@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-dropdown-menu": "^2.1.15", @@ -16,6 +17,7 @@ "@trpc/client": "^11.0.0", "@trpc/react-query": "^11.0.0", "@trpc/server": "^11.0.0", + "better-auth": "^1.2.8", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "drizzle-orm": "^0.44.1", @@ -55,6 +57,10 @@ "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="], + "@better-auth/utils": ["@better-auth/utils@0.2.5", "", { "dependencies": { "typescript": "^5.8.2", "uncrypto": "^0.1.3" } }, "sha512-uI2+/8h/zVsH8RrYdG8eUErbuGBk16rZKQfz8CjxQOyCE6v7BqFYEbFwvOkvl1KbUdxhqOnXp78+uE5h8qVEgQ=="], + + "@better-fetch/fetch": ["@better-fetch/fetch@1.1.18", "", {}, "sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA=="], + "@clerk/backend": ["@clerk/backend@1.34.0", "", { "dependencies": { "@clerk/shared": "^3.9.5", "@clerk/types": "^4.59.3", "cookie": "1.0.2", "snakecase-keys": "8.0.1", "tslib": "2.8.1" }, "peerDependencies": { "svix": "^1.62.0" }, "optionalPeers": ["svix"] }, "sha512-9rZ8hQJVpX5KX2bEpiuVXfpjhojQCiqCWADJDdCI0PCeKxn58Ep0JPYiIcczg4VKUc3a7jve9vXylykG2XajLQ=="], "@clerk/clerk-react": ["@clerk/clerk-react@5.31.8", "", { "dependencies": { "@clerk/shared": "^3.9.5", "@clerk/types": "^4.59.3", "tslib": "2.8.1" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-0", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0" } }, "sha512-GPhOdI7drAaamiKIhzfWiOVe4zw4wUi1sKp6khgUzcjr9hRopdZvzMts0fU+XLHFnYUSX8IPw4c0CDXY1wBKuw=="], @@ -153,6 +159,8 @@ "@floating-ui/utils": ["@floating-ui/utils@0.2.9", "", {}, "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg=="], + "@hexagon/base64": ["@hexagon/base64@1.1.28", "", {}, "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw=="], + "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], "@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="], @@ -215,6 +223,8 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], + "@levischuck/tiny-cbor": ["@levischuck/tiny-cbor@0.2.11", "", {}, "sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow=="], + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.10", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.9.0" } }, "sha512-bCsCyeZEwVErsGmyPNSzwfwFn4OdxBj0mmv6hOFucB/k81Ojdu68RbZdxYsRQUPc9l6SU5F/cG+bXgWs3oUgsQ=="], "@neondatabase/serverless": ["@neondatabase/serverless@github:neondatabase/serverless#915f90f", { "dependencies": { "@types/node": "^22.10.2", "@types/pg": "^8.8.0" } }, "neondatabase-serverless-915f90f"], @@ -239,6 +249,10 @@ "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.3.3", "", { "os": "win32", "cpu": "x64" }, "sha512-4QZG6F8enl9/S2+yIiOiju0iCTFd93d8VC1q9LZS4p/Xuk81W2QDjCFeoogmrWWkAD59z8ZxepBQap2dKS5ruw=="], + "@noble/ciphers": ["@noble/ciphers@0.6.0", "", {}, "sha512-mIbq/R9QXk5/cTfESb1OKtyFnk7oc1Om/8onA1158K9/OZUQFDEVy55jVTato+xmp3XX6F6Qh0zz0Nc1AxAlRQ=="], + + "@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], @@ -247,8 +261,20 @@ "@nolyfill/is-core-module": ["@nolyfill/is-core-module@1.0.39", "", {}, "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA=="], + "@peculiar/asn1-android": ["@peculiar/asn1-android@2.3.16", "", { "dependencies": { "@peculiar/asn1-schema": "^2.3.15", "asn1js": "^3.0.5", "tslib": "^2.8.1" } }, "sha512-a1viIv3bIahXNssrOIkXZIlI2ePpZaNmR30d4aBL99mu2rO+mT9D6zBsp7H6eROWGtmwv0Ionp5olJurIo09dw=="], + + "@peculiar/asn1-ecc": ["@peculiar/asn1-ecc@2.3.15", "", { "dependencies": { "@peculiar/asn1-schema": "^2.3.15", "@peculiar/asn1-x509": "^2.3.15", "asn1js": "^3.0.5", "tslib": "^2.8.1" } }, "sha512-/HtR91dvgog7z/WhCVdxZJ/jitJuIu8iTqiyWVgRE9Ac5imt2sT/E4obqIVGKQw7PIy+X6i8lVBoT6wC73XUgA=="], + + "@peculiar/asn1-rsa": ["@peculiar/asn1-rsa@2.3.15", "", { "dependencies": { "@peculiar/asn1-schema": "^2.3.15", "@peculiar/asn1-x509": "^2.3.15", "asn1js": "^3.0.5", "tslib": "^2.8.1" } }, "sha512-p6hsanvPhexRtYSOHihLvUUgrJ8y0FtOM97N5UEpC+VifFYyZa0iZ5cXjTkZoDwxJ/TTJ1IJo3HVTB2JJTpXvg=="], + + "@peculiar/asn1-schema": ["@peculiar/asn1-schema@2.3.15", "", { "dependencies": { "asn1js": "^3.0.5", "pvtsutils": "^1.3.6", "tslib": "^2.8.1" } }, "sha512-QPeD8UA8axQREpgR5UTAfu2mqQmm97oUqahDtNdBcfj3qAnoXzFdQW+aNf/tD2WVXF8Fhmftxoj0eMIT++gX2w=="], + + "@peculiar/asn1-x509": ["@peculiar/asn1-x509@2.3.15", "", { "dependencies": { "@peculiar/asn1-schema": "^2.3.15", "asn1js": "^3.0.5", "pvtsutils": "^1.3.6", "tslib": "^2.8.1" } }, "sha512-0dK5xqTqSLaxv1FHXIcd4Q/BZNuopg+u1l23hT9rOmQ1g4dNtw0g/RnEi+TboB0gOwGtrWn269v27cMgchFIIg=="], + "@radix-ui/primitive": ["@radix-ui/primitive@1.1.2", "", {}, "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA=="], + "@radix-ui/react-alert-dialog": ["@radix-ui/react-alert-dialog@1.1.14", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dialog": "1.1.14", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-IOZfZ3nPvN6lXpJTBCunFQPRSvK8MDgSc1FB85xnIpUKOw9en0dJj8JmCAxV7BiZdtYlUpmrQjoTFkVYtdoWzQ=="], + "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="], "@radix-ui/react-avatar": ["@radix-ui/react-avatar@1.1.10", "", { "dependencies": { "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog=="], @@ -311,6 +337,10 @@ "@rushstack/eslint-patch": ["@rushstack/eslint-patch@1.11.0", "", {}, "sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ=="], + "@simplewebauthn/browser": ["@simplewebauthn/browser@13.1.0", "", {}, "sha512-WuHZ/PYvyPJ9nxSzgHtOEjogBhwJfC8xzYkPC+rR/+8chl/ft4ngjiK8kSU5HtRJfczupyOh33b25TjYbvwAcg=="], + + "@simplewebauthn/server": ["@simplewebauthn/server@13.1.1", "", { "dependencies": { "@hexagon/base64": "^1.1.27", "@levischuck/tiny-cbor": "^0.2.2", "@peculiar/asn1-android": "^2.3.10", "@peculiar/asn1-ecc": "^2.3.8", "@peculiar/asn1-rsa": "^2.3.8", "@peculiar/asn1-schema": "^2.3.8", "@peculiar/asn1-x509": "^2.3.8" } }, "sha512-1hsLpRHfSuMB9ee2aAdh0Htza/X3f4djhYISrggqGe3xopNjOcePiSDkDDoPzDYaaMCrbqGP1H2TYU7bgL9PmA=="], + "@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="], "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], @@ -459,6 +489,8 @@ "arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="], + "asn1js": ["asn1js@3.0.6", "", { "dependencies": { "pvtsutils": "^1.3.6", "pvutils": "^1.1.3", "tslib": "^2.8.1" } }, "sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA=="], + "ast-types-flow": ["ast-types-flow@0.0.8", "", {}, "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ=="], "async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="], @@ -471,6 +503,10 @@ "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "better-auth": ["better-auth@1.2.8", "", { "dependencies": { "@better-auth/utils": "0.2.5", "@better-fetch/fetch": "^1.1.18", "@noble/ciphers": "^0.6.0", "@noble/hashes": "^1.6.1", "@simplewebauthn/browser": "^13.0.0", "@simplewebauthn/server": "^13.0.0", "better-call": "^1.0.8", "defu": "^6.1.4", "jose": "^5.9.6", "kysely": "^0.28.1", "nanostores": "^0.11.3", "zod": "^3.24.1" } }, "sha512-y8ry7ZW3/3ZIr82Eo1zUDtMzdoQlFnwNuZ0+b0RxoNZgqmvgTIc/0tCDC7NDJerqSu4UCzer0dvYxBsv3WMIGg=="], + + "better-call": ["better-call@1.0.9", "", { "dependencies": { "@better-fetch/fetch": "^1.1.4", "rou3": "^0.5.1", "set-cookie-parser": "^2.7.1", "uncrypto": "^0.1.3" } }, "sha512-Qfm0gjk0XQz0oI7qvTK1hbqTsBY4xV2hsHAxF8LZfUYl3RaECCIifXuVqtPpZJWvlCCMlQSvkvhhyuApGUba6g=="], + "brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], @@ -533,6 +569,8 @@ "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], + "defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="], + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="], @@ -749,6 +787,8 @@ "jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="], + "jose": ["jose@5.10.0", "", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="], + "js-cookie": ["js-cookie@3.0.5", "", {}, "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw=="], "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], @@ -767,6 +807,8 @@ "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + "kysely": ["kysely@0.28.2", "", {}, "sha512-4YAVLoF0Sf0UTqlhgQMFU9iQECdah7n+13ANkiuVfRvlK+uI0Etbgd7bVP36dKlG+NXWbhGua8vnGt+sdhvT7A=="], + "language-subtag-registry": ["language-subtag-registry@0.3.23", "", {}, "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ=="], "language-tags": ["language-tags@1.0.9", "", { "dependencies": { "language-subtag-registry": "^0.3.20" } }, "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA=="], @@ -829,6 +871,8 @@ "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "nanostores": ["nanostores@0.11.4", "", {}, "sha512-k1oiVNN4hDK8NcNERSZLQiMfRzEGtfnvZvdBvey3SQbgn8Dcrk0h1I6vpxApjb10PFUflZrgJ2WEZyJQ+5v7YQ=="], + "napi-postinstall": ["napi-postinstall@0.2.4", "", { "bin": { "napi-postinstall": "lib/cli.js" } }, "sha512-ZEzHJwBhZ8qQSbknHqYcdtQVr8zUgGyM/q6h6qAyhtyVMNrSgDhrC4disf03dYW0e+czXyLnZINnCTEkWy0eJg=="], "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], @@ -911,6 +955,10 @@ "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + "pvtsutils": ["pvtsutils@1.3.6", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg=="], + + "pvutils": ["pvutils@1.1.3", "", {}, "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ=="], + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], "react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="], @@ -937,6 +985,8 @@ "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + "rou3": ["rou3@0.5.1", "", {}, "sha512-OXMmJ3zRk2xeXFGfA3K+EOPHC5u7RDFG7lIOx0X1pdnhUkI8MdVrbV+sNsD80ElpUZ+MRHdyxPnFthq9VHs8uQ=="], + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], "safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="], @@ -951,6 +1001,8 @@ "server-only": ["server-only@0.0.1", "", {}, "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA=="], + "set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="], + "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], "set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="], @@ -1055,6 +1107,8 @@ "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], + "uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="], + "undici-types": ["undici-types@6.19.8", "", {}, "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="], "unrs-resolver": ["unrs-resolver@1.7.8", "", { "dependencies": { "napi-postinstall": "^0.2.2" }, "optionalDependencies": { "@unrs/resolver-binding-darwin-arm64": "1.7.8", "@unrs/resolver-binding-darwin-x64": "1.7.8", "@unrs/resolver-binding-freebsd-x64": "1.7.8", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.7.8", "@unrs/resolver-binding-linux-arm-musleabihf": "1.7.8", "@unrs/resolver-binding-linux-arm64-gnu": "1.7.8", "@unrs/resolver-binding-linux-arm64-musl": "1.7.8", "@unrs/resolver-binding-linux-ppc64-gnu": "1.7.8", "@unrs/resolver-binding-linux-riscv64-gnu": "1.7.8", "@unrs/resolver-binding-linux-riscv64-musl": "1.7.8", "@unrs/resolver-binding-linux-s390x-gnu": "1.7.8", "@unrs/resolver-binding-linux-x64-gnu": "1.7.8", "@unrs/resolver-binding-linux-x64-musl": "1.7.8", "@unrs/resolver-binding-wasm32-wasi": "1.7.8", "@unrs/resolver-binding-win32-arm64-msvc": "1.7.8", "@unrs/resolver-binding-win32-ia32-msvc": "1.7.8", "@unrs/resolver-binding-win32-x64-msvc": "1.7.8" } }, "sha512-2zsXwyOXmCX9nGz4vhtZRYhe30V78heAv+KDc21A/KMdovGHbZcixeD5JHEF0DrFXzdytwuzYclcPbvp8A3Jlw=="], diff --git a/drizzle.config.ts b/drizzle.config.ts index 52fc6ff..3d1aec3 100644 --- a/drizzle.config.ts +++ b/drizzle.config.ts @@ -3,7 +3,7 @@ import { type Config } from "drizzle-kit"; import { env } from "@/env"; export default { - schema: "./src/server/db/schema.ts", + schema: ["./src/server/db/schema.ts", "./src/server/db/auth-schema.ts"], dialect: "postgresql", dbCredentials: { url: env.DATABASE_URL, diff --git a/drizzle/0005_freezing_spyke.sql b/drizzle/0005_freezing_spyke.sql new file mode 100644 index 0000000..5648570 --- /dev/null +++ b/drizzle/0005_freezing_spyke.sql @@ -0,0 +1,50 @@ +CREATE TABLE "account" ( + "id" text PRIMARY KEY NOT NULL, + "account_id" text NOT NULL, + "provider_id" text NOT NULL, + "user_id" text NOT NULL, + "access_token" text, + "refresh_token" text, + "id_token" text, + "access_token_expires_at" timestamp, + "refresh_token_expires_at" timestamp, + "scope" text, + "password" text, + "created_at" timestamp NOT NULL, + "updated_at" timestamp NOT NULL +); +--> statement-breakpoint +CREATE TABLE "session" ( + "id" text PRIMARY KEY NOT NULL, + "expires_at" timestamp NOT NULL, + "token" text NOT NULL, + "created_at" timestamp NOT NULL, + "updated_at" timestamp NOT NULL, + "ip_address" text, + "user_agent" text, + "user_id" text NOT NULL, + CONSTRAINT "session_token_unique" UNIQUE("token") +); +--> statement-breakpoint +CREATE TABLE "user" ( + "id" text PRIMARY KEY NOT NULL, + "name" text NOT NULL, + "email" text NOT NULL, + "email_verified" boolean NOT NULL, + "image" text, + "created_at" timestamp NOT NULL, + "updated_at" timestamp NOT NULL, + CONSTRAINT "user_email_unique" UNIQUE("email") +); +--> statement-breakpoint +CREATE TABLE "verification" ( + "id" text PRIMARY KEY NOT NULL, + "identifier" text NOT NULL, + "value" text NOT NULL, + "expires_at" timestamp NOT NULL, + "created_at" timestamp, + "updated_at" timestamp +); +--> statement-breakpoint +ALTER TABLE "account" ADD CONSTRAINT "account_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "session" ADD CONSTRAINT "session_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; \ No newline at end of file diff --git a/drizzle/meta/0005_snapshot.json b/drizzle/meta/0005_snapshot.json new file mode 100644 index 0000000..32a337c --- /dev/null +++ b/drizzle/meta/0005_snapshot.json @@ -0,0 +1,405 @@ +{ + "id": "5a7397c2-956f-48d2-9bac-20b7d77d5229", + "prevId": "2aee8837-ca7a-451c-9fa3-02172faedbd0", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.flipside_article": { + "name": "flipside_article", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image_url": { + "name": "image_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tags": { + "name": "tags", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "title_idx": { + "name": "title_idx", + "columns": [ + { + "expression": "title", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 5c011c3..85f5757 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -36,6 +36,13 @@ "when": 1749021231634, "tag": "0004_opposite_dark_phoenix", "breakpoints": true + }, + { + "idx": 5, + "version": "7", + "when": 1749081960612, + "tag": "0005_freezing_spyke", + "breakpoints": true } ] } \ No newline at end of file diff --git a/package.json b/package.json index 467c8cd..3e6fbbd 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "@trpc/client": "^11.0.0", "@trpc/react-query": "^11.0.0", "@trpc/server": "^11.0.0", + "better-auth": "^1.2.8", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "drizzle-orm": "^0.44.1", diff --git a/src/app/api/auth/[...all]/route.ts b/src/app/api/auth/[...all]/route.ts new file mode 100644 index 0000000..7cbe91b --- /dev/null +++ b/src/app/api/auth/[...all]/route.ts @@ -0,0 +1,4 @@ +import { auth } from "@/lib/auth"; +import { toNextJsHandler } from "better-auth/next-js"; + +export const { POST, GET } = toNextJsHandler(auth); diff --git a/src/app/articles/old-page.tsx b/src/app/articles/old-page.tsx new file mode 100644 index 0000000..7e92cb3 --- /dev/null +++ b/src/app/articles/old-page.tsx @@ -0,0 +1,169 @@ +"use client"; + +import { useState, useMemo } from "react"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Plus, Search } from "lucide-react"; +import { ArticleList } from "@/components/article-list"; +import { NewArticleModal } from "@/components/modals/new-article-modal"; +import { EditArticleModal } from "@/components/modals/edit-article-modal"; +import type { Article } from "@/lib/types"; +import { api } from "@/trpc/react"; + +export default function ArticlesPage() { + const { + data: articles, + isLoading, + isError, + error, + } = api.articles.getAll.useQuery(); + const [searchQuery, setSearchQuery] = useState(""); + const [isNewArticleModalOpen, setIsNewArticleModalOpen] = useState(false); + const [isEditArticleModalOpen, setIsEditArticleModalOpen] = useState(false); + const [editingArticle, setEditingArticle] = useState
(null); + const utils = api.useUtils(); + + const deleteArticle = api.articles.delete.useMutation({ + onSuccess: () => { + // Invalidate and refetch articles query + utils.articles.getAll.invalidate().catch((error) => { + console.error("Failed to invalidate cache:", error); + }); + console.log("Article deleted successfully!"); + }, + onError: (error) => { + console.log(`Failed to delete article: ${error.message}`); + }, + }); + + const filteredArticles = useMemo((): Article[] => { + if (!articles) return []; + return articles.filter((article) => { + const matchesSearch = article.title + .toLowerCase() + .includes(searchQuery.toLowerCase()); + + return matchesSearch; + }); + }, [articles, searchQuery]); + + const handleNewArticle = () => { + // Refresh articles list - in real app, this would refetch from API + console.log("Refreshing articles list..."); + }; + + const handleEditArticle = (article: Article) => { + setEditingArticle(article); + setIsEditArticleModalOpen(true); + }; + + const handleUpdateArticle = () => { + // Refresh articles list - in real app, this would refetch from API + console.log("Refreshing articles list after update..."); + }; + + const handleDeleteArticle = async (id: string) => { + try { + deleteArticle.mutateAsync({ id: id }); + } catch (error) { + console.log("Failed to delete article", error); + } + }; + + if (isLoading) { + return ( +
+

Loading articles...

+
+ ); + } + + if (isError) { + return ( +
+

Error loading articles: {error?.message}

+ +
+ ); + } + + if (!articles) { + return
Something went wrong
; + } + + return ( +
+ {/* Header */} +
+
+

My Articles

+

+ {articles.length} article{articles.length !== 1 ? "s" : ""} saved +

+
+ +
+ + {/* Search Bar */} +
+ + setSearchQuery(e.target.value)} + className="pl-10" + /> +
+ + {/* Articles List or Empty State */} + {articles.length === 0 ? ( +
+
📚
+

+ No articles yet +

+

+ Add your first article to get started! +

+ +
+ ) : ( + + )} + + {/* Modals */} + setIsNewArticleModalOpen(false)} + onSave={handleNewArticle} + /> + + { + setIsEditArticleModalOpen(false); + setEditingArticle(null); + }} + onSave={handleUpdateArticle} + /> +
+ ); +} diff --git a/src/app/articles/page.tsx b/src/app/articles/page.tsx index 7e92cb3..ce4d3be 100644 --- a/src/app/articles/page.tsx +++ b/src/app/articles/page.tsx @@ -1,169 +1,42 @@ "use client"; -import { useState, useMemo } from "react"; import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Plus, Search } from "lucide-react"; -import { ArticleList } from "@/components/article-list"; -import { NewArticleModal } from "@/components/modals/new-article-modal"; -import { EditArticleModal } from "@/components/modals/edit-article-modal"; -import type { Article } from "@/lib/types"; -import { api } from "@/trpc/react"; - -export default function ArticlesPage() { - const { - data: articles, - isLoading, - isError, - error, - } = api.articles.getAll.useQuery(); - const [searchQuery, setSearchQuery] = useState(""); - const [isNewArticleModalOpen, setIsNewArticleModalOpen] = useState(false); - const [isEditArticleModalOpen, setIsEditArticleModalOpen] = useState(false); - const [editingArticle, setEditingArticle] = useState
(null); - const utils = api.useUtils(); - - const deleteArticle = api.articles.delete.useMutation({ - onSuccess: () => { - // Invalidate and refetch articles query - utils.articles.getAll.invalidate().catch((error) => { - console.error("Failed to invalidate cache:", error); - }); - console.log("Article deleted successfully!"); - }, - onError: (error) => { - console.log(`Failed to delete article: ${error.message}`); - }, - }); - - const filteredArticles = useMemo((): Article[] => { - if (!articles) return []; - return articles.filter((article) => { - const matchesSearch = article.title - .toLowerCase() - .includes(searchQuery.toLowerCase()); - - return matchesSearch; +import { authClient } from "@/lib/auth-client"; +import React from "react"; + +export default function page() { + const signIn = async () => { + const data = await authClient.signIn.social({ + provider: "google", + callbackURL: "/articles", }); - }, [articles, searchQuery]); - - const handleNewArticle = () => { - // Refresh articles list - in real app, this would refetch from API - console.log("Refreshing articles list..."); + console.log(data); + // If ID token is provided no redirection will happen, and the user will be signed in directly. + // const data = await authClient.signIn.social({ + // provider: "google", + // idToken: { + // token: // Google ID Token, + // accessToken: // Google Access Token + // } + // }) }; - const handleEditArticle = (article: Article) => { - setEditingArticle(article); - setIsEditArticleModalOpen(true); + const signOut = async () => { + await authClient.signOut(); }; - const handleUpdateArticle = () => { - // Refresh articles list - in real app, this would refetch from API - console.log("Refreshing articles list after update..."); - }; - - const handleDeleteArticle = async (id: string) => { - try { - deleteArticle.mutateAsync({ id: id }); - } catch (error) { - console.log("Failed to delete article", error); - } - }; - - if (isLoading) { - return ( -
-

Loading articles...

-
- ); - } - - if (isError) { - return ( -
-

Error loading articles: {error?.message}

- -
- ); - } - - if (!articles) { - return
Something went wrong
; - } + const session = authClient.useSession(); return ( -
- {/* Header */} -
-
-

My Articles

-

- {articles.length} article{articles.length !== 1 ? "s" : ""} saved -

-
- +
+

Hello

+
+
- - {/* Search Bar */} -
- - setSearchQuery(e.target.value)} - className="pl-10" - /> +
{JSON.stringify(session)}
+
+
- - {/* Articles List or Empty State */} - {articles.length === 0 ? ( -
-
📚
-

- No articles yet -

-

- Add your first article to get started! -

- -
- ) : ( - - )} - - {/* Modals */} - setIsNewArticleModalOpen(false)} - onSave={handleNewArticle} - /> - - { - setIsEditArticleModalOpen(false); - setEditingArticle(null); - }} - onSave={handleUpdateArticle} - />
); } diff --git a/src/lib/auth-client.ts b/src/lib/auth-client.ts new file mode 100644 index 0000000..d1775da --- /dev/null +++ b/src/lib/auth-client.ts @@ -0,0 +1,5 @@ +import { createAuthClient } from "better-auth/react"; +export const authClient = createAuthClient({ + /** The base URL of the server (optional if you're using the same domain) */ + // baseURL: "http://localhost:3000" +}); diff --git a/src/lib/auth.ts b/src/lib/auth.ts new file mode 100644 index 0000000..c3432b8 --- /dev/null +++ b/src/lib/auth.ts @@ -0,0 +1,22 @@ +import { betterAuth } from "better-auth"; +import { drizzleAdapter } from "better-auth/adapters/drizzle"; +import { db } from "@/server/db"; // your drizzle instance +import { account, session, user, verification } from "@/server/db/auth-schema"; + +export const auth = betterAuth({ + database: drizzleAdapter(db, { + provider: "pg", // or "mysql", "sqlite" + schema: { + verification: verification, + account: account, + session: session, + user: user, + }, + }), + socialProviders: { + google: { + clientId: process.env.GOOGLE_CLIENT_ID as string, + clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, + }, + }, +}); diff --git a/src/server/db/auth-schema.ts b/src/server/db/auth-schema.ts new file mode 100644 index 0000000..3acdc54 --- /dev/null +++ b/src/server/db/auth-schema.ts @@ -0,0 +1,47 @@ +import { pgTable, text, timestamp, boolean, integer } from "drizzle-orm/pg-core"; + +export const user = pgTable("user", { + id: text('id').primaryKey(), + name: text('name').notNull(), + email: text('email').notNull().unique(), + emailVerified: boolean('email_verified').$defaultFn(() => !1).notNull(), + image: text('image'), + createdAt: timestamp('created_at').$defaultFn(() => new Date).notNull(), + updatedAt: timestamp('updated_at').$defaultFn(() => new Date).notNull() + }); + +export const session = pgTable("session", { + id: text('id').primaryKey(), + expiresAt: timestamp('expires_at').notNull(), + token: text('token').notNull().unique(), + createdAt: timestamp('created_at').notNull(), + updatedAt: timestamp('updated_at').notNull(), + ipAddress: text('ip_address'), + userAgent: text('user_agent'), + userId: text('user_id').notNull().references(()=> user.id, { onDelete: 'cascade' }) + }); + +export const account = pgTable("account", { + id: text('id').primaryKey(), + accountId: text('account_id').notNull(), + providerId: text('provider_id').notNull(), + userId: text('user_id').notNull().references(()=> user.id, { onDelete: 'cascade' }), + accessToken: text('access_token'), + refreshToken: text('refresh_token'), + idToken: text('id_token'), + accessTokenExpiresAt: timestamp('access_token_expires_at'), + refreshTokenExpiresAt: timestamp('refresh_token_expires_at'), + scope: text('scope'), + password: text('password'), + createdAt: timestamp('created_at').notNull(), + updatedAt: timestamp('updated_at').notNull() + }); + +export const verification = pgTable("verification", { + id: text('id').primaryKey(), + identifier: text('identifier').notNull(), + value: text('value').notNull(), + expiresAt: timestamp('expires_at').notNull(), + createdAt: timestamp('created_at').$defaultFn(() => new Date), + updatedAt: timestamp('updated_at').$defaultFn(() => new Date) + }); From 6951976b7df3b19cbb828fa916bd3249f01335cb Mon Sep 17 00:00:00 2001 From: akashamba Date: Thu, 5 Jun 2025 03:49:49 -0400 Subject: [PATCH 2/6] fully removed clerk --- src/app/(auth)/sign-in/page.tsx | 7 +---- src/app/(auth)/sign-up/page.tsx | 7 +---- src/app/layout.tsx | 25 +++++++-------- src/components/navbar.tsx | 37 ++++++++++------------ src/middleware.ts | 12 -------- src/server/api/routers/articles.ts | 49 ++++++++++++++++++++++-------- src/server/db/schema.ts | 2 +- 7 files changed, 66 insertions(+), 73 deletions(-) delete mode 100644 src/middleware.ts diff --git a/src/app/(auth)/sign-in/page.tsx b/src/app/(auth)/sign-in/page.tsx index 26f4587..fbd19ab 100644 --- a/src/app/(auth)/sign-in/page.tsx +++ b/src/app/(auth)/sign-in/page.tsx @@ -1,10 +1,5 @@ -import { SignIn as ClerkSignInUI } from "@clerk/nextjs"; import React from "react"; export default function SignInPage() { - return ( -
- -
- ); + return
; } diff --git a/src/app/(auth)/sign-up/page.tsx b/src/app/(auth)/sign-up/page.tsx index 568a737..fbd19ab 100644 --- a/src/app/(auth)/sign-up/page.tsx +++ b/src/app/(auth)/sign-up/page.tsx @@ -1,10 +1,5 @@ -import { SignUp as ClerkSignUpUI } from "@clerk/nextjs"; import React from "react"; export default function SignInPage() { - return ( -
- -
- ); + return
; } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index d813161..0ea4d99 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -4,7 +4,6 @@ import { Inter } from "next/font/google"; import "@/styles/globals.css"; import { Navbar } from "@/components/navbar"; import { Footer } from "@/components/footer"; -import { ClerkProvider } from "@clerk/nextjs"; import { TRPCReactProvider } from "@/trpc/react"; const inter = Inter({ subsets: ["latin"] }); @@ -21,18 +20,16 @@ export default function RootLayout({ children: React.ReactNode; }) { return ( - - - - -
- -
{children}
-
-
- - -
-
+ + + +
+ +
{children}
+
+
+ + +
); } diff --git a/src/components/navbar.tsx b/src/components/navbar.tsx index 116328e..3f56477 100644 --- a/src/components/navbar.tsx +++ b/src/components/navbar.tsx @@ -11,29 +11,24 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; - -// Mock user state - in real app, this would come from Clerk -const mockUser = { - id: "1", - name: "John Doe", - email: "john@example.com", - imageUrl: "/placeholder.svg?height=32&width=32", -}; +import { authClient } from "@/lib/auth-client"; export function Navbar() { const [isMenuOpen, setIsMenuOpen] = useState(false); const [isSignedIn, setIsSignedIn] = useState(true); // Mock auth state + const user = authClient.useSession().data?.user; const handleSignOut = () => { console.log("Signing out..."); setIsSignedIn(false); - // In real app: clerk.signOut() + // In real app: signOut() }; - const handleSignIn = () => { - console.log("Signing in..."); - setIsSignedIn(true); - // In real app: clerk.openSignIn() + const handleSignIn = async () => { + await authClient.signIn.social({ + provider: "google", + callbackURL: "/articles", + }); }; return ( @@ -71,7 +66,7 @@ export function Navbar() { {/* Auth Section */}
- {isSignedIn ? ( + {user ? ( + if (error) { + return ( +
+

Error loading articles: {error?.message}

+
-
{JSON.stringify(session)}
+ ); + } + + if (!session) { + return (
- + Something went wrong + {/* Should be buttons to sign in or go home */}
-
- ); + ); + } + + return ; } diff --git a/src/components/navbar.tsx b/src/components/navbar.tsx index 3f56477..6fe1ece 100644 --- a/src/components/navbar.tsx +++ b/src/components/navbar.tsx @@ -15,13 +15,10 @@ import { authClient } from "@/lib/auth-client"; export function Navbar() { const [isMenuOpen, setIsMenuOpen] = useState(false); - const [isSignedIn, setIsSignedIn] = useState(true); // Mock auth state const user = authClient.useSession().data?.user; - const handleSignOut = () => { - console.log("Signing out..."); - setIsSignedIn(false); - // In real app: signOut() + const handleSignOut = async () => { + await authClient.signOut(); }; const handleSignIn = async () => { @@ -37,7 +34,7 @@ export function Navbar() {
{/* Logo */} @@ -46,7 +43,7 @@ export function Navbar() { {/* Desktop Navigation */}
- {isSignedIn && ( + {user && ( <> Date: Thu, 5 Jun 2025 04:39:13 -0400 Subject: [PATCH 4/6] delete old pages --- src/components/old:ArticleList.tsx | 35 ------ src/components/old:HomePage.tsx | 167 ----------------------------- src/components/old:LandingPage.tsx | 14 --- 3 files changed, 216 deletions(-) delete mode 100644 src/components/old:ArticleList.tsx delete mode 100644 src/components/old:HomePage.tsx delete mode 100644 src/components/old:LandingPage.tsx diff --git a/src/components/old:ArticleList.tsx b/src/components/old:ArticleList.tsx deleted file mode 100644 index 9789930..0000000 --- a/src/components/old:ArticleList.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import type { Article } from "@/types/articles"; -import React from "react"; -import { Button } from "./ui/button"; -import Link from "next/link"; - -interface ArticleListProps { - articles: Article[]; - handleDeleteArticle: (id: string) => void; -} - -export default function ArticleList({ - articles, - handleDeleteArticle, -}: ArticleListProps) { - if (!articles) { - return
You do not have any saved articles
; - } - return ( -
- {articles.map((article: Article) => ( -
- -
- {article.title} - {article.description && ` : ${article.description}`} - -
- -
- ))} -
- ); -} diff --git a/src/components/old:HomePage.tsx b/src/components/old:HomePage.tsx deleted file mode 100644 index 811be92..0000000 --- a/src/components/old:HomePage.tsx +++ /dev/null @@ -1,167 +0,0 @@ -"use client"; -import { api } from "@/trpc/react"; -import React, { useState } from "react"; -import ArticleList from "./ArticleList"; -import { Button } from "./ui/button"; -import { - Dialog, - DialogContent, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog"; -import { Input } from "@/components/ui/input"; -import { DialogClose } from "@radix-ui/react-dialog"; -import { z } from "zod"; - -export default function HomePage() { - const utils = api.useUtils(); - const [url, setUrl] = useState(""); - const [open, setOpen] = useState(false); - - const { - data: articles, - isLoading, - isError, - error, - } = api.articles.getAll.useQuery(); - - const createArticle = api.articles.create.useMutation({ - onSuccess: () => { - // Invalidate and refetch articles query - utils.articles.getAll.invalidate().catch((error) => { - console.error("Failed to invalidate cache:", error); - }); - console.log("Article created successfully!"); - }, - onError: (error) => { - console.log(`Failed to create article: ${error.message}`); - }, - }); - - const deleteArticle = api.articles.delete.useMutation({ - onSuccess: () => { - // Invalidate and refetch articles query - utils.articles.getAll.invalidate().catch((error) => { - console.error("Failed to invalidate cache:", error); - }); - console.log("Article deleted successfully!"); - }, - onError: (error) => { - console.log(`Failed to delete article: ${error.message}`); - }, - }); - - const handleSubmit = async () => { - const parseResult = z.string().url().safeParse(url); - - if (!parseResult.success) { - alert("Please enter a valid url"); - return; - } - - try { - await createArticle.mutateAsync({ url: url }); - setUrl(""); - setOpen(false); - } catch (error) { - // Error is already handled in onError callback - console.error("Failed to create article:", error); - } - }; - - const handleDeleteArticle = async (articleId: string) => { - try { - await deleteArticle.mutateAsync({ - id: articleId, - }); - } catch (error) { - // Error is already handled in onError callback - console.error("Failed to delete article:", error); - } - }; - - if (isLoading) { - return ( -
-

Loading articles...

-
- ); - } - - if (isError) { - return ( -
-

Error loading articles: {error?.message}

- -
- ); - } - - return ( -
-
-

Articles

-

Manage your articles

-
- - {deleteArticle.isPending && ( - - - Deleting... - - - )} - - -
- - - - - - Add a URL - -
-
- setUrl(e.target.value)} - placeholder="e.g. https://flipside.akashamba.me" - /> -
-
- - - - - - -
-
-
- - {articles && ( - - )} -
- ); -} diff --git a/src/components/old:LandingPage.tsx b/src/components/old:LandingPage.tsx deleted file mode 100644 index ea886fd..0000000 --- a/src/components/old:LandingPage.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from "react"; -import { Button } from "./ui/button"; -import Link from "next/link"; - -export default function LandingPage() { - return ( -
-

Sign in to start using Flipside

- -
- ); -} From 8ac4e0ab5d34d6920ca7fa7c7084721aef2dc3c8 Mon Sep 17 00:00:00 2001 From: akashamba Date: Thu, 5 Jun 2025 05:01:51 -0400 Subject: [PATCH 5/6] fixed db issues by deleting all migrations :( --- drizzle.config.ts | 5 +- drizzle/0000_classy_lilandra.sql | 13 + drizzle/0000_harsh_iron_fist.sql | 9 - drizzle/0001_glamorous_ben_parker.sql | 2 - drizzle/0002_magical_nico_minoru.sql | 5 - drizzle/0003_perpetual_spectrum.sql | 1 - drizzle/0004_opposite_dark_phoenix.sql | 1 - drizzle/0005_freezing_spyke.sql | 50 --- drizzle/meta/0000_snapshot.json | 43 ++- drizzle/meta/0001_snapshot.json | 92 ------ drizzle/meta/0002_snapshot.json | 105 ------- drizzle/meta/0003_snapshot.json | 105 ------- drizzle/meta/0004_snapshot.json | 105 ------- drizzle/meta/0005_snapshot.json | 405 ------------------------- drizzle/meta/_journal.json | 39 +-- 15 files changed, 55 insertions(+), 925 deletions(-) create mode 100644 drizzle/0000_classy_lilandra.sql delete mode 100644 drizzle/0000_harsh_iron_fist.sql delete mode 100644 drizzle/0001_glamorous_ben_parker.sql delete mode 100644 drizzle/0002_magical_nico_minoru.sql delete mode 100644 drizzle/0003_perpetual_spectrum.sql delete mode 100644 drizzle/0004_opposite_dark_phoenix.sql delete mode 100644 drizzle/0005_freezing_spyke.sql delete mode 100644 drizzle/meta/0001_snapshot.json delete mode 100644 drizzle/meta/0002_snapshot.json delete mode 100644 drizzle/meta/0003_snapshot.json delete mode 100644 drizzle/meta/0004_snapshot.json delete mode 100644 drizzle/meta/0005_snapshot.json diff --git a/drizzle.config.ts b/drizzle.config.ts index 3d1aec3..f23b33e 100644 --- a/drizzle.config.ts +++ b/drizzle.config.ts @@ -3,7 +3,10 @@ import { type Config } from "drizzle-kit"; import { env } from "@/env"; export default { - schema: ["./src/server/db/schema.ts", "./src/server/db/auth-schema.ts"], + schema: [ + "./src/server/db/schema.ts", + // "./src/server/db/auth-schema.ts" + ], dialect: "postgresql", dbCredentials: { url: env.DATABASE_URL, diff --git a/drizzle/0000_classy_lilandra.sql b/drizzle/0000_classy_lilandra.sql new file mode 100644 index 0000000..8c7b4a4 --- /dev/null +++ b/drizzle/0000_classy_lilandra.sql @@ -0,0 +1,13 @@ +CREATE TABLE "flipside_article" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "user_id" text NOT NULL, + "url" text NOT NULL, + "image_url" text, + "title" text NOT NULL, + "description" text, + "tags" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE INDEX "title_idx" ON "flipside_article" USING btree ("title"); \ No newline at end of file diff --git a/drizzle/0000_harsh_iron_fist.sql b/drizzle/0000_harsh_iron_fist.sql deleted file mode 100644 index 2175db4..0000000 --- a/drizzle/0000_harsh_iron_fist.sql +++ /dev/null @@ -1,9 +0,0 @@ -CREATE TABLE "flipside_articles" ( - "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, - "user_id" uuid NOT NULL, - "url" text NOT NULL, - "title" text, - "description" text, - "tags" text, - "created_at" timestamp DEFAULT now() -); diff --git a/drizzle/0001_glamorous_ben_parker.sql b/drizzle/0001_glamorous_ben_parker.sql deleted file mode 100644 index cd4c48c..0000000 --- a/drizzle/0001_glamorous_ben_parker.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE "flipside_articles" RENAME TO "flipside_article";--> statement-breakpoint -CREATE INDEX "title_idx" ON "flipside_article" USING btree ("title"); \ No newline at end of file diff --git a/drizzle/0002_magical_nico_minoru.sql b/drizzle/0002_magical_nico_minoru.sql deleted file mode 100644 index 86d91de..0000000 --- a/drizzle/0002_magical_nico_minoru.sql +++ /dev/null @@ -1,5 +0,0 @@ -ALTER TABLE "flipside_article" ALTER COLUMN "user_id" SET DATA TYPE text;--> statement-breakpoint -ALTER TABLE "flipside_article" ALTER COLUMN "title" SET NOT NULL;--> statement-breakpoint -ALTER TABLE "flipside_article" ALTER COLUMN "created_at" SET NOT NULL;--> statement-breakpoint -ALTER TABLE "flipside_article" ADD COLUMN "image_url" text;--> statement-breakpoint -ALTER TABLE "flipside_article" ADD COLUMN "updated_at" timestamp DEFAULT now() NOT NULL; \ No newline at end of file diff --git a/drizzle/0003_perpetual_spectrum.sql b/drizzle/0003_perpetual_spectrum.sql deleted file mode 100644 index 4e4ddba..0000000 --- a/drizzle/0003_perpetual_spectrum.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE "flipside_article" ALTER COLUMN "title" DROP NOT NULL; \ No newline at end of file diff --git a/drizzle/0004_opposite_dark_phoenix.sql b/drizzle/0004_opposite_dark_phoenix.sql deleted file mode 100644 index 503ec6b..0000000 --- a/drizzle/0004_opposite_dark_phoenix.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE "flipside_article" ALTER COLUMN "title" SET NOT NULL; \ No newline at end of file diff --git a/drizzle/0005_freezing_spyke.sql b/drizzle/0005_freezing_spyke.sql deleted file mode 100644 index 5648570..0000000 --- a/drizzle/0005_freezing_spyke.sql +++ /dev/null @@ -1,50 +0,0 @@ -CREATE TABLE "account" ( - "id" text PRIMARY KEY NOT NULL, - "account_id" text NOT NULL, - "provider_id" text NOT NULL, - "user_id" text NOT NULL, - "access_token" text, - "refresh_token" text, - "id_token" text, - "access_token_expires_at" timestamp, - "refresh_token_expires_at" timestamp, - "scope" text, - "password" text, - "created_at" timestamp NOT NULL, - "updated_at" timestamp NOT NULL -); ---> statement-breakpoint -CREATE TABLE "session" ( - "id" text PRIMARY KEY NOT NULL, - "expires_at" timestamp NOT NULL, - "token" text NOT NULL, - "created_at" timestamp NOT NULL, - "updated_at" timestamp NOT NULL, - "ip_address" text, - "user_agent" text, - "user_id" text NOT NULL, - CONSTRAINT "session_token_unique" UNIQUE("token") -); ---> statement-breakpoint -CREATE TABLE "user" ( - "id" text PRIMARY KEY NOT NULL, - "name" text NOT NULL, - "email" text NOT NULL, - "email_verified" boolean NOT NULL, - "image" text, - "created_at" timestamp NOT NULL, - "updated_at" timestamp NOT NULL, - CONSTRAINT "user_email_unique" UNIQUE("email") -); ---> statement-breakpoint -CREATE TABLE "verification" ( - "id" text PRIMARY KEY NOT NULL, - "identifier" text NOT NULL, - "value" text NOT NULL, - "expires_at" timestamp NOT NULL, - "created_at" timestamp, - "updated_at" timestamp -); ---> statement-breakpoint -ALTER TABLE "account" ADD CONSTRAINT "account_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "session" ADD CONSTRAINT "session_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; \ No newline at end of file diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json index 3a0df57..70b9ece 100644 --- a/drizzle/meta/0000_snapshot.json +++ b/drizzle/meta/0000_snapshot.json @@ -1,11 +1,11 @@ { - "id": "d564ae8e-acdb-4f1d-8da9-325cc8bb8a5a", + "id": "a4eb6f81-6111-40ae-8ab1-f3b0c4148a2d", "prevId": "00000000-0000-0000-0000-000000000000", "version": "7", "dialect": "postgresql", "tables": { - "public.flipside_articles": { - "name": "flipside_articles", + "public.flipside_article": { + "name": "flipside_article", "schema": "", "columns": { "id": { @@ -17,7 +17,7 @@ }, "user_id": { "name": "user_id", - "type": "uuid", + "type": "text", "primaryKey": false, "notNull": true }, @@ -27,11 +27,17 @@ "primaryKey": false, "notNull": true }, + "image_url": { + "name": "image_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, "title": { "name": "title", "type": "text", "primaryKey": false, - "notNull": false + "notNull": true }, "description": { "name": "description", @@ -49,11 +55,34 @@ "name": "created_at", "type": "timestamp", "primaryKey": false, - "notNull": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, "default": "now()" } }, - "indexes": {}, + "indexes": { + "title_idx": { + "name": "title_idx", + "columns": [ + { + "expression": "title", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, "foreignKeys": {}, "compositePrimaryKeys": {}, "uniqueConstraints": {}, diff --git a/drizzle/meta/0001_snapshot.json b/drizzle/meta/0001_snapshot.json deleted file mode 100644 index af84528..0000000 --- a/drizzle/meta/0001_snapshot.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "id": "9c8a25c5-9684-438a-a61d-1df97a052646", - "prevId": "d564ae8e-acdb-4f1d-8da9-325cc8bb8a5a", - "version": "7", - "dialect": "postgresql", - "tables": { - "public.flipside_article": { - "name": "flipside_article", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "uuid", - "primaryKey": true, - "notNull": true, - "default": "gen_random_uuid()" - }, - "user_id": { - "name": "user_id", - "type": "uuid", - "primaryKey": false, - "notNull": true - }, - "url": { - "name": "url", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "title": { - "name": "title", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "tags": { - "name": "tags", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false, - "default": "now()" - } - }, - "indexes": { - "title_idx": { - "name": "title_idx", - "columns": [ - { - "expression": "title", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": false, - "method": "btree", - "with": {} - } - }, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - } - }, - "enums": {}, - "schemas": {}, - "sequences": {}, - "roles": {}, - "policies": {}, - "views": {}, - "_meta": { - "columns": {}, - "schemas": {}, - "tables": {} - } -} \ No newline at end of file diff --git a/drizzle/meta/0002_snapshot.json b/drizzle/meta/0002_snapshot.json deleted file mode 100644 index 41cfc20..0000000 --- a/drizzle/meta/0002_snapshot.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "id": "b097010d-2b82-4f2e-af77-b0728110f64e", - "prevId": "9c8a25c5-9684-438a-a61d-1df97a052646", - "version": "7", - "dialect": "postgresql", - "tables": { - "public.flipside_article": { - "name": "flipside_article", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "uuid", - "primaryKey": true, - "notNull": true, - "default": "gen_random_uuid()" - }, - "user_id": { - "name": "user_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "url": { - "name": "url", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "image_url": { - "name": "image_url", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "title": { - "name": "title", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "tags": { - "name": "tags", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": { - "title_idx": { - "name": "title_idx", - "columns": [ - { - "expression": "title", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": false, - "method": "btree", - "with": {} - } - }, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - } - }, - "enums": {}, - "schemas": {}, - "sequences": {}, - "roles": {}, - "policies": {}, - "views": {}, - "_meta": { - "columns": {}, - "schemas": {}, - "tables": {} - } -} \ No newline at end of file diff --git a/drizzle/meta/0003_snapshot.json b/drizzle/meta/0003_snapshot.json deleted file mode 100644 index 45e0073..0000000 --- a/drizzle/meta/0003_snapshot.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "id": "53528728-7967-459d-b52e-07421fc0a36a", - "prevId": "b097010d-2b82-4f2e-af77-b0728110f64e", - "version": "7", - "dialect": "postgresql", - "tables": { - "public.flipside_article": { - "name": "flipside_article", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "uuid", - "primaryKey": true, - "notNull": true, - "default": "gen_random_uuid()" - }, - "user_id": { - "name": "user_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "url": { - "name": "url", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "image_url": { - "name": "image_url", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "title": { - "name": "title", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "tags": { - "name": "tags", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": { - "title_idx": { - "name": "title_idx", - "columns": [ - { - "expression": "title", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": false, - "method": "btree", - "with": {} - } - }, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - } - }, - "enums": {}, - "schemas": {}, - "sequences": {}, - "roles": {}, - "policies": {}, - "views": {}, - "_meta": { - "columns": {}, - "schemas": {}, - "tables": {} - } -} \ No newline at end of file diff --git a/drizzle/meta/0004_snapshot.json b/drizzle/meta/0004_snapshot.json deleted file mode 100644 index a9d9b3f..0000000 --- a/drizzle/meta/0004_snapshot.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "id": "2aee8837-ca7a-451c-9fa3-02172faedbd0", - "prevId": "53528728-7967-459d-b52e-07421fc0a36a", - "version": "7", - "dialect": "postgresql", - "tables": { - "public.flipside_article": { - "name": "flipside_article", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "uuid", - "primaryKey": true, - "notNull": true, - "default": "gen_random_uuid()" - }, - "user_id": { - "name": "user_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "url": { - "name": "url", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "image_url": { - "name": "image_url", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "title": { - "name": "title", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "tags": { - "name": "tags", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": { - "title_idx": { - "name": "title_idx", - "columns": [ - { - "expression": "title", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": false, - "method": "btree", - "with": {} - } - }, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - } - }, - "enums": {}, - "schemas": {}, - "sequences": {}, - "roles": {}, - "policies": {}, - "views": {}, - "_meta": { - "columns": {}, - "schemas": {}, - "tables": {} - } -} \ No newline at end of file diff --git a/drizzle/meta/0005_snapshot.json b/drizzle/meta/0005_snapshot.json deleted file mode 100644 index 32a337c..0000000 --- a/drizzle/meta/0005_snapshot.json +++ /dev/null @@ -1,405 +0,0 @@ -{ - "id": "5a7397c2-956f-48d2-9bac-20b7d77d5229", - "prevId": "2aee8837-ca7a-451c-9fa3-02172faedbd0", - "version": "7", - "dialect": "postgresql", - "tables": { - "public.flipside_article": { - "name": "flipside_article", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "uuid", - "primaryKey": true, - "notNull": true, - "default": "gen_random_uuid()" - }, - "user_id": { - "name": "user_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "url": { - "name": "url", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "image_url": { - "name": "image_url", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "title": { - "name": "title", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "tags": { - "name": "tags", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": { - "title_idx": { - "name": "title_idx", - "columns": [ - { - "expression": "title", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": false, - "method": "btree", - "with": {} - } - }, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.account": { - "name": "account", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "account_id": { - "name": "account_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "provider_id": { - "name": "provider_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "user_id": { - "name": "user_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "access_token": { - "name": "access_token", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "refresh_token": { - "name": "refresh_token", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "id_token": { - "name": "id_token", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "access_token_expires_at": { - "name": "access_token_expires_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false - }, - "refresh_token_expires_at": { - "name": "refresh_token_expires_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false - }, - "scope": { - "name": "scope", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "password": { - "name": "password", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "account_user_id_user_id_fk": { - "name": "account_user_id_user_id_fk", - "tableFrom": "account", - "tableTo": "user", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.session": { - "name": "session", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "expires_at": { - "name": "expires_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "token": { - "name": "token", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "ip_address": { - "name": "ip_address", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "user_agent": { - "name": "user_agent", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "user_id": { - "name": "user_id", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "session_user_id_user_id_fk": { - "name": "session_user_id_user_id_fk", - "tableFrom": "session", - "tableTo": "user", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "session_token_unique": { - "name": "session_token_unique", - "nullsNotDistinct": false, - "columns": [ - "token" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.user": { - "name": "user", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "email": { - "name": "email", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "email_verified": { - "name": "email_verified", - "type": "boolean", - "primaryKey": false, - "notNull": true - }, - "image": { - "name": "image", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "user_email_unique": { - "name": "user_email_unique", - "nullsNotDistinct": false, - "columns": [ - "email" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.verification": { - "name": "verification", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "identifier": { - "name": "identifier", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "value": { - "name": "value", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "expires_at": { - "name": "expires_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - } - }, - "enums": {}, - "schemas": {}, - "sequences": {}, - "roles": {}, - "policies": {}, - "views": {}, - "_meta": { - "columns": {}, - "schemas": {}, - "tables": {} - } -} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 85f5757..5a39d70 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -5,43 +5,8 @@ { "idx": 0, "version": "7", - "when": 1748671340868, - "tag": "0000_harsh_iron_fist", - "breakpoints": true - }, - { - "idx": 1, - "version": "7", - "when": 1748671600706, - "tag": "0001_glamorous_ben_parker", - "breakpoints": true - }, - { - "idx": 2, - "version": "7", - "when": 1749018745913, - "tag": "0002_magical_nico_minoru", - "breakpoints": true - }, - { - "idx": 3, - "version": "7", - "when": 1749021195475, - "tag": "0003_perpetual_spectrum", - "breakpoints": true - }, - { - "idx": 4, - "version": "7", - "when": 1749021231634, - "tag": "0004_opposite_dark_phoenix", - "breakpoints": true - }, - { - "idx": 5, - "version": "7", - "when": 1749081960612, - "tag": "0005_freezing_spyke", + "when": 1749113694834, + "tag": "0000_classy_lilandra", "breakpoints": true } ] From 353453a422023268ec99212f68a9e59f44196dbc Mon Sep 17 00:00:00 2001 From: akashamba Date: Thu, 5 Jun 2025 05:05:52 -0400 Subject: [PATCH 6/6] temp fix for bug in edit --- src/components/modals/edit-article-modal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/modals/edit-article-modal.tsx b/src/components/modals/edit-article-modal.tsx index 2d92930..94a20c3 100644 --- a/src/components/modals/edit-article-modal.tsx +++ b/src/components/modals/edit-article-modal.tsx @@ -65,7 +65,7 @@ export function EditArticleModal({ await updateArticle.mutateAsync({ id: article.id, title: title, - tags: tags || article.tags, + tags: tags || article.tags || "", }); onSave();