feat(api): add issues and version endpoints

This commit is contained in:
wanhose 2024-10-10 11:43:13 +02:00
parent b7bf1c1e2b
commit c6faa2796b
13 changed files with 192 additions and 44 deletions

View File

@ -8,8 +8,11 @@ import v2ReportRoutes from 'routes/v2/report';
import v3DataRoutes from 'routes/v3/data';
import v3ReportRoutes from 'routes/v3/report';
import v4DataRoutes from 'routes/v4/data';
import v4IssuesRoutes from 'routes/v4/issues';
import v4ReportRoutes from 'routes/v4/report';
import v5DataRoutes from 'routes/v5/data';
import v5IssuesRoutes from 'routes/v5/issues';
import v5ReportRoutes from 'routes/v5/report';
import v5VersionRoutes from 'routes/v5/version';
import environment from 'services/environment';
const server = fastify({ logger: true });
@ -35,8 +38,11 @@ server.register(v2ReportRoutes, { prefix: '/rest/v2' });
server.register(v3DataRoutes, { prefix: '/rest/v3' });
server.register(v3ReportRoutes, { prefix: '/rest/v3' });
server.register(v4DataRoutes, { prefix: '/rest/v4' });
server.register(v4IssuesRoutes, { prefix: '/rest/v4' });
server.register(v4ReportRoutes, { prefix: '/rest/v4' });
server.register(v5DataRoutes, { prefix: '/rest/v5' });
server.register(v5IssuesRoutes, { prefix: '/rest/v5' });
server.register(v5ReportRoutes, { prefix: '/rest/v5' });
server.register(v5VersionRoutes, { prefix: '/rest/v5' });
server.listen({ host: '0.0.0.0', port: environment.port }, (error, address) => {
if (error) {

View File

@ -3,8 +3,8 @@ import { FastifyInstance, RouteShorthandOptions } from 'fastify';
/**
* @deprecated This API route is no longer supported. Please use a newer version
*/
export default (server: FastifyInstance, options: RouteShorthandOptions, done: () => void) => {
server.get('/entries/', async (request, reply) => {
export default (server: FastifyInstance, _options: RouteShorthandOptions, done: () => void) => {
server.get('/entries/', async (_request, reply) => {
reply.send({
success: false,
errors: ['This API route is no longer supported. Please use a newer version'],

View File

@ -3,7 +3,7 @@ import { FastifyInstance, RouteShorthandOptions } from 'fastify';
/**
* @deprecated This API route is no longer supported. Please use a newer version
*/
export default (server: FastifyInstance, options: RouteShorthandOptions, done: () => void) => {
export default (server: FastifyInstance, _options: RouteShorthandOptions, done: () => void) => {
server.post('/report/', {}, async (_request, reply) => {
reply.send({
success: false,

View File

@ -1,13 +1,13 @@
import { FastifyInstance, RouteShorthandOptions } from 'fastify';
import fetch from 'node-fetch';
import { parseNewFix } from 'services/compatibility';
import environment from 'services/environment';
export default (server: FastifyInstance, options: RouteShorthandOptions, done: () => void) => {
server.get('/data/', async (request, reply) => {
export default (server: FastifyInstance, _options: RouteShorthandOptions, done: () => void) => {
server.get('/data/', async (_request, reply) => {
try {
const databaseUrl =
'https://raw.githubusercontent.com/wanhose/cookie-dialog-monster/main/database.json';
const result = await (await fetch(databaseUrl)).json();
const url = `${environment.github.files}/database.json`;
const result = await (await fetch(url)).json();
reply.send({
data: {

View File

@ -18,7 +18,7 @@ const PostReportBodySchema = yup.object().shape({
type PostReportBody = yup.InferType<typeof PostReportBodySchema>;
export default (server: FastifyInstance, options: RouteShorthandOptions, done: () => void) => {
export default (server: FastifyInstance, _options: RouteShorthandOptions, done: () => void) => {
server.post<{ Body: PostReportBody }>(
'/report/',
{

View File

@ -1,13 +1,13 @@
import { FastifyInstance, RouteShorthandOptions } from 'fastify';
import fetch from 'node-fetch';
import { parseNewFix } from 'services/compatibility';
import environment from 'services/environment';
export default (server: FastifyInstance, _options: RouteShorthandOptions, done: () => void) => {
server.get('/data/', async (_request, reply) => {
try {
const databaseUrl =
'https://raw.githubusercontent.com/wanhose/cookie-dialog-monster/main/database.json';
const result = await (await fetch(databaseUrl)).json();
const url = `${environment.github.files}/database.json`;
const result = await (await fetch(url)).json();
reply.send({
data: {

View File

@ -35,25 +35,30 @@ export default (server: FastifyInstance, _options: RouteShorthandOptions, done:
per_page: 50,
q: `in:title+is:issue+repo:${environment.github.owner}/${environment.github.repo}+${hostname}`,
});
const existingIssue = existingIssues.data.items.find((issue) => hostname === issue.title);
const existingIssue = existingIssues.data.items.find(
(issue) =>
hostname === issue.title &&
(issue.state === 'open' ||
(issue.state === 'closed' && issue.labels.some((label) => label.name === 'wontfix')))
);
try {
if (existingIssue) {
if (existingIssue.state === 'closed') {
await octokit.request('PATCH /repos/{owner}/{repo}/issues/{issue_number}', {
owner: environment.github.owner,
repo: environment.github.repo,
issue_number: existingIssue.number,
labels: ['bug'],
owner: environment.github.owner,
repo: environment.github.repo,
state: 'open',
});
}
await octokit.request('POST /repos/{owner}/{repo}/issues/{issue_number}/comments', {
body: generateText(request.body, ua),
issue_number: existingIssue.number,
owner: environment.github.owner,
repo: environment.github.repo,
issue_number: existingIssue.number,
body: generateText(request.body, ua),
});
reply.send({

View File

@ -1,38 +1,18 @@
import { FastifyInstance, RouteShorthandOptions } from 'fastify';
import fetch from 'node-fetch';
import environment from 'services/environment';
export default (server: FastifyInstance, _options: RouteShorthandOptions, done: () => void) => {
server.get('/data/', async (_request, reply) => {
try {
const databaseUrl =
'https://raw.githubusercontent.com/wanhose/cookie-dialog-monster/main/database.json';
const fetchOptions = {
headers: { 'Cache-Control': 'no-cache' },
};
const { rules, ...result } = await (await fetch(databaseUrl, fetchOptions)).json();
const options = { headers: { 'Cache-Control': 'no-cache' } };
const url = `${environment.github.files}/database.json`;
const { rules, ...result } = await (await fetch(url, options)).json();
reply.send({
data: {
...result,
rules: (rules as readonly string[]).map((urlFilter, index) => ({
id: index + 1,
priority: 1,
action: {
type: 'block',
},
condition: {
resourceTypes: [
'font',
'image',
'media',
'object',
'script',
'stylesheet',
'xmlhttprequest',
],
urlFilter,
},
})),
rules: rules.map(toDeclarativeNetRequestRule),
},
success: true,
});
@ -46,3 +26,17 @@ export default (server: FastifyInstance, _options: RouteShorthandOptions, done:
done();
};
function toDeclarativeNetRequestRule(urlFilter: string, index: number) {
return {
action: {
type: 'block',
},
condition: {
resourceTypes: ['font', 'image', 'media', 'object', 'script', 'stylesheet', 'xmlhttprequest'],
urlFilter,
},
id: index + 1,
priority: 1,
};
}

View File

@ -0,0 +1 @@
export { default as default } from '../v4/data';

View File

@ -0,0 +1,114 @@
import { FastifyInstance, RouteShorthandOptions } from 'fastify';
import environment from 'services/environment';
import { octokit } from 'services/octokit';
import { validatorCompiler } from 'services/validation';
import { UAParser } from 'ua-parser-js';
import * as yup from 'yup';
const PostReportBodySchema = yup.object().shape({
reason: yup.string().min(10).max(1000).required(),
url: yup.string().max(1000).url().required(),
userAgent: yup.string().max(1000).optional(),
version: yup
.string()
.max(10)
.matches(/^\d+(\.\d+){0,3}$/)
.required(),
});
type PostReportBody = yup.InferType<typeof PostReportBodySchema>;
export default (server: FastifyInstance, _options: RouteShorthandOptions, done: () => void) => {
server.post<{ Body: PostReportBody }>(
'/report/',
{
schema: {
body: PostReportBodySchema,
},
validatorCompiler,
},
async (request, reply) => {
const { reason, url, userAgent, version } = request.body;
const ua = new UAParser(userAgent ?? '').getResult();
const hostname = new URL(url).hostname.split('.').slice(-3).join('.').replace('www.', '');
const existingIssues = await octokit.request('GET /search/issues', {
per_page: 50,
q: `in:title+is:issue+repo:${environment.github.owner}/${environment.github.repo}+${hostname}`,
});
const existingIssue = existingIssues.data.items.find((issue) => hostname === issue.title);
try {
if (existingIssue) {
if (existingIssue.labels.some((label) => label.name === 'wontfix')) {
reply.send({
data: existingIssue.html_url,
errors: ['This issue has been marked as "wontfix" and will not be addressed.'],
success: false,
});
return;
}
if (existingIssue.state === 'open') {
reply.send({
data: existingIssue.html_url,
errors: [
'This issue already exists. Please refer to the existing issue for updates.',
],
success: false,
});
return;
}
await octokit.request('PATCH /repos/{owner}/{repo}/issues/{issue_number}', {
issue_number: existingIssue.number,
labels: ['bug'],
owner: environment.github.owner,
repo: environment.github.repo,
state: 'open',
});
reply.send({
data: existingIssue.html_url,
success: true,
});
return;
}
const response = await octokit.request('POST /repos/{owner}/{repo}/issues', {
assignees: [environment.github.owner],
body: [
'## Issue information',
...(ua.browser.name && ua.browser.version
? ['#### 🖥️ Browser', `${ua.browser.name} (${ua.browser.version})`]
: []),
...(ua.device.type && ua.device.vendor
? ['#### 📱 Device', `${ua.device.vendor} (${ua.device.type})`]
: []),
'#### 📝 Reason',
reason,
'#### 🔗 URL',
url,
'#### 🏷️ Version',
version,
].join('\n'),
labels: ['bug'],
owner: environment.github.owner,
repo: environment.github.repo,
title: hostname,
});
reply.send({
data: response.data.html_url,
success: true,
});
} catch (error) {
reply.send({
errors: [error.message],
success: false,
});
}
}
);
done();
};

View File

@ -0,0 +1,27 @@
import { FastifyInstance, RouteShorthandOptions } from 'fastify';
import fetch from 'node-fetch';
import environment from 'services/environment';
export default (server: FastifyInstance, _options: RouteShorthandOptions, done: () => void) => {
server.get('/version/', async (_request, reply) => {
try {
const options = { headers: { 'Cache-Control': 'no-cache' } };
const url = `${environment.github.files}/packages/browser-extension/src/manifest.json`;
const { version } = await (await fetch(url, options)).json();
reply.send({
data: {
version,
},
success: true,
});
} catch (error) {
reply.send({
errors: [error.message],
success: false,
});
}
});
done();
};

View File

@ -1,5 +1,6 @@
export default {
github: {
files: 'https://raw.githubusercontent.com/wanhose/cookie-dialog-monster/main',
owner: 'wanhose',
repo: 'cookie-dialog-monster',
token: process.env.GITHUB_TOKEN ?? '',