mirror of https://github.com/nocodb/nocodb
աɨռɢӄաօռɢ
3 years ago
committed by
GitHub
59 changed files with 44093 additions and 1120 deletions
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,48 @@
|
||||
import Noco from '../../../../Noco'; |
||||
import Model from '../../../../../noco-models/Model'; |
||||
import Project from '../../../../../noco-models/Project'; |
||||
import { getModelPaths, getViewPaths } from './templates/paths'; |
||||
import { SwaggerColumn } from './getSwaggerColumnMetas'; |
||||
import { SwaggerView } from './getSwaggerJSON'; |
||||
|
||||
export default async function getPaths( |
||||
{ |
||||
project, |
||||
model, |
||||
columns, |
||||
views |
||||
}: { |
||||
project: Project; |
||||
model: Model; |
||||
columns: SwaggerColumn[]; |
||||
views: SwaggerView[]; |
||||
}, |
||||
_ncMeta = Noco.ncMeta |
||||
) { |
||||
const swaggerPaths = await getModelPaths({ |
||||
tableName: model.title, |
||||
type: model.type, |
||||
orgs: 'noco', |
||||
columns, |
||||
projectName: project.title |
||||
}); |
||||
|
||||
for (const { view, columns: viewColumns } of views) { |
||||
const swaggerColumns = columns.filter( |
||||
c => viewColumns.find(vc => vc.fk_column_id === c.column.id)?.show |
||||
); |
||||
Object.assign( |
||||
swaggerPaths, |
||||
await getViewPaths({ |
||||
tableName: model.title, |
||||
viewName: view.title, |
||||
type: model.type, |
||||
orgs: 'noco', |
||||
columns: swaggerColumns, |
||||
projectName: project.title |
||||
}) |
||||
); |
||||
} |
||||
|
||||
return swaggerPaths; |
||||
} |
@ -0,0 +1,46 @@
|
||||
import Noco from '../../../../Noco'; |
||||
import Model from '../../../../../noco-models/Model'; |
||||
import Project from '../../../../../noco-models/Project'; |
||||
import { getModelSchemas, getViewSchemas } from './templates/schemas'; |
||||
import { SwaggerColumn } from './getSwaggerColumnMetas'; |
||||
import { SwaggerView } from './getSwaggerJSON'; |
||||
|
||||
export default async function getSchemas( |
||||
{ |
||||
project, |
||||
model, |
||||
columns, |
||||
views |
||||
}: { |
||||
project: Project; |
||||
model: Model; |
||||
columns: SwaggerColumn[]; |
||||
views: SwaggerView[]; |
||||
}, |
||||
_ncMeta = Noco.ncMeta |
||||
) { |
||||
const swaggerSchemas = getModelSchemas({ |
||||
tableName: model.title, |
||||
orgs: 'noco', |
||||
projectName: project.title, |
||||
columns |
||||
}); |
||||
|
||||
for (const { view, columns: viewColumns } of views) { |
||||
const swaggerColumns = columns.filter( |
||||
c => viewColumns.find(vc => vc.fk_column_id === c.column.id)?.show |
||||
); |
||||
Object.assign( |
||||
swaggerSchemas, |
||||
getViewSchemas({ |
||||
tableName: model.title, |
||||
viewName: view.title, |
||||
orgs: 'noco', |
||||
columns: swaggerColumns, |
||||
projectName: project.title |
||||
}) |
||||
); |
||||
} |
||||
|
||||
return swaggerSchemas; |
||||
} |
@ -0,0 +1,59 @@
|
||||
import { UITypes } from 'nocodb-sdk'; |
||||
import LinkToAnotherRecordColumn from '../../../../../noco-models/LinkToAnotherRecordColumn'; |
||||
import SwaggerTypes from '../../../../../sqlMgr/code/routers/xc-ts/SwaggerTypes'; |
||||
import Column from '../../../../../noco-models/Column'; |
||||
import Noco from '../../../../Noco'; |
||||
import Project from '../../../../../noco-models/Project'; |
||||
|
||||
export default async ( |
||||
columns: Column[], |
||||
project: Project, |
||||
ncMeta = Noco.ncMeta |
||||
): Promise<SwaggerColumn[]> => { |
||||
const dbType = await project.getBases().then(b => b?.[0]?.type); |
||||
return Promise.all( |
||||
columns.map(async c => { |
||||
const field: SwaggerColumn = { |
||||
title: c.title, |
||||
type: 'object', |
||||
virtual: true, |
||||
column: c |
||||
}; |
||||
|
||||
switch (c.uidt) { |
||||
case UITypes.LinkToAnotherRecord: |
||||
{ |
||||
const colOpt = await c.getColOptions<LinkToAnotherRecordColumn>( |
||||
ncMeta |
||||
); |
||||
const relTable = await colOpt.getRelatedTable(ncMeta); |
||||
field.type = undefined; |
||||
field.$ref = `#/components/schemas/${relTable.title}Request`; |
||||
} |
||||
break; |
||||
case UITypes.Formula: |
||||
case UITypes.Lookup: |
||||
field.type = 'object'; |
||||
break; |
||||
case UITypes.Rollup: |
||||
field.type = 'number'; |
||||
break; |
||||
default: |
||||
field.virtual = false; |
||||
SwaggerTypes.setSwaggerType(c, field, dbType); |
||||
break; |
||||
} |
||||
|
||||
return field; |
||||
}) |
||||
); |
||||
}; |
||||
|
||||
export interface SwaggerColumn { |
||||
type: any; |
||||
title: string; |
||||
description?: string; |
||||
virtual?: boolean; |
||||
$ref?: any; |
||||
column: Column; |
||||
} |
@ -0,0 +1,66 @@
|
||||
import Noco from '../../../../Noco'; |
||||
import Model from '../../../../../noco-models/Model'; |
||||
import swaggerBase from './swagger-base.json'; |
||||
import getPaths from './getPaths'; |
||||
import getSchemas from './getSchemas'; |
||||
import Project from '../../../../../noco-models/Project'; |
||||
import getSwaggerColumnMetas from './getSwaggerColumnMetas'; |
||||
import { ViewTypes } from 'nocodb-sdk'; |
||||
import GridViewColumn from '../../../../../noco-models/GridViewColumn'; |
||||
import View from '../../../../../noco-models/View'; |
||||
|
||||
export default async function getSwaggerJSON( |
||||
project: Project, |
||||
models: Model[], |
||||
ncMeta = Noco.ncMeta |
||||
) { |
||||
// base swagger object
|
||||
const swaggerObj = { |
||||
...swaggerBase, |
||||
paths: {}, |
||||
components: { |
||||
...swaggerBase.components, |
||||
schemas: { ...swaggerBase.components.schemas } |
||||
} |
||||
}; |
||||
|
||||
// iterate and populate swagger schema and path for models and views
|
||||
for (const model of models) { |
||||
let paths = {}; |
||||
|
||||
const columns = await getSwaggerColumnMetas( |
||||
await model.getColumns(ncMeta), |
||||
project, |
||||
ncMeta |
||||
); |
||||
|
||||
const views: SwaggerView[] = []; |
||||
|
||||
for (const view of (await model.getViews(false, ncMeta)) || []) { |
||||
if (view.type !== ViewTypes.GRID) continue; |
||||
views.push({ |
||||
view, |
||||
columns: await view.getColumns(ncMeta) |
||||
}); |
||||
} |
||||
|
||||
// skip mm tables
|
||||
if (!model.mm) |
||||
paths = await getPaths({ project, model, columns, views }, ncMeta); |
||||
|
||||
const schemas = await getSchemas( |
||||
{ project, model, columns, views }, |
||||
ncMeta |
||||
); |
||||
|
||||
Object.assign(swaggerObj.paths, paths); |
||||
Object.assign(swaggerObj.components.schemas, schemas); |
||||
} |
||||
|
||||
return swaggerObj; |
||||
} |
||||
|
||||
export interface SwaggerView { |
||||
view: View; |
||||
columns: Array<GridViewColumn>; |
||||
} |
@ -0,0 +1,61 @@
|
||||
{ |
||||
"openapi": "3.0.0", |
||||
"info": { |
||||
"title": "nocodb", |
||||
"version": "1.0" |
||||
}, |
||||
"servers": [ |
||||
{ |
||||
"url": "http://localhost:8080" |
||||
} |
||||
], |
||||
"paths": { |
||||
}, |
||||
"components": { |
||||
"schemas": { |
||||
"Paginated": { |
||||
"title": "Paginated", |
||||
"type": "object", |
||||
"properties": { |
||||
"pageSize": { |
||||
"type": "integer" |
||||
}, |
||||
"totalRows": { |
||||
"type": "integer" |
||||
}, |
||||
"isFirstPage": { |
||||
"type": "boolean" |
||||
}, |
||||
"isLastPage": { |
||||
"type": "boolean" |
||||
}, |
||||
"page": { |
||||
"type": "number" |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
"securitySchemes": { |
||||
"xcAuth": { |
||||
"type": "apiKey", |
||||
"in": "header", |
||||
"name": "xc-auth", |
||||
"description": "JWT access token" |
||||
}, |
||||
"xcToken": { |
||||
"type": "apiKey", |
||||
"in": "header", |
||||
"name": "xc-token", |
||||
"description": "API token" |
||||
} |
||||
} |
||||
}, |
||||
"security": [ |
||||
{ |
||||
"xcAuth": [] |
||||
}, |
||||
{ |
||||
"xcToken": [] |
||||
} |
||||
] |
||||
} |
@ -0,0 +1,10 @@
|
||||
export const csvExportResponseHeader = { |
||||
'nc-export-offset': { |
||||
schema: { |
||||
type: 'integer' |
||||
}, |
||||
description: |
||||
'Offset of next set of data which will be helpful if there is large amount of data. It will returns `-1` if all set of data exported.', |
||||
example: '1000' |
||||
} |
||||
}; |
@ -0,0 +1,192 @@
|
||||
import { SwaggerColumn } from '../getSwaggerColumnMetas'; |
||||
import { RelationTypes, UITypes } from 'nocodb-sdk'; |
||||
import LinkToAnotherRecordColumn from '../../../../../../noco-models/LinkToAnotherRecordColumn'; |
||||
|
||||
export const rowIdParam = { |
||||
schema: { |
||||
type: 'string' |
||||
}, |
||||
name: 'rowId', |
||||
in: 'path', |
||||
required: true, |
||||
example: 1, |
||||
description: |
||||
'Primary key of the record you want to read. If the table have composite primary key then combine them by using `___` and pass it as primary key.' |
||||
}; |
||||
|
||||
export const relationTypeParam = { |
||||
schema: { |
||||
type: 'string', |
||||
enum: ['mm', 'hm'] |
||||
}, |
||||
name: 'relationType', |
||||
in: 'path', |
||||
required: true |
||||
}; |
||||
|
||||
export const fieldsParam = { |
||||
schema: { |
||||
type: 'string' |
||||
}, |
||||
in: 'query', |
||||
name: 'fields', |
||||
description: |
||||
'Array of field names or comma separated filed names to include in the response objects. In array syntax pass it like `fields[]=field1&fields[]=field2` or alternately `fields=field1,field2`.' |
||||
}; |
||||
export const sortParam = { |
||||
schema: { |
||||
type: 'string' |
||||
}, |
||||
in: 'query', |
||||
name: 'sort', |
||||
description: |
||||
'Comma separated field names to sort rows, rows will sort in ascending order based on provided columns. To sort in descending order provide `-` prefix along with column name, like `-field`. Example : `sort=field1,-field2`' |
||||
}; |
||||
export const whereParam = { |
||||
schema: { |
||||
type: 'string' |
||||
}, |
||||
in: 'query', |
||||
name: 'where', |
||||
description: |
||||
'This can be used for filtering rows, which accepts complicated where conditions. For more info visit [here](https://docs.nocodb.com/developer-resources/rest-apis#comparison-operators). Example : `where=(field1,eq,value)`' |
||||
}; |
||||
export const limitParam = { |
||||
schema: { |
||||
type: 'number', |
||||
minimum: 1 |
||||
}, |
||||
in: 'query', |
||||
name: 'limit', |
||||
description: |
||||
'The `limit` parameter used for pagination, the response collection size depends on limit value and default value is `25`.', |
||||
example: 25 |
||||
}; |
||||
export const offsetParam = { |
||||
schema: { |
||||
type: 'number', |
||||
minimum: 0 |
||||
}, |
||||
in: 'query', |
||||
name: 'offset', |
||||
description: |
||||
'The `offset` parameter used for pagination, the value helps to select collection from a certain index.', |
||||
example: 0 |
||||
}; |
||||
|
||||
export const columnNameParam = (columns: SwaggerColumn[]) => { |
||||
const columnNames = []; |
||||
for (const { column } of columns) { |
||||
if (column.uidt !== UITypes.LinkToAnotherRecord || column.system) continue; |
||||
columnNames.push(column.title); |
||||
} |
||||
|
||||
return { |
||||
schema: { |
||||
type: 'enum', |
||||
enum: columnNames |
||||
}, |
||||
name: 'columnName', |
||||
in: 'path', |
||||
required: true |
||||
}; |
||||
}; |
||||
|
||||
export const referencedRowIdParam = { |
||||
schema: { |
||||
type: 'string' |
||||
}, |
||||
name: 'refRowId', |
||||
in: 'path', |
||||
required: true |
||||
}; |
||||
|
||||
export const exportTypeParam = { |
||||
schema: { |
||||
type: 'string', |
||||
enum: ['csv', 'excel'] |
||||
}, |
||||
name: 'type', |
||||
in: 'path', |
||||
required: true |
||||
}; |
||||
|
||||
export const csvExportOffsetParam = { |
||||
schema: { |
||||
type: 'number', |
||||
minimum: 0 |
||||
}, |
||||
in: 'query', |
||||
name: 'offset', |
||||
description: |
||||
'Helps to start export from a certain index. You can get the next set of data offset from previous response header named `nc-export-offset`.', |
||||
example: 0 |
||||
}; |
||||
|
||||
export const nestedWhereParam = colName => ({ |
||||
schema: { |
||||
type: 'string' |
||||
}, |
||||
in: 'query', |
||||
name: `nested[${colName}][where]`, |
||||
description: `This can be used for filtering rows in nested column \`${colName}\`, which accepts complicated where conditions. For more info visit [here](https://docs.nocodb.com/developer-resources/rest-apis#comparison-operators). Example : \`nested[${colName}][where]=(field1,eq,value)\`` |
||||
}); |
||||
|
||||
export const nestedFieldParam = colName => ({ |
||||
schema: { |
||||
type: 'string' |
||||
}, |
||||
in: 'query', |
||||
name: `nested[${colName}][fields]`, |
||||
description: `Array of field names or comma separated filed names to include in the in nested column \`${colName}\` result. In array syntax pass it like \`fields[]=field1&fields[]=field2.\`. Example : \`nested[${colName}][fields]=field1,field2\`` |
||||
}); |
||||
export const nestedSortParam = colName => ({ |
||||
schema: { |
||||
type: 'string' |
||||
}, |
||||
in: 'query', |
||||
name: `nested[${colName}][sort]`, |
||||
description: `Comma separated field names to sort rows in nested column \`${colName}\` rows, it will sort in ascending order based on provided columns. To sort in descending order provide \`-\` prefix along with column name, like \`-field\`. Example : \`nested[${colName}][sort]=field1,-field2\`` |
||||
}); |
||||
export const nestedLimitParam = colName => ({ |
||||
schema: { |
||||
type: 'number', |
||||
minimum: 1 |
||||
}, |
||||
in: 'query', |
||||
name: `nested[${colName}][limit]`, |
||||
description: `The \`limit\` parameter used for pagination of nested \`${colName}\` rows, the response collection size depends on limit value and default value is \`25\`.`, |
||||
example: '25' |
||||
}); |
||||
export const nestedOffsetParam = colName => ({ |
||||
schema: { |
||||
type: 'number', |
||||
minimum: 0 |
||||
}, |
||||
in: 'query', |
||||
name: `nested[${colName}][offset]`, |
||||
description: `The \`offset\` parameter used for pagination of nested \`${colName}\` rows, the value helps to select collection from a certain index.`, |
||||
example: 0 |
||||
}); |
||||
|
||||
export const getNestedParams = async ( |
||||
columns: SwaggerColumn[] |
||||
): Promise<any[]> => { |
||||
return await columns.reduce(async (paramsArr, { column }) => { |
||||
if (column.uidt === UITypes.LinkToAnotherRecord) { |
||||
const colOpt = await column.getColOptions<LinkToAnotherRecordColumn>(); |
||||
if (colOpt.type !== RelationTypes.BELONGS_TO) { |
||||
return [ |
||||
...(await paramsArr), |
||||
nestedWhereParam(column.title), |
||||
nestedOffsetParam(column.title), |
||||
nestedLimitParam(column.title), |
||||
nestedFieldParam(column.title), |
||||
nestedSortParam(column.title) |
||||
]; |
||||
} |
||||
} |
||||
|
||||
return paramsArr; |
||||
}, Promise.resolve([])); |
||||
}; |
@ -0,0 +1,611 @@
|
||||
import { ModelTypes, UITypes } from 'nocodb-sdk'; |
||||
import { |
||||
columnNameParam, |
||||
csvExportOffsetParam, |
||||
exportTypeParam, |
||||
fieldsParam, |
||||
getNestedParams, |
||||
limitParam, |
||||
offsetParam, |
||||
referencedRowIdParam, |
||||
relationTypeParam, |
||||
rowIdParam, |
||||
sortParam, |
||||
whereParam |
||||
} from './params'; |
||||
import { csvExportResponseHeader } from './headers'; |
||||
import { SwaggerColumn } from '../getSwaggerColumnMetas'; |
||||
|
||||
export const getModelPaths = async (ctx: { |
||||
tableName: string; |
||||
orgs: string; |
||||
type: ModelTypes; |
||||
columns: SwaggerColumn[]; |
||||
projectName: string; |
||||
}): Promise<{ [path: string]: any }> => ({ |
||||
[`/api/v1/db/data/${ctx.orgs}/${ctx.projectName}/${ctx.tableName}`]: { |
||||
get: { |
||||
summary: `${ctx.tableName} list`, |
||||
operationId: 'db-table-row-list', |
||||
description: `List of all rows from ${ctx.tableName} ${ctx.type} and response data fields can be filtered based on query params.`, |
||||
tags: [ctx.tableName], |
||||
parameters: [ |
||||
fieldsParam, |
||||
sortParam, |
||||
whereParam, |
||||
limitParam, |
||||
offsetParam, |
||||
...(await getNestedParams(ctx.columns)) |
||||
], |
||||
responses: { |
||||
'200': { |
||||
description: 'OK', |
||||
content: { |
||||
'application/json': { |
||||
schema: getPaginatedResponseType(`${ctx.tableName}Response`) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
...(ctx.type === ModelTypes.TABLE |
||||
? { |
||||
post: { |
||||
summary: `${ctx.tableName} create`, |
||||
description: |
||||
'Insert a new row in table by providing a key value pair object where key refers to the column alias. All the required fields should be included with payload excluding `autoincrement` and column with default value.', |
||||
operationId: `${ctx.tableName.toLowerCase()}-create`, |
||||
responses: { |
||||
'200': { |
||||
description: 'OK', |
||||
content: { |
||||
'application/json': { |
||||
schema: { |
||||
$ref: `#/components/schemas/${ctx.tableName}Response` |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
tags: [ctx.tableName], |
||||
requestBody: { |
||||
content: { |
||||
'application/json': { |
||||
schema: { |
||||
$ref: `#/components/schemas/${ctx.tableName}Request` |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
: {}) |
||||
}, |
||||
[`/api/v1/db/data/${ctx.orgs}/${ctx.projectName}/${ctx.tableName}/{rowId}`]: { |
||||
parameters: [rowIdParam], |
||||
...(ctx.type === ModelTypes.TABLE |
||||
? { |
||||
get: { |
||||
parameters: [fieldsParam], |
||||
summary: `${ctx.tableName} read`, |
||||
description: |
||||
'Read a row data by using the **primary key** column value.', |
||||
operationId: `${ctx.tableName.toLowerCase()}-read`, |
||||
responses: { |
||||
'201': { |
||||
description: 'Created', |
||||
content: { |
||||
'application/json': { |
||||
schema: { |
||||
$ref: `#/components/schemas/${ctx.tableName}Response` |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
tags: [ctx.tableName] |
||||
}, |
||||
patch: { |
||||
summary: `${ctx.tableName} update`, |
||||
operationId: `${ctx.tableName.toLowerCase()}-update`, |
||||
description: |
||||
'Partial update row in table by providing a key value pair object where key refers to the column alias. You need to only include columns which you want to update.', |
||||
responses: { |
||||
'200': { |
||||
description: 'OK', |
||||
content: { |
||||
'application/json': { |
||||
schema: { |
||||
$ref: `#/components/schemas/${ctx.tableName}Request` |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
tags: [ctx.tableName], |
||||
requestBody: { |
||||
content: { |
||||
'application/json': { |
||||
schema: {} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
delete: { |
||||
summary: `${ctx.tableName} delete`, |
||||
operationId: `${ctx.tableName.toLowerCase()}-delete`, |
||||
responses: { |
||||
'200': { |
||||
description: 'OK' |
||||
} |
||||
}, |
||||
tags: [ctx.tableName], |
||||
description: |
||||
'Delete a row by using the **primary key** column value.' |
||||
} |
||||
} |
||||
: {}) |
||||
}, |
||||
[`/api/v1/db/data/${ctx.orgs}/${ctx.projectName}/${ctx.tableName}/count`]: { |
||||
get: { |
||||
summary: `${ctx.tableName} count`, |
||||
operationId: `${ctx.tableName.toLowerCase()}-count`, |
||||
description: 'Get rows count of a table by applying optional filters.', |
||||
tags: [ctx.tableName], |
||||
parameters: [whereParam], |
||||
responses: { |
||||
'200': { |
||||
description: 'OK', |
||||
content: { |
||||
'application/json': { |
||||
schema: {} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
...(ctx.type === ModelTypes.TABLE |
||||
? { |
||||
[`/api/v1/db/data/bulk/${ctx.orgs}/${ctx.projectName}/${ctx.tableName}`]: { |
||||
post: { |
||||
summary: `${ctx.tableName} bulk insert`, |
||||
description: |
||||
"To insert large amount of data in a single api call you can use this api. It's similar to insert method but here you can pass array of objects to insert into table. Array object will be key value paired column name and value.", |
||||
operationId: `${ctx.tableName.toLowerCase()}-bulk-create`, |
||||
responses: { |
||||
'200': { |
||||
description: 'OK', |
||||
content: { |
||||
'application/json': { |
||||
schema: {} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
tags: [ctx.tableName], |
||||
requestBody: { |
||||
content: { |
||||
'application/json': { |
||||
schema: {} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
patch: { |
||||
summary: `${ctx.tableName} bulk update`, |
||||
description: |
||||
"To update multiple records using it's primary key you can use this api. Bulk updated api accepts array object in which each object should contain it's primary columns value mapped to corresponding alias. In addition to primary key you can include the fields which you want to update", |
||||
operationId: `${ctx.tableName.toLowerCase()}-bulk-update`, |
||||
responses: { |
||||
'200': { |
||||
description: 'OK', |
||||
content: { |
||||
'application/json': { |
||||
schema: {} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
tags: [ctx.tableName], |
||||
requestBody: { |
||||
content: { |
||||
'application/json': { |
||||
schema: {} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
delete: { |
||||
summary: `${ctx.tableName} bulk delete by IDs`, |
||||
description: |
||||
"To delete multiple records using it's primary key you can use this api. Bulk delete api accepts array object in which each object should contain it's primary columns value mapped to corresponding alias.", |
||||
operationId: `${ctx.tableName.toLowerCase()}-bulk-delete`, |
||||
responses: { |
||||
'200': { |
||||
description: 'OK', |
||||
content: { |
||||
'application/json': { |
||||
schema: {} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
tags: [ctx.tableName], |
||||
requestBody: { |
||||
content: { |
||||
'application/json': { |
||||
schema: {} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
[`/api/v1/db/data/bulk/${ctx.orgs}/${ctx.projectName}/${ctx.tableName}/all`]: { |
||||
parameters: [whereParam], |
||||
patch: { |
||||
summary: `${ctx.tableName} Bulk update with conditions`, |
||||
description: |
||||
"This api helps you update multiple table rows in a single api call. You don't have to pass the record id instead you can filter records and apply the changes to filtered records. Payload is similar as normal update in which you can pass any partial row data to be updated.", |
||||
operationId: `${ctx.tableName.toLowerCase()}-bulk-update-all`, |
||||
responses: { |
||||
'200': { |
||||
description: 'OK', |
||||
content: { |
||||
'application/json': { |
||||
schema: {} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
tags: [ctx.tableName], |
||||
requestBody: { |
||||
content: { |
||||
'application/json': { |
||||
schema: {} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
delete: { |
||||
summary: 'Bulk delete with conditions', |
||||
description: |
||||
"This api helps you delete multiple table rows in a single api call. You don't have to pass the record id instead you can filter records and delete filtered records.", |
||||
operationId: `${ctx.tableName.toLowerCase()}-bulk-delete-all`, |
||||
responses: { |
||||
'200': { |
||||
description: 'OK', |
||||
content: { |
||||
'application/json': { |
||||
schema: {} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
tags: [ctx.tableName], |
||||
requestBody: { |
||||
content: { |
||||
'application/json': { |
||||
schema: {} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
|
||||
...(isRelationExist(ctx.columns) |
||||
? { |
||||
[`/api/v1/db/data/${ctx.orgs}/${ctx.projectName}/${ctx.tableName}/{rowId}/{relationType}/{columnName}`]: { |
||||
parameters: [ |
||||
rowIdParam, |
||||
relationTypeParam, |
||||
columnNameParam(ctx.columns) |
||||
], |
||||
get: { |
||||
summary: 'Relation row list', |
||||
operationId: `${ctx.tableName.toLowerCase()}-nested-list`, |
||||
responses: { |
||||
'200': { |
||||
description: 'OK', |
||||
content: { |
||||
'application/json': { |
||||
schema: {} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
tags: [ctx.tableName], |
||||
parameters: [limitParam, offsetParam] |
||||
} |
||||
}, |
||||
[`/api/v1/db/data/${ctx.orgs}/${ctx.projectName}/${ctx.tableName}/{rowId}/{relationType}/{columnName}/{refRowId}`]: { |
||||
parameters: [ |
||||
rowIdParam, |
||||
relationTypeParam, |
||||
columnNameParam(ctx.columns), |
||||
referencedRowIdParam |
||||
], |
||||
post: { |
||||
summary: 'Relation row add', |
||||
operationId: `${ctx.tableName.toLowerCase()}-nested-add`, |
||||
responses: { |
||||
'200': { |
||||
description: 'OK', |
||||
content: { |
||||
'application/json': { |
||||
schema: {} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
tags: [ctx.tableName], |
||||
parameters: [limitParam, offsetParam], |
||||
description: '' |
||||
}, |
||||
delete: { |
||||
summary: 'Relation row remove', |
||||
operationId: `${ctx.tableName.toLowerCase()}-nested-remove`, |
||||
responses: { |
||||
'200': { |
||||
description: 'OK', |
||||
content: { |
||||
'application/json': { |
||||
schema: {} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
tags: [ctx.tableName] |
||||
} |
||||
}, |
||||
[`/api/v1/db/data/${ctx.orgs}/${ctx.projectName}/${ctx.tableName}/{rowId}/{relationType}/{columnName}/exclude`]: { |
||||
parameters: [ |
||||
rowIdParam, |
||||
relationTypeParam, |
||||
columnNameParam(ctx.columns) |
||||
], |
||||
get: { |
||||
summary: |
||||
'Referenced tables rows excluding current records children/parent', |
||||
operationId: `${ctx.tableName.toLowerCase()}-nested-children-excluded-list`, |
||||
responses: { |
||||
'200': { |
||||
description: 'OK', |
||||
content: { |
||||
'application/json': { |
||||
schema: {} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
tags: [ctx.tableName], |
||||
parameters: [limitParam, offsetParam] |
||||
} |
||||
} |
||||
} |
||||
: {}) |
||||
} |
||||
: {}), |
||||
[`/api/v1/db/data/${ctx.orgs}/${ctx.projectName}/${ctx.tableName}/export/{type}`]: { |
||||
parameters: [exportTypeParam], |
||||
get: { |
||||
summary: 'Rows export', |
||||
operationId: `${ctx.tableName.toLowerCase()}-csv-export`, |
||||
description: |
||||
'Export all the records from a table.Currently we are only supports `csv` export.', |
||||
tags: [ctx.tableName], |
||||
wrapped: true, |
||||
responses: { |
||||
'200': { |
||||
description: 'OK', |
||||
content: { |
||||
'application/octet-stream': { |
||||
schema: {} |
||||
} |
||||
}, |
||||
headers: csvExportResponseHeader |
||||
} |
||||
}, |
||||
parameters: [csvExportOffsetParam] |
||||
} |
||||
} |
||||
}); |
||||
|
||||
export const getViewPaths = async (ctx: { |
||||
tableName: string; |
||||
viewName: string; |
||||
type: ModelTypes; |
||||
orgs: string; |
||||
projectName: string; |
||||
columns: SwaggerColumn[]; |
||||
}): Promise<any> => ({ |
||||
[`/api/v1/db/data/${ctx.orgs}/${ctx.projectName}/${ctx.tableName}/views/${ctx.viewName}`]: { |
||||
get: { |
||||
summary: `${ctx.viewName} list`, |
||||
operationId: `${ctx.tableName}-${ctx.viewName}-row-list`, |
||||
description: `List of all rows from ${ctx.viewName} grid view and data of fields can be filtered based on query params. Data and fields in a grid view will be filtered and sorted by default based on the applied options in Dashboard.`, |
||||
tags: [`${ctx.viewName} ( ${ctx.tableName} grid )`], |
||||
parameters: [ |
||||
fieldsParam, |
||||
sortParam, |
||||
whereParam, |
||||
...(await getNestedParams(ctx.columns)) |
||||
], |
||||
responses: { |
||||
'200': { |
||||
description: 'OK', |
||||
content: { |
||||
'application/json': { |
||||
schema: getPaginatedResponseType( |
||||
`${ctx.tableName}${ctx.viewName}GridResponse` |
||||
) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
...(ctx.type === ModelTypes.TABLE |
||||
? { |
||||
post: { |
||||
summary: `${ctx.viewName} create`, |
||||
description: |
||||
'Insert a new row in table by providing a key value pair object where key refers to the column alias. All the required fields should be included with payload excluding `autoincrement` and column with default value.', |
||||
operationId: `${ctx.tableName}-${ctx.viewName}-row-create`, |
||||
responses: { |
||||
'200': { |
||||
description: 'OK', |
||||
content: { |
||||
'application/json': { |
||||
schema: {} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
tags: [`${ctx.viewName} ( ${ctx.tableName} grid )`], |
||||
requestBody: { |
||||
content: { |
||||
'application/json': { |
||||
schema: { |
||||
$ref: `#/components/schemas/${ctx.tableName}${ctx.viewName}GridRequest` |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
: {}) |
||||
}, |
||||
[`/api/v1/db/data/${ctx.orgs}/${ctx.projectName}/${ctx.tableName}/views/${ctx.viewName}/count`]: { |
||||
get: { |
||||
summary: `${ctx.viewName} count`, |
||||
operationId: `${ctx.tableName}-${ctx.viewName}-row-count`, |
||||
description: '', |
||||
tags: [`${ctx.viewName} ( ${ctx.tableName} grid )`], |
||||
parameters: [whereParam], |
||||
responses: { |
||||
'200': { |
||||
description: 'OK', |
||||
content: { |
||||
'application/json': { |
||||
schema: { |
||||
type: 'object', |
||||
properties: { |
||||
count: 'number' |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
...(ctx.type === ModelTypes.TABLE |
||||
? { |
||||
[`/api/v1/db/data/${ctx.orgs}/${ctx.projectName}/${ctx.tableName}/views/${ctx.viewName}/{rowId}`]: { |
||||
parameters: [rowIdParam], |
||||
get: { |
||||
summary: `${ctx.viewName} read`, |
||||
description: |
||||
'Read a row data by using the **primary key** column value.', |
||||
operationId: `${ctx.tableName}-${ctx.viewName}-row-read`, |
||||
responses: { |
||||
'200': { |
||||
description: 'Created', |
||||
content: { |
||||
'application/json': { |
||||
schema: { |
||||
$ref: `#/components/schemas/${ctx.tableName}${ctx.viewName}GridResponse` |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
tags: [`${ctx.viewName} ( ${ctx.tableName} grid )`] |
||||
}, |
||||
patch: { |
||||
summary: `${ctx.viewName} update`, |
||||
description: |
||||
'Partial update row in table by providing a key value pair object where key refers to the column alias. You need to only include columns which you want to update.', |
||||
operationId: `${ctx.tableName}-${ctx.viewName}-row-update`, |
||||
responses: { |
||||
'200': { |
||||
description: 'OK', |
||||
content: { |
||||
'application/json': { |
||||
schema: {} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
tags: [`${ctx.viewName} ( ${ctx.tableName} grid )`], |
||||
requestBody: { |
||||
content: { |
||||
'application/json': { |
||||
schema: { |
||||
$ref: `#/components/schemas/${ctx.tableName}${ctx.viewName}GridRequest` |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
delete: { |
||||
summary: `${ctx.viewName} delete`, |
||||
operationId: `${ctx.tableName}-${ctx.viewName}-row-delete`, |
||||
responses: { |
||||
'200': { |
||||
description: 'OK' |
||||
} |
||||
}, |
||||
tags: [`${ctx.viewName} ( ${ctx.tableName} grid )`], |
||||
description: |
||||
'Delete a row by using the **primary key** column value.' |
||||
} |
||||
} |
||||
} |
||||
: {}), |
||||
[`/api/v1/db/data/${ctx.orgs}/${ctx.projectName}/${ctx.tableName}/views/${ctx.viewName}/export/{type}`]: { |
||||
parameters: [exportTypeParam], |
||||
get: { |
||||
summary: `${ctx.viewName} export`, |
||||
operationId: `${ctx.tableName}-${ctx.viewName}-row-export`, |
||||
description: |
||||
'Export all the records from a table view. Currently we are only supports `csv` export.', |
||||
tags: [`${ctx.viewName} ( ${ctx.tableName} grid )`], |
||||
wrapped: true, |
||||
responses: { |
||||
'200': { |
||||
description: 'OK', |
||||
content: { |
||||
'application/octet-stream': { |
||||
schema: {} |
||||
} |
||||
}, |
||||
headers: csvExportResponseHeader |
||||
} |
||||
}, |
||||
parameters: [] |
||||
} |
||||
} |
||||
}); |
||||
|
||||
function getPaginatedResponseType(type: string) { |
||||
return { |
||||
type: 'object', |
||||
properties: { |
||||
list: { |
||||
type: 'array', |
||||
items: { |
||||
$ref: `#/components/schemas/${type}` |
||||
} |
||||
}, |
||||
PageInfo: { |
||||
$ref: `#/components/schemas/Paginated` |
||||
} |
||||
} |
||||
}; |
||||
} |
||||
function isRelationExist(columns: SwaggerColumn[]) { |
||||
return columns.some( |
||||
c => c.column.uidt === UITypes.LinkToAnotherRecord && !c.column.system |
||||
); |
||||
} |
@ -0,0 +1,85 @@
|
||||
import { SwaggerColumn } from '../getSwaggerColumnMetas'; |
||||
|
||||
export const getModelSchemas = (ctx: { |
||||
tableName: string; |
||||
orgs: string; |
||||
projectName: string; |
||||
columns: Array<SwaggerColumn>; |
||||
}) => ({ |
||||
[`${ctx.tableName}Response`]: { |
||||
title: `${ctx.tableName} Response`, |
||||
type: 'object', |
||||
description: '', |
||||
'x-internal': false, |
||||
properties: { |
||||
...(ctx.columns?.reduce( |
||||
(colsObj, { title, virtual, column, ...fieldProps }) => ({ |
||||
...colsObj, |
||||
[title]: fieldProps |
||||
}), |
||||
{} |
||||
) || {}) |
||||
} |
||||
}, |
||||
[`${ctx.tableName}Request`]: { |
||||
title: `${ctx.tableName} Request`, |
||||
type: 'object', |
||||
description: '', |
||||
'x-internal': false, |
||||
properties: { |
||||
...(ctx.columns?.reduce( |
||||
(colsObj, { title, virtual, column, ...fieldProps }) => ({ |
||||
...colsObj, |
||||
...(virtual |
||||
? {} |
||||
: { |
||||
[title]: fieldProps |
||||
}) |
||||
}), |
||||
{} |
||||
) || {}) |
||||
} |
||||
} |
||||
}); |
||||
export const getViewSchemas = (ctx: { |
||||
tableName: string; |
||||
viewName: string; |
||||
orgs: string; |
||||
projectName: string; |
||||
columns: Array<SwaggerColumn>; |
||||
}) => ({ |
||||
[`${ctx.tableName}${ctx.viewName}GridResponse`]: { |
||||
title: `${ctx.tableName} : ${ctx.viewName} Response`, |
||||
type: 'object', |
||||
description: '', |
||||
'x-internal': false, |
||||
properties: { |
||||
...(ctx.columns?.reduce( |
||||
(colsObj, { title, virtual, column, ...fieldProps }) => ({ |
||||
...colsObj, |
||||
[title]: fieldProps |
||||
}), |
||||
{} |
||||
) || {}) |
||||
} |
||||
}, |
||||
[`${ctx.tableName}${ctx.viewName}GridRequest`]: { |
||||
title: `${ctx.tableName} : ${ctx.viewName} Request`, |
||||
type: 'object', |
||||
description: '', |
||||
'x-internal': false, |
||||
properties: { |
||||
...(ctx.columns?.reduce( |
||||
(colsObj, { title, virtual, column, ...fieldProps }) => ({ |
||||
...colsObj, |
||||
...(virtual |
||||
? {} |
||||
: { |
||||
[title]: fieldProps |
||||
}) |
||||
}), |
||||
{} |
||||
) || {}) |
||||
} |
||||
} |
||||
}); |
@ -0,0 +1,24 @@
|
||||
export default `<!DOCTYPE html>
|
||||
<html> |
||||
<head> |
||||
<title>NocoDB API Documentation</title> |
||||
<!-- needed for adaptive design --> |
||||
<meta charset="utf-8"/> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1"> |
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet"> |
||||
|
||||
<!-- |
||||
Redoc doesn't change outer page styles |
||||
--> |
||||
<style> |
||||
body { |
||||
margin: 0; |
||||
padding: 0; |
||||
} |
||||
</style> |
||||
</head> |
||||
<body> |
||||
<redoc spec-url='./swagger.json'></redoc> |
||||
<script src="https://cdn.jsdelivr.net/npm/redoc@latest/bundles/redoc.standalone.js"> </script> |
||||
</body> |
||||
</html>`;
|
@ -0,0 +1,35 @@
|
||||
// @ts-ignore
|
||||
import catchError from '../../helpers/catchError'; |
||||
import { Router } from 'express'; |
||||
import Model from '../../../../noco-models/Model'; |
||||
import getSwaggerJSON from './helpers/getSwaggerJSON'; |
||||
import Project from '../../../../noco-models/Project'; |
||||
import swaggerHtml from './swaggerHtml'; |
||||
import redocHtml from './redocHtml'; |
||||
|
||||
async function swaggerJson(req, res) { |
||||
const project = await Project.get(req.params.projectId); |
||||
const models = await Model.list({ |
||||
project_id: req.params.project_id, |
||||
base_id: null |
||||
}); |
||||
|
||||
const swagger = await getSwaggerJSON(project, models); |
||||
res.json(swagger); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
// todo: auth
|
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/swagger.json', |
||||
catchError(swaggerJson) |
||||
); |
||||
router.get('/api/v1/db/meta/projects/:projectId/swagger', (_req, res) => |
||||
res.send(swaggerHtml) |
||||
); |
||||
router.get('/api/v1/db/meta/projects/:projectId/redoc', (_req, res) => |
||||
res.send(redocHtml) |
||||
); |
||||
|
||||
export default router; |
File diff suppressed because one or more lines are too long
@ -1,119 +0,0 @@
|
||||
export default `<!DOCTYPE html>
|
||||
<html> |
||||
<head> |
||||
<title>NocoDB - Sign In</title> |
||||
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet"> |
||||
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@5.x/css/materialdesignicons.min.css" rel="stylesheet"> |
||||
<link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet"> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui"> |
||||
|
||||
<script src="https://unpkg.com/vue"></script> |
||||
</head> |
||||
<body> |
||||
<div id="app"> |
||||
<v-app> |
||||
<v-container> |
||||
<v-row class="justify-center"> |
||||
<v-col class="col-12 col-md-6"> |
||||
<v-form ref="form" v-model="validForm" ref="formType" class="ma-auto" |
||||
lazy-validation> |
||||
|
||||
|
||||
<v-text-field |
||||
name="input-10-2" |
||||
label="email" |
||||
type="email" |
||||
v-model="formdata.email" |
||||
:rules="[v => !!v || 'Email is required']" |
||||
></v-text-field> |
||||
|
||||
<v-text-field |
||||
name="input-10-2" |
||||
type="password" |
||||
label="Password" |
||||
v-model="formdata.password" |
||||
:rules="[v => !!v || 'Password is required']" |
||||
></v-text-field> |
||||
|
||||
<v-btn |
||||
:disabled="!validForm" |
||||
large |
||||
@click="signin" |
||||
> |
||||
Sign In |
||||
</v-btn> |
||||
|
||||
</v-form> |
||||
|
||||
<br> |
||||
<pre style="overflow: auto" v-if="success" v-html="success"></pre> |
||||
<v-alert v-else-if="errMsg" type="error"> |
||||
{{errMsg}} |
||||
</v-alert> |
||||
|
||||
|
||||
</v-col> |
||||
</v-row> |
||||
</v-container> |
||||
</v-app> |
||||
</div> |
||||
<script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script> |
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js"></script> |
||||
|
||||
<script> |
||||
var app = new Vue({ |
||||
el: '#app', |
||||
vuetify: new Vuetify(), |
||||
data: { |
||||
valid: null, |
||||
validForm: false, |
||||
greeting: 'Password Reset', |
||||
formdata: { |
||||
password: '', |
||||
newPassword: '' |
||||
}, |
||||
success: false, |
||||
errMsg: null |
||||
}, |
||||
methods: { |
||||
async signin() { |
||||
if (this.$refs.form.validate()) { |
||||
try { |
||||
const res = await axios.post('<%- baseUrl %>auth/signin', this.formdata); |
||||
this.success = res.data; |
||||
} catch (e) { |
||||
if (e.response && e.response.data && e.response.data.msg) { |
||||
this.errMsg = e.response.data.msg; |
||||
} else { |
||||
this.errMsg = 'Some error occurred'; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}) |
||||
</script> |
||||
</body> |
||||
</html>`;
|
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* 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 <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
@ -1,135 +0,0 @@
|
||||
export default `<!DOCTYPE html>
|
||||
<html> |
||||
<head> |
||||
<title>NocoDB - Sign Up</title> |
||||
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet"> |
||||
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@5.x/css/materialdesignicons.min.css" rel="stylesheet"> |
||||
<link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet"> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui"> |
||||
|
||||
<script src="https://unpkg.com/vue"></script> |
||||
</head> |
||||
<body> |
||||
<div id="app"> |
||||
<v-app> |
||||
<v-container> |
||||
<v-row class="justify-center"> |
||||
<v-col class="col-12 col-md-6"> |
||||
<v-form ref="form" v-model="validForm" ref="formType" class="ma-auto" |
||||
lazy-validation> |
||||
|
||||
|
||||
<v-text-field |
||||
name="input-10-2" |
||||
label="First Name" |
||||
type="text" |
||||
v-model="formdata.firstname" |
||||
:rules="[v => !!v || 'First Name is required']" |
||||
></v-text-field> |
||||
|
||||
<v-text-field |
||||
name="input-10-2" |
||||
label="Last Name" |
||||
type="text" |
||||
v-model="formdata.lastname" |
||||
:rules="[v => !!v || 'Last Name is required']" |
||||
></v-text-field> |
||||
|
||||
<v-text-field |
||||
name="input-10-2" |
||||
label="email" |
||||
type="email" |
||||
v-model="formdata.email" |
||||
:rules="[v => !!v || 'Email is required']" |
||||
></v-text-field> |
||||
|
||||
<v-text-field |
||||
name="input-10-2" |
||||
type="password" |
||||
label="Password" |
||||
v-model="formdata.password" |
||||
:rules="[v => !!v || 'Password is required']" |
||||
></v-text-field> |
||||
|
||||
<v-btn |
||||
:disabled="!validForm" |
||||
large |
||||
@click="signin" |
||||
> |
||||
Sign Up |
||||
</v-btn> |
||||
|
||||
</v-form> |
||||
|
||||
<br> |
||||
<pre style="overflow: auto" v-if="success" v-html="success"></pre> |
||||
<v-alert v-else-if="errMsg" type="error"> |
||||
{{errMsg}} |
||||
</v-alert> |
||||
|
||||
|
||||
</v-col> |
||||
</v-row> |
||||
</v-container> |
||||
</v-app> |
||||
</div> |
||||
<script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script> |
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js"></script> |
||||
|
||||
<script> |
||||
var app = new Vue({ |
||||
el: '#app', |
||||
vuetify: new Vuetify(), |
||||
data: { |
||||
valid: null, |
||||
validForm: false, |
||||
greeting: 'Password Reset', |
||||
formdata: { |
||||
password: '', |
||||
newPassword: '' |
||||
}, |
||||
success: false, |
||||
errMsg: null |
||||
}, |
||||
methods: { |
||||
async signin() { |
||||
if (this.$refs.form.validate()) { |
||||
try { |
||||
const res = await axios.post('<%- baseUrl %>auth/signup', this.formdata); |
||||
this.success = res.data; |
||||
} catch (e) { |
||||
if (e.response && e.response.data && e.response.data.msg) { |
||||
this.errMsg = e.response.data.msg; |
||||
} else { |
||||
this.errMsg = 'Some error occurred'; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}) |
||||
</script> |
||||
</body> |
||||
</html>`;
|
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* 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 <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
@ -1,425 +0,0 @@
|
||||
{ |
||||
"swagger": "2.0", |
||||
"info": { |
||||
"description": "Create APIs at the speed of your thoughts", |
||||
"version": "1.0.0", |
||||
"title": "NocoDB", |
||||
"contact": {} |
||||
}, |
||||
"host": "localhost:8080", |
||||
"basePath": "/", |
||||
"tags": [ |
||||
{ |
||||
"name": "common" |
||||
} |
||||
], |
||||
"schemes": [ |
||||
"http" |
||||
], |
||||
"paths": { |
||||
"/auth/signin": { |
||||
"post": { |
||||
"security": [ |
||||
], |
||||
"tags": [ |
||||
"Authentication" |
||||
], |
||||
"summary": "User login", |
||||
"description": "", |
||||
"operationId": "login", |
||||
"consumes": [ |
||||
"application/json" |
||||
], |
||||
"produces": [ |
||||
"application/json" |
||||
], |
||||
"parameters": [ |
||||
{ |
||||
"in": "body", |
||||
"name": "body", |
||||
"description": "Authentication user details", |
||||
"required": true, |
||||
"schema": { |
||||
"$ref": "#/definitions/userAuth" |
||||
} |
||||
} |
||||
], |
||||
"responses": { |
||||
"200": { |
||||
"description": "Authenticated successfully", |
||||
"schema": { |
||||
"$ref": "#/definitions/token" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
"/auth/signup": { |
||||
"post": { |
||||
"tags": [ |
||||
"Authentication" |
||||
], |
||||
"summary": "User signup", |
||||
"description": "", |
||||
"operationId": "signup", |
||||
"consumes": [ |
||||
"application/json" |
||||
], |
||||
"produces": [ |
||||
"application/json" |
||||
], |
||||
"parameters": [ |
||||
{ |
||||
"in": "body", |
||||
"name": "body", |
||||
"description": "Signup user details", |
||||
"required": true, |
||||
"schema": { |
||||
"$ref": "#/definitions/user" |
||||
} |
||||
} |
||||
], |
||||
"responses": { |
||||
"200": { |
||||
"description": "Registration success", |
||||
"schema": { |
||||
"$ref": "#/definitions/token" |
||||
} |
||||
}, |
||||
"400": { |
||||
"description": "Bad request" |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
"/auth/password/forgot": { |
||||
"post": { |
||||
"tags": [ |
||||
"Authentication" |
||||
], |
||||
"summary": "Password Forgot", |
||||
"description": "", |
||||
"operationId": "passwordForgot", |
||||
"consumes": [ |
||||
"application/json" |
||||
], |
||||
"produces": [ |
||||
"application/json" |
||||
], |
||||
"parameters": [ |
||||
{ |
||||
"in": "body", |
||||
"name": "body", |
||||
"description": "Email address", |
||||
"required": true, |
||||
"schema": { |
||||
"type": "object", |
||||
"properties": { |
||||
"email": { |
||||
"type": "string", |
||||
"required": true, |
||||
"example": "test@nocodb.com" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
], |
||||
"responses": { |
||||
"200": { |
||||
"description": "Mailed password reset link", |
||||
"schema": { |
||||
"type": "boolean" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
"/auth/email/validate/{tokenId}": { |
||||
"post": { |
||||
"tags": [ |
||||
"Authentication" |
||||
], |
||||
"summary": "Email validate link", |
||||
"description": "", |
||||
"operationId": "emailValidate", |
||||
"produces": [ |
||||
"application/json" |
||||
], |
||||
"parameters": [ |
||||
{ |
||||
"name": "tokenId", |
||||
"in": "path", |
||||
"description": "random token id received", |
||||
"required": true, |
||||
"type": "string", |
||||
"format": "uuid" |
||||
} |
||||
], |
||||
"responses": { |
||||
"200": { |
||||
"description": "Validated successfully" |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
"/auth/token/validate/{tokenId}": { |
||||
"get": { |
||||
"tags": [ |
||||
"Authentication" |
||||
], |
||||
"summary": "Validate password reset token", |
||||
"description": "", |
||||
"operationId": "passwordResetTokenValidate", |
||||
"produces": [ |
||||
"application/json" |
||||
], |
||||
"parameters": [ |
||||
{ |
||||
"name": "tokenId", |
||||
"in": "path", |
||||
"description": "random token id received", |
||||
"required": true, |
||||
"type": "string", |
||||
"format": "uuid" |
||||
} |
||||
], |
||||
"responses": { |
||||
"200": { |
||||
"description": "Validated successfully" |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
"/auth/password/reset/": { |
||||
"post": { |
||||
"tags": [ |
||||
"Authentication" |
||||
], |
||||
"summary": "Password reset", |
||||
"description": "", |
||||
"operationId": "passwordReset", |
||||
"consumes": [ |
||||
"application/json" |
||||
], |
||||
"produces": [ |
||||
"application/json" |
||||
], |
||||
"parameters": [ |
||||
{ |
||||
"name": "tokenId", |
||||
"in": "path", |
||||
"description": "random token id received", |
||||
"required": true, |
||||
"type": "string", |
||||
"format": "uuid" |
||||
}, |
||||
{ |
||||
"in": "body", |
||||
"name": "body", |
||||
"description": "Reset password details", |
||||
"required": true, |
||||
"schema": { |
||||
"type": "object", |
||||
"properties": { |
||||
"password": { |
||||
"type": "string", |
||||
"format": "password", |
||||
"example": "password", |
||||
"required": true |
||||
} |
||||
} |
||||
} |
||||
} |
||||
], |
||||
"responses": { |
||||
"200": { |
||||
"description": "Password reset successfully" |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
"/user/me": { |
||||
"get": { |
||||
"tags": [ |
||||
"Authentication" |
||||
], |
||||
"summary": "User details", |
||||
"description": "", |
||||
"operationId": "userDetails", |
||||
"produces": [ |
||||
"application/json" |
||||
], |
||||
"responses": { |
||||
"200": { |
||||
"description": "User details" |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
"/user": { |
||||
"put": { |
||||
"tags": [ |
||||
"Authentication" |
||||
], |
||||
"summary": "Update user details", |
||||
"description": "", |
||||
"operationId": "updateUserDetails", |
||||
"consumes": [ |
||||
"application/json" |
||||
], |
||||
"produces": [ |
||||
"application/json" |
||||
], |
||||
"responses": { |
||||
"200": { |
||||
"description": "User details" |
||||
} |
||||
}, |
||||
"parameters": [ |
||||
{ |
||||
"in": "body", |
||||
"name": "body", |
||||
"description": "Updated user details", |
||||
"required": true, |
||||
"schema": { |
||||
"$ref": "#/definitions/user" |
||||
} |
||||
} |
||||
] |
||||
} |
||||
}, |
||||
"/user/password/change": { |
||||
"post": { |
||||
"tags": [ |
||||
"Authentication" |
||||
], |
||||
"summary": "Update user details", |
||||
"description": "", |
||||
"operationId": "passwordChange", |
||||
"consumes": [ |
||||
"application/json" |
||||
], |
||||
"produces": [ |
||||
"application/json" |
||||
], |
||||
"responses": { |
||||
"200": { |
||||
"description": "User details" |
||||
} |
||||
}, |
||||
"parameters": [ |
||||
{ |
||||
"in": "body", |
||||
"name": "body", |
||||
"description": "Current password and new password", |
||||
"required": true, |
||||
"schema": { |
||||
"type": "object", |
||||
"properties": { |
||||
"currentPassword": { |
||||
"type": "string", |
||||
"format": "password", |
||||
"example": "password" |
||||
}, |
||||
"newPassword": { |
||||
"type": "string", |
||||
"format": "password", |
||||
"example": "newPassword" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
] |
||||
} |
||||
} |
||||
}, |
||||
"definitions": { |
||||
"userAuth": { |
||||
"type": "object", |
||||
"properties": { |
||||
"email": { |
||||
"type": "string", |
||||
"format": "email", |
||||
"nullable": false, |
||||
"example": "test@nocodb.com" |
||||
}, |
||||
"password": { |
||||
"type": "string", |
||||
"format": "password", |
||||
"nullable": false, |
||||
"example": "password" |
||||
} |
||||
} |
||||
}, |
||||
"token": { |
||||
"type": "object", |
||||
"properties": { |
||||
"token": { |
||||
"type": "string", |
||||
"format": "email", |
||||
"nullable": false, |
||||
"example": "< JWT Token >" |
||||
} |
||||
} |
||||
}, |
||||
"user": { |
||||
"allOf": [ |
||||
{ |
||||
"$ref": "#/definitions/userAuth" |
||||
}, |
||||
{ |
||||
"type": "object", |
||||
"properties": { |
||||
"id": { |
||||
"type": "integer", |
||||
"nullable": false, |
||||
"readOnly": true |
||||
}, |
||||
"firstname": { |
||||
"type": "string", |
||||
"nullable": false, |
||||
"example": "FirstName" |
||||
}, |
||||
"lastname": { |
||||
"type": "string", |
||||
"nullable": false, |
||||
"example": "LastName" |
||||
}, |
||||
"roles": { |
||||
"type": "object", |
||||
"readOnly": true |
||||
}, |
||||
"created_at": { |
||||
"type": "string", |
||||
"readOnly": true |
||||
}, |
||||
"updated_at": { |
||||
"type": "string", |
||||
"readOnly": true |
||||
}, |
||||
"email_verified": { |
||||
"type": "boolean", |
||||
"readOnly": true |
||||
} |
||||
} |
||||
} |
||||
] |
||||
} |
||||
}, |
||||
"security": [ |
||||
{ |
||||
"xcAuth": [] |
||||
} |
||||
], |
||||
"externalDocs": { |
||||
"description": "Find out more about NocoDB", |
||||
"url": "http://nocodb.com" |
||||
}, |
||||
"securityDefinitions": { |
||||
"xcAuth": { |
||||
"type": "apiKey", |
||||
"in": "header", |
||||
"name": "xc-auth" |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue