From 6676c789619526a7bef3fc415c759f577f582e6e Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Thu, 13 Jul 2023 11:00:56 +0200 Subject: [PATCH] feat: introduce API v3, obsolete older versions --- src/api/v0/index.js | 399 -------------------------------- src/api/v1/index.js | 23 -- src/api/v2/keyoxide_profile.js | 370 ----------------------------- src/api/{v2 => v3}/index.js | 2 +- src/api/v3/keyoxide_profile.js | 185 +++++++++++++++ src/api/{v2 => v3}/proxy_get.js | 23 -- 6 files changed, 186 insertions(+), 816 deletions(-) delete mode 100644 src/api/v0/index.js delete mode 100644 src/api/v1/index.js delete mode 100644 src/api/v2/keyoxide_profile.js rename src/api/{v2 => v3}/index.js (98%) create mode 100644 src/api/v3/keyoxide_profile.js rename src/api/{v2 => v3}/proxy_get.js (92%) diff --git a/src/api/v0/index.js b/src/api/v0/index.js deleted file mode 100644 index 32c7764..0000000 --- a/src/api/v0/index.js +++ /dev/null @@ -1,399 +0,0 @@ -/* -Copyright (C) 2021 Yarmo Mackenbach - -This program is free software: you can redistribute it and/or modify it under -the terms of the GNU Affero General Public License as published by the Free -Software Foundation, either version 3 of the License, or (at your option) -any later version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -details. - -You should have received a copy of the GNU Affero General Public License along -with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - -If your software can interact with users remotely through a computer network, -you should also make sure that it provides a way for users to get its source. -For example, if your program is a web application, its interface could display -a "Source" link that leads users to an archive of the code. There are many -ways you could offer source, and different solutions will be better for different -programs; see section 13 for the specific requirements. - -You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. For -more information on this, and how to apply and follow the GNU AGPL, see . -*/ -import express from 'express' -import { check, validationResult } from 'express-validator' -import Ajv from 'ajv' -import { generateWKDProfile, generateHKPProfile, generateAutoProfile } from '../../server/index.js' -import * as dotenv from 'dotenv' -dotenv.config() - -const router = express.Router() -const ajv = new Ajv({ coerceTypes: true }) - -const apiProfileSchema = { - type: 'object', - properties: { - keyData: { - type: 'object', - properties: { - fingerprint: { - type: 'string' - }, - openpgp4fpr: { - type: 'string' - }, - users: { - type: 'array', - items: { - type: 'object', - properties: { - userData: { - type: 'object', - properties: { - id: { type: 'string' }, - name: { type: 'string' }, - email: { type: 'string' }, - comment: { type: 'string' }, - isPrimary: { type: 'boolean' }, - isRevoked: { type: 'boolean' } - } - }, - claims: { - type: 'array', - items: { - type: 'object', - properties: { - claimVersion: { type: 'integer' }, - uri: { type: 'string' }, - fingerprint: { type: 'string' }, - status: { type: 'string' }, - matches: { - type: 'array', - items: { - type: 'object', - properties: { - serviceProvider: { - type: 'object', - properties: { - type: { type: 'string' }, - name: { type: 'string' } - } - }, - match: { - type: 'object', - properties: { - regularExpression: { type: 'object' }, - isAmbiguous: { type: 'boolean' } - } - }, - profile: { - type: 'object', - properties: { - display: { type: 'string' }, - uri: { type: 'string' }, - qr: { type: 'string' } - } - }, - proof: { - type: 'object', - properties: { - uri: { type: 'string' }, - request: { - type: 'object', - properties: { - fetcher: { type: 'string' }, - access: { type: 'string' }, - format: { type: 'string' }, - data: { type: 'object' } - } - } - } - }, - claim: { - type: 'object', - properties: { - format: { type: 'string' }, - relation: { type: 'string' }, - path: { - type: 'array', - items: { - type: 'string' - } - } - } - } - } - } - }, - verification: { - type: 'object' - }, - summary: { - type: 'object', - properties: { - profileName: { type: 'string' }, - profileURL: { type: 'string' }, - serviceProviderName: { type: 'string' }, - isVerificationDone: { type: 'boolean' }, - isVerified: { type: 'boolean' } - } - } - } - } - } - } - } - }, - primaryUserIndex: { - type: 'integer' - }, - key: { - type: 'object', - properties: { - data: { type: 'object' }, - fetchMethod: { type: 'string' }, - uri: { type: 'string' } - } - } - } - }, - keyoxide: { - type: 'object', - properties: { - url: { type: 'string' } - } - }, - extra: { - type: 'object', - properties: { - avatarURL: { type: 'string' } - } - }, - errors: { - type: 'array' - } - }, - required: ['keyData', 'keyoxide', 'extra', 'errors'], - additionalProperties: false -} - -const apiProfileValidate = ajv.compile(apiProfileSchema) - -const doVerification = async (data) => { - const promises = [] - const results = [] - const verificationOptions = { - proxy: { - hostname: process.env.PROXY_HOSTNAME, - policy: (process.env.PROXY_HOSTNAME !== '') ? 'adaptive' : 'never' - } - } - - // Return early if no users in key - if (!data.keyData.users) { - return data - } - - for (let iUser = 0; iUser < data.keyData.users.length; iUser++) { - const user = data.keyData.users[iUser] - - for (let iClaim = 0; iClaim < user.claims.length; iClaim++) { - const claim = user.claims[iClaim] - - promises.push( - new Promise((resolve, reject) => { - (async () => { - await claim.verify(verificationOptions) - results.push([iUser, iClaim, claim]) - resolve() - })() - }) - ) - } - } - await Promise.all(promises) - - results.forEach(result => { - data.keyData.users[result[0]].claims[result[1]] = result[2] - }) - - return data -} - -const sanitize = (data) => { - const dataClone = JSON.parse(JSON.stringify(data)) - - if (dataClone.keyData.users) { - for (let iUser = 0; iUser < dataClone.keyData.users.length; iUser++) { - const user = dataClone.keyData.users[iUser] - - for (let iClaim = 0; iClaim < user.claims.length; iClaim++) { - const claim = user.claims[iClaim] - - // TODO Fix upstream - for (let iMatch = 0; iMatch < claim.matches.length; iMatch++) { - const match = claim.matches[iMatch] - if (Array.isArray(match.claim)) { - match.claim = match.claim[0] - } - } - // TODO Fix upstream - if (!claim.verification) { - claim.verification = {} - } - // TODO Fix upstream - claim.matches.forEach(match => { - match.proof.request.access = ['generic', 'nocors', 'granted', 'server'][match.proof.request.access] - match.claim.format = ['uri', 'fingerprint', 'message'][match.claim.format] - match.claim.relation = ['contains', 'equals', 'oneof'][match.claim.relation] - }) - - data.keyData.users[iUser].claims[iClaim] = claim - } - } - } - - const valid = apiProfileValidate(data) - if (!valid) { - throw new Error('Profile data sanitization error') - } - - return data -} - -const addSummaryToClaims = (data) => { - // Return early if no users in key - if (!data.keyData.users) { - return data - } - - // To be removed when data is added by DOIP library - for (let userIndex = 0; userIndex < data.keyData.users.length; userIndex++) { - const user = data.keyData.users[userIndex] - - for (let claimIndex = 0; claimIndex < user.claims.length; claimIndex++) { - const claim = user.claims[claimIndex] - - const isVerificationDone = claim.status === 'verified' - const isVerified = isVerificationDone ? claim.verification.result : false - const isAmbiguous = isVerified - ? false - : claim.matches.length > 1 || claim.matches[0].match.isAmbiguous - - data.keyData.users[userIndex].claims[claimIndex].summary = { - profileName: !isAmbiguous ? claim.matches[0].profile.display : claim.uri, - profileURL: !isAmbiguous ? claim.matches[0].profile.uri : '', - serviceProviderName: !isAmbiguous ? claim.matches[0].serviceprovider.name : '', - isVerificationDone, - isVerified - } - } - } - - return data -} - -router.get('/profile/fetch', - check('query').exists(), - check('protocol').optional().toLowerCase().isIn(['hkp', 'wkd']), - check('doVerification').default(false).isBoolean().toBoolean(), - check('returnPublicKey').default(false).isBoolean().toBoolean(), - async (req, res) => { - const valRes = validationResult(req) - if (!valRes.isEmpty()) { - res.status(400).send(valRes) - return - } - - // Generate profile - let data - switch (req.query.protocol) { - case 'wkd': - data = await generateWKDProfile(req.query.query) - break - case 'hkp': - data = await generateHKPProfile(req.query.query) - break - default: - data = await generateAutoProfile(req.query.query) - break - } - - if (data.errors.length > 0) { - data.key = undefined - res.status(500).send(data) - } - - // Return public key - if (req.query.returnPublicKey) { - data.keyData.key.data = data.key.publicKey - } - data.key = undefined - - // Do verification - if (req.query.doVerification) { - data = await doVerification(data) - } - - try { - // Sanitize JSON - data = sanitize(data) - } catch (error) { - data.keyData = {} - data.extra = {} - data.errors = [error.message] - } - - // Add missing data - data = addSummaryToClaims(data) - - let statusCode = 200 - if (data.errors.length > 0) { - statusCode = 500 - } - - res.status(statusCode).send(data) - } -) - -router.get('/profile/verify', - check('data').exists().isJSON(), - async (req, res) => { - const valRes = validationResult(req) - if (!valRes.isEmpty()) { - res.status(400).send(valRes) - return - } - - // Do verification - let data = await doVerification(req.query.data) - - try { - // Sanitize JSON - data = sanitize(data) - } catch (error) { - data.keyData = {} - data.extra = {} - data.errors = [error.message] - } - - // Add missing data - data = addSummaryToClaims(data) - - let statusCode = 200 - if (data.errors.length > 0) { - statusCode = 500 - } - - res.status(statusCode).send(data) - } -) - -export default router diff --git a/src/api/v1/index.js b/src/api/v1/index.js deleted file mode 100644 index c61d247..0000000 --- a/src/api/v1/index.js +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2022 Yarmo Mackenbach - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -import express from 'express' -const router = express.Router() - -router.get('*', (req, res) => { - return res.status(501).send('Proxy v1 API endpoint is no longer supported, please migrate to proxy v2 API endpoint') -}) - -export default router diff --git a/src/api/v2/keyoxide_profile.js b/src/api/v2/keyoxide_profile.js deleted file mode 100644 index f13a7c2..0000000 --- a/src/api/v2/keyoxide_profile.js +++ /dev/null @@ -1,370 +0,0 @@ -/* -Copyright (C) 2022 Yarmo Mackenbach - -This program is free software: you can redistribute it and/or modify it under -the terms of the GNU Affero General Public License as published by the Free -Software Foundation, either version 3 of the License, or (at your option) -any later version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -details. - -You should have received a copy of the GNU Affero General Public License along -with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - -If your software can interact with users remotely through a computer network, -you should also make sure that it provides a way for users to get its source. -For example, if your program is a web application, its interface could display -a "Source" link that leads users to an archive of the code. There are many -ways you could offer source, and different solutions will be better for different -programs; see section 13 for the specific requirements. - -You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. For -more information on this, and how to apply and follow the GNU AGPL, see . -*/ -import express from 'express' -import { check, validationResult } from 'express-validator' -import Ajv from 'ajv' -import { generateWKDProfile, generateHKPProfile, generateAutoProfile } from '../../server/index.js' -import * as dotenv from 'dotenv' -dotenv.config() - -const router = express.Router() -const ajv = new Ajv({ coerceTypes: true }) - -const apiProfileSchema = { - type: 'object', - properties: { - keyData: { - type: 'object', - properties: { - fingerprint: { - type: 'string' - }, - openpgp4fpr: { - type: 'string' - }, - users: { - type: 'array', - items: { - type: 'object', - properties: { - userData: { - type: 'object', - properties: { - id: { type: 'string' }, - name: { type: 'string' }, - email: { type: 'string' }, - comment: { type: 'string' }, - isPrimary: { type: 'boolean' }, - isRevoked: { type: 'boolean' } - } - }, - claims: { - type: 'array', - items: { - type: 'object', - properties: { - claimVersion: { type: 'integer' }, - uri: { type: 'string' }, - fingerprint: { type: 'string' }, - status: { type: 'string' }, - matches: { - type: 'array', - items: { - type: 'object', - properties: { - serviceProvider: { - type: 'object', - properties: { - type: { type: 'string' }, - name: { type: 'string' } - } - }, - match: { - type: 'object', - properties: { - regularExpression: { type: 'object' }, - isAmbiguous: { type: 'boolean' } - } - }, - profile: { - type: 'object', - properties: { - display: { type: 'string' }, - uri: { type: 'string' }, - qr: { type: 'string' } - } - }, - proof: { - type: 'object', - properties: { - uri: { type: 'string' }, - request: { - type: 'object', - properties: { - fetcher: { type: 'string' }, - access: { type: 'string' }, - format: { type: 'string' }, - data: { type: 'object' } - } - } - } - }, - claim: { - type: 'array', - items: { - type: 'object', - properties: { - format: { type: 'string' }, - relation: { type: 'string' }, - path: { - type: 'array', - items: { - type: 'string' - } - } - } - } - } - } - } - }, - verification: { - type: 'object' - }, - summary: { - type: 'object', - properties: { - profileName: { type: 'string' }, - profileURL: { type: 'string' }, - serviceProviderName: { type: 'string' }, - isVerificationDone: { type: 'boolean' }, - isVerified: { type: 'boolean' } - } - } - } - } - } - } - } - }, - primaryUserIndex: { - type: 'integer' - }, - key: { - type: 'object', - properties: { - data: { type: 'object' }, - fetchMethod: { type: 'string' }, - uri: { type: 'string' } - } - } - } - }, - keyoxide: { - type: 'object', - properties: { - url: { type: 'string' } - } - }, - extra: { - type: 'object', - properties: { - avatarURL: { type: 'string' } - } - }, - errors: { - type: 'array' - } - }, - required: ['keyData', 'keyoxide', 'extra', 'errors'], - additionalProperties: false -} - -const apiProfileValidate = ajv.compile(apiProfileSchema) - -const doVerification = async (data) => { - const promises = [] - const results = [] - const verificationOptions = { - proxy: { - hostname: process.env.PROXY_HOSTNAME, - policy: (process.env.PROXY_HOSTNAME !== '') ? 'adaptive' : 'never' - } - } - - // Return early if no users in key - if (!data.keyData.users) { - return data - } - - for (let iUser = 0; iUser < data.keyData.users.length; iUser++) { - const user = data.keyData.users[iUser] - - for (let iClaim = 0; iClaim < user.claims.length; iClaim++) { - const claim = user.claims[iClaim] - - promises.push( - new Promise((resolve, reject) => { - (async () => { - await claim.verify(verificationOptions) - results.push([iUser, iClaim, claim]) - resolve() - })() - }) - ) - } - } - await Promise.all(promises) - - results.forEach(result => { - data.keyData.users[result[0]].claims[result[1]] = result[2] - }) - - return data -} - -const sanitize = (data) => { - const valid = apiProfileValidate(data) - if (!valid) { - throw new Error('Profile data sanitization error') - } - - return data -} - -const addSummaryToClaims = (data) => { - // Return early if no users in key - if (!data.keyData.users) { - return data - } - - // To be removed when data is added by DOIP library - for (let userIndex = 0; userIndex < data.keyData.users.length; userIndex++) { - const user = data.keyData.users[userIndex] - - for (let claimIndex = 0; claimIndex < user.claims.length; claimIndex++) { - const claim = user.claims[claimIndex] - - const isVerificationDone = claim.status === 'verified' - const isVerified = isVerificationDone ? claim.verification.result : false - const isAmbiguous = isVerified - ? false - : claim.matches.length > 1 || claim.matches[0].match.isAmbiguous - - data.keyData.users[userIndex].claims[claimIndex].summary = { - profileName: !isAmbiguous ? claim.matches[0].profile.display : claim.uri, - profileURL: !isAmbiguous ? claim.matches[0].profile.uri : '', - serviceProviderName: !isAmbiguous ? claim.matches[0].serviceprovider.name : '', - isVerificationDone, - isVerified - } - } - } - - return data -} - -router.get('/fetch', - check('query').exists(), - check('protocol').optional().toLowerCase().isIn(['hkp', 'wkd']), - check('doVerification').default(false).isBoolean().toBoolean(), - check('returnPublicKey').default(false).isBoolean().toBoolean(), - async (req, res) => { - const valRes = validationResult(req) - if (!valRes.isEmpty()) { - res.status(400).send(valRes) - return - } - - // Generate profile - let data - switch (req.query.protocol) { - case 'wkd': - data = await generateWKDProfile(req.query.query) - break - case 'hkp': - data = await generateHKPProfile(req.query.query) - break - default: - data = await generateAutoProfile(req.query.query) - break - } - - if (data.errors.length > 0) { - data.key = undefined - res.status(500).send(data) - } - - // Return public key - if (req.query.returnPublicKey) { - data.keyData.key.data = data.key.publicKey - } - data.key = undefined - - // Do verification - if (req.query.doVerification) { - data = await doVerification(data) - } - - try { - // Sanitize JSON - data = sanitize(data) - } catch (error) { - data.keyData = {} - data.extra = {} - data.errors = [error.message] - } - - // Add missing data - data = addSummaryToClaims(data) - - let statusCode = 200 - if (data.errors.length > 0) { - statusCode = 500 - } - - res.status(statusCode).send(data) - } -) - -router.get('/verify', - check('data').exists().isJSON(), - async (req, res) => { - const valRes = validationResult(req) - if (!valRes.isEmpty()) { - res.status(400).send(valRes) - return - } - - // Do verification - let data = await doVerification(req.query.data) - - try { - // Sanitize JSON - data = sanitize(data) - } catch (error) { - data.keyData = {} - data.extra = {} - data.errors = [error.message] - } - - // Add missing data - data = addSummaryToClaims(data) - - let statusCode = 200 - if (data.errors.length > 0) { - statusCode = 500 - } - - res.status(statusCode).send(data) - } -) - -export default router diff --git a/src/api/v2/index.js b/src/api/v3/index.js similarity index 98% rename from src/api/v2/index.js rename to src/api/v3/index.js index 1b17e75..80a0d28 100644 --- a/src/api/v2/index.js +++ b/src/api/v3/index.js @@ -1,5 +1,5 @@ /* -Copyright (C) 2022 Yarmo Mackenbach +Copyright (C) 2023 Yarmo Mackenbach This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free diff --git a/src/api/v3/keyoxide_profile.js b/src/api/v3/keyoxide_profile.js new file mode 100644 index 0000000..b1ba960 --- /dev/null +++ b/src/api/v3/keyoxide_profile.js @@ -0,0 +1,185 @@ +/* +Copyright (C) 2023 Yarmo Mackenbach + +This program is free software: you can redistribute it and/or modify it under +the terms of the GNU Affero General Public License as published by the Free +Software Foundation, either version 3 of the License, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +details. + +You should have received a copy of the GNU Affero General Public License along +with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If your software can interact with users remotely through a computer network, +you should also make sure that it provides a way for users to get its source. +For example, if your program is a web application, its interface could display +a "Source" link that leads users to an archive of the code. There are many +ways you could offer source, and different solutions will be better for different +programs; see section 13 for the specific requirements. + +You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. For +more information on this, and how to apply and follow the GNU AGPL, see . +*/ +import express from 'express' +import { check, validationResult } from 'express-validator' +import Ajv from 'ajv/dist/2020.js' +import * as dotenv from 'dotenv' +import { Claim } from 'doipjs' +import { generateAspeProfile, generateWKDProfile, generateHKPProfile, generateAutoProfile } from '../../server/index.js' +import { claimSchema, personaSchema, profileSchema, serviceProviderSchema } from '../../schemas.js' +dotenv.config() + +const router = express.Router() +const ajv = new Ajv({ + schemas: [profileSchema, personaSchema, claimSchema, serviceProviderSchema] +}) + +const apiProfileValidate = ajv.compile(profileSchema) + +const doVerification = async (profile) => { + const promises = [] + const results = [] + const verificationOptions = { + proxy: { + hostname: process.env.PROXY_HOSTNAME, + policy: (process.env.PROXY_HOSTNAME !== '') ? 'adaptive' : 'never' + } + } + + // Return early if no users in key + if (!profile.personas) { + return profile + } + + for (let iUser = 0; iUser < profile.personas.length; iUser++) { + const user = profile.personas[iUser] + + for (let iClaim = 0; iClaim < user.claims.length; iClaim++) { + const claim = user.claims[iClaim] + + promises.push( + new Promise((resolve, reject) => { + (async () => { + await claim.verify(verificationOptions) + results.push([iUser, iClaim, claim]) + resolve() + })() + }) + ) + } + } + await Promise.all(promises) + + results.forEach(result => { + profile.personas[result[0]].claims[result[1]] = result[2] + }) + + return profile +} + +const validate = (profile) => { + const valid = apiProfileValidate(profile) + if (!valid) { + throw new Error(`Profile data validation error: ${apiProfileValidate.errors.map(x => x.message).join(', ')}`) + } +} + +router.get('/fetch', + check('query').exists(), + check('protocol').optional().toLowerCase().isIn(['aspe', 'hkp', 'wkd']), + check('doVerification').default(false).isBoolean().toBoolean(), + async (req, res) => { + const valRes = validationResult(req) + if (!valRes.isEmpty()) { + res.status(400).send(valRes) + return + } + + // Generate profile + let data + switch (req.query.protocol) { + case 'aspe': + data = await generateAspeProfile(req.query.query) + break + case 'wkd': + data = await generateWKDProfile(req.query.query) + break + case 'hkp': + data = await generateHKPProfile(req.query.query) + break + default: + data = await generateAutoProfile(req.query.query) + break + } + + if ('errors' in data && data.errors.length > 0) { + res.status(500).send(data) + } + + // Do verification + if (req.query.doVerification) { + data = await doVerification(data) + } + + data = data.toJSON() + + try { + // Validate JSON + validate(data) + } catch (error) { + data = { + errors: [error.message] + } + } + + let statusCode = 200 + if ('errors' in data && data.errors.length > 0) { + statusCode = 500 + } + + res.status(statusCode).send(data) + } +) + +router.get('/verify', + check('data').exists().isJSON(), + async (req, res) => { + const valRes = validationResult(req) + if (!valRes.isEmpty()) { + res.status(400).send(valRes) + return + } + + let profile = Claim.fromJson(req.query.data) + + // Do verification + let data = await doVerification(profile) + + data = data.toJSON() + + try { + // Validate JSON + validate(data) + } catch (error) { + data = { + errors: [error.message] + } + } + + let statusCode = 200 + if ('errors' in data) { + statusCode = 500 + } + + res.status(statusCode).send(data) + } +) + +export default router diff --git a/src/api/v2/proxy_get.js b/src/api/v3/proxy_get.js similarity index 92% rename from src/api/v2/proxy_get.js rename to src/api/v3/proxy_get.js index 5cacfa6..a766735 100644 --- a/src/api/v2/proxy_get.js +++ b/src/api/v3/proxy_get.js @@ -51,9 +51,6 @@ const opts = { telegram: { token: process.env.TELEGRAM_TOKEN || null }, - twitter: { - bearerToken: process.env.TWITTER_BEARER_TOKEN || null - }, xmpp: { service: process.env.XMPP_SERVICE || null, username: process.env.XMPP_USERNAME || null, @@ -138,26 +135,6 @@ router.get( } ) -// Twitter route -router.get('/twitter', query('tweetId').isInt(), async (req, res) => { - if (!opts.claims.twitter.bearerToken) { - return res.status(501).json({ errors: 'Twitter not enabled on server' }) - } - const errors = validationResult(req) - if (!errors.isEmpty()) { - return res.status(400).json({ errors: errors.array() }) - } - - fetcher.twitter - .fn(req.query, opts) - .then((data) => { - return res.status(200).send(data) - }) - .catch((err) => { - return res.status(400).json({ errors: err.message ? err.message : err }) - }) -}) - // Matrix route router.get( '/matrix',