Fixed 267U.pre2
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
export const errors = {
|
||||
"400": {
|
||||
INVALID_PARAM: {
|
||||
value: {
|
||||
error: {
|
||||
message: "Invalid parameter.",
|
||||
code: "INVALID_PARAM",
|
||||
id: "3d81ceae-475f-4600-b2a8-2bc116157532"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
CREDENTIAL_REQUIRED: {
|
||||
value: {
|
||||
error: {
|
||||
message: "Credential required.",
|
||||
code: "CREDENTIAL_REQUIRED",
|
||||
id: "1384574d-a912-4b81-8601-c7b1c4085df1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
AUTHENTICATION_FAILED: {
|
||||
value: {
|
||||
error: {
|
||||
message: "Authentication failed.",
|
||||
code: "AUTHENTICATION_FAILED",
|
||||
id: "b0a7f5f8-dc2f-4171-b91f-de88ad238e14"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"418": {
|
||||
I_AM_CALC: {
|
||||
value: {
|
||||
error: {
|
||||
message: "You sent a request to Calc instead of the server. How did this happen?",
|
||||
code: "I_AM_CALC",
|
||||
id: "60c46cd1-f23a-46b1-bebe-5d2b73951a84"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"429": {
|
||||
RATE_LIMIT_EXCEEDED: {
|
||||
value: {
|
||||
error: {
|
||||
message: "Rate limit exceeded. Please try again later.",
|
||||
code: "RATE_LIMIT_EXCEEDED",
|
||||
id: "d5826d14-3982-4d2e-8011-b9e9f02499ef"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
INTERNAL_ERROR: {
|
||||
value: {
|
||||
error: {
|
||||
message: "Internal error occurred. Please contact us if the error persists.",
|
||||
code: "INTERNAL_ERROR",
|
||||
id: "5d37dbcb-891e-41ca-a3d6-e690c97775ac"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,200 @@
|
||||
import endpoints from "../endpoints.js";
|
||||
import config from "../../../config/index.js";
|
||||
import { errors as basicErrors } from "./errors.js";
|
||||
import { schemas, convertSchemaToOpenApiSchema } from "./schemas.js";
|
||||
export function genOpenapiSpec() {
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
info: {
|
||||
version: "v1",
|
||||
title: "FrozenFriendsYume API",
|
||||
"x-logo": {
|
||||
url: "/static-assets/api-doc.png"
|
||||
}
|
||||
},
|
||||
externalDocs: {
|
||||
description: "Repository",
|
||||
url: "https://iceshrimp.dev/iceshrimp/iceshrimp"
|
||||
},
|
||||
servers: [
|
||||
{
|
||||
url: config.apiUrl
|
||||
}
|
||||
],
|
||||
paths: {},
|
||||
components: {
|
||||
schemas: schemas,
|
||||
securitySchemes: {
|
||||
ApiKeyAuth: {
|
||||
type: "apiKey",
|
||||
in: "body",
|
||||
name: "i"
|
||||
},
|
||||
// TODO: change this to oauth2 when the remaining oauth stuff is set up
|
||||
Bearer: {
|
||||
type: "http",
|
||||
scheme: "bearer"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
for (const endpoint of endpoints.filter((ep)=>!ep.meta.secure)){
|
||||
const errors = {};
|
||||
if (endpoint.meta.errors) {
|
||||
for (const e of Object.values(endpoint.meta.errors)){
|
||||
errors[e.code] = {
|
||||
value: {
|
||||
error: e
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
const resSchema = endpoint.meta.res ? convertSchemaToOpenApiSchema(endpoint.meta.res) : {};
|
||||
let desc = (endpoint.meta.description ? endpoint.meta.description : "No description provided.") + "\n\n";
|
||||
desc += `**Credential required**: *${endpoint.meta.requireCredential ? "Yes" : "No"}*`;
|
||||
if (endpoint.meta.kind) {
|
||||
const kind = endpoint.meta.kind;
|
||||
desc += ` / **Permission**: *${kind}*`;
|
||||
}
|
||||
const requestType = endpoint.meta.requireFile ? "multipart/form-data" : "application/json";
|
||||
const schema = endpoint.params;
|
||||
if (endpoint.meta.requireFile) {
|
||||
schema.properties.file = {
|
||||
type: "string",
|
||||
format: "binary",
|
||||
description: "The file contents."
|
||||
};
|
||||
schema.required.push("file");
|
||||
}
|
||||
const security = [
|
||||
{
|
||||
ApiKeyAuth: []
|
||||
},
|
||||
{
|
||||
Bearer: []
|
||||
}
|
||||
];
|
||||
if (!endpoint.meta.requireCredential) {
|
||||
// add this to make authentication optional
|
||||
security.push({});
|
||||
}
|
||||
const info = {
|
||||
operationId: endpoint.name,
|
||||
summary: endpoint.name,
|
||||
description: desc,
|
||||
externalDocs: {
|
||||
description: "Source code",
|
||||
url: `https://iceshrimp.dev/iceshrimp/iceshrimp/src/branch/dev/packages/backend/src/server/api/endpoints/${endpoint.name}.ts`
|
||||
},
|
||||
tags: endpoint.meta.tags || undefined,
|
||||
security,
|
||||
requestBody: {
|
||||
required: true,
|
||||
content: {
|
||||
[requestType]: {
|
||||
schema
|
||||
}
|
||||
}
|
||||
},
|
||||
responses: {
|
||||
...endpoint.meta.res ? {
|
||||
"200": {
|
||||
description: "OK (with results)",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: resSchema
|
||||
}
|
||||
}
|
||||
}
|
||||
} : {
|
||||
"204": {
|
||||
description: "OK (without any results)"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
description: "Client error",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
$ref: "#/components/schemas/Error"
|
||||
},
|
||||
examples: {
|
||||
...errors,
|
||||
...basicErrors["400"]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
description: "Authentication error",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
$ref: "#/components/schemas/Error"
|
||||
},
|
||||
examples: basicErrors["401"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
description: "Forbidden error",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
$ref: "#/components/schemas/Error"
|
||||
},
|
||||
examples: basicErrors["403"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"418": {
|
||||
description: "I'm Calc",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
$ref: "#/components/schemas/Error"
|
||||
},
|
||||
examples: basicErrors["418"]
|
||||
}
|
||||
}
|
||||
},
|
||||
...endpoint.meta.limit ? {
|
||||
"429": {
|
||||
description: "Too many requests",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
$ref: "#/components/schemas/Error"
|
||||
},
|
||||
examples: basicErrors["429"]
|
||||
}
|
||||
}
|
||||
}
|
||||
} : {},
|
||||
"500": {
|
||||
description: "Internal server error",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
$ref: "#/components/schemas/Error"
|
||||
},
|
||||
examples: basicErrors["500"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const path = {
|
||||
post: info
|
||||
};
|
||||
if (endpoint.meta.allowGet) {
|
||||
path.get = {
|
||||
...info
|
||||
};
|
||||
// API Key authentication is not permitted for GET requests
|
||||
path.get.security = path.get.security.filter((elem)=>!Object.prototype.hasOwnProperty.call(elem, "ApiKeyAuth"));
|
||||
}
|
||||
spec.paths[`/${endpoint.name}`] = path;
|
||||
}
|
||||
return spec;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { refs } from "../../../misc/schema.js";
|
||||
export function convertSchemaToOpenApiSchema(schema) {
|
||||
const res = schema;
|
||||
if (schema.type === "object" && schema.properties) {
|
||||
res.required = Object.entries(schema.properties).filter(([k, v])=>!v.optional).map(([k])=>k);
|
||||
for (const k of Object.keys(schema.properties)){
|
||||
res.properties[k] = convertSchemaToOpenApiSchema(schema.properties[k]);
|
||||
}
|
||||
}
|
||||
if (schema.type === "array" && schema.items) {
|
||||
res.items = convertSchemaToOpenApiSchema(schema.items);
|
||||
}
|
||||
if (schema.anyOf) res.anyOf = schema.anyOf.map(convertSchemaToOpenApiSchema);
|
||||
if (schema.oneOf) res.oneOf = schema.oneOf.map(convertSchemaToOpenApiSchema);
|
||||
if (schema.allOf) res.allOf = schema.allOf.map(convertSchemaToOpenApiSchema);
|
||||
if (schema.ref) {
|
||||
res.$ref = `#/components/schemas/${schema.ref}`;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
export const schemas = {
|
||||
Error: {
|
||||
type: "object",
|
||||
properties: {
|
||||
error: {
|
||||
type: "object",
|
||||
description: "An error object.",
|
||||
properties: {
|
||||
code: {
|
||||
type: "string",
|
||||
description: "An error code. Unique within the endpoint."
|
||||
},
|
||||
message: {
|
||||
type: "string",
|
||||
description: "An error message."
|
||||
},
|
||||
id: {
|
||||
type: "string",
|
||||
format: "uuid",
|
||||
description: "An error ID. This ID is static."
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"code",
|
||||
"id",
|
||||
"message"
|
||||
]
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"error"
|
||||
]
|
||||
},
|
||||
...Object.fromEntries(Object.entries(refs).map(([key, schema])=>[
|
||||
key,
|
||||
convertSchemaToOpenApiSchema(schema)
|
||||
]))
|
||||
};
|
||||
Reference in New Issue
Block a user