From e70d4d34c2e46f2ca7d3f8acb1747ed0eb6a0f6f Mon Sep 17 00:00:00 2001 From: afiqzudinhadi Date: Mon, 23 Jun 2025 14:36:26 +0800 Subject: [PATCH 1/7] feat: add clerk-helpers package --- packages/auth/helpers.ts | 3 +++ packages/auth/index.ts | 2 ++ packages/auth/package.json | 12 ++++++++++++ packages/auth/roles.ts | 5 +++++ packages/auth/tsconfig.json | 14 ++++++++++++++ 5 files changed, 36 insertions(+) create mode 100644 packages/auth/helpers.ts create mode 100644 packages/auth/index.ts create mode 100644 packages/auth/package.json create mode 100644 packages/auth/roles.ts create mode 100644 packages/auth/tsconfig.json diff --git a/packages/auth/helpers.ts b/packages/auth/helpers.ts new file mode 100644 index 0000000..625ec6b --- /dev/null +++ b/packages/auth/helpers.ts @@ -0,0 +1,3 @@ +export function hasRole(userRoles: string[], role: string): boolean { + return userRoles.includes(role); +} diff --git a/packages/auth/index.ts b/packages/auth/index.ts new file mode 100644 index 0000000..836ce67 --- /dev/null +++ b/packages/auth/index.ts @@ -0,0 +1,2 @@ +export * from "./roles"; +export * from "./helpers"; diff --git a/packages/auth/package.json b/packages/auth/package.json new file mode 100644 index 0000000..77eabf1 --- /dev/null +++ b/packages/auth/package.json @@ -0,0 +1,12 @@ +{ + "name": "@repo/auth", + "version": "0.0.0", + "author": "Afiq Zudin Hadi", + "private": true, + "license": "MIT", + "main": "index.ts", + "type": "module", + "exports": { + "./indext.ts": "./index.ts" + } +} diff --git a/packages/auth/roles.ts b/packages/auth/roles.ts new file mode 100644 index 0000000..04c1a9d --- /dev/null +++ b/packages/auth/roles.ts @@ -0,0 +1,5 @@ +export const ROLES = Object.freeze({ + SUPER_ADMIN: "super_admin", + ADMIN: "admin", + USER: "user", +}); diff --git a/packages/auth/tsconfig.json b/packages/auth/tsconfig.json new file mode 100644 index 0000000..b745739 --- /dev/null +++ b/packages/auth/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "@repo/typescript-config/base.json", + "compilerOptions": { + "lib": ["dom", "ES2015"], + "sourceMap": true, + "types": ["jest", "node"], + "baseUrl": ".", + "paths": { + "@repo/auth/index.ts": ["./index.ts"] + } + }, + "include": [".", "src"], + "exclude": ["dist", "build", "node_modules"] +} From da8aaa1db6489fa4f0696ba48cfadb67362f4d6e Mon Sep 17 00:00:00 2001 From: afiqzudinhadi Date: Mon, 23 Jun 2025 14:36:45 +0800 Subject: [PATCH 2/7] feat: add clerk integration to remix-template --- apps/remix-template/app/root.tsx | 10 +- apps/remix-template/app/routes/clerk.tsx | 40 ++++ apps/remix-template/package.json | 2 + package-lock.json | 249 +++++++++++++++++++++++ 4 files changed, 300 insertions(+), 1 deletion(-) create mode 100644 apps/remix-template/app/routes/clerk.tsx diff --git a/apps/remix-template/app/root.tsx b/apps/remix-template/app/root.tsx index 5fc8e46..b6d7be6 100644 --- a/apps/remix-template/app/root.tsx +++ b/apps/remix-template/app/root.tsx @@ -6,9 +6,15 @@ import { ScrollRestoration, } from "@remix-run/react"; import { Analytics } from "@vercel/analytics/react"; + import "@repo/ui/styles/styles.css"; import "@repo/ui/styles/global.css"; +import type { LoaderFunction } from "@remix-run/node"; +import { rootAuthLoader } from "@clerk/remix/ssr.server"; +import { ClerkApp } from "@clerk/remix"; +export const loader: LoaderFunction = (args) => rootAuthLoader(args); + export function Layout({ children }: { children: React.ReactNode }) { return ( @@ -31,6 +37,8 @@ export function Layout({ children }: { children: React.ReactNode }) { ); } -export default function App() { +export function App() { return ; } + +export default ClerkApp(App); diff --git a/apps/remix-template/app/routes/clerk.tsx b/apps/remix-template/app/routes/clerk.tsx new file mode 100644 index 0000000..8c1c646 --- /dev/null +++ b/apps/remix-template/app/routes/clerk.tsx @@ -0,0 +1,40 @@ +import { SignInButton, UserButton, SignedIn, SignedOut } from "@clerk/remix"; +import { + Card, + CardContent, + CardHeader, + CardTitle, +} from "@repo/ui/components/card"; +import { Button } from "@repo/ui/components/button"; + +export default function Index() { + return ( +
+ + + Welcome + + + +
+

+ You are signed in! +

+ +
+
+ +
+

+ You are signed out +

+ + + +
+
+
+
+
+ ); +} diff --git a/apps/remix-template/package.json b/apps/remix-template/package.json index ff13669..c1f947a 100644 --- a/apps/remix-template/package.json +++ b/apps/remix-template/package.json @@ -11,10 +11,12 @@ "lint": "eslint app/ --max-warnings 0" }, "dependencies": { + "@clerk/remix": "^4.8.1", "@radix-ui/react-slot": "^1.2.3", "@remix-run/node": "^2.15.2", "@remix-run/react": "^2.15.2", "@remix-run/server-runtime": "^2.15.2", + "@repo/auth": "*", "@repo/ui": "*", "@vercel/analytics": "^1.5.0", "@vercel/remix": "2.16.6", diff --git a/package-lock.json b/package-lock.json index cf44244..783852b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -166,10 +166,12 @@ "apps/remix-template": { "version": "0.0.0", "dependencies": { + "@clerk/remix": "^4.8.1", "@radix-ui/react-slot": "^1.2.3", "@remix-run/node": "^2.15.2", "@remix-run/react": "^2.15.2", "@remix-run/server-runtime": "^2.15.2", + "@repo/auth": "*", "@repo/ui": "*", "@vercel/analytics": "^1.5.0", "@vercel/remix": "2.16.6", @@ -1034,6 +1036,124 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "license": "MIT" }, + "node_modules/@clerk/backend": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@clerk/backend/-/backend-2.1.0.tgz", + "integrity": "sha512-Ltb3IzdGeahM1Jr+u0UAVnVjEIYXUsq9/mTb1adolsOc9oR2LCKHE7rTRnmkwY8y+IwCzXVuywIYW+zLTk47+Q==", + "license": "MIT", + "dependencies": { + "@clerk/shared": "^3.9.7", + "@clerk/types": "^4.60.1", + "cookie": "1.0.2", + "snakecase-keys": "8.0.1", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=18.17.0" + } + }, + "node_modules/@clerk/backend/node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@clerk/clerk-react": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@clerk/clerk-react/-/clerk-react-5.32.0.tgz", + "integrity": "sha512-RdipOVBdbUbFaDb8kF3EzhJw0eh5LPxSWWRTu4k+pOdRB1gzETiToLchj5nDWFkl8qW/uL4gQbZH+Pfc7izO0A==", + "license": "MIT", + "dependencies": { + "@clerk/shared": "^3.9.7", + "@clerk/types": "^4.60.1", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=18.17.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-0", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0" + } + }, + "node_modules/@clerk/remix": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/@clerk/remix/-/remix-4.8.1.tgz", + "integrity": "sha512-QOK9tuijOrAzRDo8Cy+d4PqQfVChXr93+RyozI1gLlCOwTVtJHhlxUhWeR3hg+w+QRmuilSgv99odGwaEre4WQ==", + "license": "MIT", + "dependencies": { + "@clerk/backend": "^2.1.0", + "@clerk/clerk-react": "^5.32.0", + "@clerk/shared": "^3.9.7", + "@clerk/types": "^4.60.1", + "cookie": "0.7.2", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=18.17.0" + }, + "peerDependencies": { + "@remix-run/react": "^2.0.0", + "@remix-run/server-runtime": "^2.0.0", + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-0", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0", + "react-router": "^6.0.0" + } + }, + "node_modules/@clerk/remix/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@clerk/shared": { + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-3.9.7.tgz", + "integrity": "sha512-D3iuzuDoadK6BKb7qFZEH332z4Sl5mxLuHMf8rKdIMHuOu1RQK0RqFJtKPcFvDxq0RvJ9MC+rK24P2Sa2tOoNA==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@clerk/types": "^4.60.1", + "dequal": "2.0.3", + "glob-to-regexp": "0.4.1", + "js-cookie": "3.0.5", + "std-env": "^3.9.0", + "swr": "^2.3.3" + }, + "engines": { + "node": ">=18.17.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-0", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@clerk/types": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@clerk/types/-/types-4.60.1.tgz", + "integrity": "sha512-Lbt0/Q9W9BJtGz1IITdmeqal7y1zp9EaxOEMvsTEDhOxKf/0F/M1d0gJmHEU0puQzYPp9zJsIWC2yJjbgy3Y3Q==", + "license": "MIT", + "dependencies": { + "csstype": "3.1.3" + }, + "engines": { + "node": ">=18.17.0" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -3525,6 +3645,10 @@ "web-streams-polyfill": "^3.1.1" } }, + "node_modules/@repo/auth": { + "resolved": "packages/auth", + "link": true + }, "node_modules/@repo/eslint-config": { "resolved": "packages/config-eslint", "link": true @@ -7663,6 +7787,16 @@ "node": ">=12" } }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/dotenv": { "version": "16.5.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", @@ -9409,6 +9543,12 @@ "node": ">= 6" } }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "license": "BSD-2-Clause" + }, "node_modules/globals": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/globals/-/globals-16.2.0.tgz", @@ -11333,6 +11473,15 @@ "node": ">=10" } }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -11936,6 +12085,15 @@ "loose-envify": "cli.js" } }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -11994,6 +12152,18 @@ "tmpl": "1.0.5" } }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/markdown-extensions": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-1.1.1.tgz", @@ -13363,6 +13533,16 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -15509,6 +15689,42 @@ "node": ">=8" } }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/snakecase-keys": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/snakecase-keys/-/snakecase-keys-8.0.1.tgz", + "integrity": "sha512-Sj51kE1zC7zh6TDlNNz0/Jn1n5HiHdoQErxO8jLtnyrkJW/M5PrI7x05uDgY3BO7OUQYKCvmeMurW6BPUdwEOw==", + "license": "MIT", + "dependencies": { + "map-obj": "^4.1.0", + "snake-case": "^3.0.4", + "type-fest": "^4.15.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/snakecase-keys/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", @@ -15636,6 +15852,12 @@ "node": ">= 0.8" } }, + "node_modules/std-env": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", + "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", + "license": "MIT" + }, "node_modules/stdin-discarder": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", @@ -16097,6 +16319,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swr": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.3.tgz", + "integrity": "sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -17880,6 +18115,15 @@ "requires-port": "^1.0.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/util": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", @@ -19062,6 +19306,11 @@ "url": "https://github.com/sponsors/wooorm" } }, + "packages/auth": { + "name": "@repo/auth", + "version": "0.0.0", + "license": "MIT" + }, "packages/config-eslint": { "name": "@repo/eslint-config", "version": "0.0.0", From 14ef2719c799524014d4bd3a0c8bc9e43c00a7ac Mon Sep 17 00:00:00 2001 From: afiqzudinhadi Date: Mon, 23 Jun 2025 14:50:04 +0800 Subject: [PATCH 3/7] feat: add clerk to vite-template --- apps/vite-template/package.json | 1 + apps/vite-template/src/app/index.tsx | 69 ++++++++++++++++++---------- apps/vite-template/src/main.tsx | 25 +++++++--- package-lock.json | 1 + 4 files changed, 65 insertions(+), 31 deletions(-) diff --git a/apps/vite-template/package.json b/apps/vite-template/package.json index c039e03..cdc9fe0 100644 --- a/apps/vite-template/package.json +++ b/apps/vite-template/package.json @@ -10,6 +10,7 @@ "lint": "eslint src/ --max-warnings 0" }, "dependencies": { + "@clerk/clerk-react": "^5.32.0", "@repo/ui": "*", "react": "^18.3.1", "react-dom": "^18.3.1" diff --git a/apps/vite-template/src/app/index.tsx b/apps/vite-template/src/app/index.tsx index 70d0076..5217819 100644 --- a/apps/vite-template/src/app/index.tsx +++ b/apps/vite-template/src/app/index.tsx @@ -10,33 +10,54 @@ import { CardHeader, CardTitle, } from "@repo/ui/components/card"; +import { + SignedIn, + SignedOut, + SignInButton, + UserButton, +} from "@clerk/clerk-react"; function App() { return ( -
-

- Admin
- Kitchen Sink -

- - - - shadcn/ui Card Example - - - - - -

- Built With{" "} - - Turborepo - - {" & "} - - Vite - -

+
+
+
+ + + + + + + + +
+
+ +
+

+ Admin
+ Kitchen Sink +

+ + + + shadcn/ui Card Example + + + + + +

+ Built With{" "} + + Turborepo + + {" & "} + + Vite + +

+
); } diff --git a/apps/vite-template/src/main.tsx b/apps/vite-template/src/main.tsx index 4582464..04b9577 100644 --- a/apps/vite-template/src/main.tsx +++ b/apps/vite-template/src/main.tsx @@ -2,15 +2,26 @@ import React from "react"; import { createRoot } from "react-dom/client"; import App from "./app"; import "./index.css"; +import { ClerkProvider } from "@clerk/clerk-react"; + +const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY; + +if (!PUBLISHABLE_KEY) { + throw new Error( + "VITE_PUBLISHABLE_KEY is not defined. Please set it in your environment variables." + ); +} const el = document.getElementById("root"); if (el) { - const root = createRoot(el); - root.render( - - - - ); + const root = createRoot(el); + root.render( + + + + + + ); } else { - throw new Error("Could not find root element"); + throw new Error("Could not find root element"); } diff --git a/package-lock.json b/package-lock.json index 783852b..8225977 100644 --- a/package-lock.json +++ b/package-lock.json @@ -276,6 +276,7 @@ "apps/vite-template": { "version": "0.0.0", "dependencies": { + "@clerk/clerk-react": "^5.32.0", "@repo/ui": "*", "react": "^18.3.1", "react-dom": "^18.3.1" From a5faf21953a417d59232b7177dccc803f0b68b09 Mon Sep 17 00:00:00 2001 From: afiqzudinhadi Date: Mon, 23 Jun 2025 16:32:05 +0800 Subject: [PATCH 4/7] feat: add clerk integration to next-template --- apps/next-template/.gitignore | 3 +++ apps/next-template/package.json | 1 + apps/next-template/src/app/clerk/page.tsx | 23 +++++++++++++++++ apps/next-template/src/app/layout.tsx | 31 ++++++++++++++++++++--- apps/next-template/src/middleware.ts | 28 ++++++++++++++++++++ package-lock.json | 29 +++++++++++++++++++++ 6 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 apps/next-template/.gitignore create mode 100644 apps/next-template/src/app/clerk/page.tsx create mode 100644 apps/next-template/src/middleware.ts diff --git a/apps/next-template/.gitignore b/apps/next-template/.gitignore new file mode 100644 index 0000000..4e4766d --- /dev/null +++ b/apps/next-template/.gitignore @@ -0,0 +1,3 @@ + +# clerk configuration (can include secrets) +/.clerk/ diff --git a/apps/next-template/package.json b/apps/next-template/package.json index 07039fe..b6cd03e 100644 --- a/apps/next-template/package.json +++ b/apps/next-template/package.json @@ -12,6 +12,7 @@ "clean": "rm -rf .next" }, "dependencies": { + "@clerk/nextjs": "^6.22.0", "@repo/logger": "*", "@repo/ui": "*", "next": "^15.3.0", diff --git a/apps/next-template/src/app/clerk/page.tsx b/apps/next-template/src/app/clerk/page.tsx new file mode 100644 index 0000000..7a8aa4a --- /dev/null +++ b/apps/next-template/src/app/clerk/page.tsx @@ -0,0 +1,23 @@ +import { + Card, + CardHeader, + CardTitle, + CardContent, +} from "@repo/ui/components/card"; + +export default function ClerkTestPage() { + return ( +
+ + + Welcome, you are logged in! + + +

+ This is a protected test page. +

+
+
+
+ ); +} diff --git a/apps/next-template/src/app/layout.tsx b/apps/next-template/src/app/layout.tsx index ac4f087..33e9b0c 100644 --- a/apps/next-template/src/app/layout.tsx +++ b/apps/next-template/src/app/layout.tsx @@ -1,5 +1,14 @@ import "@repo/ui/styles/styles.css"; import "@repo/ui/styles/global.css"; +import { + ClerkProvider, + SignInButton, + SignUpButton, + SignedIn, + SignedOut, + UserButton, +} from "@clerk/nextjs"; +import { Button } from "@repo/ui/components/button"; export default function RootLayout({ children, @@ -7,8 +16,24 @@ export default function RootLayout({ children: React.ReactNode; }) { return ( - - {children} - + + + +
+
+ + + + + + + + +
+
+ {children} + + +
); } diff --git a/apps/next-template/src/middleware.ts b/apps/next-template/src/middleware.ts new file mode 100644 index 0000000..800429b --- /dev/null +++ b/apps/next-template/src/middleware.ts @@ -0,0 +1,28 @@ +import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server"; + +const isProtectedRoute = createRouteMatcher(["/clerk(.*)"]); + +export default clerkMiddleware(async (auth, req) => { + // Uncomment the following line to enable Clerk's default behavior | Redirect to sign-in page if the user is not authenticated + // if (isProtectedRoute(req)) await auth.protect(); + + // More control over authentication flow + const { userId, redirectToSignIn } = await auth(); + + if (!userId && isProtectedRoute(req)) { + // Add custom logic to run before redirecting + console.log( + "User is not authenticated, redirecting to sign-in page..." + ); + return redirectToSignIn(); + } +}); + +export const config = { + matcher: [ + // Skip Next.js internals and all static files, unless found in search params + "/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)", + // Always run for API routes + "/(api|trpc)(.*)", + ], +}; diff --git a/package-lock.json b/package-lock.json index 8225977..e1ab148 100644 --- a/package-lock.json +++ b/package-lock.json @@ -125,6 +125,7 @@ "apps/next-template": { "version": "0.0.0", "dependencies": { + "@clerk/nextjs": "^6.22.0", "@repo/logger": "*", "@repo/ui": "*", "next": "^15.3.0", @@ -1080,6 +1081,28 @@ "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0" } }, + "node_modules/@clerk/nextjs": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/@clerk/nextjs/-/nextjs-6.22.0.tgz", + "integrity": "sha512-Lh+hq4iuvuSgiWSDHDM9BAM8+YZU/wtADGo2TAU7FBVEZ6I3SwyXo5JEY4F7s30TpH3kS9qC/Ye3YA6VIOOu7Q==", + "license": "MIT", + "dependencies": { + "@clerk/backend": "^2.1.0", + "@clerk/clerk-react": "^5.32.0", + "@clerk/shared": "^3.9.7", + "@clerk/types": "^4.60.1", + "server-only": "0.0.1", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=18.17.0" + }, + "peerDependencies": { + "next": "^13.5.7 || ^14.2.25 || ^15.2.3", + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-0", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0" + } + }, "node_modules/@clerk/remix": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/@clerk/remix/-/remix-4.8.1.tgz", @@ -15457,6 +15480,12 @@ "node": ">= 0.8.0" } }, + "node_modules/server-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz", + "integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==", + "license": "MIT" + }, "node_modules/set-cookie-parser": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", From 3fcf8da40688eb4a18ddc3caa8ad0d0ade083673 Mon Sep 17 00:00:00 2001 From: afiqzudinhadi Date: Mon, 23 Jun 2025 19:45:53 +0800 Subject: [PATCH 5/7] chore: cleanup and make ui looks nicer --- .../app/routes/clerk._index.tsx | 40 +++++++++++++++++ apps/remix-template/app/routes/clerk.tsx | 44 ++++++------------- apps/vite-template/src/app/index.tsx | 14 +++++- 3 files changed, 67 insertions(+), 31 deletions(-) create mode 100644 apps/remix-template/app/routes/clerk._index.tsx diff --git a/apps/remix-template/app/routes/clerk._index.tsx b/apps/remix-template/app/routes/clerk._index.tsx new file mode 100644 index 0000000..89ed32d --- /dev/null +++ b/apps/remix-template/app/routes/clerk._index.tsx @@ -0,0 +1,40 @@ +import { SignInButton, UserButton, SignedIn, SignedOut } from "@clerk/remix"; +import { + Card, + CardContent, + CardHeader, + CardTitle, +} from "@repo/ui/components/card"; +import { Button } from "@repo/ui/components/button"; + +export default function Index() { + return ( +
+ + + Welcome + + + +
+

+ You are signed in! +

+ +
+
+ +
+

+ You are signed out +

+ + + +
+
+
+
+
+ ); +} diff --git a/apps/remix-template/app/routes/clerk.tsx b/apps/remix-template/app/routes/clerk.tsx index 8c1c646..15c1afe 100644 --- a/apps/remix-template/app/routes/clerk.tsx +++ b/apps/remix-template/app/routes/clerk.tsx @@ -1,40 +1,24 @@ import { SignInButton, UserButton, SignedIn, SignedOut } from "@clerk/remix"; -import { - Card, - CardContent, - CardHeader, - CardTitle, -} from "@repo/ui/components/card"; import { Button } from "@repo/ui/components/button"; +import { Outlet } from "@remix-run/react"; export default function Index() { return ( -
- - - Welcome - - - -
-

- You are signed in! -

- -
-
+
+
+
-
-

- You are signed out -

- - - -
+ + +
- - + + + +
+
+ +
); } diff --git a/apps/vite-template/src/app/index.tsx b/apps/vite-template/src/app/index.tsx index 5217819..ad4be23 100644 --- a/apps/vite-template/src/app/index.tsx +++ b/apps/vite-template/src/app/index.tsx @@ -28,7 +28,7 @@ function App() { - +
@@ -39,6 +39,7 @@ function App() { Kitchen Sink + shadcn/ui Card Example @@ -47,6 +48,17 @@ function App() { + + + + Clerk Authentication Example + + + You are signed out! + You are signed in! + + +

Built With{" "} From eb1dec1d40b42da382e7a48584c10a327c145dcc Mon Sep 17 00:00:00 2001 From: afiqzudinhadi Date: Mon, 23 Jun 2025 21:12:25 +0800 Subject: [PATCH 6/7] docs: add readme in packages/auth and add .env.example in each repo --- apps/next-template/.env.example | 2 ++ apps/remix-template/.env.example | 2 ++ apps/vite-template/.env.example | 1 + packages/auth/README.md | 3 +++ 4 files changed, 8 insertions(+) create mode 100644 apps/next-template/.env.example create mode 100644 apps/remix-template/.env.example create mode 100644 apps/vite-template/.env.example create mode 100644 packages/auth/README.md diff --git a/apps/next-template/.env.example b/apps/next-template/.env.example new file mode 100644 index 0000000..1b5e347 --- /dev/null +++ b/apps/next-template/.env.example @@ -0,0 +1,2 @@ +NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY= +CLERK_SECRET_KEY= \ No newline at end of file diff --git a/apps/remix-template/.env.example b/apps/remix-template/.env.example new file mode 100644 index 0000000..379149b --- /dev/null +++ b/apps/remix-template/.env.example @@ -0,0 +1,2 @@ +CLERK_PUBLISHABLE_KEY= +CLERK_SECRET_KEY= \ No newline at end of file diff --git a/apps/vite-template/.env.example b/apps/vite-template/.env.example new file mode 100644 index 0000000..a32bad2 --- /dev/null +++ b/apps/vite-template/.env.example @@ -0,0 +1 @@ +VITE_CLERK_PUBLISHABLE_KEY= \ No newline at end of file diff --git a/packages/auth/README.md b/packages/auth/README.md new file mode 100644 index 0000000..7a01660 --- /dev/null +++ b/packages/auth/README.md @@ -0,0 +1,3 @@ +# @repo/auth + +Helper tools package for Clerk authentication. As of now, this helper package is not being used anywhere in the repo. From 2a199f36798069a73fc86d2fd2e0c95df24ef282 Mon Sep 17 00:00:00 2001 From: afiqzudinhadi Date: Tue, 24 Jun 2025 10:48:12 +0800 Subject: [PATCH 7/7] docs: update root and @repo/auth readme --- README.md | 1 + packages/auth/README.md | 2 ++ 2 files changed, 3 insertions(+) diff --git a/README.md b/README.md index 77951b4..2a38e05 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ This Turborepo includes the following packages and apps: - `@repo/ui`: shared UI components for frontend use. - A dummy React UI library (which contains `` and `` components) - And shadcn/ui components (which contains `