From f6d6520fb8ce069dafc6dee6ff758f86f788ed00 Mon Sep 17 00:00:00 2001 From: joostdecock Date: Tue, 30 May 2023 16:47:22 +0200 Subject: [PATCH] feat(backend): Add support for (creating) issues --- sites/backend/src/config.mjs | 4 +- sites/backend/src/controllers/issues.mjs | 17 ++++++ sites/backend/src/models/issue.mjs | 74 ++++++++++++++++++++++++ sites/backend/src/models/pattern.mjs | 4 ++ sites/backend/src/routes/index.mjs | 2 + sites/backend/src/routes/issues.mjs | 10 ++++ 6 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 sites/backend/src/controllers/issues.mjs create mode 100644 sites/backend/src/models/issue.mjs create mode 100644 sites/backend/src/routes/issues.mjs diff --git a/sites/backend/src/config.mjs b/sites/backend/src/config.mjs index 5b77d55921d..a9c2d999c88 100644 --- a/sites/backend/src/config.mjs +++ b/sites/backend/src/config.mjs @@ -62,6 +62,9 @@ const baseConfig = { encryption: { key: encryptionKey, }, + github: { + token: process.env.BACKEND_GITHUB_TOKEN, + }, jwt: { secretOrKey: encryptionKey, issuer: process.env.BACKEND_JWT_ISSUER || 'freesewing.org', @@ -84,7 +87,6 @@ const baseConfig = { scheme: process.env.BACKEND_WEBSITE_SCHEME || 'https', }, oauth: {}, - github: {}, } /* diff --git a/sites/backend/src/controllers/issues.mjs b/sites/backend/src/controllers/issues.mjs new file mode 100644 index 00000000000..25dfb4dd8ab --- /dev/null +++ b/sites/backend/src/controllers/issues.mjs @@ -0,0 +1,17 @@ +import { IssueModel } from '../models/issue.mjs' +import { UserModel } from '../models/user.mjs' + +export function IssuesController() {} + +/* + * Create Issue + * + * This is the endpoint that handles creation of Github issues + * See: https://freesewing.dev/reference/backend/api/apikey + */ +IssuesController.prototype.create = async (req, res, tools) => { + const Issue = new IssueModel(tools) + await Issue.create(req) + + return Issue.sendResponse(res) +} diff --git a/sites/backend/src/models/issue.mjs b/sites/backend/src/models/issue.mjs new file mode 100644 index 00000000000..dbe669a9aff --- /dev/null +++ b/sites/backend/src/models/issue.mjs @@ -0,0 +1,74 @@ +import fetch from 'node-fetch' +import { log } from '../utils/log.mjs' +import { UserModel } from './user.mjs' + +export function IssueModel(tools) { + this.config = tools.config + this.prisma = tools.prisma + this.User = new UserModel(tools) + this.token = tools.config.github.token + + return this +} + +IssueModel.prototype.setResponse = function (status = 200, error = false, data = {}) { + this.response = { + status, + body: { + result: 'success', + ...data, + }, + } + if (status === 201) this.response.body.result = 'created' + else if (status > 204) { + this.response.body.error = error + this.response.body.result = 'error' + this.error = true + } else this.error = false + + return this +} + +IssueModel.prototype.sendResponse = async function (res) { + return res.status(this.response.status).send(this.response.body) +} + +IssueModel.prototype.unguardedDelete = async function () { + await this.prisma.apikey.delete({ where: { id: this.record.id } }) + this.record = null + this.clear = null + + return this.setExists() +} + +IssueModel.prototype.create = async function ({ body }) { + if (!this.token) return this.setResponse(400, 'notEnabled') + if (Object.keys(body).length < 1) return this.setResponse(400, 'postBodyMissing') + if (!body.title) return this.setResponse(400, 'titleMissing') + if (!body.body) return this.setResponse(400, 'bodyMissing') + + const apiUrl = `https://api.github.com/repos/freesewing/freesewing/issues` + let response + try { + response = await fetch(apiUrl, { + method: 'POST', + headers: { + Authorization: `Bearer ${this.token}`, + Accept: 'application/vnd.github.v3+json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }) + + if (response.status === 201) response = await response.json() + else { + console.log(response) + response = false + } + } catch (error) { + console.error('An error occurred while creating a GitHub issue:', error.message) + response = false + } + + return response ? this.setResponse(201, 'created', { issue: response }) : this.setResponse(400) +} diff --git a/sites/backend/src/models/pattern.mjs b/sites/backend/src/models/pattern.mjs index 81a6a360557..a654abc3b4b 100644 --- a/sites/backend/src/models/pattern.mjs +++ b/sites/backend/src/models/pattern.mjs @@ -145,8 +145,12 @@ PatternModel.prototype.publicRead = async function ({ params }) { PatternModel.prototype.guardedRead = async function ({ params, user }) { if (!this.rbac.readSome(user)) return this.setResponse(403, 'insufficientAccessLevel') if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking') + if (typeof params.id !== 'undefined' && !Number(params.id)) + return this.setResponse(403, 'idNotNumeric') await this.read({ id: parseInt(params.id) }) + if (!this.record) return this.setResponse(404, 'notFound') + if (this.record.userId !== user.uid && !this.rbac.bughunter(user)) { return this.setResponse(403, 'insufficientAccessLevel') } diff --git a/sites/backend/src/routes/index.mjs b/sites/backend/src/routes/index.mjs index 8f6a5aa9eae..60851db0e10 100644 --- a/sites/backend/src/routes/index.mjs +++ b/sites/backend/src/routes/index.mjs @@ -4,6 +4,7 @@ import { setsRoutes } from './sets.mjs' import { patternsRoutes } from './patterns.mjs' import { confirmationsRoutes } from './confirmations.mjs' import { curatedSetsRoutes } from './curated-sets.mjs' +import { issuesRoutes } from './issues.mjs' export const routes = { apikeysRoutes, @@ -12,4 +13,5 @@ export const routes = { patternsRoutes, confirmationsRoutes, curatedSetsRoutes, + issuesRoutes, } diff --git a/sites/backend/src/routes/issues.mjs b/sites/backend/src/routes/issues.mjs new file mode 100644 index 00000000000..2d48dc7758d --- /dev/null +++ b/sites/backend/src/routes/issues.mjs @@ -0,0 +1,10 @@ +import { IssuesController } from '../controllers/issues.mjs' + +const Issues = new IssuesController() + +export function issuesRoutes(tools) { + const { app } = tools + + // Create Issue - No auth needed + app.post('/issues', (req, res) => Issues.create(req, res, tools)) +}