feat(backend): initial setup for cms backend service
This commit is contained in:
112
backend/src/services/gitea.service.ts
Normal file
112
backend/src/services/gitea.service.ts
Normal file
@ -0,0 +1,112 @@
|
||||
import crypto from 'crypto';
|
||||
import { env } from '../config/env.js';
|
||||
|
||||
interface GiteaUser {
|
||||
id: number;
|
||||
login: string;
|
||||
email: string;
|
||||
full_name: string;
|
||||
avatar_url: string;
|
||||
}
|
||||
|
||||
interface OAuthTokenResponse {
|
||||
access_token: string;
|
||||
token_type: string;
|
||||
expires_in: number;
|
||||
refresh_token?: string;
|
||||
}
|
||||
|
||||
export class GiteaService {
|
||||
private giteaUrl: string;
|
||||
private clientId: string;
|
||||
private clientSecret: string;
|
||||
private redirectUri: string;
|
||||
private allowedUsers: Set<string>;
|
||||
|
||||
constructor() {
|
||||
this.giteaUrl = env.GITEA_URL;
|
||||
this.clientId = env.GITEA_CLIENT_ID;
|
||||
this.clientSecret = env.GITEA_CLIENT_SECRET;
|
||||
this.redirectUri = env.GITEA_REDIRECT_URI;
|
||||
|
||||
const allowed = env.GITEA_ALLOWED_USERS;
|
||||
this.allowedUsers = new Set(allowed.split(',').map(u => u.trim()).filter(Boolean));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate OAuth authorization URL
|
||||
*/
|
||||
getAuthorizationUrl(state: string): string {
|
||||
const params = new URLSearchParams({
|
||||
client_id: this.clientId,
|
||||
redirect_uri: this.redirectUri,
|
||||
response_type: 'code',
|
||||
state,
|
||||
scope: 'read:user',
|
||||
});
|
||||
|
||||
return `${this.giteaUrl}/login/oauth/authorize?${params.toString()}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exchange authorization code for access token
|
||||
*/
|
||||
async exchangeCodeForToken(code: string): Promise<OAuthTokenResponse> {
|
||||
const response = await fetch(`${this.giteaUrl}/login/oauth/access_token`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
client_id: this.clientId,
|
||||
client_secret: this.clientSecret,
|
||||
code,
|
||||
redirect_uri: this.redirectUri,
|
||||
grant_type: 'authorization_code',
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to exchange code: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch user info from Gitea using access token
|
||||
*/
|
||||
async getUserInfo(accessToken: string): Promise<GiteaUser> {
|
||||
const response = await fetch(`${this.giteaUrl}/api/v1/user`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${accessToken}`,
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch user info: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user is allowed to access the CMS
|
||||
*/
|
||||
isUserAllowed(username: string): boolean {
|
||||
// If no allowed users specified, allow all
|
||||
if (this.allowedUsers.size === 0) {
|
||||
return true;
|
||||
}
|
||||
return this.allowedUsers.has(username);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate random state for CSRF protection
|
||||
*/
|
||||
generateState(): string {
|
||||
return crypto.randomBytes(32).toString('hex');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user