Skip to content

Commit 9481610

Browse files
committed
Merge main: resolve merge conflict in analytics.ts
Merged main branch changes while preserving pagination parameter validation fix. - Uses type-safe userId: request.user.id (from main) - Retains pagination validation: Math.max(1, parseInt(...) || 1) (from fix/217)
2 parents d190726 + b1bd3c2 commit 9481610

179 files changed

Lines changed: 40233 additions & 18071 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ DATABASE_URL=postgresql://devcard:devcard@localhost:5432/devcard?schema=public
44
# ─── Redis ───
55
REDIS_URL=redis://localhost:6379
66

7+
# ─── Set The Url ───
8+
PUBLIC_APP_URL=
9+
710
# ─── JWT ───
811
# JWT_SECRET: any long random string, minimum 32 characters
912
# Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
@@ -29,4 +32,7 @@ MOBILE_REDIRECT_URI=devcard://oauth/callback
2932

3033
# ─── Server ───
3134
PORT=3000
32-
NODE_ENV=development
35+
NODE_ENV=development
36+
37+
# ─── Refresh Token Cleanup ───
38+
REFRESH_TOKEN_CLEANUP_INTERVAL_MS=86400000

.github/pull_request_template.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@ Closes #
4343

4444
## Checklist
4545

46-
- [ ] My code follows the project's coding style (`pnpm -r run lint` passes).
47-
- [ ] TypeScript compiles without errors (`pnpm -r run typecheck`).
46+
- [ ] My code follows the project's coding style (`npm run lint` passes).
47+
- [ ] TypeScript compiles without errors (`npm run typecheck --workspaces --if-present`).
4848
- [ ] I have added or updated tests for the changes I made.
49-
- [ ] All tests pass locally (`pnpm -r run test`).
49+
- [ ] All tests pass locally (`npm run test --workspaces --if-present`).
5050
- [ ] I have updated documentation where necessary.
5151
- [ ] No new `console.log` or debug statements left in the code.
5252
- [ ] Breaking changes are documented in this PR description.

.github/scripts/ciScript.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
const isTestFile = (file) => /\.(test|spec)\.[jt]sx?$/.test(file);
2+
3+
const deriveTestFiles = (files) => {
4+
return files.map((file) => {
5+
if (isTestFile(file)) return file;
6+
7+
const withoutExt = file.replace(/\.[jt]sx?$/, '');
8+
const parts = withoutExt.split('/');
9+
const baseName = parts[parts.length - 1];
10+
const dir = parts.slice(0, -1).join('/');
11+
12+
return `${dir}/__tests__/${baseName}.test.ts`;
13+
});
14+
};
15+
16+
17+
module.exports = async ({ github, context, core }) => {
18+
const owner = context.repo.owner;
19+
const repo = context.repo.repo;
20+
const pr = context.payload.pull_request;
21+
const prNumber = pr.number;
22+
const prState = pr.state;
23+
24+
const backendFiles = [];
25+
const mobileFiles = [];
26+
const webFiles = [];
27+
const dbFiles = [];
28+
29+
try {
30+
if (prState === 'closed') {
31+
console.log(`PR state is: ${prState}`);
32+
return {
33+
backendChanged: false,
34+
mobileChanged: false,
35+
webChanged: false
36+
};
37+
}
38+
39+
const changedFiles = await github.paginate(
40+
github.rest.pulls.listFiles,
41+
{
42+
owner,
43+
repo,
44+
pull_number: prNumber
45+
}
46+
);
47+
48+
changedFiles.forEach((file) => {
49+
const fileName = file.filename;
50+
51+
if (fileName.startsWith('apps/backend/')) {
52+
backendFiles.push(fileName);
53+
} else if (fileName.startsWith('apps/mobile/')) {
54+
mobileFiles.push(fileName);
55+
} else if (fileName.startsWith('apps/web/')) {
56+
webFiles.push(fileName);
57+
}else if(fileName.startsWith('apps/backend/prisma')){
58+
dbFiles.push(fileName)
59+
}else if(fileName.includes('schema.prisma') || fileName.includes('/migrations/')){
60+
dbFiles.push(fileName)
61+
}
62+
});
63+
64+
const strippedBackend = backendFiles.map(f => f.replace('apps/backend/', ''));
65+
const strippedMobile = mobileFiles.map(f => f.replace('apps/mobile/', ''));
66+
67+
console.log({ backendFiles, mobileFiles, webFiles, dbFiles });
68+
69+
core.setOutput('backendFiles', strippedBackend.join(' '));
70+
core.setOutput('mobileFiles', strippedMobile.join(' '));
71+
core.setOutput('dbFiles', dbFiles.join(' '));
72+
core.setOutput('webFiles', webFiles.map(f => f.replace('apps/web/', '')).join(' '));
73+
core.setOutput('backendTestFiles', deriveTestFiles(strippedBackend).join(' '));
74+
core.setOutput('mobileTestFiles', deriveTestFiles(strippedMobile).join(' '));
75+
core.setOutput('backendChanged', backendFiles.length > 0);
76+
core.setOutput('mobileChanged', mobileFiles.length > 0);
77+
core.setOutput('webChanged', webFiles.length > 0);
78+
79+
} catch (error) {
80+
console.error(error);
81+
82+
return {
83+
backendChanged: false,
84+
mobileChanged: false,
85+
webChanged: false
86+
};
87+
}
88+
};

.github/scripts/commentResults.js

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
module.exports = async ({
2+
github,
3+
context,
4+
backend,
5+
mobile,
6+
web,
7+
backendLint,
8+
backendTest,
9+
backendTypecheck,
10+
mobileLint,
11+
mobileTest,
12+
webBuild,
13+
backendLintOutput,
14+
mobileLintOutput,
15+
}) => {
16+
const owner = context.repo.owner;
17+
const repo = context.repo.repo;
18+
const prNumber = context.payload.pull_request.number;
19+
20+
const status = (s) => {
21+
if (s === 'success') return 'PASS';
22+
if (s === 'failure') return 'FAIL';
23+
if (s === 'skipped') return 'SKIP';
24+
return '-';
25+
};
26+
27+
const lintDetails = (output) => {
28+
if (!output || !output.trim()) return '';
29+
return `\n<details>\n<summary>View lint errors</summary>\n\n\`\`\`\n${output.trim()}\n\`\`\`\n</details>`;
30+
};
31+
32+
const anyFailure = [backend, mobile, web].includes('failure');
33+
const title = anyFailure ? 'CI — Checks Failed' : 'CI — All Checks Passed';
34+
const timestamp = new Date().toUTCString();
35+
36+
const body = `## ${title}
37+
38+
### Backend — ${status(backend)}
39+
40+
| Check | Result |
41+
|---|---|
42+
| Lint | ${status(backendLint)} |
43+
| Test | ${status(backendTest)} |
44+
| Typecheck | ${status(backendTypecheck)} |
45+
${backendLint === 'failure' ? lintDetails(backendLintOutput) : ''}
46+
47+
### Mobile — ${status(mobile)}
48+
49+
| Check | Result |
50+
|---|---|
51+
| Lint | ${status(mobileLint)} |
52+
| Test | ${status(mobileTest)} |
53+
${mobileLint === 'failure' ? lintDetails(mobileLintOutput) : ''}
54+
55+
### Web — ${status(web)}
56+
57+
| Check | Result |
58+
|---|---|
59+
| Build | ${status(webBuild)} |
60+
61+
---
62+
Last updated: \`${timestamp}\``;
63+
64+
const COMMENT_MARKER = '## CI —';
65+
66+
try {
67+
const comments = await github.paginate(
68+
github.rest.issues.listComments,
69+
{
70+
owner,
71+
repo,
72+
issue_number: prNumber
73+
}
74+
);
75+
76+
const existing = comments.find(
77+
c => c.body && c.body.startsWith(COMMENT_MARKER)
78+
);
79+
80+
if (existing) {
81+
await github.rest.issues.updateComment({
82+
owner,
83+
repo,
84+
comment_id: existing.id,
85+
body
86+
});
87+
} else {
88+
await github.rest.issues.createComment({
89+
owner,
90+
repo,
91+
issue_number: prNumber,
92+
body
93+
});
94+
}
95+
} catch (err) {
96+
console.error(err);
97+
}
98+
};
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
module.exports = async ({ github, context }) => {
2+
const pr = context.payload.pull_request;
3+
const ignoreUsers = [
4+
'ShantKhatri',
5+
'Harxhit',
6+
'blankirigaya',
7+
];
8+
9+
try {
10+
if (!pr || !pr.merged) {
11+
console.log('PR not merged.');
12+
return;
13+
}
14+
15+
const prNumber = pr.number;
16+
const contributor = pr.user.login;
17+
18+
if (ignoreUsers.includes(contributor)) {
19+
console.log(`Ignoring PR #${prNumber} by ${contributor}`);
20+
return;
21+
}
22+
23+
await github.rest.issues.createComment({
24+
owner: context.repo.owner,
25+
repo: context.repo.repo,
26+
issue_number: prNumber,
27+
body: `Congratulations @${contributor} on getting PR #${prNumber} merged!
28+
29+
Thank you for your contribution to the project.
30+
31+
To receive the appropriate GSSoC labels and recognition, please mention @Harxhit in the **#get-labels** channel on our Discord server and share your merged PR link.`,
32+
});
33+
34+
console.log(`Comment added to PR #${prNumber}`);
35+
} catch (error) {
36+
console.error(error);
37+
}
38+
};
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
module.exports = async ({ github, context }) => {
2+
const owner = context.repo.owner;
3+
const repo = context.repo.repo;
4+
5+
const EXCLUDED = new Set([
6+
'shantkhatri',
7+
'harxhit',
8+
'blankirigaya'
9+
]);
10+
11+
const contributors = new Map();
12+
13+
const ensure = (login, avatarUrl, profileUrl) => {
14+
if (!contributors.has(login)) {
15+
contributors.set(login, {
16+
login,
17+
avatarUrl,
18+
profileUrl,
19+
mergedPrs: 0,
20+
openPrs: 0,
21+
issues: 0
22+
});
23+
}
24+
25+
return contributors.get(login);
26+
};
27+
28+
const mergedPrs = await github.paginate(
29+
github.rest.pulls.list,
30+
{
31+
owner,
32+
repo,
33+
state: 'closed',
34+
per_page: 100
35+
}
36+
);
37+
38+
for (const pr of mergedPrs) {
39+
if (!pr.merged_at || !pr.user) continue;
40+
41+
const login = pr.user.login;
42+
43+
if (EXCLUDED.has(login.toLowerCase())) continue;
44+
45+
const user = ensure(
46+
login,
47+
pr.user.avatar_url,
48+
pr.user.html_url
49+
);
50+
51+
user.mergedPrs++;
52+
}
53+
54+
const openPrs = await github.paginate(
55+
github.rest.pulls.list,
56+
{
57+
owner,
58+
repo,
59+
state: 'open',
60+
per_page: 100
61+
}
62+
);
63+
64+
for (const pr of openPrs) {
65+
if (!pr.user) continue;
66+
67+
const login = pr.user.login;
68+
69+
if (EXCLUDED.has(login.toLowerCase())) continue;
70+
71+
const user = ensure(
72+
login,
73+
pr.user.avatar_url,
74+
pr.user.html_url
75+
);
76+
77+
user.openPrs++;
78+
}
79+
80+
const issues = await github.paginate(
81+
github.rest.issues.listForRepo,
82+
{
83+
owner,
84+
repo,
85+
state: 'all',
86+
per_page: 100
87+
}
88+
);
89+
90+
for (const issue of issues) {
91+
if (issue.pull_request || !issue.user) continue;
92+
93+
const login = issue.user.login;
94+
95+
if (EXCLUDED.has(login.toLowerCase())) continue;
96+
97+
const user = ensure(
98+
login,
99+
issue.user.avatar_url,
100+
issue.user.html_url
101+
);
102+
103+
user.issues++;
104+
}
105+
106+
const leaderboard = [...contributors.values()].sort(
107+
(a, b) =>
108+
b.mergedPrs - a.mergedPrs ||
109+
b.issues - a.issues ||
110+
b.openPrs - a.openPrs ||
111+
a.login.localeCompare(b.login)
112+
);
113+
114+
const fs = require('fs');
115+
const path = require('path');
116+
117+
const outputDir = path.join('apps', 'web', 'public');
118+
const outputFile = path.join(outputDir, 'leaderboard.json');
119+
120+
fs.mkdirSync(outputDir, { recursive: true });
121+
122+
fs.writeFileSync(
123+
outputFile,
124+
JSON.stringify(leaderboard, null, 2),
125+
'utf8'
126+
);
127+
128+
console.log(`Generated ${leaderboard.length} contributors`);
129+
console.log(`Leaderboard written to ${outputFile}`);
130+
};

0 commit comments

Comments
 (0)