parent
b5f210b68d
commit
2c261ad4af
|
@ -8,4 +8,6 @@ server/config.js
|
|||
server/old.config.js
|
||||
production-server
|
||||
.idea/
|
||||
dump.rdb
|
||||
dump.rdb
|
||||
docs/api/*.js
|
||||
docs/api/static
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
|
@ -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
|
||||
}),
|
||||
|
||||
]);
|
||||
})();
|
File diff suppressed because it is too large
Load Diff
|
@ -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"
|
||||
|
|
|
@ -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…
Reference in New Issue