Browse Source

feat: add license get/set api and integrate

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/4134/head
Pranav C 2 years ago
parent
commit
c4af225697
  1. 54
      packages/nc-gui/components/admin/License.vue
  2. 2
      packages/nc-gui/components/admin/SignupSettings.vue
  3. 2
      packages/nc-gui/components/admin/UserList.vue
  4. 2
      packages/nc-gui/layouts/base.vue
  5. 2
      packages/nocodb/src/lib/meta/api/index.ts
  6. 39
      packages/nocodb/src/lib/meta/api/orgLicenseApis.ts
  7. 18
      packages/nocodb/src/lib/meta/api/orgUserApis.ts
  8. 46
      packages/nocodb/src/lib/models/Store.ts
  9. 1
      packages/nocodb/src/lib/utils/globals.ts
  10. 61
      scripts/sdk/swagger.json

54
packages/nc-gui/components/admin/License.vue

@ -1,48 +1,30 @@
<script lang="ts" setup>
import { message } from 'ant-design-vue'
import type { RequestParams, UserType } from 'nocodb-sdk'
import { extractSdkResponseErrorMsg, useApi, useCopy } from '#imports'
import { extractSdkResponseErrorMsg, useApi } from '#imports'
const { api, isLoading } = useApi()
let key = $ref('')
const { copy } = useCopy()
let tokens = $ref<UserType[]>([])
let currentPage = $ref(1)
const currentLimit = $ref(10)
const showUserModal = ref(false)
const searchText = ref<string>('')
const loadLicense = async () => {
try {
const response = await api.orgLicense.get()
const pagination = reactive({
total: 0,
pageSize: 10,
})
const loadTokens = async (page = currentPage, limit = currentLimit) => {
currentPage = page
key = response.key
} catch (e) {
message.error(await extractSdkResponseErrorMsg(e))
}
}
const setLicense = async () => {
try {
const response: any = await api.orgTokens.list({
query: {
limit,
offset: searchText.value.length === 0 ? (page - 1) * limit : 0,
},
} as RequestParams)
if (!response) return
pagination.total = response.pageInfo.totalRows ?? 0
pagination.pageSize = 10
tokens = response.list as UserType[]
} catch (e: any) {
await api.orgLicense.set({ key: key })
message.success('License key updated')
} catch (e) {
message.error(await extractSdkResponseErrorMsg(e))
}
}
loadTokens()
loadLicense()
</script>
@ -50,8 +32,10 @@ loadTokens()
<div class="h-full overflow-y-scroll scrollbar-thin-dull pt-2">
<div class="text-xl mt-4">License</div>
<a-divider class="!my-3" />
<a-textarea placeholder="License key" class="!mt-4 max-w-[300px]"></a-textarea>
<a-button class="mt-4 float-right" type="primary">Save license key</a-button>
<div class="">
<a-textarea v-model:value="key" placeholder="License key" class="!mt-4 !max-w-[600px]"></a-textarea>
</div>
<a-button class="mt-4" @click="setLicense" type="primary">Save license key</a-button>
</div>
</template>

2
packages/nc-gui/components/admin/SignupSettings.vue

@ -1,6 +1,6 @@
<template>
<div>
<div class="text-xl mt-4">Settings</div>
<div class="text-xl">Settings</div>
<a-divider class="!my-3" />
<a-form-item>
<a-checkbox name="virtual">Enable user signup</a-checkbox>

2
packages/nc-gui/components/admin/UserList.vue

@ -123,7 +123,7 @@ const copyPasswordResetUrl = async (user: User) => {
<template>
<div>
<div class="text-xl mt-4">User Management</div>
<div class="text-xl ">User Management</div>
<a-divider class="!my-3" />
<div class="max-w-[900px] mx-auto p-4">
<div class="py-2 flex gap-4 items-center">

2
packages/nc-gui/layouts/base.vue

@ -39,7 +39,7 @@ hooks.hook('page:finish', () => {
<a-layout class="!flex-col">
<a-layout-header
v-if="!route.meta.public && signedIn && !route.meta.hideHeader"
class="flex !bg-primary items-center text-white pl-4 pr-5 shadow-lg"
class="flex !bg-primary items-center text-white pl-4 pr-5"
>
<div
v-if="!route.params.projectType"

2
packages/nocodb/src/lib/meta/api/index.ts

@ -1,4 +1,5 @@
import { Tele } from '../../utils/Tele';
import orgLicenseApis from './orgLicenseApis'
import orgTokenApis from './orgTokenApis';
import orgUserApis from './orgUserApis';
import projectApis from './projectApis';
@ -91,6 +92,7 @@ export default function (router: Router, server) {
router.use(projectUserApis);
router.use(orgUserApis);
router.use(orgTokenApis);
router.use(orgLicenseApis);
router.use(sharedBaseApis);
router.use(modelVisibilityApis);
router.use(metaDiffApis);

39
packages/nocodb/src/lib/meta/api/orgLicenseApis.ts

@ -0,0 +1,39 @@
import { Router } from 'express';
import { OrgUserRoles } from '../../../enums/OrgUserRoles';
import Store from '../../models/Store';
import { metaApiMetrics } from '../helpers/apiMetrics';
import ncMetaAclMw from '../helpers/ncMetaAclMw';
const LICENSE_KEY = 'nc-license-key';
async function licenseGet(_req, res) {
const license = await Store.get(LICENSE_KEY);
res.json({ key: license?.value });
}
async function licenseSet(req, res) {
await Store.saveOrUpdate({ value: req.body.key, key: LICENSE_KEY });
res.json({ msg: 'License key saved' });
}
const router = Router({ mergeParams: true });
router.get(
'/api/v1/license',
metaApiMetrics,
ncMetaAclMw(licenseGet, 'licenseGet', {
allowedRoles: [OrgUserRoles.SUPER],
blockApiTokenAccess: true,
})
);
router.post(
'/api/v1/license',
metaApiMetrics,
ncMetaAclMw(licenseSet, 'licenseSet', {
allowedRoles: [OrgUserRoles.SUPER],
blockApiTokenAccess: true,
})
);
export default router;

18
packages/nocodb/src/lib/meta/api/orgUserApis.ts

@ -276,9 +276,27 @@ router.post(
blockApiTokenAccess: true,
})
);
router.post(
'/api/v1/users/:userId/generate-reset-url',
metaApiMetrics,
ncMetaAclMw(generateResetUrl, 'generateResetUrl', {
allowedRoles: [OrgUserRoles.SUPER],
blockApiTokenAccess: true,
}));
router.get(
'/api/v1/users/settings',
metaApiMetrics,
ncMetaAclMw(generateResetUrl, 'generateResetUrl', {
allowedRoles: [OrgUserRoles.SUPER],
blockApiTokenAccess: true,
})
);
router.post(
'/api/v1/users/settings',
metaApiMetrics,
ncMetaAclMw(generateResetUrl, 'generateResetUrl', {
allowedRoles: [OrgUserRoles.SUPER],
blockApiTokenAccess: true,

46
packages/nocodb/src/lib/models/Store.ts

@ -0,0 +1,46 @@
import { NcError } from '../meta/helpers/catchError';
import { extractProps } from '../meta/helpers/extractProps';
import Noco from '../Noco';
import { MetaTable } from '../utils/globals';
import { SortType } from 'nocodb-sdk';
export default class Store {
key?: string;
value?: string;
type?: string;
env?: string;
tag?: string;
project_id?: string;
db_alias?: string;
constructor(data: Partial<SortType>) {
Object.assign(this, data);
}
public static get(key: string, ncMeta = Noco.ncMeta): Promise<Store> {
return ncMeta.metaGet(null, null, MetaTable.STORE, { key });
}
static async saveOrUpdate(store: Store, ncMeta = Noco.ncMeta) {
if (!store.key) {
NcError.badRequest('Key is required');
}
const insertObj = extractProps(store, [
'key',
'value',
'type',
'env',
'tag',
]);
const existing = await Store.get(store.key, ncMeta);
if (existing) {
await ncMeta.metaUpdate(null, null, MetaTable.STORE, insertObj, {
key: store.key,
});
} else {
await ncMeta.metaInsert(null, null, MetaTable.STORE, insertObj);
}
}
}

1
packages/nocodb/src/lib/utils/globals.ts

@ -37,6 +37,7 @@ export enum MetaTable {
API_TOKENS = 'nc_api_tokens',
SYNC_SOURCE = 'nc_sync_source_v2',
SYNC_LOGS = 'nc_sync_logs_v2',
STORE = 'nc_store',
}
export const orderedMetaTables = [

61
scripts/sdk/swagger.json

@ -464,8 +464,11 @@
"$ref": "#/components/schemas/ApiToken"
},
{
created_by: {
type: "string"
"type": "object",
"properties": {
"created_by": {
"type": "string"
}
}
}
],
@ -515,6 +518,60 @@
]
}
},
"/api/v1/license": {
"get": {
"summary": "App license get",
"operationId": "org-license-get",
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"key": {
"type": "string"
}
}
}
}
}
}
},
"description": "",
"tags": [
"Org license"
]
},
"parameters": [],
"post": {
"summary": "App license get",
"operationId": "org-license-set",
"responses": {
"200": {
"description": "OK"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"key": {
"type": "string"
}
}
}
}
}
},
"tags": [
"Org license"
]
}
},
"/api/v1/tokens/{token}": {
"parameters": [
{

Loading…
Cancel
Save