mirror of
https://codeberg.org/keyoxide/keyoxide-web.git
synced 2024-12-22 14:59:29 -07:00
Upgrade API endpoints, add proxy API
This commit is contained in:
parent
d6b9d5bec9
commit
50b3254c7d
5 changed files with 745 additions and 0 deletions
23
api/v1/index.js
Normal file
23
api/v1/index.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
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
|
39
api/v2/index.js
Normal file
39
api/v2/index.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import express from 'express'
|
||||
import keyoxideProfileApiRouter from './keyoxide_profile.js'
|
||||
import proxyGetApiRouter from './proxy_get.js'
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
router.use('/profile', keyoxideProfileApiRouter)
|
||||
router.use('/get', proxyGetApiRouter)
|
||||
|
||||
export default router
|
390
api/v2/keyoxide_profile.js
Normal file
390
api/v2/keyoxide_profile.js
Normal file
|
@ -0,0 +1,390 @@
|
|||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import express from 'express'
|
||||
import { check, validationResult } from 'express-validator'
|
||||
import Ajv from 'ajv'
|
||||
import { generateWKDProfile, generateHKPProfile } from '../../server/index.js'
|
||||
import 'dotenv/config.js'
|
||||
|
||||
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) => {
|
||||
let promises = []
|
||||
let results = []
|
||||
let verificationOptions = {
|
||||
proxy: {
|
||||
hostname: process.env.PROXY_HOSTNAME,
|
||||
policy: (process.env.PROXY_HOSTNAME != "") ? 'adaptive' : 'never'
|
||||
}
|
||||
}
|
||||
|
||||
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(async (resolve, reject) => {
|
||||
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) => {
|
||||
let results = []
|
||||
|
||||
const dataClone = JSON.parse(JSON.stringify(data))
|
||||
|
||||
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) => {
|
||||
// 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: isVerificationDone,
|
||||
isVerified: 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:
|
||||
if (req.query.query.includes('@')) {
|
||||
data = await generateWKDProfile(req.query.query)
|
||||
} else {
|
||||
data = await generateHKPProfile(req.query.query)
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (data.errors.length > 0) {
|
||||
delete data.key
|
||||
res.status(500).send(data)
|
||||
}
|
||||
|
||||
// Return public key
|
||||
if (req.query.returnPublicKey) {
|
||||
data.keyData.key.data = data.key.publicKey
|
||||
}
|
||||
delete data.key
|
||||
|
||||
// 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
|
||||
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
|
289
api/v2/proxy_get.js
Normal file
289
api/v2/proxy_get.js
Normal file
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import express from 'express'
|
||||
import { query, validationResult } from 'express-validator'
|
||||
import { fetcher, enums as E } from 'doipjs'
|
||||
import 'dotenv/config.js'
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
const opts = {
|
||||
claims: {
|
||||
activitypub: {
|
||||
url: process.env.ACTIVITYPUB_URL || null,
|
||||
privateKey: process.env.ACTIVITYPUB_PRIVATE_KEY || null
|
||||
},
|
||||
irc: {
|
||||
nick: process.env.IRC_NICK || null
|
||||
},
|
||||
matrix: {
|
||||
instance: process.env.MATRIX_INSTANCE || null,
|
||||
accessToken: process.env.MATRIX_ACCESS_TOKEN || null
|
||||
},
|
||||
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,
|
||||
password: process.env.XMPP_PASSWORD || null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Root route
|
||||
router.get('/', async (req, res) => {
|
||||
return res.status(400).json({ errors: 'Invalid endpoint' })
|
||||
})
|
||||
|
||||
// HTTP route
|
||||
router.get(
|
||||
'/http',
|
||||
query('url').isURL(),
|
||||
query('format').isIn([E.ProofFormat.JSON, E.ProofFormat.TEXT]),
|
||||
(req, res) => {
|
||||
const errors = validationResult(req)
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({ errors: errors.array() })
|
||||
}
|
||||
|
||||
fetcher.http
|
||||
.fn(req.query, opts)
|
||||
.then((result) => {
|
||||
switch (req.query.format) {
|
||||
case E.ProofFormat.JSON:
|
||||
return res.status(200).json(result)
|
||||
|
||||
case E.ProofFormat.TEXT:
|
||||
return res.status(200).send(result)
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
return res.status(400).json({ errors: err.message ? err.message : err })
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
// DNS route
|
||||
router.get('/dns', query('domain').isFQDN(), (req, res) => {
|
||||
const errors = validationResult(req)
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({ errors: errors.array() })
|
||||
}
|
||||
|
||||
fetcher.dns
|
||||
.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 })
|
||||
})
|
||||
})
|
||||
|
||||
// XMPP route
|
||||
router.get(
|
||||
'/xmpp',
|
||||
query('id').isEmail(),
|
||||
query('field').isIn([
|
||||
'fn',
|
||||
'number',
|
||||
'userid',
|
||||
'url',
|
||||
'bday',
|
||||
'nickname',
|
||||
'note',
|
||||
'desc'
|
||||
]),
|
||||
async (req, res) => {
|
||||
if (
|
||||
!opts.claims.xmpp.service ||
|
||||
!opts.claims.xmpp.username ||
|
||||
!opts.claims.xmpp.password
|
||||
) {
|
||||
return res.status(501).json({ errors: 'XMPP not enabled on server' })
|
||||
}
|
||||
const errors = validationResult(req)
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({ errors: errors.array() })
|
||||
}
|
||||
|
||||
fetcher.xmpp
|
||||
.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 })
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
// 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',
|
||||
query('roomId').isString(),
|
||||
query('eventId').isString(),
|
||||
async (req, res) => {
|
||||
if (!opts.claims.matrix.instance || !opts.claims.matrix.accessToken) {
|
||||
return res.status(501).json({ errors: 'Matrix not enabled on server' })
|
||||
}
|
||||
const errors = validationResult(req)
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({ errors: errors.array() })
|
||||
}
|
||||
|
||||
fetcher.matrix
|
||||
.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 })
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
// Telegram route
|
||||
router.get(
|
||||
'/telegram',
|
||||
query('user').isString(),
|
||||
query('chat').isString(),
|
||||
async (req, res) => {
|
||||
if (!opts.claims.telegram.token) {
|
||||
return res.status(501).json({ errors: 'Telegram not enabled on server' })
|
||||
}
|
||||
|
||||
const errors = validationResult(req)
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({ errors: errors.array() })
|
||||
}
|
||||
|
||||
fetcher.telegram
|
||||
.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 })
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
// IRC route
|
||||
router.get('/irc', query('nick').isString(), async (req, res) => {
|
||||
if (!opts.claims.irc.nick) {
|
||||
return res.status(501).json({ errors: 'IRC not enabled on server' })
|
||||
}
|
||||
const errors = validationResult(req)
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({ errors: errors.array() })
|
||||
}
|
||||
|
||||
fetcher.irc
|
||||
.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 })
|
||||
})
|
||||
})
|
||||
|
||||
// Gitlab route
|
||||
router.get(
|
||||
'/gitlab',
|
||||
query('domain').isFQDN(),
|
||||
query('username').isString(),
|
||||
async (req, res) => {
|
||||
const errors = validationResult(req)
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({ errors: errors.array() })
|
||||
}
|
||||
|
||||
fetcher.http
|
||||
.fn({
|
||||
url: `https://${req.query.domain}/api/v4/projects/${req.query.username}%2Fgitlab_proof`,
|
||||
format: 'json'
|
||||
}, opts)
|
||||
.then((data) => {
|
||||
return res.status(200).send(data)
|
||||
})
|
||||
.catch((err) => {
|
||||
return res.status(400).json({ errors: err.message ? err.message : err })
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
// ActivityPub route
|
||||
router.get(
|
||||
'/activitypub',
|
||||
query('url').isURL(),
|
||||
async (req, res) => {
|
||||
const errors = validationResult(req)
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({ errors: errors.array() })
|
||||
}
|
||||
|
||||
fetcher.activitypub
|
||||
.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 })
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
export default router
|
|
@ -29,9 +29,13 @@ more information on this, and how to apply and follow the GNU AGPL, see <https:/
|
|||
*/
|
||||
import express from 'express'
|
||||
import apiRouter0 from '../api/v0/index.js'
|
||||
import apiRouter1 from '../api/v1/index.js'
|
||||
import apiRouter2 from '../api/v2/index.js'
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
router.use('/0', apiRouter0)
|
||||
router.use('/1', apiRouter1)
|
||||
router.use('/2', apiRouter2)
|
||||
|
||||
export default router
|
||||
|
|
Loading…
Reference in a new issue