Skip to content

Commit bf9acaf

Browse files
authored
fix(backend): backend linting issue (#204)
cc @PengyuChen01
1 parent acc75b6 commit bf9acaf

File tree

8 files changed

+17542
-13993
lines changed

8 files changed

+17542
-13993
lines changed

backend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
"graphql-subscriptions": "^2.0.0",
6262
"graphql-upload-minimal": "^1.6.1",
6363
"graphql-ws": "^5.16.0",
64+
"jsonwebtoken": "^9.0.2",
6465
"lodash": "^4.17.21",
6566
"markdown-to-txt": "^2.0.1",
6667
"nodemailer": "^6.10.0",

backend/src/github/github.controller.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ import { UserService } from 'src/user/user.service';
1111
export class GitHuController {
1212
private readonly webhookMiddleware;
1313

14-
constructor(private readonly gitHubAppService: GitHubAppService, private readonly userService: UserService) {
14+
constructor(
15+
private readonly gitHubAppService: GitHubAppService,
16+
private readonly userService: UserService,
17+
) {
1518
// Get the App instance from the service
1619
const app = this.gitHubAppService.getApp();
1720

@@ -24,7 +27,7 @@ export class GitHuController {
2427
@Post('webhook')
2528
async handleWebhook(@Req() req: Request, @Res() res: Response) {
2629
console.log('📩 Received POST /github/webhook');
27-
30+
2831
return this.webhookMiddleware(req, res, (error?: any) => {
2932
if (error) {
3033
console.error('Webhook middleware error:', error);
@@ -35,13 +38,17 @@ export class GitHuController {
3538
}
3639
});
3740
}
38-
41+
3942
@Post('storeInstallation')
4043
async storeInstallation(
41-
@Body() body: { installationId: string, githubCode: string },
44+
@Body() body: { installationId: string; githubCode: string },
4245
@GetUserIdFromToken() userId: string,
4346
) {
44-
await this.userService.bindUserIdAndInstallId(userId, body.installationId, body.githubCode);
47+
await this.userService.bindUserIdAndInstallId(
48+
userId,
49+
body.installationId,
50+
body.githubCode,
51+
);
4552
return { success: true };
4653
}
4754
}

backend/src/github/github.module.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,14 @@ import { UserModule } from 'src/user/user.module';
2626
forwardRef(() => UserModule),
2727
],
2828
controllers: [GitHuController],
29-
providers: [ProjectService, ProjectGuard, GitHubAppService, GitHubService, ConfigService, ChatService],
29+
providers: [
30+
ProjectService,
31+
ProjectGuard,
32+
GitHubAppService,
33+
GitHubService,
34+
ConfigService,
35+
ChatService,
36+
],
3037
exports: [GitHubService],
3138
})
3239
export class GitHubModule {}

backend/src/github/github.service.ts

Lines changed: 126 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,26 @@ import { Repository } from 'typeorm';
1313
@Injectable()
1414
export class GitHubService {
1515
private readonly logger = new Logger(GitHubService.name);
16-
16+
1717
private readonly appId: string;
1818
private privateKey: string;
1919
private ignored = ['node_modules', '.git', '.gitignore', '.env'];
2020

2121
constructor(
22-
private configService: ConfigService,
22+
private configService: ConfigService,
2323
@InjectRepository(Project)
24-
private projectsRepository: Repository<Project>,)
25-
{
26-
24+
private projectsRepository: Repository<Project>,
25+
) {
2726
this.appId = this.configService.get<string>('GITHUB_APP_ID');
28-
29-
const privateKeyPath = this.configService.get<string>('GITHUB_PRIVATE_KEY_PATH');
27+
28+
const privateKeyPath = this.configService.get<string>(
29+
'GITHUB_PRIVATE_KEY_PATH',
30+
);
3031

3132
if (!privateKeyPath) {
32-
throw new Error('GITHUB_PRIVATE_KEY_PATH is not set in environment variables');
33+
throw new Error(
34+
'GITHUB_PRIVATE_KEY_PATH is not set in environment variables',
35+
);
3336
}
3437

3538
this.logger.log(`Reading GitHub private key from: ${privateKeyPath}`);
@@ -50,9 +53,9 @@ export class GitHubService {
5053
// 1) Create a JWT (valid for ~10 minutes)
5154
const now = Math.floor(Date.now() / 1000);
5255
const payload = {
53-
iat: now, // Issued at time
54-
exp: now + 600, // JWT expiration (10 minute maximum)
55-
iss: this.appId, // Your GitHub App's App ID
56+
iat: now, // Issued at time
57+
exp: now + 600, // JWT expiration (10 minute maximum)
58+
iss: this.appId, // Your GitHub App's App ID
5659
};
5760

5861
const gitHubAppJwt = jwt.sign(payload, this.privateKey, {
@@ -77,13 +80,16 @@ export class GitHubService {
7780
return token;
7881
}
7982

80-
8183
async exchangeOAuthCodeForToken(code: string): Promise<string> {
8284
const clientId = this.configService.get<string>('GITHUB_CLIENT_ID');
8385
const clientSecret = this.configService.get<string>('GITHUB_CLIENT_SECRET');
84-
85-
console.log('Exchanging OAuth Code:', { code, clientId, clientSecretExists: !!clientSecret });
86-
86+
87+
console.log('Exchanging OAuth Code:', {
88+
code,
89+
clientId,
90+
clientSecretExists: !!clientSecret,
91+
});
92+
8793
try {
8894
const response = await axios.post(
8995
'https://github.com/login/oauth/access_token',
@@ -98,30 +104,37 @@ export class GitHubService {
98104
},
99105
},
100106
);
101-
107+
102108
console.log('GitHub Token Exchange Response:', response.data);
103-
109+
104110
if (response.data.error) {
105111
console.error('GitHub OAuth error:', response.data);
106-
throw new BadRequestException(`GitHub OAuth error: ${response.data.error_description}`);
112+
throw new BadRequestException(
113+
`GitHub OAuth error: ${response.data.error_description}`,
114+
);
107115
}
108-
116+
109117
const accessToken = response.data.access_token;
110118
if (!accessToken) {
111-
throw new Error('GitHub token exchange failed: No access token returned.');
119+
throw new Error(
120+
'GitHub token exchange failed: No access token returned.',
121+
);
112122
}
113-
123+
114124
return accessToken;
115125
} catch (error: any) {
116-
console.error('OAuth exchange failed:', error.response?.data || error.message);
126+
console.error(
127+
'OAuth exchange failed:',
128+
error.response?.data || error.message,
129+
);
117130
// throw new Error(`GitHub OAuth exchange failed: ${error.response?.data?.error_description || error.message}`);
118131
}
119132
}
120-
133+
121134
/**
122135
* Create a new repository under the *user's* account.
123136
* If you need an org-level repo, use POST /orgs/{org}/repos.
124-
*/
137+
*/
125138
async createUserRepo(
126139
repoName: string,
127140
isPublic: boolean,
@@ -151,12 +164,17 @@ export class GitHubService {
151164
const data = response.data;
152165
return {
153166
owner: data.owner.login, // e.g. "octocat"
154-
repo: data.name, // e.g. "my-new-repo"
155-
htmlUrl: data.html_url, // e.g. "https://github.com/octocat/my-new-repo"
167+
repo: data.name, // e.g. "my-new-repo"
168+
htmlUrl: data.html_url, // e.g. "https://github.com/octocat/my-new-repo"
156169
};
157170
}
158171

159-
async pushMultipleFiles(installationToken: string, owner: string, repo: string, files: string[]) {
172+
async pushMultipleFiles(
173+
installationToken: string,
174+
owner: string,
175+
repo: string,
176+
files: string[],
177+
) {
160178
for (const file of files) {
161179
const fileName = path.basename(file);
162180
await this.pushFileContent(
@@ -165,88 +183,98 @@ export class GitHubService {
165183
repo,
166184
file,
167185
`myFolder/${fileName}`,
168-
'Initial commit of file ' + fileName
186+
'Initial commit of file ' + fileName,
169187
);
170188
}
171189
}
172190

173191
/**
174192
* Push a single file to the given path in the repo using GitHub Contents API.
175-
*
193+
*
176194
* @param relativePathInRepo e.g. "backend/index.js" or "frontend/package.json"
177195
*/
178-
async pushFileContent(
179-
installationToken: string,
180-
owner: string,
181-
repo: string,
182-
localFilePath: string,
183-
relativePathInRepo: string,
184-
commitMessage: string,
185-
) {
186-
const fileBuffer = fs.readFileSync(localFilePath);
187-
const base64Content = fileBuffer.toString('base64');
188-
189-
const url = `https://api.github.com/repos/${owner}/${repo}/contents/${relativePathInRepo}`;
190-
191-
await axios.put(
192-
url,
193-
{
194-
message: commitMessage,
195-
content: base64Content,
196-
},
197-
{
198-
headers: {
199-
Authorization: `token ${installationToken}`,
200-
Accept: 'application/vnd.github.v3+json',
201-
},
196+
async pushFileContent(
197+
installationToken: string,
198+
owner: string,
199+
repo: string,
200+
localFilePath: string,
201+
relativePathInRepo: string,
202+
commitMessage: string,
203+
) {
204+
const fileBuffer = fs.readFileSync(localFilePath);
205+
const base64Content = fileBuffer.toString('base64');
206+
207+
const url = `https://api.github.com/repos/${owner}/${repo}/contents/${relativePathInRepo}`;
208+
209+
await axios.put(
210+
url,
211+
{
212+
message: commitMessage,
213+
content: base64Content,
214+
},
215+
{
216+
headers: {
217+
Authorization: `token ${installationToken}`,
218+
Accept: 'application/vnd.github.v3+json',
202219
},
203-
);
204-
205-
this.logger.log(`Pushed file: ${relativePathInRepo} -> https://github.com/${owner}/${repo}`);
206-
}
207-
208-
/**
209-
* Recursively push all files in a local folder to the repo.
210-
* Skips .git, node_modules, etc. (configurable)
211-
*/
212-
async pushFolderContent(
213-
installationToken: string,
214-
owner: string,
215-
repo: string,
216-
folderPath: string,
217-
basePathInRepo: string, // e.g. "" or "backend"
218-
) {
219-
const entries = fs.readdirSync(folderPath, { withFileTypes: true });
220-
221-
for (const entry of entries) {
222-
223-
// Skip unwanted files
224-
if (this.ignored.includes(entry.name)) {
225-
continue;
226-
}
220+
},
221+
);
222+
223+
this.logger.log(
224+
`Pushed file: ${relativePathInRepo} -> https://github.com/${owner}/${repo}`,
225+
);
226+
}
227227

228-
const entryPath = path.join(folderPath, entry.name);
229-
if (entry.isDirectory()) {
230-
// Skip unwanted directories
231-
if (entry.name === '.git' || entry.name === 'node_modules') {
232-
continue;
233-
}
234-
// Recurse into subdirectory
235-
const subDirInRepo = path.join(basePathInRepo, entry.name).replace(/\\/g, '/');
236-
await this.pushFolderContent(installationToken, owner, repo, entryPath, subDirInRepo);
237-
} else {
238-
// It's a file; push it
239-
const fileInRepo = path.join(basePathInRepo, entry.name).replace(/\\/g, '/');
240-
await this.pushFileContent(
241-
installationToken,
242-
owner,
243-
repo,
244-
entryPath,
245-
fileInRepo,
246-
`Add file: ${fileInRepo}`,
247-
);
228+
/**
229+
* Recursively push all files in a local folder to the repo.
230+
* Skips .git, node_modules, etc. (configurable)
231+
*/
232+
async pushFolderContent(
233+
installationToken: string,
234+
owner: string,
235+
repo: string,
236+
folderPath: string,
237+
basePathInRepo: string, // e.g. "" or "backend"
238+
) {
239+
const entries = fs.readdirSync(folderPath, { withFileTypes: true });
240+
241+
for (const entry of entries) {
242+
// Skip unwanted files
243+
if (this.ignored.includes(entry.name)) {
244+
continue;
245+
}
246+
247+
const entryPath = path.join(folderPath, entry.name);
248+
if (entry.isDirectory()) {
249+
// Skip unwanted directories
250+
if (entry.name === '.git' || entry.name === 'node_modules') {
251+
continue;
248252
}
253+
// Recurse into subdirectory
254+
const subDirInRepo = path
255+
.join(basePathInRepo, entry.name)
256+
.replace(/\\/g, '/');
257+
await this.pushFolderContent(
258+
installationToken,
259+
owner,
260+
repo,
261+
entryPath,
262+
subDirInRepo,
263+
);
264+
} else {
265+
// It's a file; push it
266+
const fileInRepo = path
267+
.join(basePathInRepo, entry.name)
268+
.replace(/\\/g, '/');
269+
await this.pushFileContent(
270+
installationToken,
271+
owner,
272+
repo,
273+
entryPath,
274+
fileInRepo,
275+
`Add file: ${fileInRepo}`,
276+
);
249277
}
250278
}
251-
279+
}
252280
}

0 commit comments

Comments
 (0)