1
0
mirror of https://github.com/spacebarchat/server.git synced 2024-09-19 17:21:35 +02:00

Merge pull request #1192 from DEVTomatoCake/feat/improve-schema-openapi-generation

This commit is contained in:
Madeline 2024-08-22 09:49:21 +10:00 committed by GitHub
commit 4f19ee19bb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 323 additions and 69856 deletions

View File

@ -2,7 +2,7 @@
"openapi": "3.1.0",
"info": {
"title": "Spacebar Server",
"description": "Spacebar is a free open source selfhostable discord compatible chat, voice and video platform",
"description": "Spacebar is a Discord.com server implementation and extension, with the goal of complete feature parity with Discord.com, all while adding some additional goodies, security, privacy, and configuration options.",
"license": {
"name": "AGPLV3",
"url": "https://www.gnu.org/licenses/agpl-3.0.en.html"
@ -61,110 +61,209 @@
"read_states"
]
},
"DiagnosticsChannel.Response": {
"ConnectedAccountCommonOAuthTokenResponse": {
"type": "object",
"properties": {
"statusCode": {
"type": "integer"
},
"statusText": {
"access_token": {
"type": "string"
},
"headers": {
"token_type": {
"type": "string"
},
"scope": {
"type": "string"
},
"refresh_token": {
"type": "string"
},
"expires_in": {
"type": "integer"
}
},
"required": [
"access_token",
"scope",
"token_type"
]
},
"ApplicationAuthorizeSchema": {
"type": "object",
"properties": {
"authorize": {
"type": "boolean"
},
"guild_id": {
"type": "string"
},
"permissions": {
"type": "string"
},
"captcha_key": {
"type": "string"
},
"code": {
"type": "string"
}
},
"required": [
"authorize",
"guild_id",
"permissions"
]
},
"ApplicationCreateSchema": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"team_id": {
"type": [
"string",
"integer"
]
}
},
"required": [
"name"
]
},
"ApplicationModifySchema": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"icon": {
"type": "string"
},
"interactions_endpoint_url": {
"type": "string"
},
"max_participants": {
"type": "integer",
"nullable": true
},
"name": {
"type": "string"
},
"privacy_policy_url": {
"type": "string"
},
"role_connections_verification_url": {
"type": "string"
},
"tags": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[0-9]+$": {
"type": "string"
}
},
"terms_of_service_url": {
"type": "string"
},
"bot_public": {
"type": "boolean"
},
"bot_require_code_grant": {
"type": "boolean"
},
"flags": {
"type": "integer"
}
}
}
}
},
"required": [
"headers",
"statusCode",
"statusText"
]
},
"Headers": {
"BackupCodesChallengeSchema": {
"type": "object",
"properties": {
"append": {
"type": "object",
"additionalProperties": false
},
"delete": {
"type": "object",
"additionalProperties": false
},
"get": {
"type": "object",
"additionalProperties": false
},
"has": {
"type": "object",
"additionalProperties": false
},
"set": {
"type": "object",
"additionalProperties": false
},
"getSetCookie": {
"type": "object",
"additionalProperties": false
},
"forEach": {
"description": "Performs the specified action for each element in an array.",
"type": "object",
"additionalProperties": false
},
"keys": {
"description": "Returns an array consisting of the keys of the object",
"type": "object",
"additionalProperties": false
},
"values": {
"type": "object",
"additionalProperties": false
},
"entries": {
"description": "Returns an array consisting of the key value pairs of the object",
"type": "object",
"additionalProperties": false
},
"__@iterator": {
"type": "object",
"additionalProperties": false
"password": {
"type": "string"
}
},
"required": [
"__@iterator",
"append",
"delete",
"entries",
"forEach",
"get",
"getSetCookie",
"has",
"keys",
"set",
"values"
"password"
]
},
"ResponseType": {
"enum": [
"basic",
"cors",
"default",
"error",
"opaque",
"opaqueredirect"
],
"BanCreateSchema": {
"type": "object",
"properties": {
"delete_message_seconds": {
"type": "string"
},
"delete_message_days": {
"type": "string"
},
"reason": {
"type": "string"
}
}
},
"BanModeratorSchema": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"user_id": {
"type": "string"
},
"guild_id": {
"type": "string"
},
"executor_id": {
"type": "string"
},
"reason": {
"type": "string"
}
},
"required": [
"executor_id",
"guild_id",
"id",
"user_id"
]
},
"BanRegistrySchema": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"user_id": {
"type": "string"
},
"guild_id": {
"type": "string"
},
"executor_id": {
"type": "string"
},
"ip": {
"type": "string"
},
"reason": {
"type": "string"
}
},
"required": [
"executor_id",
"guild_id",
"id",
"user_id"
]
},
"BotModifySchema": {
"type": "object",
"properties": {
"avatar": {
"type": "string"
},
"username": {
"type": "string"
}
}
},
"ChannelPermissionOverwriteType": {
"enum": [
0,
@ -4729,372 +4828,6 @@
"webauthn"
]
},
"_Response": {
"type": "object",
"properties": {
"headers": {
"$ref": "#/components/schemas/Headers"
},
"ok": {
"type": "boolean"
},
"status": {
"type": "integer"
},
"statusText": {
"type": "string"
},
"type": {
"$ref": "#/components/schemas/ResponseType"
},
"url": {
"type": "string"
},
"redirected": {
"type": "boolean"
},
"body": {
"anyOf": [
{
"$ref": "#/components/schemas/ReadableStream<any>"
},
{
"type": "null"
}
]
},
"bodyUsed": {
"type": "boolean"
},
"arrayBuffer": {
"type": "object",
"additionalProperties": false
},
"blob": {
"type": "object",
"additionalProperties": false
},
"formData": {
"type": "object",
"additionalProperties": false
},
"json": {
"type": "object",
"additionalProperties": false
},
"text": {
"type": "object",
"additionalProperties": false
},
"clone": {
"type": "object",
"additionalProperties": false
}
},
"required": [
"arrayBuffer",
"blob",
"body",
"bodyUsed",
"clone",
"formData",
"headers",
"json",
"ok",
"redirected",
"status",
"statusText",
"text",
"type",
"url"
]
},
"global.Response": {
"type": "object",
"properties": {
"headers": {
"$ref": "#/components/schemas/Headers"
},
"ok": {
"type": "boolean"
},
"status": {
"type": "integer"
},
"statusText": {
"type": "string"
},
"type": {
"$ref": "#/components/schemas/ResponseType"
},
"url": {
"type": "string"
},
"redirected": {
"type": "boolean"
},
"body": {
"anyOf": [
{
"$ref": "#/components/schemas/ReadableStream<any>"
},
{
"type": "null"
}
]
},
"bodyUsed": {
"type": "boolean"
},
"arrayBuffer": {
"type": "object",
"additionalProperties": false
},
"blob": {
"type": "object",
"additionalProperties": false
},
"formData": {
"type": "object",
"additionalProperties": false
},
"json": {
"type": "object",
"additionalProperties": false
},
"text": {
"type": "object",
"additionalProperties": false
},
"clone": {
"type": "object",
"additionalProperties": false
}
},
"required": [
"arrayBuffer",
"blob",
"body",
"bodyUsed",
"clone",
"formData",
"headers",
"json",
"ok",
"redirected",
"status",
"statusText",
"text",
"type",
"url"
]
},
"ConnectedAccountCommonOAuthTokenResponse": {
"type": "object",
"properties": {
"access_token": {
"type": "string"
},
"token_type": {
"type": "string"
},
"scope": {
"type": "string"
},
"refresh_token": {
"type": "string"
},
"expires_in": {
"type": "integer"
}
},
"required": [
"access_token",
"scope",
"token_type"
]
},
"ExpressResponse": {
"type": "object"
},
"ApplicationAuthorizeSchema": {
"type": "object",
"properties": {
"authorize": {
"type": "boolean"
},
"guild_id": {
"type": "string"
},
"permissions": {
"type": "string"
},
"captcha_key": {
"type": "string"
},
"code": {
"type": "string"
}
},
"required": [
"authorize",
"guild_id",
"permissions"
]
},
"ApplicationCreateSchema": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"team_id": {
"type": [
"string",
"integer"
]
}
},
"required": [
"name"
]
},
"ApplicationModifySchema": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"icon": {
"type": "string"
},
"interactions_endpoint_url": {
"type": "string"
},
"max_participants": {
"type": "integer",
"nullable": true
},
"name": {
"type": "string"
},
"privacy_policy_url": {
"type": "string"
},
"role_connections_verification_url": {
"type": "string"
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"terms_of_service_url": {
"type": "string"
},
"bot_public": {
"type": "boolean"
},
"bot_require_code_grant": {
"type": "boolean"
},
"flags": {
"type": "integer"
}
}
},
"BackupCodesChallengeSchema": {
"type": "object",
"properties": {
"password": {
"type": "string"
}
},
"required": [
"password"
]
},
"BanCreateSchema": {
"type": "object",
"properties": {
"delete_message_seconds": {
"type": "string"
},
"delete_message_days": {
"type": "string"
},
"reason": {
"type": "string"
}
}
},
"BanModeratorSchema": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"user_id": {
"type": "string"
},
"guild_id": {
"type": "string"
},
"executor_id": {
"type": "string"
},
"reason": {
"type": "string"
}
},
"required": [
"executor_id",
"guild_id",
"id",
"user_id"
]
},
"BanRegistrySchema": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"user_id": {
"type": "string"
},
"guild_id": {
"type": "string"
},
"executor_id": {
"type": "string"
},
"ip": {
"type": "string"
},
"reason": {
"type": "string"
}
},
"required": [
"executor_id",
"guild_id",
"id",
"user_id"
]
},
"BotModifySchema": {
"type": "object",
"properties": {
"avatar": {
"type": "string"
},
"username": {
"type": "string"
}
}
},
"ChannelPermissionOverwriteSchema": {
"type": "object",
"properties": {
@ -6184,7 +5917,6 @@
"properties": {
"username": {
"minLength": 2,
"maxLength": 32,
"type": "string"
},
"password": {
@ -6600,8 +6332,7 @@
"type": "object",
"properties": {
"username": {
"minLength": 1,
"maxLength": 100,
"minLength": 2,
"type": "string"
},
"avatar": {
@ -10845,6 +10576,12 @@
},
"tags": [
"updates"
],
"x-badges": [
{
"label": "Spacebar-only",
"color": "red"
}
]
}
},
@ -10857,6 +10594,12 @@
},
"tags": [
"track"
],
"x-badges": [
{
"label": "Spacebar-only",
"color": "red"
}
]
}
},
@ -11119,6 +10862,12 @@
},
"tags": [
"scheduled-maintenances"
],
"x-badges": [
{
"label": "Spacebar-only",
"color": "red"
}
]
}
},
@ -11190,6 +10939,12 @@
},
"tags": [
"policies"
],
"x-badges": [
{
"label": "Spacebar-only",
"color": "red"
}
]
}
},
@ -11209,6 +10964,12 @@
},
"tags": [
"policies"
],
"x-badges": [
{
"label": "Spacebar-only",
"color": "red"
}
]
}
},
@ -11228,6 +10989,12 @@
},
"tags": [
"policies"
],
"x-badges": [
{
"label": "Spacebar-only",
"color": "red"
}
]
}
},
@ -11247,6 +11014,12 @@
},
"tags": [
"policies"
],
"x-badges": [
{
"label": "Spacebar-only",
"color": "red"
}
]
}
},
@ -11266,6 +11039,12 @@
},
"tags": [
"ping"
],
"x-badges": [
{
"label": "Spacebar-only",
"color": "red"
}
]
}
},
@ -17512,6 +17291,12 @@
},
"tags": [
"auth"
],
"x-badges": [
{
"label": "Spacebar-only",
"color": "red"
}
]
}
},
@ -17551,6 +17336,12 @@
},
"tags": [
"auth"
],
"x-badges": [
{
"label": "Spacebar-only",
"color": "red"
}
]
}
},
@ -17669,6 +17460,12 @@
],
"tags": [
"auth"
],
"x-badges": [
{
"label": "Spacebar-only",
"color": "red"
}
]
}
},
@ -18152,6 +17949,12 @@
},
"tags": [
"-"
],
"x-badges": [
{
"label": "Spacebar-only",
"color": "red"
}
]
}
},
@ -18164,6 +17967,12 @@
},
"tags": [
"-"
],
"x-badges": [
{
"label": "Spacebar-only",
"color": "red"
}
]
}
}

File diff suppressed because it is too large Load Diff

View File

@ -28,15 +28,13 @@ require("missing-native-js-functions");
const openapiPath = path.join(__dirname, "..", "assets", "openapi.json");
const SchemaPath = path.join(__dirname, "..", "assets", "schemas.json");
const schemas = JSON.parse(fs.readFileSync(SchemaPath, { encoding: "utf8" }));
// const specification = JSON.parse(
// fs.readFileSync(openapiPath, { encoding: "utf8" }),
// );
let specification = {
openapi: "3.1.0",
info: {
title: "Spacebar Server",
description:
"Spacebar is a free open source selfhostable discord compatible chat, voice and video platform",
"Spacebar is a Discord.com server implementation and extension, with the goal of complete feature parity with Discord.com, all while adding some additional goodies, security, privacy, and configuration options.",
license: {
name: "AGPLV3",
url: "https://www.gnu.org/licenses/agpl-3.0.en.html",
@ -68,8 +66,9 @@ let specification = {
paths: {},
};
const schemaRegEx = new RegExp(/^[\w.]+$/);
function combineSchemas(schemas) {
var definitions = {};
let definitions = {};
for (const name in schemas) {
definitions = {
@ -84,9 +83,8 @@ function combineSchemas(schemas) {
}
for (const key in definitions) {
const reg = new RegExp(/^[a-zA-Z0-9.\-_]+$/, "gm");
if (!reg.test(key)) {
console.error(`Invalid schema name: ${key} (${reg.test(key)})`);
if (!schemaRegEx.test(key)) {
console.error(`Invalid schema name: ${key}`);
continue;
}
specification.components = specification.components || {};
@ -116,7 +114,7 @@ function getTag(key) {
return key.match(/\/([\w-]+)/)[1];
}
function apiRoutes() {
function apiRoutes(missingRoutes) {
const routes = getRouteDescriptions();
// populate tags
@ -157,32 +155,30 @@ function apiRoutes() {
},
},
},
}.merge(obj.requestBody);
};
}
if (route.responses) {
for (const [k, v] of Object.entries(route.responses)) {
let schema = {
$ref: `#/components/schemas/${v.body}`,
};
obj.responses = {};
obj.responses = {
[k]: {
...(v.body
? {
description:
obj?.responses?.[k]?.description || "",
for (const [k, v] of Object.entries(route.responses)) {
if (v.body)
obj.responses[k] = {
description: obj?.responses?.[k]?.description || "",
content: {
"application/json": {
schema: schema,
schema: {
$ref: `#/components/schemas/${v.body}`,
},
},
}
: {
description: "No description available",
}),
},
}.merge(obj.responses);
};
else
obj.responses[k] = {
description:
obj?.responses?.[k]?.description ||
"No description available",
};
}
} else {
obj.responses = {
@ -218,6 +214,15 @@ function apiRoutes() {
obj.tags = [...(obj.tags || []), getTag(p)].unique();
if (missingRoutes.additional.includes(path.replace(/\/$/, ""))) {
obj["x-badges"] = [
{
label: "Spacebar-only",
color: "red",
},
];
}
specification.paths[path] = Object.assign(
specification.paths[path] || {},
{
@ -227,10 +232,21 @@ function apiRoutes() {
});
}
function main() {
async function main() {
console.log("Generating OpenAPI Specification...");
const routesRes = await fetch(
"https://github.com/spacebarchat/missing-routes/raw/main/missing.json",
{
headers: {
Accept: "application/json",
},
},
);
const missingRoutes = await routesRes.json();
combineSchemas(schemas);
apiRoutes();
apiRoutes(missingRoutes);
fs.writeFileSync(
openapiPath,

View File

@ -41,11 +41,16 @@ const Excluded = [
"EntitySchema",
"ServerResponse",
"Http2ServerResponse",
"ExpressResponse",
"global.Express.Response",
"global.Response",
"Response",
"e.Response",
"request.Response",
"supertest.Response",
"DiagnosticsChannel.Response",
"_Response",
"ReadableStream<any>",
// TODO: Figure out how to exclude schemas from node_modules?
"SomeJSONSchema",