Add db logic to gitea oauth flow
This commit is contained in:
		
							parent
							
								
									9dca46ea66
								
							
						
					
					
						commit
						36fcd2b15d
					
				
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -144,3 +144,4 @@ dist
 | 
			
		||||
# Built Visual Studio Code Extensions
 | 
			
		||||
*.vsix
 | 
			
		||||
 | 
			
		||||
gitea/
 | 
			
		||||
@ -2,6 +2,7 @@
 | 
			
		||||
import { type Request, type Response, Router } from "express";
 | 
			
		||||
import { config } from "../config/env";
 | 
			
		||||
import { createForge, listAvailableForges } from "../services/forges";
 | 
			
		||||
import { getOrCreateUser } from "../types/user";
 | 
			
		||||
 | 
			
		||||
const authRouter = Router();
 | 
			
		||||
 | 
			
		||||
@ -46,20 +47,20 @@ authRouter.get("/callback", async (req: Request, res: Response) => {
 | 
			
		||||
 | 
			
		||||
	try {
 | 
			
		||||
		const forge = createForge(forgeType);
 | 
			
		||||
 | 
			
		||||
		const { accessToken } = await forge.exchangeCodeForToken(
 | 
			
		||||
			code,
 | 
			
		||||
			codeVerifier,
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
		const userInfo = await forge.getUserInfo(accessToken);
 | 
			
		||||
		const forgeUser = await forge.getUserInfo(accessToken);
 | 
			
		||||
 | 
			
		||||
		// -----------------------------
 | 
			
		||||
		// TODO: Insert user in DB
 | 
			
		||||
		// -----------------------------
 | 
			
		||||
		const simulatedUserId = 1; // replace with actual DB user_id
 | 
			
		||||
		const internalUser = await getOrCreateUser(
 | 
			
		||||
			forgeType,
 | 
			
		||||
			forgeUser.id.toString(),
 | 
			
		||||
			accessToken,
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
		req.session.userId = simulatedUserId;
 | 
			
		||||
		req.session.userId = internalUser.user_id;
 | 
			
		||||
		req.session.forgeType = forgeType;
 | 
			
		||||
 | 
			
		||||
		// Clear OAuth session data
 | 
			
		||||
@ -68,10 +69,10 @@ authRouter.get("/callback", async (req: Request, res: Response) => {
 | 
			
		||||
		res.json({
 | 
			
		||||
			success: true,
 | 
			
		||||
			user: {
 | 
			
		||||
				id: simulatedUserId, // internal molci ID
 | 
			
		||||
				forgeUserId: userInfo.id, // forge user ID
 | 
			
		||||
				login: userInfo.login,
 | 
			
		||||
				avatar_url: userInfo.avatar_url,
 | 
			
		||||
				user_id: internalUser.user_id, // internal molci ID
 | 
			
		||||
				forgeUserId: forgeUser.id,
 | 
			
		||||
				login: forgeUser.login,
 | 
			
		||||
				avatar_url: forgeUser.avatar_url,
 | 
			
		||||
			},
 | 
			
		||||
		});
 | 
			
		||||
	} catch (err) {
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@ import { connectDB } from "./config/db";
 | 
			
		||||
import { config, isProduction } from "./config/env";
 | 
			
		||||
import authRouter from "./routes/auth";
 | 
			
		||||
import { createForge } from "./services/forges";
 | 
			
		||||
import { findUserById } from "./types/user";
 | 
			
		||||
 | 
			
		||||
const app = express();
 | 
			
		||||
app.use(express.json());
 | 
			
		||||
@ -43,11 +44,16 @@ api.register({
 | 
			
		||||
				return res.status(401).json({ error: "Not authenticated" });
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// TODO: Fetch user from DB here
 | 
			
		||||
			const simulatedAccessToken = "FAKE_TOKEN"; // remove once DB is used
 | 
			
		||||
			const user = await findUserById(userId);
 | 
			
		||||
			if (!user) return res.status(401).json({ error: "User not found" });
 | 
			
		||||
 | 
			
		||||
			const forge = createForge(forgeType);
 | 
			
		||||
			const repos = await forge.listRepositories(simulatedAccessToken);
 | 
			
		||||
 | 
			
		||||
			if (!user.access_token) {
 | 
			
		||||
				return res.status(401).json({ error: "Missing access token" });
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			const repos = await forge.listRepositories(user.access_token);
 | 
			
		||||
 | 
			
		||||
			return res.json(repos);
 | 
			
		||||
		} catch (err) {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										5
									
								
								server/src/services/forges/constants.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								server/src/services/forges/constants.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
export const FORGE_IDS: Record<string, number> = {
 | 
			
		||||
	gitea: 0,
 | 
			
		||||
	github: 1,
 | 
			
		||||
	gitlab: 2,
 | 
			
		||||
};
 | 
			
		||||
@ -1,8 +1,8 @@
 | 
			
		||||
import * as arctic from "arctic";
 | 
			
		||||
import { config } from "../../config/env";
 | 
			
		||||
import type { Forge } from "../../types/forge";
 | 
			
		||||
import type { ForgeUser } from "../../types/forgeuser";
 | 
			
		||||
import type { Repository } from "../../types/openapi";
 | 
			
		||||
import type { User } from "../../types/user";
 | 
			
		||||
 | 
			
		||||
export class GiteaForge implements Forge {
 | 
			
		||||
	private gitea: arctic.Gitea;
 | 
			
		||||
@ -45,7 +45,7 @@ export class GiteaForge implements Forge {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	async getUserInfo(accessToken: string): Promise<User> {
 | 
			
		||||
	async getUserInfo(accessToken: string): Promise<ForgeUser> {
 | 
			
		||||
		const res = await fetch(`${config.GITEA_URL}/api/v1/user`, {
 | 
			
		||||
			headers: { Authorization: `token ${accessToken}` },
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import type { Repository } from "../types/openapi";
 | 
			
		||||
import type { User } from "../types/user";
 | 
			
		||||
import type { ForgeUser } from "./forgeuser";
 | 
			
		||||
 | 
			
		||||
export interface Forge {
 | 
			
		||||
	getAuthorizationUrl(): { url: string; state: string; codeVerifier: string };
 | 
			
		||||
@ -9,7 +9,7 @@ export interface Forge {
 | 
			
		||||
		codeVerifier: string,
 | 
			
		||||
	): Promise<{ accessToken: string }>;
 | 
			
		||||
 | 
			
		||||
	getUserInfo(accessToken: string): Promise<User>;
 | 
			
		||||
	getUserInfo(accessToken: string): Promise<ForgeUser>;
 | 
			
		||||
 | 
			
		||||
	listRepositories(accessToken: string): Promise<Repository[]>;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										5
									
								
								server/src/types/forgeuser.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								server/src/types/forgeuser.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
export interface ForgeUser {
 | 
			
		||||
	id: number; // forge user ID
 | 
			
		||||
	login: string;
 | 
			
		||||
	avatar_url?: string;
 | 
			
		||||
}
 | 
			
		||||
@ -1,8 +1,81 @@
 | 
			
		||||
import { pool } from "../config/db";
 | 
			
		||||
import { FORGE_IDS } from "../services/forges/constants";
 | 
			
		||||
 | 
			
		||||
export interface User {
 | 
			
		||||
	id: number | string;
 | 
			
		||||
	login: string;
 | 
			
		||||
	email?: string;
 | 
			
		||||
	user_id: number; // internal molci ID
 | 
			
		||||
	avatar_url?: string;
 | 
			
		||||
	access_token?: string;
 | 
			
		||||
	forge_id: number;
 | 
			
		||||
	forge_user_id: string;
 | 
			
		||||
	forge_type: string;
 | 
			
		||||
	token_expires_at?: Date;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// logic to insert user in db
 | 
			
		||||
const resolveForgeId = (forgeType: string): number => {
 | 
			
		||||
	const forgeId = FORGE_IDS[forgeType];
 | 
			
		||||
	if (forgeId === undefined)
 | 
			
		||||
		throw new Error(`Unsupported forge type: ${forgeType}`);
 | 
			
		||||
	return forgeId;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Find a user by internal Molci ID
 | 
			
		||||
 */
 | 
			
		||||
export const findUserById = async (userId: number): Promise<User | null> => {
 | 
			
		||||
	const result = await pool.query(`SELECT * FROM users WHERE user_id = $1`, [
 | 
			
		||||
		userId,
 | 
			
		||||
	]);
 | 
			
		||||
	return result.rows[0] ?? null;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Find a user by forge_id + forge_user_id
 | 
			
		||||
 */
 | 
			
		||||
export const findUserByForge = async (
 | 
			
		||||
	forgeId: number,
 | 
			
		||||
	forgeUserId: string,
 | 
			
		||||
): Promise<User | null> => {
 | 
			
		||||
	const result = await pool.query(
 | 
			
		||||
		`SELECT * FROM users WHERE forge_id = $1 AND forge_user_id = $2`,
 | 
			
		||||
		[forgeId, forgeUserId],
 | 
			
		||||
	);
 | 
			
		||||
	return result.rows[0] ?? null;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const insertUser = async (
 | 
			
		||||
	forgeType: string,
 | 
			
		||||
	forgeUserId: string,
 | 
			
		||||
	access_token?: string,
 | 
			
		||||
	token_expires_at?: Date,
 | 
			
		||||
): Promise<User> => {
 | 
			
		||||
	const forgeId = resolveForgeId(forgeType);
 | 
			
		||||
 | 
			
		||||
	const result = await pool.query(
 | 
			
		||||
		`INSERT INTO users
 | 
			
		||||
		(forge_id, forge_user_id, access_token, token_expires_at, forge_type)
 | 
			
		||||
		VALUES ($1, $2, $3, $4, $5)
 | 
			
		||||
		 RETURNING *`,
 | 
			
		||||
		[forgeId, forgeUserId, access_token, token_expires_at, forgeType],
 | 
			
		||||
	);
 | 
			
		||||
	return result.rows[0];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const getOrCreateUser = async (
 | 
			
		||||
	forgeType: string,
 | 
			
		||||
	forgeUserId: string,
 | 
			
		||||
	access_token?: string,
 | 
			
		||||
	token_expires_at?: Date,
 | 
			
		||||
): Promise<User> => {
 | 
			
		||||
	const forgeId = resolveForgeId(forgeType);
 | 
			
		||||
 | 
			
		||||
	let user = await findUserByForge(forgeId, forgeUserId);
 | 
			
		||||
	if (!user) {
 | 
			
		||||
		user = await insertUser(
 | 
			
		||||
			forgeType,
 | 
			
		||||
			forgeUserId,
 | 
			
		||||
			access_token,
 | 
			
		||||
			token_expires_at,
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
	return user;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user