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