Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Supabase — create project at supabase.com
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

# NextAuth
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your_nextauth_secret_min_32_chars

# GitHub OAuth App — create at github.com/settings/applications/new
GITHUB_ID=your_github_oauth_app_client_id
GITHUB_SECRET=your_github_oauth_app_client_secret
GITLAB_ID=your_gitlab_application_id
GITLAB_SECRET=your_gitlab_application_secret
# Second callback URL to register in your GitHub OAuth App settings:
# {NEXTAUTH_URL}/api/auth/link-github/callback

# 32-byte hex string for AES-256-GCM token encryption
# Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
ENCRYPTION_KEY=
8 changes: 7 additions & 1 deletion src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,19 @@ export default async function HomePage() {
Open-source developer productivity dashboard. Track coding habits,
visualize GitHub contributions, and hit your goals.
</p>
<div className="flex gap-4 justify-center">
<div className="flex flex-col gap-4 justify-center items-center">
<Link
href="/api/auth/signin/github?callbackUrl=/dashboard"
className="bg-white text-slate-900 px-6 py-3 rounded-lg font-semibold hover:bg-slate-100 transition"
>
Sign in with GitHub
</Link>
<Link
href="/api/auth/signin/gitlab?callbackUrl=/dashboard"
className="bg-orange-500 text-white px-6 py-3 rounded-lg font-semibold hover:bg-orange-400 transition"
>
Sign in with GitLab
</Link>
<a
href="https://github.com/Priyanshu-byte-coder/devtrack"
target="_blank"
Expand Down
37 changes: 35 additions & 2 deletions src/lib/auth.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { type NextAuthOptions } from "next-auth";
import GitHubProvider from "next-auth/providers/github";
import GitLabProvider from "next-auth/providers/gitlab";
import { supabaseAdmin } from "./supabase";

const SESSION_MAX_AGE = 30 * 24 * 60 * 60;
Expand All @@ -15,6 +16,13 @@ export const authOptions: NextAuthOptions = {
params: { scope: "read:user user:email repo" },
},
}),
GitLabProvider({
clientId: process.env.GITLAB_ID ?? "",
clientSecret: process.env.GITLAB_SECRET ?? "",
authorization: {
params: { scope: "read_user api" },
},
}),
],
session: {
strategy: "jwt",
Expand Down Expand Up @@ -51,15 +59,34 @@ export const authOptions: NextAuthOptions = {
{ onConflict: "github_id" }
);
}
if (account?.provider === "gitlab" && profile) {
const p = profile as { id: number; username: string; email: string };
await supabaseAdmin.from("users").upsert(
{
email: p.email,
gitlab_id: String(p.id),
gitlab_login: p.username,
updated_at: new Date().toISOString(),
},
{ onConflict: "gitlab_id" }
);
}
return true;
},
async jwt({ token, account, profile }) {
if (account?.access_token) token.accessToken = account.access_token;
if (profile) {
if (account?.provider === "github" && account.access_token)
token.accessToken = account.access_token;
if (account?.provider === "github" && profile) {
const p = profile as { id: number; login: string };
token.githubId = String(p.id);
token.githubLogin = p.login;
}
if (account?.provider === "gitlab" && profile) {
const p = profile as { id: number; username: string };
token.gitlabToken = account.access_token;
token.gitlabId = String(p.id);
token.gitlabLogin = p.username;
}
return token;
},
async session({ session, token }) {
Expand All @@ -69,6 +96,12 @@ export const authOptions: NextAuthOptions = {
session.githubId = token.githubId;
if (typeof token.githubLogin === "string")
session.githubLogin = token.githubLogin;
if (typeof token.gitlabToken === "string")
session.gitlabToken = token.gitlabToken;
if (typeof token.gitlabId === "string")
session.gitlabId = token.gitlabId;
if (typeof token.gitlabLogin === "string")
session.gitlabLogin = token.gitlabLogin;
return session;
},
},
Expand Down
6 changes: 6 additions & 0 deletions src/types/next-auth.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ declare module "next-auth" {
accessToken?: string;
githubId?: string;
githubLogin?: string;
gitlabToken?: string;
gitlabId?: string;
gitlabLogin?: string;
user?: DefaultSession["user"];
}
}
Expand All @@ -14,5 +17,8 @@ declare module "next-auth/jwt" {
accessToken?: string;
githubId?: string;
githubLogin?: string;
gitlabToken?: string;
gitlabId?: string;
gitlabLogin?: string;
}
}
2 changes: 2 additions & 0 deletions supabase/migrations/20260519000001_add_gitlab_columns.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE users ADD COLUMN IF NOT EXISTS gitlab_id text UNIQUE;
ALTER TABLE users ADD COLUMN IF NOT EXISTS gitlab_login text;
3 changes: 3 additions & 0 deletions supabase/migrations/20260520000000_add_gitlab_columns.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
sqlalter table users add column if not exists gitlab_id text unique;
alter table users add column if not exists gitlab_login text;
alter table users alter column github_id drop not null;
4 changes: 3 additions & 1 deletion supabase/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

create table if not exists users (
id text primary key default gen_random_uuid()::text,
github_id text unique not null,
github_id text unique,
github_login text not null,
gitlab_id text unique,
gitlab_login text,
created_at timestamptz default now(),
updated_at timestamptz default now()
);
Expand Down
Loading