Browse Source

API doc generation! (#280)

Co-authored-by: trgwii <trgwii@hotmail.com>
tags/v2.3.11
Pouria Ezzati 8 months ago
committed by GitHub
parent
commit
2c261ad4af
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 1649 additions and 97 deletions
  1. +3
    -1
      .gitignore
  2. +529
    -0
      docs/api/api.ts
  3. +48
    -0
      docs/api/generate.ts
  4. +1058
    -90
      package-lock.json
  5. +3
    -1
      package.json
  6. +8
    -5
      tsconfig.json

+ 3
- 1
.gitignore View File

@@ -8,4 +8,6 @@ server/config.js
server/old.config.js
production-server
.idea/
dump.rdb
dump.rdb
docs/api/*.js
docs/api/static

+ 529
- 0
docs/api/api.ts View File

@@ -0,0 +1,529 @@
import * as p from '../../package.json';

export default {
openapi: '3.0.0',
info: {
title: "Kutt.it",
description: "API referrence for [http://kutt.it](http://kutt.it).\n",
version: p.version
},
servers: [{
url: "https://kutt.it/api/v2"
}],
tags: [{
name: "health"
}, {
name: "links"
}, {
name: "domains"
}, {
name: "users"
}],
paths: {
'/health': {
get: {
tags: ["health"],
summary: "API health",
responses: {
200: {
description: "Health",
content: {
'text/html': {
example: "OK"
}
}
}
}
}
},
'/links': {
get: {
tags: ["links"],
description: "Get list of links",
parameters: [{
name: "limit",
in: "query",
description: "Limit",
required: false,
style: "form",
explode: true,
schema: {
type: "number",
example: 10
}
}, {
name: "skip",
in: "query",
description: "Skip",
required: false,
style: "form",
explode: true,
schema: {
type: "number",
example: 0
}
}, {
name: "all",
in: "query",
description: "All links (ADMIN only)",
required: false,
style: "form",
explode: true,
schema: {
type: "boolean",
example: false
}
}],
responses: {
200: {
description: "List of links",
content: {
'application/json': {
schema: {
$ref: "#/components/schemas/inline_response_200"
}
}
}
}
},
security: [{
APIKeyAuth: []
}]
},
post: {
tags: ["links"],
description: "Create a short link",
requestBody: {
content: {
'application/json': {
schema: {
$ref: "#/components/schemas/body"
}
}
}
},
responses: {
200: {
description: "Craeted link",
content: {
'application/json': {
schema: {
$ref: "#/components/schemas/Link"
}
}
}
}
},
security: [{
APIKeyAuth: []
}]
}
},
'/links/{id}': {
delete: {
tags: ["links"],
description: "Delete a link",
parameters: [{
name: "id",
in: "path",
required: true,
style: "simple",
explode: false,
schema: {
type: "string",
format: "uuid"
}
}],
responses: {
200: {
description: "Deleted link successfully",
content: {
'application/json': {
schema: {
$ref: "#/components/schemas/inline_response_200_1"
}
}
}
}
},
security: [{
APIKeyAuth: []
}]
}
},
'/links/{id}/stats': {
get: {
tags: ["links"],
description: "Get link stats",
parameters: [{
name: "id",
in: "path",
required: true,
style: "simple",
explode: false,
schema: {
type: "string",
format: "uuid"
}
}],
responses: {
200: {
description: "Link stats",
content: {
'application/json': {
schema: {
$ref: "#/components/schemas/Stats"
}
}
}
}
},
security: [{
APIKeyAuth: []
}]
}
},
'/domains': {
post: {
tags: ["domains"],
description: "Create a domain",
requestBody: {
content: {
'application/json': {
schema: {
$ref: "#/components/schemas/body_1"
}
}
}
},
responses: {
200: {
description: "Created domain",
content: {
'application/json': {
schema: {
$ref: "#/components/schemas/Domain"
}
}
}
}
},
security: [{
APIKeyAuth: []
}]
}
},
'/domains/{id}': {
delete: {
tags: ["domains"],
description: "Delete a domain",
parameters: [{
name: "id",
in: "path",
required: true,
style: "simple",
explode: false,
schema: {
type: "string",
format: "uuid"
}
}],
responses: {
200: {
description: "Deleted domain successfully",
content: {
'application/json': {
schema: {
$ref: "#/components/schemas/inline_response_200_1"
}
}
}
}
},
security: [{
APIKeyAuth: []
}]
}
},
'/users': {
get: {
tags: ["users"],
description: "Get user info",
responses: {
200: {
description: "User info",
content: {
'application/json': {
schema: {
$ref: "#/components/schemas/User"
}
}
}
}
},
security: [{
APIKeyAuth: []
}]
}
}
},
components: {
schemas: {
Link: {
type: "object",
properties: {
address: {
type: "string"
},
banned: {
type: "boolean",
default: false
},
created_at: {
type: "string",
format: "date-time"
},
id: {
type: "string",
format: "uuid"
},
link: {
type: "string"
},
password: {
type: "boolean",
default: false
},
target: {
type: "string"
},
updated_at: {
type: "string",
format: "date-time"
},
visit_count: {
type: "number"
}
}
},
Domain: {
type: "object",
properties: {
address: {
type: "string"
},
banned: {
type: "boolean",
default: false
},
created_at: {
type: "string",
format: "date-time"
},
id: {
type: "string",
format: "uuid"
},
homepage: {
type: "string"
},
updated_at: {
type: "string",
format: "date-time"
}
}
},
User: {
type: "object",
properties: {
apikey: {
type: "string"
},
email: {
type: "string"
},
domains: {
type: "array",
items: {
$ref: "#/components/schemas/Domain"
}
}
}
},
StatsItem: {
type: "object",
properties: {
stats: {
$ref: "#/components/schemas/StatsItem_stats"
},
views: {
type: "array",
items: {
type: "number"
}
}
}
},
Stats: {
type: "object",
properties: {
allTime: {
$ref: "#/components/schemas/StatsItem"
},
lastDay: {
$ref: "#/components/schemas/StatsItem"
},
lastMonth: {
$ref: "#/components/schemas/StatsItem"
},
lastWeek: {
$ref: "#/components/schemas/StatsItem"
},
updatedAt: {
type: "string"
},
address: {
type: "string"
},
banned: {
type: "boolean",
default: false
},
created_at: {
type: "string",
format: "date-time"
},
id: {
type: "string",
format: "uuid"
},
link: {
type: "string"
},
password: {
type: "boolean",
default: false
},
target: {
type: "string"
},
updated_at: {
type: "string",
format: "date-time"
},
visit_count: {
type: "number"
}
}
},
inline_response_200: {
properties: {
limit: {
type: "number",
default: 10
},
skip: {
type: "number",
default: 0
},
total: {
type: "number",
default: 0
},
data: {
type: "array",
items: {
$ref: "#/components/schemas/Link"
}
}
}
},
body: {
required: ["target"],
properties: {
target: {
type: "string"
},
password: {
type: "string"
},
customurl: {
type: "string"
},
reuse: {
type: "boolean",
default: false
},
domain: {
type: "string"
}
}
},
inline_response_200_1: {
properties: {
message: {
type: "string"
}
}
},
body_1: {
required: ["address"],
properties: {
address: {
type: "string"
},
homepage: {
type: "string"
}
}
},
StatsItem_stats_browser: {
type: "object",
properties: {
name: {
type: "string"
},
value: {
type: "number"
}
}
},
StatsItem_stats: {
type: "object",
properties: {
browser: {
type: "array",
items: {
$ref: "#/components/schemas/StatsItem_stats_browser"
}
},
os: {
type: "array",
items: {
$ref: "#/components/schemas/StatsItem_stats_browser"
}
},
country: {
type: "array",
items: {
$ref: "#/components/schemas/StatsItem_stats_browser"
}
},
referrer: {
type: "array",
items: {
$ref: "#/components/schemas/StatsItem_stats_browser"
}
}
}
}
},
securitySchemes: {
APIKeyAuth: {
type: "apiKey",
name: "X-API-KEY",
in: "header"
}
}
}
};

+ 48
- 0
docs/api/generate.ts View File

@@ -0,0 +1,48 @@
import { join, dirname } from 'path';

import { promises as fs } from 'fs';

import api from './api';

const Template = (output, { api, title, redoc }) =>
fs.writeFile(output,
`<DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>${title}</title>
</head>
<body>
<redoc spec-url="${api}" />
<script src="${redoc}"></script>
</body>
</html>
`);

const Api = output =>
fs.writeFile(output, JSON.stringify(api));

const Redoc = output =>
fs.copyFile(join(
dirname(require.resolve('redoc')),
'redoc.standalone.js'),
output);

export default (async () => {
const out = join(__dirname, 'static');
const apiFile = 'api.json';
const redocFile = 'redoc.js';
await fs.mkdir(out, { recursive: true });
return Promise.all([
Api(join(out, apiFile)),
Redoc(join(out, redocFile)),
Template(join(out, 'index.html'), {
api: apiFile,
title: api.info.title,
redoc: redocFile
}),

]);
})();

+ 1058
- 90
package-lock.json
File diff suppressed because it is too large
View File


+ 3
- 1
package.json View File

@@ -12,7 +12,8 @@
"start": "NODE_ENV=production node production-server/server.js",
"migrate": "knex migrate:up --env production",
"lint": "eslint server/ --ext .js,.ts --fix",
"lint:nofix": "eslint server/ --ext .js,.ts"
"lint:nofix": "eslint server/ --ext .js,.ts",
"docs:build": "cd docs/api && tsc generate.ts --resolveJsonModule && node generate && cd ../.."
},
"husky": {
"hooks": {
@@ -147,6 +148,7 @@
"nock": "^9.3.3",
"nodemon": "^1.19.4",
"prettier": "^1.19.1",
"redoc": "^2.0.0-rc.20",
"rimraf": "^3.0.0",
"sinon": "^6.0.0",
"typescript": "^3.7.5"


+ 8
- 5
tsconfig.json View File

@@ -1,5 +1,5 @@
{
"compilerOptions": {
"compilerOptions": {
"target": "es2018",
"module": "commonjs",
"sourceMap": true,
@@ -8,9 +8,12 @@
"resolveJsonModule": true,
"esModuleInterop": true,
"noEmit": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"strict": false
},
"include": ["global.d.ts", "server"]
},
"include": [
"global.d.ts",
"server"
]
}

Loading…
Cancel
Save