Browse Source

fix: pull latest changes

Signed-off-by: mertmit <mertmit99@gmail.com>
pull/1620/head
mertmit 2 years ago
parent
commit
a9f2571e2b
  1. 92
      .github/workflows/ci-cd.yml
  2. 16
      .github/workflows/release-docker.yml
  3. 8
      .github/workflows/release-nightly-dev.yml
  4. 20
      .github/workflows/release-npm.yml
  5. 5
      .run/Clear metadb.run.xml
  6. 14
      .run/build.run.xml
  7. 12
      .run/dev.run.xml
  8. 14
      .run/watch_run_mysql.run.xml
  9. 2
      README.md
  10. 4
      package.json
  11. 1
      packages/nc-gui/components/import/dropOrSelectFileModal.vue
  12. 7
      packages/nc-gui/components/project/appStore.vue
  13. 20
      packages/nc-gui/components/project/spreadsheet/components/columnFilter.vue
  14. 2
      packages/nc-gui/components/project/spreadsheet/components/editColumn.vue
  15. 2
      packages/nc-gui/components/project/spreadsheet/components/editVirtualColumn.vue
  16. 13
      packages/nc-gui/components/project/spreadsheet/components/editableCell/editableAttachmentCell.vue
  17. 21
      packages/nc-gui/components/project/spreadsheet/components/expandedForm.vue
  18. 2
      packages/nc-gui/components/project/spreadsheet/components/fieldsMenu.vue
  19. 4
      packages/nc-gui/components/project/spreadsheet/components/headerCell.vue
  20. 35
      packages/nc-gui/components/project/spreadsheet/components/moreActions.vue
  21. 6
      packages/nc-gui/components/project/spreadsheet/components/pagination.vue
  22. 4
      packages/nc-gui/components/project/spreadsheet/components/sortListMenu.vue
  23. 29
      packages/nc-gui/components/project/spreadsheet/components/spreadsheetNavDrawer.vue
  24. 20
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/belongsToCell.vue
  25. 32
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/components/listChildItems.vue
  26. 37
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/components/listItems.vue
  27. 5
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/formulaCell.vue
  28. 8
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/hasManyCell.vue
  29. 24
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/manyToManyCell.vue
  30. 2
      packages/nc-gui/components/project/spreadsheet/components/virtualHeaderCell.vue
  31. 34
      packages/nc-gui/components/project/spreadsheet/mixins/spreadsheet.js
  32. 75
      packages/nc-gui/components/project/spreadsheet/public/xcForm.vue
  33. 1
      packages/nc-gui/components/project/spreadsheet/public/xcKanban.vue
  34. 181
      packages/nc-gui/components/project/spreadsheet/public/xcTable.vue
  35. 90
      packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue
  36. 83
      packages/nc-gui/components/project/spreadsheet/views/formView.vue
  37. 4
      packages/nc-gui/components/project/spreadsheet/views/galleryView.vue
  38. 6
      packages/nc-gui/components/project/spreadsheet/views/kanbanView.vue
  39. 19
      packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue
  40. 1
      packages/nc-gui/components/project/table.vue
  41. 142
      packages/nc-gui/components/projectList/createNewProjectBtn.vue
  42. 10
      packages/nc-gui/helpers/sqlUi/MssqlUi.js
  43. 4
      packages/nc-gui/layouts/default.vue
  44. 8
      packages/nc-gui/mixins/colors.js
  45. 4
      packages/nc-gui/mixins/device.js
  46. 419
      packages/nc-gui/package-lock.json
  47. 4
      packages/nc-gui/package.json
  48. 229
      packages/nc-gui/pages/projects/list.vue
  49. 63
      packages/nc-gui/plugins/tele.js
  50. 8
      packages/nc-gui/store/project.js
  51. 4
      packages/nocodb-sdk/package-lock.json
  52. 1796
      packages/nocodb-sdk/src/lib/Api.ts
  53. 451
      packages/nocodb/package-lock.json
  54. 4
      packages/nocodb/package.json
  55. 13
      packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSqlv2.ts
  56. 1
      packages/nocodb/src/lib/dataMapper/lib/sql/CustomKnex.ts
  57. 35
      packages/nocodb/src/lib/dataMapper/lib/sql/formulav2/formulaQueryBuilderv2.ts
  58. 3
      packages/nocodb/src/lib/migrator/SqlMigrator/lib/KnexMigratorv2.ts
  59. 2
      packages/nocodb/src/lib/noco-models/Audit.ts
  60. 26
      packages/nocodb/src/lib/noco-models/Column.ts
  61. 2
      packages/nocodb/src/lib/noco-models/Filter.ts
  62. 28
      packages/nocodb/src/lib/noco-models/Model.ts
  63. 9
      packages/nocodb/src/lib/noco-models/ModelRoleVisibility.ts
  64. 2
      packages/nocodb/src/lib/noco-models/ProjectUser.ts
  65. 7
      packages/nocodb/src/lib/noco-models/User.ts
  66. 3
      packages/nocodb/src/lib/noco/Noco.ts
  67. 14
      packages/nocodb/src/lib/noco/meta/api/apiTokenApis.ts
  68. 48
      packages/nocodb/src/lib/noco/meta/api/attachmentApis.ts
  69. 27
      packages/nocodb/src/lib/noco/meta/api/auditApis.ts
  70. 4
      packages/nocodb/src/lib/noco/meta/api/cacheApis.ts
  71. 39
      packages/nocodb/src/lib/noco/meta/api/columnApis.ts
  72. 20
      packages/nocodb/src/lib/noco/meta/api/dataApis/bulkDataAliasApis.ts
  73. 86
      packages/nocodb/src/lib/noco/meta/api/dataApis/dataAliasApis.ts
  74. 33
      packages/nocodb/src/lib/noco/meta/api/dataApis/dataAliasExportApis.ts
  75. 288
      packages/nocodb/src/lib/noco/meta/api/dataApis/dataAliasNestedApis.ts
  76. 31
      packages/nocodb/src/lib/noco/meta/api/dataApis/dataApis.ts
  77. 179
      packages/nocodb/src/lib/noco/meta/api/dataApis/helpers.ts
  78. 11
      packages/nocodb/src/lib/noco/meta/api/dataApis/index.ts
  79. 22
      packages/nocodb/src/lib/noco/meta/api/dataApis/oldDataApis.ts
  80. 164
      packages/nocodb/src/lib/noco/meta/api/exportApis.ts
  81. 64
      packages/nocodb/src/lib/noco/meta/api/filterApis.ts
  82. 20
      packages/nocodb/src/lib/noco/meta/api/formViewApis.ts
  83. 5
      packages/nocodb/src/lib/noco/meta/api/formViewColumnApis.ts
  84. 40
      packages/nocodb/src/lib/noco/meta/api/galleryViewApis.ts
  85. 5
      packages/nocodb/src/lib/noco/meta/api/gridViewApis.ts
  86. 10
      packages/nocodb/src/lib/noco/meta/api/gridViewColumnApis.ts
  87. 27
      packages/nocodb/src/lib/noco/meta/api/hookApis.ts
  88. 24
      packages/nocodb/src/lib/noco/meta/api/hookFilterApis.ts
  89. 39
      packages/nocodb/src/lib/noco/meta/api/index.ts
  90. 36
      packages/nocodb/src/lib/noco/meta/api/metaDiffApis.ts
  91. 4
      packages/nocodb/src/lib/noco/meta/api/modelVisibilityApis.ts
  92. 22
      packages/nocodb/src/lib/noco/meta/api/pluginApis.ts
  93. 27
      packages/nocodb/src/lib/noco/meta/api/projectApis.ts
  94. 20
      packages/nocodb/src/lib/noco/meta/api/projectUserApis.ts
  95. 98
      packages/nocodb/src/lib/noco/meta/api/publicApis/publicDataApis.ts
  96. 37
      packages/nocodb/src/lib/noco/meta/api/publicApis/publicDataExportApis.ts
  97. 24
      packages/nocodb/src/lib/noco/meta/api/publicApis/publicMetaApis.ts
  98. 31
      packages/nocodb/src/lib/noco/meta/api/sharedBaseApis.ts
  99. 28
      packages/nocodb/src/lib/noco/meta/api/sortApis.ts
  100. 31
      packages/nocodb/src/lib/noco/meta/api/tableApis.ts
  101. Some files were not shown because too many files have changed in this diff Show More

92
.github/workflows/ci-cd.yml

@ -58,10 +58,11 @@ jobs:
wait-on-timeout: 1200
config-file: scripts/cypress/cypress.json
- name: Upload screenshots
if: always()
uses: actions/upload-artifact@v2
with:
name: restTableOps-snapshots
path: scripts/cypress/videos
path: scripts/cypress/screenshots
retention-days: 2
cypress-pg-restViews-run-cache:
runs-on: ubuntu-20.04
@ -102,10 +103,11 @@ jobs:
wait-on-timeout: 1200
config-file: scripts/cypress/cypress.json
- name: Upload screenshots
if: always()
uses: actions/upload-artifact@v2
with:
name: restTableOps-snapshots
path: scripts/cypress/videos
path: scripts/cypress/screenshots
retention-days: 2
cypress-pg-restRoles-run-cache:
runs-on: ubuntu-20.04
@ -146,10 +148,11 @@ jobs:
wait-on-timeout: 1200
config-file: scripts/cypress/cypress.json
- name: Upload screenshots
if: always()
uses: actions/upload-artifact@v2
with:
name: restTableOps-snapshots
path: scripts/cypress/videos
path: scripts/cypress/screenshots
retention-days: 2
cypress-pg-restMisc-run-cache:
runs-on: ubuntu-20.04
@ -190,10 +193,11 @@ jobs:
wait-on-timeout: 1200
config-file: scripts/cypress/cypress.json
- name: Upload screenshots
if: always()
uses: actions/upload-artifact@v2
with:
name: restTableOps-snapshots
path: scripts/cypress/videos
path: scripts/cypress/screenshots
retention-days: 2
cypress-restTableOps-run-cache:
runs-on: ubuntu-20.04
@ -225,7 +229,7 @@ jobs:
uses: cypress-io/github-action@v2
with:
start: |
npm run build:common
npm run build:common
npm run start:api:cache
npm run start:web
docker-compose -f ./scripts/docker-compose-cypress.yml up -d
@ -234,10 +238,11 @@ jobs:
wait-on-timeout: 1200
config-file: scripts/cypress/cypress.json
- name: Upload screenshots
if: always()
uses: actions/upload-artifact@v2
with:
name: restTableOps-snapshots
path: scripts/cypress/videos
path: scripts/cypress/screenshots
retention-days: 2
cypress-restViews-run-cache:
runs-on: ubuntu-20.04
@ -278,10 +283,11 @@ jobs:
wait-on-timeout: 1200
config-file: scripts/cypress/cypress.json
- name: Upload screenshots
if: always()
uses: actions/upload-artifact@v2
with:
name: restTableOps-snapshots
path: scripts/cypress/videos
path: scripts/cypress/screenshots
retention-days: 2
cypress-restRoles-run-cache:
runs-on: ubuntu-20.04
@ -322,10 +328,11 @@ jobs:
wait-on-timeout: 1200
config-file: scripts/cypress/cypress.json
- name: Upload screenshots
if: always()
uses: actions/upload-artifact@v2
with:
name: restTableOps-snapshots
path: scripts/cypress/videos
path: scripts/cypress/screenshots
retention-days: 2
cypress-restMisc-run-cache:
runs-on: ubuntu-20.04
@ -366,10 +373,11 @@ jobs:
wait-on-timeout: 1200
config-file: scripts/cypress/cypress.json
- name: Upload screenshots
if: always()
uses: actions/upload-artifact@v2
with:
name: restTableOps-snapshots
path: scripts/cypress/videos
path: scripts/cypress/screenshots
retention-days: 2
cypress-xcdb-restTableOps-run-cache:
runs-on: ubuntu-20.04
@ -410,10 +418,11 @@ jobs:
wait-on-timeout: 1200
config-file: scripts/cypress/cypress.json
- name: Upload screenshots
if: always()
uses: actions/upload-artifact@v2
with:
name: restTableOps-snapshots
path: scripts/cypress/videos
path: scripts/cypress/screenshots
retention-days: 2
cypress-xcdb-restViews-run-cache:
runs-on: ubuntu-20.04
@ -454,10 +463,11 @@ jobs:
wait-on-timeout: 1200
config-file: scripts/cypress/cypress.json
- name: Upload screenshots
if: always()
uses: actions/upload-artifact@v2
with:
name: restTableOps-snapshots
path: scripts/cypress/videos
path: scripts/cypress/screenshots
retention-days: 2
cypress-xcdb-restRoles-run-cache:
runs-on: ubuntu-20.04
@ -498,10 +508,11 @@ jobs:
wait-on-timeout: 1200
config-file: scripts/cypress/cypress.json
- name: Upload screenshots
if: always()
uses: actions/upload-artifact@v2
with:
name: restTableOps-snapshots
path: scripts/cypress/videos
path: scripts/cypress/screenshots
retention-days: 2
cypress-xcdb-restMisc-run-cache:
runs-on: ubuntu-20.04
@ -542,38 +553,39 @@ jobs:
wait-on-timeout: 1200
config-file: scripts/cypress/cypress.json
- name: Upload screenshots
if: always()
uses: actions/upload-artifact@v2
with:
name: restTableOps-snapshots
path: scripts/cypress/videos
path: scripts/cypress/screenshots
retention-days: 2
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Check for update
run: |
echo "CHANGED=$([[ $(lerna ls --since ${{github.event.before}} | grep nocodb) = nocodb ]] && echo 'OK')" >> $GITHUB_ENV
- name: Test Mysql REST APIs
if: ${{ env.CHANGED == 'OK' }}
run: cd ./packages/nocodb/ && docker-compose run xc-test-mysql
- name: Test Mysql GraphQL APIs
if: ${{ env.CHANGED == 'OK' }}
run: cd ./packages/nocodb/ && docker-compose run xc-test-gql-mysql
# - name: Test MSSQL REST APIs
# run: cd ./packages/nocodb/ && docker-compose run xc-test-mssql
# - name: Test MSSQL GraphQL APIs
# run: cd ./packages/nocodb/ && docker-compose run xc-test-gql-mssql
#
- name: Test PostgreSQL REST APIs
if: ${{ env.CHANGED == 'OK' }}
run: cd ./packages/nocodb/ && docker-compose run xc-test-pg
- name: Test PostgreSQL GraphQL APIs
if: ${{ env.CHANGED == 'OK' }}
run: cd ./packages/nocodb/ && docker-compose run xc-test-gql-pg
# docker:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v2
# with:
# fetch-depth: 0
# - name: Check for update
# run: |
# echo "CHANGED=$([[ $(lerna ls --since ${{github.event.before}} | grep nocodb) = nocodb ]] && echo 'OK')" >> $GITHUB_ENV
# - name: Test Mysql REST APIs
# if: ${{ env.CHANGED == 'OK' }}
# run: cd ./packages/nocodb/ && docker-compose run xc-test-mysql
# - name: Test Mysql GraphQL APIs
# if: ${{ env.CHANGED == 'OK' }}
# run: cd ./packages/nocodb/ && docker-compose run xc-test-gql-mysql
#
# # - name: Test MSSQL REST APIs
# # run: cd ./packages/nocodb/ && docker-compose run xc-test-mssql
# # - name: Test MSSQL GraphQL APIs
# # run: cd ./packages/nocodb/ && docker-compose run xc-test-gql-mssql
# #
# - name: Test PostgreSQL REST APIs
# if: ${{ env.CHANGED == 'OK' }}
# run: cd ./packages/nocodb/ && docker-compose run xc-test-pg
# - name: Test PostgreSQL GraphQL APIs
# if: ${{ env.CHANGED == 'OK' }}
# run: cd ./packages/nocodb/ && docker-compose run xc-test-gql-pg
#
# - name: Test SQLite3 REST APIs
# run: cd ./packages/nocodb/ && docker-compose run xc-test-sqlite

16
.github/workflows/release-docker.yml

@ -42,7 +42,7 @@ jobs:
working-directory: ./packages/nocodb
strategy:
matrix:
node-version: [12]
node-version: [14]
steps:
- name: Get Docker Repository
id: get-docker-repository
@ -73,6 +73,20 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- name: upgrade packages for nightly build
if: ${{ github.event.inputs.targetEnv == 'DEV' || inputs.targetEnv == 'DEV' }}
run: |
targetEnv=${{ github.event.inputs.targetEnv || inputs.targetEnv }} targetVersion=${{ github.event.inputs.tag || inputs.tag }} node scripts/bumpNocodbSdkVersion.js
cd packages/nocodb-sdk
npm install && npm run build
cd ../..
targetEnv=${{ github.event.inputs.targetEnv || inputs.targetEnv }} node scripts/upgradeNocodbSdk.js
cd packages/nc-gui
npm install
targetEnv=${{ github.event.inputs.targetEnv || inputs.targetEnv }} targetVersion=${{ github.event.inputs.tag || inputs.tag }} npm run build:copy
cd ../..
targetEnv=${{ github.event.inputs.targetEnv || inputs.targetEnv }} node scripts/upgradeNcGui.js
- uses: bahmutov/npm-install@v1
with:
working-directory: ${{ env.working-directory }}

8
.github/workflows/release-nightly-dev.yml

@ -12,8 +12,8 @@ on:
- DEV
# - PROD
schedule:
# at the end of every day
- cron: '0 0 * * *'
# every 6 hours
- cron: '0 */6 * * *'
jobs:
# enrich tag for nightly auto release
@ -26,11 +26,11 @@ jobs:
# Get current date
CURRENT_DATE=$(date +"%Y%m%d")
CURRENT_TIME=$(date +"%H%M")
TAG_NAME=${CURRENT_DATE}
TAG_NAME=${CURRENT_DATE}-${CURRENT_TIME}
IS_DAILY='Y'
# Set the tag
if [[ ${{ github.event_name }} == 'workflow_dispatch' ]]; then
TAG_NAME=${TAG_NAME}-${CURRENT_TIME}
TAG_NAME=${TAG_NAME}
IS_DAILY='N'
fi
echo "::set-output name=NIGHTLY_BUILD_TAG::${TAG_NAME}"

20
.github/workflows/release-npm.yml

@ -37,27 +37,27 @@ jobs:
working-directory: ./packages/nocodb
strategy:
matrix:
node-version: [12]
node-version: [14]
steps:
- uses: actions/checkout@v2
# Setup .npmrc file to publish to npm
- uses: actions/setup-node@v2
- name: Checkout
uses: actions/checkout@v2
- name: NPM Setup and Publish with ${{ matrix.node-version }}
# Setup .npmrc file to publish to npm
uses: actions/setup-node@v2
with:
node-version: '16.x'
node-version: ${{ matrix.node-version }}
registry-url: 'https://registry.npmjs.org'
- run: |
targetEnv=${{ github.event.inputs.targetEnv || inputs.targetEnv }} targetVersion=${{ github.event.inputs.tag || inputs.tag }} node scripts/bumpNocodbSdkVersion.js
cd packages/nocodb-sdk
targetEnv=${{ github.event.inputs.targetEnv || inputs.targetEnv }} targetVersion=${{ github.event.inputs.tag || inputs.tag }} node ../../scripts/bumpNocodbSdkVersion.js
npm install && npm run build
npm publish
npm install && npm run build && npm publish
cd ../..
targetEnv=${{ github.event.inputs.targetEnv || inputs.targetEnv }} node scripts/upgradeNocodbSdk.js
cd packages/nc-gui
npm install
targetEnv=${{ github.event.inputs.targetEnv || inputs.targetEnv }} targetVersion=${{ github.event.inputs.tag || inputs.tag }} npm run build:copy:jsdeliver
cd ../..
npm install
targetEnv=${{ github.event.inputs.targetEnv || inputs.targetEnv }} targetVersion=${{ github.event.inputs.tag || inputs.tag }} node scripts/upgradeNcGui.js && cd packages/nocodb && npm install && npm run obfuscate:build:publish
targetEnv=${{ github.event.inputs.targetEnv || inputs.targetEnv }} node scripts/upgradeNcGui.js && cd packages/nocodb && npm install && npm run obfuscate:build:publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Create Pull Request

5
.run/Clear metadb.run.xml

@ -0,0 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Drop metadb" type="NodeJSConfigurationType" path-to-js-file="deleteMetaDb.js" working-dir="$PROJECT_DIR$/packages/nocodb/src/example">
<method v="2" />
</configuration>
</component>

14
.run/build.run.xml

@ -0,0 +1,14 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Build Nc Common" type="js.build_tools.npm">
<package-json value="$PROJECT_DIR$/packages/nocodb-sdk/package.json" />
<command value="run" />
<scripts>
<script value="build" />
</scripts>
<node-interpreter value="project" />
<envs>
<env name="NC_DISABLE_CACHE" value="true" />
</envs>
<method v="2" />
</configuration>
</component>

12
.run/dev.run.xml

@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run GUI" type="js.build_tools.npm">
<package-json value="$PROJECT_DIR$/packages/nc-gui/package.json" />
<command value="run" />
<scripts>
<script value="dev" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
</component>

14
.run/watch_run_mysql.run.xml

@ -0,0 +1,14 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run NocoDB Mysql" type="js.build_tools.npm" activateToolWindowBeforeRun="false">
<package-json value="$PROJECT_DIR$/packages/nocodb/package.json" />
<command value="run" />
<scripts>
<script value="watch:run:mysql" />
</scripts>
<node-interpreter value="project" />
<envs>
<env name="NC_DISABLE_CACHE" value="true" />
</envs>
<method v="2" />
</configuration>
</component>

2
README.md

@ -13,7 +13,7 @@ Turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart-spreadshe
<div align="center">
[![Build Status](https://travis-ci.org/dwyl/esta.svg?branch=master)](https://travis-ci.com/github/NocoDB/NocoDB)
[![Node version](https://badgen.net/npm/node/next)](http://nodejs.org/download/)
[![Node version](https://img.shields.io/badge/node-%3E%3D%2014.18.0-brightgreen)](http://nodejs.org/download/)
[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)
</div>

4
package.json

@ -14,9 +14,9 @@
},
"scripts": {
"build:common": "cd ./packages/nocodb-sdk; npm install; npm run build",
"start:api": "cd ./packages/nocodb; npm install; NC_DISABLE_CACHE=true NC_DISABLE_TELE=true npm run watch:run:cypress",
"start:api": "cd ./packages/nocodb; npm install; NC_DISABLE_CACHE=true NC_DISABLE_TELE=true npm run watch:run:cypress",
"start:xcdb-api": "cd ./packages/nocodb; npm install; NC_DISABLE_CACHE=true NC_DISABLE_TELE=true NC_INFLECTION=camelize DATABASE_URL=sqlite:../../../scripts/cypress/fixtures/sqlite-sakila/sakila.db npm run watch:run:cypress",
"start:api:cache": "cd ./packages/nocodb; npm install; NC_DISABLE_TELE=true npm run watch:run:cypress",
"start:api:cache": "cd ./packages/nocodb; npm install; NC_DISABLE_TELE=true npm run watch:run:cypress",
"start:xcdb-api:cache": "cd ./packages/nocodb; npm install; NC_DISABLE_TELE=true NC_INFLECTION=camelize DATABASE_URL=sqlite:../../../scripts/cypress/fixtures/sqlite-sakila/sakila.db npm run watch:run:cypress",
"start:web": "cd ./packages/nc-gui; npm install; npm run dev",
"cypress:run": "cypress run --config-file ./scripts/cypress/cypress.json",

1
packages/nc-gui/components/import/dropOrSelectFileModal.vue

@ -98,7 +98,6 @@ export default {
}
},
dragOverHandler(ev) {
console.log('File(s) in drop zone')
// Prevent default behavior (Prevent file from being opened)
ev.preventDefault()

7
packages/nc-gui/components/project/appStore.vue

@ -213,15 +213,8 @@ export default {
},
async created() {
await this.loadPluginList()
this.readPluginDefaults()
},
methods: {
async readPluginDefaults() {
try {
this.defaultConfig = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcPluginDemoDefaults'])
} catch (e) {
}
},
async confirmResetPlugin() {
try {
await this.$api.plugin.update(this.resetPluginRef.id, {

20
packages/nc-gui/components/project/spreadsheet/components/columnFilter.vue

@ -321,25 +321,25 @@ export default {
for (const [i, filter] of Object.entries(this.filters)) {
if (filter.status === 'delete') {
if (this.hookId || hookId) {
await this.$api.dbTableFilter.delete(this.hookId || hookId, filter.id)
await this.$api.dbTableFilter.delete(filter.id)
} else {
await this.$api.dbTableFilter.delete(this.viewId, filter.id)
await this.$api.dbTableFilter.delete(filter.id)
}
} else if (filter.status === 'update') {
if (filter.id) {
if (this.hookId || hookId) {
await this.$api.dbTableFilter.update(this.hookId || hookId, filter.id, {
await this.$api.dbTableFilter.update(filter.id, {
...filter,
fk_parent_id: this.parentId
})
} else {
await this.$api.dbTableFilter.update(this.viewId, filter.id, {
await this.$api.dbTableFilter.update(filter.id, {
...filter,
fk_parent_id: this.parentId
})
}
} else if (this.hookId || hookId) {
this.$set(this.filters, i, (await this.$api.dbTableFilter.create(this.hookId || hookId, {
this.$set(this.filters, i, (await this.$api.dbTableWebhookFilter.create(this.hookId || hookId, {
...filter,
fk_parent_id: this.parentId
})))
@ -363,12 +363,12 @@ export default {
let filters = []
if (this.viewId && this._isUIAllowed('filterSync')) {
filters = this.parentId
? (await this.$api.dbTableFilter.childrenRead(this.viewId, this.parentId))
? (await this.$api.dbTableFilter.childrenRead(this.parentId))
: (await this.$api.dbTableFilter.read(this.viewId))
}
if (this.hookId && this._isUIAllowed('filterSync')) {
filters = this.parentId
? (await this.$api.dbTableWebhookFilter.childrenRead(this.hookId, this.parentId))
? (await this.$api.dbTableFilter.childrenRead(this.parentId))
: (await this.$api.dbTableWebhookFilter.read(this.hookId))
}
@ -406,7 +406,7 @@ export default {
} else if (!this.autoApply) {
filter.status = 'update'
} else if (filter.id) {
await this.$api.dbTableFilter.update(this.viewId, filter.id, {
await this.$api.dbTableFilter.update(filter.id, {
...filter,
fk_parent_id: this.parentId
})
@ -424,13 +424,12 @@ export default {
async deleteFilter(filter, i) {
if (this.shared || !this._isUIAllowed('filterSync')) {
this.filters.splice(i, 1)
// this.$emit('input', this.filters.filter(f => f.fk_column_id && f.comparison_op))
this.$emit('updated')
} else if (filter.id) {
if (!this.autoApply) {
this.$set(filter, 'status', 'delete')
} else {
await this.$api.dbTableFilter.delete(this.viewId, filter.id)
await this.$api.dbTableFilter.delete(filter.id)
await this.loadFilter()
this.$emit('updated')
}
@ -438,7 +437,6 @@ export default {
this.filters.splice(i, 1)
this.$emit('updated')
}
// this.$emit('input', this.filters.filter(f => f.fk_column_id && f.comparison_op))
this.$tele.emit('filter:delete')
}
}

2
packages/nc-gui/components/project/spreadsheet/components/editColumn.vue

@ -565,7 +565,7 @@ export default {
this.newColumn.title = this.newColumn.column_name
if (this.editColumn) {
await this.$api.dbTableColumn.update(this.meta.id, this.column.id, this.newColumn)
await this.$api.dbTableColumn.update(this.column.id, this.newColumn)
} else {
await this.$api.dbTableColumn.create(this.meta.id, this.newColumn)
}

2
packages/nc-gui/components/project/spreadsheet/components/editVirtualColumn.vue

@ -113,7 +113,7 @@ export default {
async save() {
// todo: rollup update
try {
await this.$api.dbTableColumn.update(this.meta.id, this.column.id, this.newColumn)
await this.$api.dbTableColumn.update(this.column.id, this.newColumn)
} catch (e) {
console.log(this._extractSdkResponseErrorMsg(e))
this.$toast.error('Failed to update column alias').goAway(3000)

13
packages/nc-gui/components/project/spreadsheet/components/editableCell/editableAttachmentCell.vue

@ -333,11 +333,14 @@ export default {
this.uploading = true
for (const file of this.$refs.file.files) {
try {
const data = await this.$api.dbView.upload(this.$store.state.project.projectId, this.viewId, {
files: file,
json: '{}'
})
const data = await this.$api.storage.upload(
{
path: ['noco', this.projectName, this.meta.title, this.column.title].join('/')
}, {
files: file,
json: '{}'
}
)
this.localState.push(...data)
} catch (e) {

21
packages/nc-gui/components/project/spreadsheet/components/expandedForm.vue

@ -430,7 +430,10 @@ export default {
}, {})
if (this.isNew) {
const data = (await this.$api.data.create(this.viewId || this.meta.id, updatedObj))
const data = (await this.$api.dbTableRow.create(
'noco',
this.projectName,
this.meta.title, updatedObj))
this.localState = { ...this.localState, ...data }
// save hasmany and manytomany relations from local state
@ -447,10 +450,16 @@ export default {
if (!id) {
return this.$toast.info('Update not allowed for table which doesn\'t have primary Key').goAway(3000)
}
await this.$api.data.update(this.viewId || this.meta.id, id, updatedObj)
await this.$api.dbTableRow.update(
'noco',
this.projectName,
this.meta.title,
id,
updatedObj
)
for (const key of Object.keys(updatedObj)) {
// audit
this.$api.utils.auditRowUpdate({
this.$api.utils.auditRowUpdate(id, {
fk_model_id: this.meta.id,
column_name: key,
row_id: id,
@ -479,14 +488,16 @@ export default {
async reload() {
const id = this.meta.columns.filter(c => c.pk).map(c => this.localState[c.title]).join('___')
this.$set(this, 'changedColumns', {})
this.localState = (await this.$api.data.read(this.viewId || this.meta.id, id, { query: this.queryParams || {} }))
this.localState = (await this.$api.dbTableRow.read(
'noco',
this.projectName,
this.meta.title, id, { query: this.queryParams || {} }))
},
calculateDiff(date) {
return dayjs.utc(date).fromNow()
},
async saveComment() {
try {
await this.$api.utils.commentRow({
fk_model_id: this.meta.id,
row_id: this.meta.columns.filter(c => c.pk).map(c => this.localState[c.title]).join('___'),

2
packages/nc-gui/components/project/spreadsheet/components/fieldsMenu.vue

@ -367,7 +367,7 @@ export default {
},
async hideAll() {
if (!this.isPublic) {
await this.$api.dbView.hideAllColumn({ viewId: this.viewId })
await this.$api.dbView.hideAllColumn(this.viewId)
}
for (const f of this.fields) {
f.show = false

4
packages/nc-gui/components/project/spreadsheet/components/headerCell.vue

@ -184,7 +184,7 @@ export default {
column.altered = 4
const columns = this.meta.columns.slice()
columns[this.columnIndex] = column
await this.$api.dbTableColumn.delete(this.meta.id, column.id)
await this.$api.dbTableColumn.delete(column.id)
this.$emit('colDelete')
this.$emit('saved')
@ -196,7 +196,7 @@ export default {
async setAsPrimaryValue() {
// todo: pass only updated fields
try {
await this.$api.dbTableColumn.primaryColumnSet(this.meta.id, this.column.id)
await this.$api.dbTableColumn.primaryColumnSet(this.column.id)
this.$toast.success('Successfully updated as primary column').goAway(3000)
} catch (e) {
console.log(e)

35
packages/nc-gui/components/project/spreadsheet/components/moreActions.vue

@ -237,19 +237,36 @@ export default {
while (!isNaN(offset) && offset > -1) {
let res
if (this.publicViewId) {
res = await this.$api.public.csvExport(this.publicViewId, ExportTypes.CSV, this.reqPayload, {
res = await this.$api.public.csvExport(this.publicViewId, ExportTypes.CSV, {
responseType: 'blob',
query: {
offset
fields: this.queryParams && this.queryParams.fieldsOrder && this.queryParams.fieldsOrder.filter(c => this.queryParams.showFields[c]),
offset,
sortArrJson: JSON.stringify(this.reqPayload && this.reqPayload.sorts && this.reqPayload.sorts.map(({
fk_column_id,
direction
}) => ({
direction,
fk_column_id
}))),
filterArrJson: JSON.stringify(this.reqPayload && this.reqPayload.filters)
},
headers: {
'xc-password': this.reqPayload && this.reqPayload.password
}
})
} else {
res = await this.$api.data.csvExport(this.selectedView.id, ExportTypes.CSV, {
responseType: 'blob',
query: {
offset
}
})
res = await this.$api.dbViewRow.export(
'noco',
this.projectName,
this.meta.title,
this.selectedView.title,
ExportTypes.CSV, {
responseType: 'blob',
query: {
offset
}
})
}
const { data } = res
@ -289,7 +306,7 @@ export default {
input = '1'
}
} else if (v.uidt === UITypes.Number) {
if (input == "") input = null
if (input == '') { input = null }
}
res[col.destCn] = input
}

6
packages/nc-gui/components/project/spreadsheet/components/pagination.vue

@ -1,8 +1,8 @@
<template>
<div class="d-flex align-center">
<span v-if="count !== null && count !== Infinity" class="caption ml-2">
<!-- <span v-if="count !== null && count !== Infinity" class="caption ml-2">
{{ count }} record{{ count !== 1 ? 's' : '' }}
</span>
</span>-->
<v-spacer />
<v-pagination
v-if="count !== Infinity"
@ -35,7 +35,7 @@
</div>
<v-spacer />
<v-spacer />
<!-- <v-spacer />-->
</div>
</template>

4
packages/nc-gui/components/project/spreadsheet/components/sortListMenu.vue

@ -148,7 +148,7 @@ export default {
async saveOrUpdate(sort, i) {
if (!this.shared && this._isUIAllowed('sortSync')) {
if (sort.id) {
await this.$api.dbTableSort.update(this.viewId, sort.id, sort)
await this.$api.dbTableSort.update(sort.id, sort)
} else {
this.$set(this.sortList, i, (await this.$api.dbTableSort.create(this.viewId, sort)))
}
@ -161,7 +161,7 @@ export default {
},
async deleteSort(sort, i) {
if (!this.shared && sort.id && this._isUIAllowed('sortSync')) {
await this.$api.dbTableSort.delete(this.viewId, sort.id)
await this.$api.dbTableSort.delete(sort.id)
await this.loadSortList()
} else {
this.sortList.splice(i, 1)

29
packages/nc-gui/components/project/spreadsheet/components/spreadsheetNavDrawer.vue

@ -767,22 +767,15 @@ export default {
return
}
try {
if (this.selectedViewIdLocal === view.id) {
await this.$router.push({
query: {
...this.$route.query,
view: view.title_temp
}
})
}
// if (this.selectedViewIdLocal === view.id) {
// await this.$router.push({
// query: {
// ...this.$route.query,
// view: view.title_temp
// }
// })
// }
this.$set(view, 'title', view.title_temp)
// await this.sqlOp({ dbAlias: this.nodes.dbAlias }, 'xcVirtualTableRename', {
// id: view.id,
// old_title: oldTitle,
// title: view.title_temp,
// alias: view.alias,
// parent_model_title: this.meta.table_name
// })
await this.$api.dbView.update(view.id, {
title: view.title,
order: view.order
@ -804,12 +797,6 @@ export default {
},
async deleteView(view) {
try {
// await this.sqlOp({ dbAlias: this.nodes.dbAlias }, 'xcVirtualTableDelete', {
// id: view.id,
// title: view.alias || view.title,
// view_name: view.alias || view.title,
// parent_model_title: this.table
// })
await this.$api.dbView.delete(view.id)
this.$toast.success('View deleted successfully').goAway(3000)
await this.loadViews()

20
packages/nc-gui/components/project/spreadsheet/components/virtualCell/belongsToCell.vue

@ -34,6 +34,7 @@
:column="column"
:primary-col="parentPrimaryCol"
:primary-key="parentPrimaryKey"
:parent-meta="meta"
:api="parentApi"
:query-params="{
...parentQueryParams,
@ -278,14 +279,18 @@ export default {
return
}
const id = this.meta.columns.filter(c => c.pk).map(c => this.row[c.title]).join('___')
// todo: audit
await this.$api.data.nestedDelete(
this.meta.id,
await this.$api.dbTableRow.nestedDelete(
'noco',
this.projectName,
this.meta.title,
id,
this.column.id,
'bt',
this.column.title,
parent[this.parentPrimaryKey]
)
this.$emit('loadTableData')
if (this.isForm && this.$refs.childList) {
this.$refs.childList.loadData()
@ -346,12 +351,13 @@ export default {
this.newRecordModal = false
return
}
await this.$api.data.nestedAdd(
this.meta.id,
await this.$api.dbTableRow.nestedAdd(
'noco',
this.projectName,
this.meta.title,
id,
this.column.id,
'bt',
this.column.title,
pid
)

32
packages/nc-gui/components/project/spreadsheet/components/virtualCell/components/listChildItems.vue

@ -179,13 +179,13 @@ export default {
methods: {
async loadData() {
if ((!this.isForm && this.isPublic) && this.$route.params.id) {
if (this.column && this.column.colOptions && this.rowId) {
this.data = (await this.$api.public.dataNestedList(
this.$route.params.id,
this.rowId,
this.column.colOptions.type,
this.column.fk_column_id || this.column.id, {
this.column.fk_column_id || this.column.id,
{
limit: this.size,
offset: this.size * (this.page - 1)
}, {}))
@ -198,26 +198,24 @@ export default {
return
}
if (this.column && this.column.colOptions) {
this.data = (await this.$api.data.nestedList(
this.column.fk_model_id,
this.data = (await this.$api.dbTableRow.nestedList(
'noco',
this.projectName,
this.parentMeta.title,
this.rowId,
this.column.id,
this.column.colOptions.type,
this.column.title,
{
query: {
limit: this.size,
offset: this.size * (this.page - 1)
// ...this.queryParams
}
limit: this.size,
offset: this.size * (this.page - 1)
}))
} else {
this.data = (await this.$api.data.list(
this.meta.id, {
query: {
limit: this.size,
offset: this.size * (this.page - 1),
...this.queryParams
}
this.data = (await this.$api.dbTableRow.list(
'noco',
this.projectName, this.meta.title, {
limit: this.size,
offset: this.size * (this.page - 1),
...this.queryParams
}))
}
}

37
packages/nc-gui/components/project/spreadsheet/components/virtualCell/components/listItems.vue

@ -144,7 +144,10 @@ export default {
this.data = (await this.$api.public.dataRelationList(
this.$route.params.id,
this.column.id,
{ password: this.password }, {
{}, {
headers: {
'xc-password': this.password
},
query: {
limit: this.size,
offset: this.size * (this.page - 1),
@ -156,27 +159,27 @@ export default {
// eslint-disable-next-line no-lonely-if
if (this.column && this.column.colOptions && this.rowId) {
this.data = (await this.$api.data.nestedExcludedList(
this.column.fk_model_id,
this.data = (await this.$api.dbTableRow.nestedChildrenExcludedList(
'noco',
this.projectName,
this.parentMeta.title,
this.rowId,
this.column.id,
this.column.colOptions.type,
this.column.title,
{
query: {
limit: this.size,
offset: this.size * (this.page - 1),
where: this.query && `(${this.primaryCol},like,${this.query})`
}
limit: this.size,
offset: this.size * (this.page - 1),
where: this.query && `(${this.primaryCol},like,${this.query})`
}))
} else {
this.data = (await this.$api.data.list(
this.meta.id, {
query: {
limit: this.size,
offset: this.size * (this.page - 1),
...this.queryParams,
where
}
this.data = (await this.$api.dbTableRow.list(
'noco',
this.projectName,
this.meta.title, {
limit: this.size,
offset: this.size * (this.page - 1),
...this.queryParams,
where
}))
}
}

5
packages/nc-gui/components/project/spreadsheet/components/virtualCell/formulaCell.vue

@ -25,12 +25,13 @@ export default {
const rawText = this.row[this.column.title].toString()
let found = false
const out = rawText.match(/URI::\((.*?)\)/g, (_, url) => {
const out = rawText.replace(/URI::\((.*?)\)/g, (_, url) => {
found = true
const a = document.createElement('a')
a.textContent = url
a.setAttribute('href', url)
return a.innerHTML
a.setAttribute('target', '_blank')
return a.outerHTML
})
return found && out

8
packages/nc-gui/components/project/spreadsheet/components/virtualCell/hasManyCell.vue

@ -382,11 +382,13 @@ export default {
const id = this.childMeta.columns.filter(c => c.pk).map(c => child[c.title]).join('___')
this.newRecordModal = false
await this.$api.data.nestedAdd(
this.meta.id,
await this.$api.dbTableRow.nestedAdd(
'noco',
this.projectName,
this.meta.title,
this.parentId,
this.column.id,
'hm',
this.column.title,
id
)

24
packages/nc-gui/components/project/spreadsheet/components/virtualCell/manyToManyCell.vue

@ -44,6 +44,7 @@
:meta="childMeta"
:primary-col="childPrimaryCol"
:primary-key="childPrimaryKey"
:parent-meta="meta"
:api="api"
:mm="mm"
:tn="mm && mm.rtn"
@ -316,13 +317,17 @@ export default {
await Promise.all([this.loadChildMeta(), this.loadAssociateTableMeta()])
const cid = this.childMeta.columns.filter(c => c.pk).map(c => child[c.title]).join('___')
const pid = this.meta.columns.filter(c => c.pk).map(c => this.row[c.title]).join('___')
await this.$api.data.nestedDelete(
this.meta.id,
await this.$api.dbTableRow.nestedDelete(
'noco',
this.projectName,
this.meta.title,
pid,
this.column.id,
'mm',
this.column.title,
cid
)
this.$emit('loadTableData')
if ((this.childListModal || this.isForm) && this.$refs.childList) {
this.$refs.childList.loadData()
@ -400,20 +405,17 @@ export default {
// const vcidCol = this.assocMeta.columns.find(c => c.id === this.column.colOptions.fk_mm_parent_column_id).title
// const vpidCol = this.assocMeta.columns.find(c => c.id === this.column.colOptions.fk_mm_child_column_id).title
await this.$api.data.nestedAdd(
this.meta.id,
await this.$api.dbTableRow.nestedAdd(
'noco',
this.projectName,
this.meta.title,
pid,
this.column.id,
'mm',
this.column.title,
cid
)
try {
// await this.$api.data.create(this.assocMeta.id, {
// [vcidCol]: parseIfInteger(cid),
// [vpidCol]: parseIfInteger(pid)
// })
this.$emit('loadTableData')
} catch (e) {
// todo: handle

2
packages/nc-gui/components/project/spreadsheet/components/virtualHeaderCell.vue

@ -235,7 +235,7 @@ export default {
methods: {
async deleteColumn() {
try {
await this.$api.dbTableColumn.delete(this.meta.id, this.column.id)
await this.$api.dbTableColumn.delete(this.column.id)
this.$emit('saved')
this.columnDeleteDialog = false
} catch (e) {

34
packages/nc-gui/components/project/spreadsheet/mixins/spreadsheet.js

@ -108,25 +108,12 @@ export default {
}
{
const _ref = {}
columns.forEach((c) => {
// if (c.virtual && c.lk) {
// c.alias = `${c.lk._lcn} (from ${c.lk._ltn})`
// } else {
c.alias = c.title
// }
if (c.alias in _ref) {
c.alias += _ref[c.alias]++
} else {
_ref[c.alias] = 1
}
})
}
return columns
},
// allColumnsNames() {
// return this.allColumns && this.allColumns.length ? this.allColumns.reduce((a, c) => [...a, c.column_name, c.title], []) : []
// },
availableColumns() {
let columns = []
@ -140,32 +127,15 @@ export default {
columns = filterOutSystemColumns(this.meta.columns)
}
if (this.meta && this.meta.v) {
columns = [...columns, ...this.meta.v.map(v => ({
...v,
virtual: 1
}))]
}
{
const _ref = {}
columns.forEach((c) => {
// if (c.virtual && c.lk) {
// c.alias = `${c.lk._lcn} (from ${c.lk._ltn})`
// } else {
c.alias = c.title
// }
if (c.alias in _ref) {
c.alias += _ref[c.alias]++
} else {
_ref[c.alias] = 1
}
})
}
if (this.fieldsOrder.length) {
return [...columns].sort((c1, c2) => {
const i1 = this.fieldsOrder.indexOf(c1.alias)
const i2 = this.fieldsOrder.indexOf(c2.alias)
const i1 = this.fieldsOrder.indexOf(c1.title)
const i2 = this.fieldsOrder.indexOf(c2.title)
return (i1 === -1 ? Infinity : i1) - (i2 === -1 ? Infinity : i2)
})
}

75
packages/nc-gui/components/project/spreadsheet/public/xcForm.vue

@ -298,7 +298,7 @@ export default {
this.loading = true
try {
this.viewMeta = (await this.$api.public.sharedViewMetaGet(this.$route.params.id, {
password: this.password
headers: { 'xc-password': this.password }
}))
this.view = this.viewMeta.view
@ -306,66 +306,6 @@ export default {
this.metas = this.viewMeta.relatedMetas
this.columns = this.meta.columns.filter(c => c.show)
this.client = this.viewMeta.client
// try {
// // eslint-disable-next-line camelcase
// const {
// meta,
// // model_name,
// client,
// query_params: qp,
// db_alias: dbAlias,
// relatedTableMetas
// } = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'sharedViewGet', {
// view_id: this.$route.params.id,
// password: this.password
// }])
// this.client = client
// this.meta = meta
// this.query_params = qp
// this.dbAlias = dbAlias
// this.metas = relatedTableMetas
//
// const showFields = this.query_params.showFields || {}
// let fields = this.query_params.fieldsOrder || []
// if (!fields.length) {
// fields = Object.keys(showFields)
// }
// // eslint-disable-next-line camelcase
//
// let columns = this.meta.columns
// if (this.meta && this.meta.v) {
// columns = [...columns, ...this.meta.v.map(v => ({ ...v, virtual: 1 }))]
// }
//
// {
// const _ref = {}
// columns.forEach((c) => {
// if (c.virtual && c.bt) {
// c.prop = `${c.bt.rtn}Read`
// }
// if (c.virtual && c.mm) {
// c.prop = `${c.mm.rtn}MMList`
// }
// if (c.virtual && c.hm) {
// c.prop = `${c.hm.table_name}List`
// }
//
// // if (c.virtual && c.lk) {
// // c.alias = `${c.lk._lcn} (from ${c.lk._ltn})`
// // } else {
// c.alias = c.title
// // }
// if (c.alias in _ref) {
// c.alias += _ref[c.alias]++
// } else {
// _ref[c.alias] = 1
// }
// })
// }
// // this.modelName = model_name
// this.columns = columns.filter(c => showFields[c.alias]).sort((a, b) => fields.indexOf(a.alias) - fields.indexOf(b.alias))
//
// this.localParams = (this.query_params.extraViewParams && this.query_params.extraViewParams.formParams) || {}
} catch (e) {
if (e.response && e.response.status === 404) {
this.notFound = true
@ -385,16 +325,6 @@ export default {
}
this.submitting = true
// const id = this.meta.columns.filter(c => c.pk).map(c => this.localState[c.title]).join('___')
// const updatedObj = Object.keys(this.changedColumns).reduce((obj, col) => {
// obj[col] = this.localState[col]
// return obj
// }, {})
// if (this.isNew) {
// const formData = new FormData()
const data = { ...this.localState, ...this.virtual }
const attachment = {}
@ -407,8 +337,9 @@ export default {
await this.$api.public.dataCreate(this.$route.params.id, {
data,
password: this.password,
...attachment
}, {
headers: { 'xc-password': this.password }
})
this.virtual = {}

1
packages/nc-gui/components/project/spreadsheet/public/xcKanban.vue

@ -74,7 +74,6 @@ export default {
props: {
env: String,
nodes: Object,
addNewRelationTab: Function,
relationType: String,
relation: Object,
relationIdValue: [String, Number],

181
packages/nc-gui/components/project/spreadsheet/public/xcTable.vue

@ -22,80 +22,14 @@
class="nc-table-toolbar elevation-0 xc-toolbar xc-border-bottom"
style="z-index: 7;border-radius: 4px"
>
<!--
<div class="d-flex xc-border align-center search-box" style="min-width:156px">
<v-menu bottom offset-y>
<template #activator="{on}">
<div style="min-width: 56px" v-on="on">
<v-icon
class="ml-2"
small
color="grey"
>
mdi-magnify
</v-icon>
<v-icon
color="grey"
class="pl-0 pa-1"
small
>
mdi-menu-down
</v-icon>
</div>
</template>
<v-list dense>
<v-list-item
v-for="col in meta.columns"
:key="col.title"
@click="searchField = col.title"
>
<span class="caption">{{ col.title }}</span>
</v-list-item>
</v-list>
</v-menu>
<v-divider
vertical
/>
<v-text-field
v-model="searchQuery"
autocomplete="off"
style="min-width: 100px ; width: 150px"
flat
dense
solo
hide-details
:placeholder="searchField ? `Search '${searchField}' column` : 'Search all columns'"
class="elevation-0 pa-0 flex-grow-1 caption search-field"
@keyup.enter="loadTableData"
@blur="loadTableData"
/>
</div>
<span
v-if="relationType && false"
class="caption grey&#45;&#45;text"
>{{ refTable }}({{
relationPrimaryValue
}}) -> {{ relationType === 'hm' ? ' Has Many ' : ' Belongs To ' }} -> {{ table }}</span>
-->
<div class="d-inline-flex">
<!-- <v-btn outlined small text @click="reload">
<v-icon small class="mr-1" color="grey darken-3">
mdi-reload
</v-icon>
Reload
</v-btn>-->
<fields-menu
v-model="showFields"
:field-list="fieldList"
:fields-order.sync="fieldsOrder"
is-public
:meta="meta"
:show-system-fields="showSystemFields"
/>
<sort-list-menu
@ -116,39 +50,13 @@
<csv-export-import
:is-view="isView"
:query-params="{...queryParams, showFields}"
:query-params="{...queryParams, showFields, fieldsOrder}"
:public-view-id="$route.params.id"
:meta="meta"
:req-payload="{filters, sorts, password}"
/>
</div>
<v-spacer class="h-100" @dblclick="debug=true" />
<!-- <v-menu>
<template #activator="{ on, attrs }">
<v-icon
v-bind="attrs"
small
class="mx-2"
color="grey darken-3"
v-on="on"
>
mdi-arrow-collapse-vertical
</v-icon>
</template>
<v-list dense class="caption">
<v-list-item v-for="h in cellHeights" :key="h.size" dense @click.stop="cellHeight = h.size">
<v-list-item-icon class="mr-1">
<v-icon small :color="cellHeight === h.size && 'primary'">
{{ h.icon }}
</v-icon>
</v-list-item-icon>
<v-list-item-title :class="{'primary&#45;&#45;text' : cellHeight === h.size}" style="text-transform: capitalize">
{{ h.size }}
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>-->
</v-toolbar>
<div
@ -164,7 +72,7 @@
<xc-grid-view
v-else
is-public-view
:meta="meta"
:meta="tableMeta"
:metas="metas"
:data="data"
:available-columns="availableColumns"
@ -185,9 +93,6 @@
color="primary lighten-2"
@input="loadTableData"
/>
<!-- <div v-else class="d-flex justify-center py-4">-->
<!-- <v-alert type="info" dense class="ma-1 flex-shrink-1">Table is empty</v-alert>-->
<!-- </div>-->
</div>
</div>
</template>
@ -223,14 +128,12 @@
import { ErrorMessages } from 'nocodb-sdk'
import spreadsheet from '../mixins/spreadsheet'
import ApiFactory from '../apis/apiFactory'
// import EditableCell from "../editableCell";
import FieldsMenu from '../components/fieldsMenu'
import SortListMenu from '../components/sortListMenu'
import ColumnFilterMenu from '../components/columnFilterMenu'
import XcGridView from '../views/xcGridView'
import { SqlUI } from '@/helpers/sqlUi'
import CsvExportImport from '~/components/project/spreadsheet/components/moreActions'
// import ExpandedForm from "../expandedForm";
export default {
name: 'XcTable',
@ -245,7 +148,6 @@ export default {
props: {
env: String,
nodes: Object,
addNewRelationTab: Function,
relationType: String,
relation: Object,
relationIdValue: [String, Number],
@ -323,7 +225,8 @@ export default {
icon: 'mdi-card'
}],
rowContextMenu: null,
modelName: null
modelName: null,
tableMeta: null
}),
computed: {
concatenatedXWhere() {
@ -369,23 +272,6 @@ export default {
edited() {
return this.data && this.data.some(r => r.rowMeta && (r.rowMeta.new || r.rowMeta.changed))
},
// hasMany() {
// return this.meta && this.meta.hasMany
// ? this.meta.hasMany.reduce((hm, o) => {
// hm[o.rcn] = hm[o.rcn] || []
// hm[o.rcn].push(o)
// return hm
// }, {})
// : {}
// },
// belongsTo() {
// return this.meta && this.meta.belongsTo
// ? this.meta.belongsTo.reduce((bt, o) => {
// bt[o.title] = o
// return bt
// }, {})
// : {}
// },
table() {
if (this.relationType === 'hm') {
return this.relation.table_name
@ -426,21 +312,6 @@ export default {
this.searchField = this.primaryValueColumn
},
created() {
/* if (this.relationType === 'hm') {
this.filters.push({
field: this.relation.column_name,
op: 'is equal',
value: this.relationIdValue,
readOnly: true
})
} else if (this.relationType === 'bt') {
this.filters.push({
field: this.relation.rcn,
op: 'is equal',
value: this.relationIdValue,
readOnly: true
})
} */
document.addEventListener('keydown', this.onKeyDown)
},
beforeDestroy() {
@ -519,10 +390,15 @@ export default {
this.loading = true
try {
this.viewMeta = (await this.$api.public.sharedViewMetaGet(this.$route.params.id, {
password: this.password
headers: {
'xc-password': this.password
}
}))
this.meta = this.viewMeta.model
this.tableMeta = this.viewMeta.model
this.meta = { ...this.viewMeta.model }
this.meta.columns = this.meta.columns.filter(c => c.show)
this.metas = this.viewMeta.relatedMetas
this.showSystemFields = this.viewMeta.show_system_fields
this.sorts = this.viewMeta.sorts
this.viewName = this.viewMeta.title
@ -547,26 +423,23 @@ export default {
list,
pageInfo: { totalRows: count }
}
} = (await this.$api.public.dataList(this.$route.params.id, {
password: this.password,
sorts: this.sorts && this.sorts.map(({
fk_column_id,
direction
}) => ({
direction,
fk_column_id
})),
filters: this.filters
}, this.queryParams
} = (await this.$api.public.dataList(
this.$route.params.id,
{
sortArrJson: JSON.stringify(this.sorts && this.sorts.map(({
fk_column_id,
direction
}) => ({
direction,
fk_column_id
}))),
filterArrJson: JSON.stringify(this.filters),
...this.queryParams
}, {
headers: { 'xc-password': this.password }
}
))
// this.client = client
// this.showFields = queryParams && queryParams.showFields
// this.meta = meta
// eslint-disable-next-line camelcase
// this.modelName = model_name
this.count = count
this.data = list.map(row => ({
row,

90
packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue

@ -357,7 +357,6 @@
@onCellValueChange="onCellValueChange"
@insertNewRow="insertNewRow"
@showRowContextMenu="showRowContextMenu"
@addNewRelationTab="addNewRelationTab"
@expandRow="expandRow"
@onRelationDelete="loadMeta"
@loadTableData="loadTableData"
@ -439,6 +438,7 @@
:primary-value-column="primaryValueColumn"
:form-params.sync="extraViewParams.formParams"
:view.sync="selectedView"
:view-title="selectedView.title"
@onNewColCreation="loadMeta(false)"
/>
</template>
@ -602,24 +602,6 @@
<span class="caption">Set column value to <strong>null</strong></span>
</v-tooltip>
</template>
<!-- <template v-if="meta.hasMany && meta.hasMany.length">
<v-divider v-if="isEditable && !isLocked" />
<span class="ml-3 grey&#45;&#45;text " style="font-size: 9px">Has Many</span>
<v-list-item v-for="(hm,i) in meta.hasMany" :key="i" @click="addNewRelationTabCtxMenu(hm,'hm')">
<span class="caption text-capitalize">{{ hm.title }}</span>
</v-list-item>
</template>
<template v-if="meta.belongsTo && meta.belongsTo.length">
<v-divider />
<span class="ml-3 grey&#45;&#45;text " style="font-size: 9px">Belongs To</span>
<v-list-item v-for="(bt,i) in belongsTo" :key="i" @click="addNewRelationTabCtxMenu(bt,'bt')">
<span class="caption text-capitalize">{{ bt._rtn }}</span>
</v-list-item>
</template>-->
</v-list>
</v-menu>
<v-dialog
@ -752,7 +734,6 @@ export default {
tabId: String,
env: String,
nodes: Object,
addNewRelationTab: Function,
relationType: String,
relation: Object,
relationIdValue: [String, Number],
@ -973,8 +954,7 @@ export default {
if (this.nodes.newTable && !this.nodes.tableCreated) {
const columns = this.sqlUi.getNewTableColumns().filter(col => this.nodes.newTable.columns.includes(col.column_name))
await this.$api.dbTable.create(
this.$store.state.project.projectId,
this.$store.state.project.project.bases[0].id,
this.projectId,
{
table_name: this.nodes.table_name,
title: this.nodes.title,
@ -997,22 +977,6 @@ export default {
comingSoon() {
this.$toast.info('Coming soon!').goAway(3000)
},
addNewRelationTabCtxMenu(obj, type) {
const rowObj = this.rowContextMenu.row
this.addNewRelationTab(
obj,
this.table,
this.meta.title || this.table,
type === 'hm' ? obj.table_name : obj.rtn,
type === 'hm' ? obj.title : obj._rtn,
// todo: column name alias
rowObj[type === 'hm' ? obj.rcn : obj.title],
type,
rowObj,
rowObj[this.primaryValueColumn]
)
},
changed(col, row) {
this.$set(this.data[row].rowMeta, 'changed', this.data[row].rowMeta.changed || {})
if (this.data[row].rowMeta) {
@ -1050,7 +1014,12 @@ export default {
}, {})
// const insertedData = await this.api.insert(insertObj)
const insertedData = (await this.$api.data.create(this.meta.id, insertObj))
const insertedData = await this.$api.dbViewRow.create(
'noco',
this.projectName,
this.meta.title,
this.selectedView.title, insertObj
)
this.data.splice(row, 1, {
row: insertedData,
@ -1088,7 +1057,9 @@ export default {
return
}
const { row: rowObj, rowMeta, oldRow, saving, lastSave } = this.data[row]
if (!lastSave) { this.$set(this.data[row], 'lastSave', rowObj[column.title]) }
if (!lastSave) {
this.$set(this.data[row], 'lastSave', rowObj[column.title])
}
if (rowMeta.new) {
// return if there is no change
if ((column && oldRow[column.title] === rowObj[column.title]) || saving) {
@ -1101,10 +1072,12 @@ export default {
// return
// }
// return if there is no change
if (oldRow[column.title] === rowObj[column.title] && ((lastSave || rowObj[column.title]) === rowObj[column.title])) {
if (!column || (oldRow[column.title] === rowObj[column.title] && ((lastSave || rowObj[column.title]) === rowObj[column.title]))) {
return
}
if (saved) { this.$set(this.data[row], 'lastSave', oldRow[column.title]) }
if (saved) {
this.$set(this.data[row], 'lastSave', oldRow[column.title])
}
const id = this.meta.columns.filter(c => c.pk).map(c => rowObj[c.title]).join('___')
if (!id) {
@ -1113,14 +1086,19 @@ export default {
this.$set(this.data[row], 'saving', true)
// eslint-disable-next-line promise/param-names
const newData = (await this.$api.data.update(this.meta.id, id, {
[column.title]: rowObj[column.title]
}, {
query: { ignoreWebhook: !saved }
}))// { [column.title]: oldRow[column.title] })
const newData = (await this.$api.dbViewRow.update(
'noco',
this.projectName,
this.meta.title,
this.selectedView.title,
id, {
[column.title]: rowObj[column.title]
}, {
query: { ignoreWebhook: !saved }
}))
// audit
this.$api.utils.auditRowUpdate({
this.$api.utils.auditRowUpdate(id, {
fk_model_id: this.meta.id,
column_name: column.title,
row_id: id,
@ -1155,7 +1133,7 @@ export default {
if (!id) {
return this.$toast.info('Delete not allowed for table which doesn\'t have primary Key').goAway(3000)
}
await this.$api.data.delete(this.meta.id, id)
await this.$api.dbViewRow.delete('noco', this.projectName, this.meta.title, this.selectedView.title, id)
}
this.data.splice(this.rowContextMenu.index, 1)
this.syncCount()
@ -1182,7 +1160,7 @@ export default {
if (!id) {
return this.$toast.info('Delete not allowed for table which doesn\'t have primary Key').goAway(3000)
}
await this.$api.data.delete(this.meta.id, id)
await this.$api.dbViewRow.delete('noco', this.projectName, this.meta.title, this.selectedView.title, id)
}
this.data.splice(row, 1)
} catch (e) {
@ -1312,7 +1290,11 @@ export default {
const {
list,
pageInfo
} = (await this.$api.dbViewRow.list('noco', this.$store.getters['project/GtrProjectName'], this.meta.title, this.selectedView.title,
} = (await this.$api.dbViewRow.list(
'noco',
this.projectName,
this.meta.title,
this.selectedView.title,
{
...this.queryParams,
...(this._isUIAllowed('sortSync') ? {} : { sortArrJson: JSON.stringify(this.sortList) }),
@ -1519,13 +1501,13 @@ export default {
async exportCache() {
try {
const data = (await this.$api.utils.cacheGet())
if (!data.length) {
if (!data) {
this.$toast.info('Cache is empty').goAway(3000)
return
}
const blob = new Blob([JSON.stringify(data)], { type: 'text/plain;charset=utf-8' })
FileSaver.saveAs(blob, 'cache_exported.json')
this.$toast.info('Copied Cache to clipboard').goAway(3000)
this.$toast.info('Exported Cache Successfully').goAway(3000)
} catch (e) {
console.log(e)
this.$toast.error(e.message).goAway(3000)
@ -1534,7 +1516,7 @@ export default {
async deleteCache() {
try {
await this.$api.utils.cacheDelete()
this.$toast.info('Deleted Cache').goAway(3000)
this.$toast.info('Deleted Cache Successfully').goAway(3000)
} catch (e) {
console.log(e)
this.$toast.error(e.message).goAway(3000)

83
packages/nc-gui/components/project/spreadsheet/views/formView.vue

@ -56,7 +56,7 @@
>
<v-card
v-for="(col) in hiddenColumns"
:key="col.alias"
:key="col.title"
class="pa-2 my-2 item pointer elevation-0"
@mousedown="moved=false"
@mousemove="moved=false"
@ -178,13 +178,13 @@
>
<div
v-for="(col,i) in columns"
:key="col.alias"
:key="col.title"
class="nc-field-wrapper item px-4 my-3 pointer"
:class="{'nc-editable':isEditable , 'active-row': isActiveRow(col) , 'py-4': !isActiveRow(col) , 'pb-4':isActiveRow(col)}"
>
<div
v-click-outside="() => onClickOutside(col)"
@click="activeRow= col.alias"
@click="activeRow= col.title"
>
<template
v-if="_isUIAllowed('editFormView')"
@ -195,10 +195,10 @@
</template>
<div
v-if="localParams.fields && localParams.fields[col.alias]"
v-if="localParams.fields && localParams.fields[col.title]"
:class="{
'active-row' : active === col.title,
required: isRequired(col, localState, localParams.fields[col.alias].required)
required: isRequired(col, localState, localParams.fields[col.title].required)
}"
>
<div class="nc-field-editables" :class="{'nc-show' : isActiveRow(col)}">
@ -228,7 +228,7 @@
</div>
<!--placeholder=" Enter form input label"-->
<editable
v-model="col.label"
v-model.lazy="col.label"
style="width:300px;white-space: pre-wrap"
:placeholder="$t('msg.info.formInput')"
class="caption pa-1 backgroundColor darken-1 mb-2 "
@ -236,7 +236,7 @@
/>
<!--placeholder=" Add some help text"-->
<editable
v-model="col.description"
v-model.lazy="col.description"
style="width:300px;white-space: pre-wrap"
:placeholder="$t('msg.info.formHelpText')"
class="caption pa-1 backgroundColor darken-1 mb-2"
@ -256,7 +256,7 @@
:nodes="nodes"
:is-form="true"
:meta="meta"
:required="isRequired(col, localState, localParams.fields[col.alias].required)"
:required="isRequired(col, localState, localParams.fields[col.title].required)"
/>
<header-cell
v-else
@ -265,7 +265,7 @@
:value="col.label || col.title"
:column="col"
:sql-ui="sqlUi"
:required="isRequired(col, localState, localParams.fields[col.alias].required)"
:required="isRequired(col, localState, localParams.fields[col.title].required)"
/>
</label>
@ -287,11 +287,11 @@
:is-form="true"
:hint="col.description"
:required="col.required"
@update:localState="state => $set(virtual,col.alias, state)"
@update:localState="state => $set(virtual,col.title, state)"
@updateCol="updateCol"
/>
<div
v-if="$v.virtual && $v.virtual.$dirty && $v.virtual[col.alias] && (!$v.virtual[col.alias].required || !$v.virtual[col.alias].minLength)"
v-if="$v.virtual && $v.virtual.$dirty && $v.virtual[col.title] && (!$v.virtual[col.title].required || !$v.virtual[col.alias].minLength)"
class="error--text caption"
>
Field is required.
@ -486,7 +486,7 @@ export default {
'meta', 'availableColumns', 'nodes',
'sqlUi', 'formParams', 'showFields',
'fieldsOrder', 'allColumns', 'dbAlias',
'api', 'id', 'viewId'
'api', 'id', 'viewId', 'viewTitle'
],
data: () => ({
isVirtualCol,
@ -512,18 +512,18 @@ export default {
virtual: {}
}
for (const column of this.columns) {
if (!this.localParams || !this.localParams.fields || !this.localParams.fields[column.alias]) {
if (!this.localParams || !this.localParams.fields || !this.localParams.fields[column.title]) {
continue
}
if (!column.virtual && (((column.rqd || column.notnull) && !column.default) || (column.pk && !(column.ai || column.default)) || this.localParams.fields[column.alias].required)) {
if (!column.virtual && (((column.rqd || column.notnull) && !column.cdf) || (column.pk && !(column.ai || column.default)) || this.localParams.fields[column.title].required)) {
obj.localState[column.title] = { required }
} else if (column.bt) {
const col = this.meta.columns.find(c => c.column_name === column.bt.column_name)
if ((col.rqd && !col.default) || this.localParams.fields[column.alias].required) {
if ((col.rqd && !col.default) || this.localParams.fields[column.title].required) {
obj.localState[col.title] = { required }
}
} else if (column.virtual && this.localParams.fields[column.alias].required && (column.mm || column.hm)) {
obj.virtual[column.alias] = {
} else if (column.virtual && this.localParams.fields[column.title].required && (column.mm || column.hm)) {
obj.virtual[column.title] = {
minLength: minLength(1),
required
}
@ -575,19 +575,21 @@ export default {
get() {
return this.fields.filter(f => !f.show && !this.systemFieldsIds.includes(f.fk_column_id))
},
set(v) {}
set(v) {
}
},
columns: {
get() {
return this.fields.filter(f => f.show).sort((a, b) => a.order - b.order)
},
set(v) {}
set(v) {
}
}
},
watch: {
'meta.columns'() {
this.meta.columns.forEach((c) => {
this.localParams.fields[c.alias] = this.localParams.fields[c.alias] || {}
this.localParams.fields[c.title] = this.localParams.fields[c.title] || {}
})
},
submitted(val) {
@ -614,7 +616,7 @@ export default {
fields: {}
}, this.localParams)
this.availableColumns.forEach((c) => {
localParams.fields[c.alias] = localParams.fields[c.alias] || {}
localParams.fields[c.title] = localParams.fields[c.title] || {}
})
this.localParams = localParams
// this.columns = [...this.availableColumns]
@ -680,6 +682,7 @@ export default {
this.$emit('update:fieldsOrder', this.fields.map(c => c.title))
},
async updateColMeta(col, i) {
// todo: introduce debounce to avoid consecutive api call
if (col.id) {
await this.$api.dbView.formColumnUpdate(col.id, col)
}
@ -703,7 +706,6 @@ export default {
const meta = this.$store.state.meta.metas[this.meta.id]
this.fields = meta.columns.map(c => ({
...c,
alias: c.title,
fk_column_id: c.id,
fk_view_id: this.viewId,
...(fieldById[c.id] ? fieldById[c.id] : {}),
@ -712,20 +714,6 @@ export default {
})
).sort((a, b) => a.order - b.order)
},
// async loadFormColumns() {
// this.formColumns = (await this.$api.meta.viewColumnList(this.viewId))
// let order = 1
// const fieldById = this.formColumns.reduce((o, f) => ({ ...o, [f.fk_column_id]: f }), {})
// this.fields = this.meta.columns.map(c => ({
// _cn: c.title,
// uidt: c.uidt,
// alias: c.title,
// fk_column_id: c.id,
// ...(fieldById[c.id] ? fieldById[c.id] : {}),
// order: (fieldById[c.id] && fieldById[c.id].order) || order++
// })
// ).sort((a, b) => a.order - b.order)
// },
hideColumn(i) {
if (this.isDbRequired(this.columns[i])) {
this.$toast.info('Required field can\'t be removed').goAway(3000)
@ -789,7 +777,7 @@ export default {
(column.pk && !column.ai && !column.cdf)
if (column.uidt === UITypes.LinkToAnotherRecord && column.colOptions.type === RelationTypes.BELONGS_TO) {
const col = this.meta.columns.find(c => c.id === column.colOptions.fk_child_column_id)
if ((col.rqd && !col.default) || this.localParams.fields[column.alias].required) {
if ((col.rqd && !col.default) || this.localParams.fields[column.title].required) {
isRequired = true
}
}
@ -809,10 +797,10 @@ export default {
this.$set(this.localState, column, id)
},
isActiveRow(col) {
return this.activeRow === col.alias
return this.activeRow === col.title
},
onClickOutside(col) {
this.activeRow = this.activeRow === col.alias ? null : this.activeRow
this.activeRow = this.activeRow === col.title ? null : this.activeRow
},
handleMouseUp(col) {
if (!this.moved) {
@ -850,18 +838,15 @@ export default {
}
this.loading = true
// const id = this.meta.columns.filter(c => c.pk).map(c => this.localState[c.title]).join('___')
// const updatedObj = Object.keys(this.changedColumns).reduce((obj, col) => {
// obj[col] = this.localState[col]
// return obj
// }, {})
// if (this.isNew) {
let data = await this.$api.dbViewRow.create(
'noco',
this.projectName,
this.meta.title,
this.viewTitle,
this.localState
)
// todo: add params option in GraphQL
// let data = await this.api.insert(this.localState, { params: { form: this.$route.query.view } })
let data = await this.$api.data.create(this.meta.id, this.localState, { query: { form: this.$route.query.view } })
data = { ...this.localState, ...data }
// save hasmany and manytomany relations from local state

4
packages/nc-gui/components/project/spreadsheet/views/galleryView.vue

@ -43,8 +43,8 @@
<v-row class="">
<v-col
v-for="(col) in fields"
v-show="showFields[col.alias|| col.title]"
:key="col.alias || col.title"
v-show="showFields[ col.title]"
:key="col.title"
class="col-12 mt-1 mb-2 "
>
<label :for="`data-table-form-${col.title}`" class="body-2 text-capitalize caption grey--text">

6
packages/nc-gui/components/project/spreadsheet/views/kanbanView.vue

@ -21,8 +21,8 @@
<v-row class="">
<v-col
v-for="(col) in fields"
v-show="showFields[col.alias|| col.title]"
:key="col.alias || col.title"
v-show="showFields[col.title]"
:key="col.title"
class="kanban-col col-12"
>
<label :for="`data-table-form-${col.title}`" class="body-2 text-capitalize caption grey--text">
@ -137,7 +137,7 @@ export default {
) || []
},
groupingFieldColumn() {
return this.fields.filter(o => o.alias === this.groupingField)[0]
return this.fields.filter(f => f.title === this.groupingField)[0]
}
},
mounted() {

19
packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue

@ -34,14 +34,14 @@
</th>
<th
v-for="(col) in availableColumns"
v-show="showFields[col.alias]"
:key="col.alias"
v-show="showFields[col.title]"
:key="col.title"
v-xc-ver-resize
class="grey-border caption font-wight-regular nc-grid-header-cell"
:class="$store.state.windows.darkTheme ? 'grey darken-3 grey--text text--lighten-1' : 'grey lighten-4 grey--text text--darken-2'"
:data-col="col.alias"
:data-col="col.title"
@xcresize="onresize(col.id,$event), log('xcresize')"
@xcresizing="onXcResizing(col.alias,$event)"
@xcresizing="onXcResizing(col.title,$event)"
@xcresized="resizingCol = null"
>
<!-- :style="columnsWidth[col.title] ? `min-width:${columnsWidth[col.title]}; max-width:${columnsWidth[col.title]}` : ''"
@ -170,8 +170,8 @@
</td>
<td
v-for="(columnObj,col) in availableColumns"
v-show="showFields[columnObj.alias]"
:key="row + columnObj.alias"
v-show="showFields[columnObj.title]"
:key="row + columnObj.title"
class="cell pointer nc-grid-cell"
:class="{
'active' :!isPublicView && selected.col === col && selected.row === row && isEditable ,
@ -179,7 +179,7 @@
'text-center': isCentrallyAligned(columnObj),
'required': isRequired(columnObj,rowObj)
}"
:data-col="columnObj.alias"
:data-col="columnObj.title"
@dblclick="makeEditable(col,row,columnObj.ai,rowMeta)"
@click="makeSelected(col,row);"
@contextmenu="showRowContextMenu($event,rowObj,rowMeta,row,col, columnObj)"
@ -392,7 +392,7 @@ export default {
const val = (this.gridViewCols && this.gridViewCols[c.id] && this.gridViewCols[c.id].width) || '200px'
if (val && c.key !== this.resizingCol) {
style += `[data-col="${c.alias}"]{min-width:${val};max-width:${val};width: ${val};}`
style += `[data-col="${c.title}"]{min-width:${val};max-width:${val};width: ${val};}`
}
}
@ -659,9 +659,6 @@ export default {
this.selected.row--
}
},
addNewRelationTab(...args) {
this.$emit('addNewRelationTab', ...args)
},
makeSelected(col, row) {
if (this.selected.col !== col || this.selected.row !== row) {
this.selected = {

1
packages/nc-gui/components/project/table.vue

@ -35,7 +35,6 @@
:mtd-new-table-update="mtdNewTableUpdate"
:delete-table="deleteTable"
:is-meta-table="isMetaTable"
:add-new-relation-tab="addNewRelationTab"
/>
</v-tab-item>
</template>

142
packages/nc-gui/components/projectList/createNewProjectBtn.vue

@ -0,0 +1,142 @@
<template>
<v-menu v-if="connectToExternalDB" offset-y bottom open-on-hover>
<template #activator="{ on }">
<slot :on="on">
<div>
<v-btn
v-if="_isUIAllowed('projectCreate',true)"
v-ge="['home', 'project-new']"
:x-large="$vuetify.breakpoint.lgAndUp"
:large="$vuetify.breakpoint.mdAndDown"
data-v-step="1"
outlined
rounded
color="primary"
class="nc-new-project-menu elevation-3"
v-on="on"
>
<v-icon class="mr-2">
mdi-plus
</v-icon>
<!-- New Project -->
{{ $t('title.newProj') }}
<v-icon class="mr-1" small>
mdi-menu-down
</v-icon>
</v-btn>
</div>
</slot>
</template>
<v-list dense>
<v-list-item
class="create-xc-db-project nc-create-xc-db-project"
@click="onCreateProject('xcdb')"
>
<v-list-item-icon class="mr-2">
<v-icon small color="blue">
mdi-plus
</v-icon>
</v-list-item-icon>
<v-list-item-title>
<!-- Create -->
<span>{{
$t('general.create')
}}</span>
</v-list-item-title>
<v-spacer />
<v-tooltip right>
<template #activator="{ on }">
<v-icon
x-small
color="grey"
class="ml-4"
v-on="on"
>
mdi-information-outline
</v-icon>
</template>
<!-- Create a new project -->
<span class="caption">{{
$t('tooltip.xcDB')
}}</span>
</v-tooltip>
</v-list-item>
<v-list-item
title
class="pt-2 create-external-db-project nc-create-external-db-project"
@click="onCreateProject()"
>
<v-list-item-icon class="mr-2">
<v-icon small color="green">
mdi-database-outline
</v-icon>
</v-list-item-icon>
<v-list-item-title>
<!-- Create By Connecting <br>To An External Database -->
<span
style="line-height: 1.5em"
v-html="
$t('activity.createProjectExtended.extDB')
"
/>
</v-list-item-title>
<v-spacer />
<v-tooltip right>
<template #activator="{ on }">
<v-icon
x-small
color="grey"
class="ml-4"
v-on="on"
>
mdi-information-outline
</v-icon>
</template>
<!-- Supports MySQL, PostgreSQL, SQL Server & SQLite -->
<span class="caption">{{
$t('tooltip.extDB')
}}</span>
</v-tooltip>
</v-list-item>
</v-list>
</v-menu>
<x-btn
v-else-if="_isUIAllowed('projectCreate',true)"
v-ge="['home', 'project-new']"
outlined
data-v-step="1"
color="primary"
@click="onCreateProject('xcdb')"
>
<!-- New Project -->
{{ $t('title.newProj') }}
</x-btn>
<span v-else />
</template>
<script>
export default {
name: 'CreateNewProjectBtn',
computed: {
connectToExternalDB() {
return this.$store.state.project && this.$store.state.project.projectInfo && this.$store.state.project.projectInfo.connectToExternalDB
}
},
methods: {
onCreateProject(xcdb) {
if (xcdb === 'xcdb') {
this.$router.push('/project/xcdb')
this.$tele.emit('project:create:xcdb:trigger')
} else {
this.$router.push('/project/0')
this.$tele.emit('project:create:extdb:trigger')
}
}
}
}
</script>
<style scoped>
</style>

10
packages/nc-gui/helpers/sqlUi/MssqlUi.js

@ -570,16 +570,9 @@ export class MssqlUi {
}
static onCheckboxChangeAI(col) {
console.log(col)
if (col.dt === 'int' || col.dt === 'bigint' || col.dt === 'smallint' || col.dt === 'tinyint') {
col.altered = col.altered || 2
}
// if (!col.ai) {
// col.dtx = 'specificType'
// } else {
// col.dtx = ''
// }
}
static showScale(columnObj) {
@ -596,7 +589,6 @@ export class MssqlUi {
columns[i].un = false
console.log('>> resetting unsigned value', columns[i].column_name)
}
console.log(columns[i].column_name)
}
}
@ -617,8 +609,6 @@ export class MssqlUi {
}
static handleRawOutput(result, headers) {
console.log(result)
if (Array.isArray(result) && result[0]) {
const keys = Object.keys(result[0])
// set headers before settings result

4
packages/nc-gui/layouts/default.vue

@ -33,7 +33,7 @@
}})</span>
</v-tooltip>
<span class="body-1 ml-n2" @click="$router.push('/projects')"> {{ brandName }}</span>
<span class="body-1 ml-n1" @click="$router.push('/projects')"> {{ brandName }}</span>
</v-toolbar-title>
<!-- <v-toolbar-items />-->
@ -657,7 +657,7 @@ export default {
},
async copyProjectInfo() {
try {
const data = (await this.$api.project.metaGet(this.$store.state.project.projectId))// await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'ncProjectInfo'])
const data = (await this.$api.project.metaGet(this.$store.state.project.projectId))
copyTextToClipboard(Object.entries(data).map(([k, v]) => `${k}: **${v}**`).join('\n'))
this.$toast.info('Copied project info to clipboard').goAway(3000)
} catch (e) {

8
packages/nc-gui/mixins/colors.js

@ -8,5 +8,13 @@ export default {
textColors() {
return this.$store.state.windows.darkTheme ? colors.light : colors.dark
}
},
methods: {
getColor(i) {
return this.colors[i % this.colors.length]
},
getTextColor(i) {
return this.textColors[i % this.textColors.length]
}
}
}

4
packages/nc-gui/mixins/device.js

@ -52,7 +52,9 @@ export default {
return zhLan.some(l => browserLan.includes(l))
},
...mapGetters({
_isUIAllowed: 'users/GtrIsUIAllowed'
_isUIAllowed: 'users/GtrIsUIAllowed',
projectName: 'project/GtrProjectName',
projectId: 'project/GtrProjectId'
})
},
mounted() {

419
packages/nc-gui/package-lock.json generated

@ -26,12 +26,12 @@
"monaco-editor": "^0.19.3",
"monaco-themes": "^0.2.5",
"nano-assign": "^1.0.1",
"nocodb-sdk": "0.0.9",
"nocodb-sdk": "file:../nocodb-sdk",
"nuxt": "^2.14.0",
"odometer": "^0.4.8",
"papaparse": "^5.3.1",
"secure-ls": "^1.2.6",
"socket.io-client": "^2.3.0",
"socket.io-client": "^4.4.1",
"splitpanes": "^2.2.1",
"sql-formatter": "^2.3.3",
"unique-names-generator": "^4.3.1",
@ -69,6 +69,42 @@
"vue-eslint-parser": "^7.9.0"
}
},
"../nocodb-sdk": {
"version": "0.0.9",
"license": "MIT",
"dependencies": {
"axios": "^0.21.1",
"jsep": "^0.4.0"
},
"devDependencies": {
"@ava/typescript": "^1.1.1",
"@istanbuljs/nyc-config-typescript": "^1.0.1",
"@typescript-eslint/eslint-plugin": "^4.0.1",
"@typescript-eslint/parser": "^4.0.1",
"ava": "^3.12.1",
"codecov": "^3.5.0",
"cspell": "^4.1.0",
"cz-conventional-changelog": "^3.3.0",
"eslint": "^7.8.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-functional": "^3.0.2",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-prettier": "^4.0.0",
"gh-pages": "^3.1.0",
"npm-run-all": "^4.1.5",
"nyc": "^15.1.0",
"open-cli": "^6.0.1",
"prettier": "^2.1.1",
"standard-version": "^9.0.0",
"ts-node": "^9.0.0",
"typedoc": "^0.19.0",
"typescript": "^4.0.2"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@ampproject/remapping": {
"version": "2.1.2",
"license": "Apache-2.0",
@ -2923,6 +2959,19 @@
"version": "1.0.0-next.21",
"license": "MIT"
},
"node_modules/@socket.io/base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/@socket.io/component-emitter": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz",
"integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q=="
},
"node_modules/@types/html-minifier-terser": {
"version": "5.1.2",
"license": "MIT"
@ -3520,10 +3569,6 @@
"node": ">=0.8"
}
},
"node_modules/after": {
"version": "0.8.2",
"license": "MIT"
},
"node_modules/aggregate-error": {
"version": "3.1.0",
"license": "MIT",
@ -3721,10 +3766,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/arraybuffer.slice": {
"version": "0.0.7",
"license": "MIT"
},
"node_modules/asn1.js": {
"version": "5.4.1",
"license": "MIT",
@ -3980,12 +4021,6 @@
"node": ">=0.10.0"
}
},
"node_modules/base64-arraybuffer": {
"version": "0.1.4",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/base64-js": {
"version": "1.5.1",
"funding": [
@ -4026,10 +4061,6 @@
"file-uri-to-path": "1.0.0"
}
},
"node_modules/blob": {
"version": "0.0.5",
"license": "MIT"
},
"node_modules/bluebird": {
"version": "3.7.2",
"license": "MIT"
@ -4822,16 +4853,10 @@
"version": "1.0.1",
"license": "MIT"
},
"node_modules/component-bind": {
"version": "1.0.0"
},
"node_modules/component-emitter": {
"version": "1.3.0",
"license": "MIT"
},
"node_modules/component-inherit": {
"version": "0.0.3"
},
"node_modules/compressible": {
"version": "2.0.18",
"license": "MIT",
@ -5851,38 +5876,27 @@
}
},
"node_modules/engine.io-client": {
"version": "3.5.2",
"license": "MIT",
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.1.tgz",
"integrity": "sha512-V05mmDo4gjimYW+FGujoGmmmxRaDsrVr7AXA3ZIfa04MWM1jOfZfUwou0oNqhNwy/votUDvGDt4JA4QF4e0b4g==",
"dependencies": {
"component-emitter": "~1.3.0",
"component-inherit": "0.0.3",
"debug": "~3.1.0",
"engine.io-parser": "~2.2.0",
"@socket.io/component-emitter": "~3.0.0",
"debug": "~4.3.1",
"engine.io-parser": "~5.0.0",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"parseqs": "0.0.6",
"parseuri": "0.0.6",
"ws": "~7.4.2",
"xmlhttprequest-ssl": "~1.6.2",
"ws": "~8.2.3",
"xmlhttprequest-ssl": "~2.0.0",
"yeast": "0.1.2"
}
},
"node_modules/engine.io-client/node_modules/debug": {
"version": "3.1.0",
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/engine.io-client/node_modules/ms": {
"version": "2.0.0",
"license": "MIT"
},
"node_modules/engine.io-client/node_modules/ws": {
"version": "7.4.6",
"license": "MIT",
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
"engines": {
"node": ">=8.3.0"
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
@ -5898,14 +5912,14 @@
}
},
"node_modules/engine.io-parser": {
"version": "2.2.1",
"license": "MIT",
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz",
"integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==",
"dependencies": {
"after": "0.8.2",
"arraybuffer.slice": "~0.0.7",
"base64-arraybuffer": "0.1.4",
"blob": "0.0.5",
"has-binary2": "~1.0.2"
"@socket.io/base64-arraybuffer": "~1.0.2"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/enhanced-resolve": {
@ -7769,20 +7783,10 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-binary2": {
"version": "1.0.3",
"license": "MIT",
"dependencies": {
"isarray": "2.0.1"
}
},
"node_modules/has-binary2/node_modules/isarray": {
"version": "2.0.1",
"license": "MIT"
},
"node_modules/has-cors": {
"version": "1.1.0",
"license": "MIT"
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
},
"node_modules/has-flag": {
"version": "3.0.0",
@ -8302,9 +8306,6 @@
"version": "1.0.1",
"license": "MIT"
},
"node_modules/indexof": {
"version": "0.0.1"
},
"node_modules/infer-owner": {
"version": "1.0.4",
"license": "ISC"
@ -9576,16 +9577,8 @@
}
},
"node_modules/nocodb-sdk": {
"version": "0.0.9",
"resolved": "https://registry.npmjs.org/nocodb-sdk/-/nocodb-sdk-0.0.9.tgz",
"integrity": "sha512-MwvHh9WtplJtB2z41DbDUF09bYRLX4WBA+IytgPRWr5e3s8UEBzKZiKN7sLcVnmiY6a9ctT4Ad0CS4viwExoQw==",
"dependencies": {
"axios": "^0.21.1",
"jsep": "^0.4.0"
},
"engines": {
"node": ">=10"
}
"resolved": "../nocodb-sdk",
"link": true
},
"node_modules/node-fetch": {
"version": "2.6.7",
@ -10228,11 +10221,13 @@
},
"node_modules/parseqs": {
"version": "0.0.6",
"license": "MIT"
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
},
"node_modules/parseuri": {
"version": "0.0.6",
"license": "MIT"
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
},
"node_modules/parseurl": {
"version": "1.3.3",
@ -12660,57 +12655,33 @@
"license": "MIT"
},
"node_modules/socket.io-client": {
"version": "2.4.0",
"license": "MIT",
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.4.1.tgz",
"integrity": "sha512-N5C/L5fLNha5Ojd7Yeb/puKcPWWcoB/A09fEjjNsg91EDVr5twk/OEyO6VT9dlLSUNY85NpW6KBhVMvaLKQ3vQ==",
"dependencies": {
"backo2": "1.0.2",
"component-bind": "1.0.0",
"component-emitter": "~1.3.0",
"debug": "~3.1.0",
"engine.io-client": "~3.5.0",
"has-binary2": "~1.0.2",
"indexof": "0.0.1",
"parseqs": "0.0.6",
"@socket.io/component-emitter": "~3.0.0",
"backo2": "~1.0.2",
"debug": "~4.3.2",
"engine.io-client": "~6.1.1",
"parseuri": "0.0.6",
"socket.io-parser": "~3.3.0",
"to-array": "0.1.4"
}
},
"node_modules/socket.io-client/node_modules/debug": {
"version": "3.1.0",
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
"socket.io-parser": "~4.1.1"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-client/node_modules/ms": {
"version": "2.0.0",
"license": "MIT"
},
"node_modules/socket.io-parser": {
"version": "3.3.2",
"license": "MIT",
"dependencies": {
"component-emitter": "~1.3.0",
"debug": "~3.1.0",
"isarray": "2.0.1"
}
},
"node_modules/socket.io-parser/node_modules/debug": {
"version": "3.1.0",
"license": "MIT",
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.2.tgz",
"integrity": "sha512-j3kk71QLJuyQ/hh5F/L2t1goqzdTL0gvDzuhTuNSwihfuFUrcSji0qFZmJJPtG6Rmug153eOPsUizeirf1IIog==",
"dependencies": {
"ms": "2.0.0"
"@socket.io/component-emitter": "~3.0.0",
"debug": "~4.3.1"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-parser/node_modules/isarray": {
"version": "2.0.1",
"license": "MIT"
},
"node_modules/socket.io-parser/node_modules/ms": {
"version": "2.0.0",
"license": "MIT"
},
"node_modules/sort-keys": {
"version": "1.1.2",
"license": "MIT",
@ -13422,9 +13393,6 @@
"node": ">=0.6.0"
}
},
"node_modules/to-array": {
"version": "0.1.4"
},
"node_modules/to-arraybuffer": {
"version": "1.0.1",
"license": "MIT"
@ -15376,7 +15344,9 @@
}
},
"node_modules/xmlhttprequest-ssl": {
"version": "1.6.3",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
"engines": {
"node": ">=0.4.0"
}
@ -15449,7 +15419,8 @@
},
"node_modules/yeast": {
"version": "0.1.2",
"license": "MIT"
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
},
"node_modules/yocto-queue": {
"version": "0.1.0",
@ -17252,6 +17223,16 @@
"@polka/url": {
"version": "1.0.0-next.21"
},
"@socket.io/base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ=="
},
"@socket.io/component-emitter": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz",
"integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q=="
},
"@types/html-minifier-terser": {
"version": "5.1.2"
},
@ -17669,9 +17650,6 @@
"printj": "~1.1.0"
}
},
"after": {
"version": "0.8.2"
},
"aggregate-error": {
"version": "3.1.0",
"requires": {
@ -17784,9 +17762,6 @@
"es-abstract": "^1.19.0"
}
},
"arraybuffer.slice": {
"version": "0.0.7"
},
"asn1.js": {
"version": "5.4.1",
"requires": {
@ -17963,9 +17938,6 @@
}
}
},
"base64-arraybuffer": {
"version": "0.1.4"
},
"base64-js": {
"version": "1.5.1"
},
@ -17982,9 +17954,6 @@
"file-uri-to-path": "1.0.0"
}
},
"blob": {
"version": "0.0.5"
},
"bluebird": {
"version": "3.7.2"
},
@ -18477,15 +18446,9 @@
"commondir": {
"version": "1.0.1"
},
"component-bind": {
"version": "1.0.0"
},
"component-emitter": {
"version": "1.3.0"
},
"component-inherit": {
"version": "0.0.3"
},
"compressible": {
"version": "2.0.18",
"requires": {
@ -19164,44 +19127,35 @@
}
},
"engine.io-client": {
"version": "3.5.2",
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.1.tgz",
"integrity": "sha512-V05mmDo4gjimYW+FGujoGmmmxRaDsrVr7AXA3ZIfa04MWM1jOfZfUwou0oNqhNwy/votUDvGDt4JA4QF4e0b4g==",
"requires": {
"component-emitter": "~1.3.0",
"component-inherit": "0.0.3",
"debug": "~3.1.0",
"engine.io-parser": "~2.2.0",
"@socket.io/component-emitter": "~3.0.0",
"debug": "~4.3.1",
"engine.io-parser": "~5.0.0",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"parseqs": "0.0.6",
"parseuri": "0.0.6",
"ws": "~7.4.2",
"xmlhttprequest-ssl": "~1.6.2",
"ws": "~8.2.3",
"xmlhttprequest-ssl": "~2.0.0",
"yeast": "0.1.2"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0"
},
"ws": {
"version": "7.4.6",
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
"requires": {}
}
}
},
"engine.io-parser": {
"version": "2.2.1",
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz",
"integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==",
"requires": {
"after": "0.8.2",
"arraybuffer.slice": "~0.0.7",
"base64-arraybuffer": "0.1.4",
"blob": "0.0.5",
"has-binary2": "~1.0.2"
"@socket.io/base64-arraybuffer": "~1.0.2"
}
},
"enhanced-resolve": {
@ -20359,19 +20313,10 @@
"has-bigints": {
"version": "1.0.1"
},
"has-binary2": {
"version": "1.0.3",
"requires": {
"isarray": "2.0.1"
},
"dependencies": {
"isarray": {
"version": "2.0.1"
}
}
},
"has-cors": {
"version": "1.1.0"
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
},
"has-flag": {
"version": "3.0.0"
@ -20693,9 +20638,6 @@
"indexes-of": {
"version": "1.0.1"
},
"indexof": {
"version": "0.0.1"
},
"infer-owner": {
"version": "1.0.4"
},
@ -21483,12 +21425,33 @@
}
},
"nocodb-sdk": {
"version": "0.0.9",
"resolved": "https://registry.npmjs.org/nocodb-sdk/-/nocodb-sdk-0.0.9.tgz",
"integrity": "sha512-MwvHh9WtplJtB2z41DbDUF09bYRLX4WBA+IytgPRWr5e3s8UEBzKZiKN7sLcVnmiY6a9ctT4Ad0CS4viwExoQw==",
"version": "file:../nocodb-sdk",
"requires": {
"@ava/typescript": "^1.1.1",
"@istanbuljs/nyc-config-typescript": "^1.0.1",
"@typescript-eslint/eslint-plugin": "^4.0.1",
"@typescript-eslint/parser": "^4.0.1",
"ava": "^3.12.1",
"axios": "^0.21.1",
"jsep": "^0.4.0"
"codecov": "^3.5.0",
"cspell": "^4.1.0",
"cz-conventional-changelog": "^3.3.0",
"eslint": "^7.8.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-functional": "^3.0.2",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-prettier": "^4.0.0",
"gh-pages": "^3.1.0",
"jsep": "^0.4.0",
"npm-run-all": "^4.1.5",
"nyc": "^15.1.0",
"open-cli": "^6.0.1",
"prettier": "^2.1.1",
"standard-version": "^9.0.0",
"ts-node": "^9.0.0",
"typedoc": "^0.19.0",
"typescript": "^4.0.2"
}
},
"node-fetch": {
@ -21905,10 +21868,14 @@
"dev": true
},
"parseqs": {
"version": "0.0.6"
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
},
"parseuri": {
"version": "0.0.6"
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
},
"parseurl": {
"version": "1.3.3"
@ -23521,52 +23488,25 @@
}
},
"socket.io-client": {
"version": "2.4.0",
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.4.1.tgz",
"integrity": "sha512-N5C/L5fLNha5Ojd7Yeb/puKcPWWcoB/A09fEjjNsg91EDVr5twk/OEyO6VT9dlLSUNY85NpW6KBhVMvaLKQ3vQ==",
"requires": {
"backo2": "1.0.2",
"component-bind": "1.0.0",
"component-emitter": "~1.3.0",
"debug": "~3.1.0",
"engine.io-client": "~3.5.0",
"has-binary2": "~1.0.2",
"indexof": "0.0.1",
"parseqs": "0.0.6",
"@socket.io/component-emitter": "~3.0.0",
"backo2": "~1.0.2",
"debug": "~4.3.2",
"engine.io-client": "~6.1.1",
"parseuri": "0.0.6",
"socket.io-parser": "~3.3.0",
"to-array": "0.1.4"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0"
}
"socket.io-parser": "~4.1.1"
}
},
"socket.io-parser": {
"version": "3.3.2",
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.2.tgz",
"integrity": "sha512-j3kk71QLJuyQ/hh5F/L2t1goqzdTL0gvDzuhTuNSwihfuFUrcSji0qFZmJJPtG6Rmug153eOPsUizeirf1IIog==",
"requires": {
"component-emitter": "~1.3.0",
"debug": "~3.1.0",
"isarray": "2.0.1"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"requires": {
"ms": "2.0.0"
}
},
"isarray": {
"version": "2.0.1"
},
"ms": {
"version": "2.0.0"
}
"@socket.io/component-emitter": "~3.0.0",
"debug": "~4.3.1"
}
},
"sort-keys": {
@ -24031,9 +23971,6 @@
"os-tmpdir": "~1.0.2"
}
},
"to-array": {
"version": "0.1.4"
},
"to-arraybuffer": {
"version": "1.0.1"
},
@ -25317,7 +25254,9 @@
}
},
"xmlhttprequest-ssl": {
"version": "1.6.3"
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A=="
},
"xstate": {
"version": "4.30.6"
@ -25362,7 +25301,9 @@
}
},
"yeast": {
"version": "0.1.2"
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
},
"yocto-queue": {
"version": "0.1.0"

4
packages/nc-gui/package.json

@ -29,12 +29,12 @@
"monaco-editor": "^0.19.3",
"monaco-themes": "^0.2.5",
"nano-assign": "^1.0.1",
"nocodb-sdk": "0.0.9",
"nocodb-sdk": "file:../nocodb-sdk",
"nuxt": "^2.14.0",
"odometer": "^0.4.8",
"papaparse": "^5.3.1",
"secure-ls": "^1.2.6",
"socket.io-client": "^2.3.0",
"socket.io-client": "^4.4.1",
"splitpanes": "^2.2.1",
"sql-formatter": "^2.3.3",
"unique-names-generator": "^4.3.1",

229
packages/nc-gui/pages/projects/list.vue

@ -0,0 +1,229 @@
<template>
<div
class="d-flex "
style="height:calc(100vh - 52px)"
>
<v-navigation-drawer
left
permanent
width="max(300px,20%)"
class="h-100"
>
<div class="d-flex flex-column h-100">
<div class="advance-menu flex-grow-1 pt-8">
<v-list
v-if="_isUIAllowed('treeViewProjectSettings')"
shaped
dense
:class="{ 'advanced-border': overShieldIcon }"
>
<v-list-item-group v-model="activePage" color="x-active" mandatory>
<v-list-item
v-for="item in navDrawerOptions"
:key="item.title"
:value="item.title"
dense
class="body-2"
>
<v-list-item-title>
<v-icon small class="ml-5">
{{ item.icon }}
</v-icon>
<span
class="font-weight-medium ml-3"
:class="{'textColor--text text--lighten-2':item.title!==activePage}"
>
{{ item.title }}
</span>
</v-list-item-title>
</v-list-item>
</v-list-item-group>
</v-list>
</div>
<v-divider />
<extras class="pl-6 " />
<div class="sponsor ml-2 mb-2 mr-2">
<sponsor-mini nav />
</div>
</div>
</v-navigation-drawer>
<v-container class="flex-grow-1 py-9 px-15 h-100" style="overflow-y: auto">
<div class="d-flex">
<h2 class="display-1 ml-5 mb-7 font-weight-medium textColor--text text--lighten-2 flex-grow-1">
{{ activePage }}
</h2>
<div class="">
<create-new-project-btn />
</div>
</div>
<v-divider class="mb-3" />
<div v-if="projectList &&projectList.length" class="nc-project-item-container d-flex d-100">
<div
v-for="(project,i) in projectList"
:key="project.id"
class=" nc-project-item elevation-0 d-flex align-center justify-center flex-column py-5"
>
<div
class="nc-project-thumbnail pointer text-uppercase d-flex align-center justify-center"
:style="{backgroundColor:getTextColor(i)}"
@click="openProject(project)"
>
{{ project.title.split(' ').map(w => w[0]).slice(0, 2).join('') }}
<v-icon class="nc-project-star-icon" small color="white" v-on="on" @click.stop>
mdi-star-outline
</v-icon>
<v-menu bottom offset-y>
<template #activator="{on}">
<v-icon class="nc-project-option-menu-icon" color="white" v-on="on" @click.stop>
mdi-menu-down
</v-icon>
</template>
<v-list dense>
<v-list-item>
<v-list-item-title>
<v-icon small color="red">
mdi-delete-outline
</v-icon>
Delete
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</div>
<div class="text-center pa-2 nc-project-title body-2 font-weight-medium ">
{{ project.title }}
</div>
</div>
<div class="pointer nc-project-item nc-project-item elevation-0 d-flex align-center justify-center flex-column">
<create-new-project-btn>
<template #default="{on}">
<div
class="nc-project-thumbnail nc-add-project text-uppercase d-flex align-center justify-center grey lighten-2 "
v-on="on"
>
+
</div>
</template>
</create-new-project-btn>
<div class="text-center pa-2 nc-project-title body-2 font-weight-medium ">
Add project
</div>
</div>
</div>
<div
v-else
class="px-4 py-10 text-center textColor--text text--lighten-3 caption backgroundColor "
>
Please create a project
</div>
</v-container>
</div>
</template>
<script>
import colors from '~/mixins/colors'
import CreateNewProjectBtn from '~/components/projectList/createNewProjectBtn'
import Extras from '~/components/project/spreadsheet/components/extras'
import SponsorMini from '~/components/sponsorMini'
export default {
name: 'List',
components: { SponsorMini, Extras, CreateNewProjectBtn },
mixins: [colors],
data: () => ({
projectList: null,
activePage: null,
navDrawerOptions: [{
title: 'My NocoDB',
icon: 'mdi-folder-outline'
}, {
title: 'Shared With Me',
icon: 'mdi-account-group'
}, {
title: 'Recent',
icon: 'mdi-clock-outline'
}, {
title: 'Starred',
icon: 'mdi-star'
}]
}),
mounted() {
this.loadProjectList()
},
methods: {
async loadProjectList() {
const { list, pageInfo } = await this.$api.project.list()
this.projectList = list
},
async openProject(project) {
await this.$router.push({
path: `/nc/${project.id}`
})
this.$tele.emit(`project:open:${this.projects.length}`)
}
}
}
</script>
<style scoped>
.nc-project-item-container {
flex-wrap: wrap;
position: relative;
}
.nc-project-item {
width: 150px;
align-items: center;
}
.nc-project-thumbnail {
height: 100px;
width: 100px;
font-size: 50px;
color: white;
font-weight: bold;
border-radius: 4px;
margin-inside: auto;
position: relative;
}
.nc-project-option-menu-icon,.nc-project-star-icon {
position: absolute;
opacity: 0;
transition: .3s opacity;
}
.nc-project-star-icon{
top:8px;
right: 10px;
}
.nc-project-option-menu-icon{
bottom: 5px;
right: 5px;
}
.nc-project-thumbnail:hover .nc-project-option-menu-icon,.nc-project-thumbnail:hover .nc-project-star-icon{
opacity: 1;
}
.nc-project-title {
text-transform: capitalize;
text-align: center;
}
.nc-project-title.nc-add-project {
font-size: 60px;
}
/deep/ .v-navigation-drawer__border {
background-color: transparent !important;
}
.nc-project-thumbnail:hover {
background-image: linear-gradient(#0002, #0002);
}
</style>

63
packages/nc-gui/plugins/tele.js

@ -9,31 +9,45 @@ export default function({
}, inject) {
let socket
const init = () => {
if (socket) { return }
const init = async(token) => {
try {
if (socket) {
socket.disconnect()
}
const isUrl = $axios.defaults.baseURL.startsWith('http')
const url = isUrl ? $axios.defaults.baseURL : window.location.origin
const path = isUrl ? undefined : ($axios.defaults.baseURL === '..' ? window.location.pathname.split('/').slice(0, -1).join('/') : $axios.defaults.baseURL)
socket = io($axios.defaults.baseURL)
socket = io(url, {
path,
extraHeaders: { 'xc-auth': token }
})
app.router.onReady(() => {
app.router.afterEach(function(to, from) {
if (to.path === from.path && (to.query && to.query.type) === (from.query && from.query.type)) {
return
}
socket.emit('page', {
id: store.state.users.user && store.state.users.user.id,
path: to.matched[0].path + (to.query && to.query.type ? `?type=${to.query.type}` : '')
})
socket.on('connect_error', () => {
socket.disconnect()
socket = null
})
} catch { }
}
app.router.onReady(() => {
app.router.afterEach(function(to, from) {
if (!socket || (to.path === from.path && (to.query && to.query.type) === (from.query && from.query.type))) {
return
}
socket.emit('page', {
id: store.state.users.user && store.state.users.user.id,
path: route.matched[0].path + (route.query && route.query.type ? `?type=${route.query.type}` : '')
path: to.matched[0].path + (to.query && to.query.type ? `?type=${to.query.type}` : '')
})
})
if (socket) {
socket.emit('page', {
id: store.state.users.user && store.state.users.user.id,
path: route.matched[0].path + (route.query && route.query.type ? `?type=${route.query.type}` : '')
})
}
})
// socket.on('connect_error', () => {
// socket.disconnect()
// })
}
const tele = {
emit(evt, data) {
if (socket) {
@ -51,11 +65,11 @@ export default function({
function getListener(binding) {
return function(e) {
if (!socket) { return }
const cat = window.location.hash.replace(/\d+\/(?=dashboard)/, '')
const event = binding.value && binding.value[0]
const data = binding.value && binding.value[1]
const extra = binding.value && binding.value.slice(2)
tele.emit(event,
{
cat,
@ -75,10 +89,17 @@ export default function({
}
})
store.watch(state => state.project.projectInfo && state.project.projectInfo.teleEnabled, (value) => {
if (value) { init() }
store.watch(state => state.project.projectInfo && state.project.projectInfo.teleEnabled && state.users.token, (token) => {
if (token) {
init(token).then(() => {})
} else if (socket) {
socket.disconnect()
socket = null
}
})
if (store.state.project.projectInfo && store.state.project.projectInfo.teleEnabled) { init() }
if (store.state.project.projectInfo && store.state.project.projectInfo.teleEnabled && store.state.users.token) {
init(store.state.users.token).then(() => {})
}
}
function gatPath(app) {

8
packages/nc-gui/store/project.js

@ -192,10 +192,13 @@ export const getters = {
return data
},
GtrProjectName(state) {
return state.project && state.project.title// state.unserializedList && state.unserializedList[0] ? state.unserializedList[0].projectJson.title : "__project__";
return state.project && state.project.title
},
GtrProjectId(state) {
return state.project && state.project.id
},
GtrProjectPrefix(state) {
return state.project && state.project.prefix//state.unserializedList && state.unserializedList[0] ? state.unserializedList[0].projectJson.prefix : null;
return state.project && state.project.prefix
},
GtrApiEnvironment(state) {
@ -368,7 +371,6 @@ export const actions = {
const tables = (await this.$api.dbTable.list(
state.projectId,
state.project.bases[0].id,
{
includeM2M: rootState.windows.includeM2M || ''
})).list

4
packages/nocodb-sdk/package-lock.json generated

@ -1,12 +1,12 @@
{
"name": "nocodb-sdk",
"version": "0.0.6",
"version": "0.0.9",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "nocodb-sdk",
"version": "0.0.6",
"version": "0.0.9",
"license": "MIT",
"dependencies": {
"axios": "^0.21.1",

1796
packages/nocodb-sdk/src/lib/Api.ts

File diff suppressed because it is too large Load Diff

451
packages/nocodb/package-lock.json generated

@ -72,7 +72,7 @@
"nc-lib-gui": "0.84.15",
"nc-plugin": "^0.1.1",
"ncp": "^2.0.0",
"nocodb-sdk": "0.0.9",
"nocodb-sdk": "file:../nocodb-sdk",
"nodemailer": "^6.4.10",
"ora": "^4.0.4",
"os-locale": "^5.0.0",
@ -91,7 +91,7 @@
"rmdir": "^1.2.0",
"sha.js": "^2.4.11",
"slash": "^3.0.0",
"socket.io": "^2.3.0",
"socket.io": "^4.4.1",
"sqlite3": "5.0.0",
"twilio": "^3.55.1",
"unique-names-generator": "^4.3.1",
@ -227,6 +227,42 @@
"node": ">=8.9"
}
},
"../nocodb-sdk": {
"version": "0.0.9",
"license": "MIT",
"dependencies": {
"axios": "^0.21.1",
"jsep": "^0.4.0"
},
"devDependencies": {
"@ava/typescript": "^1.1.1",
"@istanbuljs/nyc-config-typescript": "^1.0.1",
"@typescript-eslint/eslint-plugin": "^4.0.1",
"@typescript-eslint/parser": "^4.0.1",
"ava": "^3.12.1",
"codecov": "^3.5.0",
"cspell": "^4.1.0",
"cz-conventional-changelog": "^3.3.0",
"eslint": "^7.8.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-functional": "^3.0.2",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-prettier": "^4.0.0",
"gh-pages": "^3.1.0",
"npm-run-all": "^4.1.5",
"nyc": "^15.1.0",
"open-cli": "^6.0.1",
"prettier": "^2.1.1",
"standard-version": "^9.0.0",
"ts-node": "^9.0.0",
"typedoc": "^0.19.0",
"typescript": "^4.0.2"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@assemblyscript/loader": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz",
@ -1681,6 +1717,14 @@
"node": ">=6"
}
},
"node_modules/@socket.io/base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/@stroncium/procfs": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@stroncium/procfs/-/procfs-1.2.1.tgz",
@ -1753,6 +1797,11 @@
"integrity": "sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==",
"dev": true
},
"node_modules/@types/component-emitter": {
"version": "1.2.11",
"resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz",
"integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ=="
},
"node_modules/@types/connect": {
"version": "3.4.35",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
@ -1762,12 +1811,22 @@
"@types/node": "*"
}
},
"node_modules/@types/cookie": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
"integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="
},
"node_modules/@types/cookiejar": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz",
"integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==",
"dev": true
},
"node_modules/@types/cors": {
"version": "2.8.12",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz",
"integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw=="
},
"node_modules/@types/express": {
"version": "4.17.13",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
@ -15653,9 +15712,9 @@
}
},
"node_modules/moment": {
"version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==",
"version": "2.29.2",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.2.tgz",
"integrity": "sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg==",
"engines": {
"node": "*"
}
@ -16193,16 +16252,8 @@
"dev": true
},
"node_modules/nocodb-sdk": {
"version": "0.0.9",
"resolved": "https://registry.npmjs.org/nocodb-sdk/-/nocodb-sdk-0.0.9.tgz",
"integrity": "sha512-MwvHh9WtplJtB2z41DbDUF09bYRLX4WBA+IytgPRWr5e3s8UEBzKZiKN7sLcVnmiY6a9ctT4Ad0CS4viwExoQw==",
"dependencies": {
"axios": "^0.21.1",
"jsep": "^0.4.0"
},
"engines": {
"node": ">=10"
}
"resolved": "../nocodb-sdk",
"link": true
},
"node_modules/node-addon-api": {
"version": "2.0.0",
@ -20121,16 +20172,19 @@
}
},
"node_modules/socket.io": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.4.1.tgz",
"integrity": "sha512-Si18v0mMXGAqLqCVpTxBa8MGqriHGQh8ccEOhmsmNS3thNCGBwO8WGrwMibANsWtQQ5NStdZwHqZR3naJVFc3w==",
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.1.tgz",
"integrity": "sha512-s04vrBswdQBUmuWJuuNTmXUVJhP0cVky8bBDhdkf8y0Ptsu7fKU2LuLbts9g+pdmAdyMMn8F/9Mf1/wbtUN0fg==",
"dependencies": {
"debug": "~4.1.0",
"engine.io": "~3.5.0",
"has-binary2": "~1.0.2",
"socket.io-adapter": "~1.1.0",
"socket.io-client": "2.4.0",
"socket.io-parser": "~3.4.0"
"accepts": "~1.3.4",
"base64id": "~2.0.0",
"debug": "~4.3.2",
"engine.io": "~6.1.0",
"socket.io-adapter": "~2.3.3",
"socket.io-parser": "~4.0.4"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-adapter": {
@ -20238,124 +20292,61 @@
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
},
"node_modules/socket.io/node_modules/debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
"dependencies": {
"ms": "^2.1.1"
}
},
"node_modules/socket.io/node_modules/engine.io": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.5.0.tgz",
"integrity": "sha512-21HlvPUKaitDGE4GXNtQ7PLP0Sz4aWLddMPw2VTyFz1FVZqu/kZsJUO8WNpKuE/OCL7nkfRaOui2ZCJloGznGA==",
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.3.tgz",
"integrity": "sha512-rqs60YwkvWTLLnfazqgZqLa/aKo+9cueVfEi/dZ8PyGyaf8TLOxj++4QMIgeG3Gn0AhrWiFXvghsoY9L9h25GA==",
"dependencies": {
"@types/cookie": "^0.4.1",
"@types/cors": "^2.8.12",
"@types/node": ">=10.0.0",
"accepts": "~1.3.4",
"base64id": "2.0.0",
"cookie": "~0.4.1",
"debug": "~4.1.0",
"engine.io-parser": "~2.2.0",
"ws": "~7.4.2"
"cors": "~2.8.5",
"debug": "~4.3.1",
"engine.io-parser": "~5.0.3",
"ws": "~8.2.3"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/socket.io/node_modules/engine.io-client": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.2.tgz",
"integrity": "sha512-QEqIp+gJ/kMHeUun7f5Vv3bteRHppHH/FMBQX/esFj/fuYfjyUKWGMo3VCvIP/V8bE9KcjHmRZrhIz2Z9oNsDA==",
"dependencies": {
"component-emitter": "~1.3.0",
"component-inherit": "0.0.3",
"debug": "~3.1.0",
"engine.io-parser": "~2.2.0",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"parseqs": "0.0.6",
"parseuri": "0.0.6",
"ws": "~7.4.2",
"xmlhttprequest-ssl": "~1.6.2",
"yeast": "0.1.2"
}
},
"node_modules/socket.io/node_modules/engine.io-client/node_modules/debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/socket.io/node_modules/engine.io-client/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"node_modules/socket.io/node_modules/isarray": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
},
"node_modules/socket.io/node_modules/parseqs": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
},
"node_modules/socket.io/node_modules/parseuri": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
},
"node_modules/socket.io/node_modules/socket.io-client": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.4.0.tgz",
"integrity": "sha512-M6xhnKQHuuZd4Ba9vltCLT9oa+YvTsP8j9NcEiLElfIg8KeYPyhWOes6x4t+LTAC8enQbE/995AdTem2uNyKKQ==",
"dependencies": {
"backo2": "1.0.2",
"component-bind": "1.0.0",
"component-emitter": "~1.3.0",
"debug": "~3.1.0",
"engine.io-client": "~3.5.0",
"has-binary2": "~1.0.2",
"indexof": "0.0.1",
"parseqs": "0.0.6",
"parseuri": "0.0.6",
"socket.io-parser": "~3.3.0",
"to-array": "0.1.4"
"node": ">=10.0.0"
}
},
"node_modules/socket.io/node_modules/socket.io-client/node_modules/debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"node_modules/socket.io/node_modules/engine.io-parser": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz",
"integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==",
"dependencies": {
"ms": "2.0.0"
"@socket.io/base64-arraybuffer": "~1.0.2"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io/node_modules/socket.io-client/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
"node_modules/socket.io/node_modules/socket.io-adapter": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz",
"integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ=="
},
"node_modules/socket.io/node_modules/socket.io-client/node_modules/socket.io-parser": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.2.tgz",
"integrity": "sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg==",
"node_modules/socket.io/node_modules/socket.io-parser": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz",
"integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==",
"dependencies": {
"@types/component-emitter": "^1.2.10",
"component-emitter": "~1.3.0",
"debug": "~3.1.0",
"isarray": "2.0.1"
"debug": "~4.3.1"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io/node_modules/ws": {
"version": "7.4.6",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
"engines": {
"node": ">=8.3.0"
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
@ -20370,14 +20361,6 @@
}
}
},
"node_modules/socket.io/node_modules/xmlhttprequest-ssl": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz",
"integrity": "sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/sort-keys": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
@ -26177,6 +26160,11 @@
"integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==",
"dev": true
},
"@socket.io/base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ=="
},
"@stroncium/procfs": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@stroncium/procfs/-/procfs-1.2.1.tgz",
@ -26235,6 +26223,11 @@
"integrity": "sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==",
"dev": true
},
"@types/component-emitter": {
"version": "1.2.11",
"resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz",
"integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ=="
},
"@types/connect": {
"version": "3.4.35",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
@ -26244,12 +26237,22 @@
"@types/node": "*"
}
},
"@types/cookie": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
"integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="
},
"@types/cookiejar": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz",
"integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==",
"dev": true
},
"@types/cors": {
"version": "2.8.12",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz",
"integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw=="
},
"@types/express": {
"version": "4.17.13",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
@ -37202,9 +37205,9 @@
"dev": true
},
"moment": {
"version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
"version": "2.29.2",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.2.tgz",
"integrity": "sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg=="
},
"moment-timezone": {
"version": "0.5.34",
@ -37652,12 +37655,33 @@
"dev": true
},
"nocodb-sdk": {
"version": "0.0.9",
"resolved": "https://registry.npmjs.org/nocodb-sdk/-/nocodb-sdk-0.0.9.tgz",
"integrity": "sha512-MwvHh9WtplJtB2z41DbDUF09bYRLX4WBA+IytgPRWr5e3s8UEBzKZiKN7sLcVnmiY6a9ctT4Ad0CS4viwExoQw==",
"version": "file:../nocodb-sdk",
"requires": {
"@ava/typescript": "^1.1.1",
"@istanbuljs/nyc-config-typescript": "^1.0.1",
"@typescript-eslint/eslint-plugin": "^4.0.1",
"@typescript-eslint/parser": "^4.0.1",
"ava": "^3.12.1",
"axios": "^0.21.1",
"jsep": "^0.4.0"
"codecov": "^3.5.0",
"cspell": "^4.1.0",
"cz-conventional-changelog": "^3.3.0",
"eslint": "^7.8.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-functional": "^3.0.2",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-prettier": "^4.0.0",
"gh-pages": "^3.1.0",
"jsep": "^0.4.0",
"npm-run-all": "^4.1.5",
"nyc": "^15.1.0",
"open-cli": "^6.0.1",
"prettier": "^2.1.1",
"standard-version": "^9.0.0",
"ts-node": "^9.0.0",
"typedoc": "^0.19.0",
"typescript": "^4.0.2"
}
},
"node-addon-api": {
@ -40706,16 +40730,16 @@
}
},
"socket.io": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.4.1.tgz",
"integrity": "sha512-Si18v0mMXGAqLqCVpTxBa8MGqriHGQh8ccEOhmsmNS3thNCGBwO8WGrwMibANsWtQQ5NStdZwHqZR3naJVFc3w==",
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.1.tgz",
"integrity": "sha512-s04vrBswdQBUmuWJuuNTmXUVJhP0cVky8bBDhdkf8y0Ptsu7fKU2LuLbts9g+pdmAdyMMn8F/9Mf1/wbtUN0fg==",
"requires": {
"debug": "~4.1.0",
"engine.io": "~3.5.0",
"has-binary2": "~1.0.2",
"socket.io-adapter": "~1.1.0",
"socket.io-client": "2.4.0",
"socket.io-parser": "~3.4.0"
"accepts": "~1.3.4",
"base64id": "~2.0.0",
"debug": "~4.3.2",
"engine.io": "~6.1.0",
"socket.io-adapter": "~2.3.3",
"socket.io-parser": "~4.0.4"
},
"dependencies": {
"component-emitter": {
@ -40723,128 +40747,51 @@
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"engine.io": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.5.0.tgz",
"integrity": "sha512-21HlvPUKaitDGE4GXNtQ7PLP0Sz4aWLddMPw2VTyFz1FVZqu/kZsJUO8WNpKuE/OCL7nkfRaOui2ZCJloGznGA==",
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.3.tgz",
"integrity": "sha512-rqs60YwkvWTLLnfazqgZqLa/aKo+9cueVfEi/dZ8PyGyaf8TLOxj++4QMIgeG3Gn0AhrWiFXvghsoY9L9h25GA==",
"requires": {
"@types/cookie": "^0.4.1",
"@types/cors": "^2.8.12",
"@types/node": ">=10.0.0",
"accepts": "~1.3.4",
"base64id": "2.0.0",
"cookie": "~0.4.1",
"debug": "~4.1.0",
"engine.io-parser": "~2.2.0",
"ws": "~7.4.2"
"cors": "~2.8.5",
"debug": "~4.3.1",
"engine.io-parser": "~5.0.3",
"ws": "~8.2.3"
}
},
"engine.io-client": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.2.tgz",
"integrity": "sha512-QEqIp+gJ/kMHeUun7f5Vv3bteRHppHH/FMBQX/esFj/fuYfjyUKWGMo3VCvIP/V8bE9KcjHmRZrhIz2Z9oNsDA==",
"engine.io-parser": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz",
"integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==",
"requires": {
"component-emitter": "~1.3.0",
"component-inherit": "0.0.3",
"debug": "~3.1.0",
"engine.io-parser": "~2.2.0",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"parseqs": "0.0.6",
"parseuri": "0.0.6",
"ws": "~7.4.2",
"xmlhttprequest-ssl": "~1.6.2",
"yeast": "0.1.2"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
"@socket.io/base64-arraybuffer": "~1.0.2"
}
},
"isarray": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
},
"parseqs": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
},
"parseuri": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
"socket.io-adapter": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz",
"integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ=="
},
"socket.io-client": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.4.0.tgz",
"integrity": "sha512-M6xhnKQHuuZd4Ba9vltCLT9oa+YvTsP8j9NcEiLElfIg8KeYPyhWOes6x4t+LTAC8enQbE/995AdTem2uNyKKQ==",
"socket.io-parser": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz",
"integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==",
"requires": {
"backo2": "1.0.2",
"component-bind": "1.0.0",
"@types/component-emitter": "^1.2.10",
"component-emitter": "~1.3.0",
"debug": "~3.1.0",
"engine.io-client": "~3.5.0",
"has-binary2": "~1.0.2",
"indexof": "0.0.1",
"parseqs": "0.0.6",
"parseuri": "0.0.6",
"socket.io-parser": "~3.3.0",
"to-array": "0.1.4"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"socket.io-parser": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.2.tgz",
"integrity": "sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg==",
"requires": {
"component-emitter": "~1.3.0",
"debug": "~3.1.0",
"isarray": "2.0.1"
}
}
"debug": "~4.3.1"
}
},
"ws": {
"version": "7.4.6",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
"requires": {}
},
"xmlhttprequest-ssl": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz",
"integrity": "sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q=="
}
}
},

4
packages/nocodb/package.json

@ -154,7 +154,7 @@
"nc-lib-gui": "0.84.15",
"nc-plugin": "^0.1.1",
"ncp": "^2.0.0",
"nocodb-sdk": "0.0.9",
"nocodb-sdk": "file:../nocodb-sdk",
"nodemailer": "^6.4.10",
"ora": "^4.0.4",
"os-locale": "^5.0.0",
@ -173,7 +173,7 @@
"rmdir": "^1.2.0",
"sha.js": "^2.4.11",
"slash": "^3.0.0",
"socket.io": "^2.3.0",
"socket.io": "^4.4.1",
"sqlite3": "5.0.0",
"twilio": "^3.55.1",
"unique-names-generator": "^4.3.1",

13
packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSqlv2.ts

@ -179,8 +179,6 @@ class BaseModelSqlv2 {
if (!ignoreFilterSort) applyPaginate(qb, rest);
const proto = await this.getProto();
console.log(qb.toQuery());
return (await qb).map(d => {
d.__proto__ = proto;
return d;
@ -248,8 +246,6 @@ class BaseModelSqlv2 {
as: 'count'
}).first();
console.log(qb.toQuery());
return ((await qb) as any).count;
}
@ -294,7 +290,7 @@ class BaseModelSqlv2 {
allowedCols && (!includePkByDefault || !col.pk)
? allowedCols[col.id] &&
(!isSystemColumn(col) || view.show_system_fields) &&
(!fields?.length || fields.include(col.title))
(!fields?.length || fields.includes(col.title))
: 1
}),
{}
@ -747,8 +743,6 @@ class BaseModelSqlv2 {
).orWhereNull(rcn);
});
console.log('----', qb.toQuery());
const aliasColObjMap = await childTable.getAliasColObjMap();
const filterObj = extractFilterFromXwhere(args.where, aliasColObjMap);
@ -2059,9 +2053,8 @@ function extractFilterFromXwhere(
function extractCondition(nestedArrayConditions, aliasColObjMap) {
return nestedArrayConditions?.map(str => {
// eslint-disable-next-line prefer-const
let [logicOp, alias, op, value] = str
.match(/(?:~(and|or|not))?\((.*?),(\w+),(.*)\)/)
.slice(1);
let [logicOp, alias, op, value] =
str.match(/(?:~(and|or|not))?\((.*?),(\w+),(.*)\)/)?.slice(1) || [];
if (op === 'is') op = 'is' + value;
else if (op === 'in') value = value.split(',');

1
packages/nocodb/src/lib/dataMapper/lib/sql/CustomKnex.ts

@ -457,7 +457,6 @@ const appendWhereCondition = function(
}
});
console.log(knexRef.toQuery());
return knexRef;
};

35
packages/nocodb/src/lib/dataMapper/lib/sql/formulav2/formulaQueryBuilderv2.ts

@ -564,6 +564,41 @@ export default async function formulaQueryBuilderv2(
}
}
break;
case 'DATEADD':
if (pt.arguments[1].value) {
pt.callee.name = 'DATE_ADD';
return fn(pt, alias, prevBinaryOp);
} else if (pt.arguments[1].operator == '-') {
pt.callee.name = 'DATE_SUB';
return fn(pt, alias, prevBinaryOp);
}
break;
case 'URL':
return fn(
{
type: 'CallExpression',
arguments: [
{
type: 'Literal',
value: 'URI::(',
raw: '"URI::("'
},
pt.arguments[0],
{
type: 'Literal',
value: ')',
raw: '")"'
}
],
callee: {
type: 'Identifier',
name: 'CONCAT'
}
},
alias,
prevBinaryOp
);
break;
default:
{
const res = mapFunctionName({

3
packages/nocodb/src/lib/migrator/SqlMigrator/lib/KnexMigratorv2.ts

@ -767,8 +767,6 @@ export default class KnexMigratorv2 {
);
}
if (!sqlClient.knex.isTransaction) await trx.commit();
console.log('========== success ');
} catch (error) {
if (!sqlClient.knex.isTransaction) await trx.rollback();
vm.emitW(
@ -938,7 +936,6 @@ export default class KnexMigratorv2 {
.del();
}
if (!sqlClient.knex.isTransaction) await trx.commit();
console.log('========== success ');
} catch (error) {
if (!sqlClient.knex.isTransaction) await trx.rollback();
vm.emitW(

2
packages/nocodb/src/lib/noco-models/Audit.ts

@ -89,8 +89,6 @@ export default class Audit implements AuditType {
if ((args.comments_only as any) == 'true')
query.where('op_type', AuditOperationTypes.COMMENT);
console.log(query.toQuery());
const audits = await query;
return audits?.map(a => new Audit(a));

26
packages/nocodb/src/lib/noco-models/Column.ts

@ -878,4 +878,30 @@ export default class Column<T = any> implements ColumnType {
exclude_id && { id: { neq: exclude_id } }
));
}
static async markAsSystemField(
colId: string,
system = true,
ncMeta = Noco.ncMeta
) {
// get existing cache
const key = `${CacheScope.COLUMN}:${colId}`;
const o = await NocoCache.get(key, CacheGetType.TYPE_OBJECT);
if (o) {
// update data
o.system = system;
// set cache
await NocoCache.set(key, o);
}
// update system field in meta db
await ncMeta.metaUpdate(
null,
null,
MetaTable.COLUMNS,
{
system
},
colId
);
}
}

2
packages/nocodb/src/lib/noco-models/Filter.ts

@ -388,7 +388,7 @@ export default class Filter {
await deleteRecursively(filter);
}
private static async get(id: string, ncMeta = Noco.ncMeta) {
public static async get(id: string, ncMeta = Noco.ncMeta) {
let filterObj =
id &&
(await NocoCache.get(

28
packages/nocodb/src/lib/noco-models/Model.ts

@ -130,7 +130,7 @@ export default class Model implements TableType {
await NocoCache.appendToList(
CacheScope.MODEL,
[projectId, baseId],
[projectId],
`${CacheScope.MODEL}:${id}`
);
@ -163,10 +163,7 @@ export default class Model implements TableType {
},
ncMeta = Noco.ncMeta
): Promise<Model[]> {
let modelList = await NocoCache.getList(CacheScope.MODEL, [
project_id,
base_id
]);
let modelList = await NocoCache.getList(CacheScope.MODEL, [project_id]);
if (!modelList.length) {
modelList = await ncMeta.metaList2(
project_id,
@ -179,11 +176,7 @@ export default class Model implements TableType {
}
);
await NocoCache.setList(
CacheScope.MODEL,
[project_id, base_id],
modelList
);
await NocoCache.setList(CacheScope.MODEL, [project_id], modelList);
}
modelList.sort(
(a, b) =>
@ -214,11 +207,7 @@ export default class Model implements TableType {
MetaTable.MODELS
);
await NocoCache.setList(
CacheScope.MODEL,
[project_id, db_alias],
modelList
);
await NocoCache.setList(CacheScope.MODEL, [project_id], modelList);
}
return modelList.map(m => new Model(m));
@ -437,10 +426,10 @@ export default class Model implements TableType {
await ncMeta.metaDelete(null, null, MetaTable.MODELS, this.id);
await NocoCache.del(
`${CacheScope.MODEL}:${this.project_id}:${this.base_id}:${this.id}`
`${CacheScope.MODEL}:${this.project_id}:${this.id}`
);
await NocoCache.del(
`${CacheScope.MODEL}:${this.project_id}:${this.base_id}:${this.title}`
`${CacheScope.MODEL}:${this.project_id}:${this.title}`
);
return true;
}
@ -632,10 +621,9 @@ export default class Model implements TableType {
) {
const modelId =
project_id &&
base_id &&
aliasOrId &&
(await NocoCache.get(
`${CacheScope.MODEL}:${project_id}:${base_id}:${aliasOrId}`,
`${CacheScope.MODEL}:${project_id}:${aliasOrId}`,
CacheGetType.TYPE_OBJECT
));
if (!modelId) {
@ -661,7 +649,7 @@ export default class Model implements TableType {
}
);
await NocoCache.set(
`${CacheScope.MODEL}:${project_id}:${base_id}:${aliasOrId}`,
`${CacheScope.MODEL}:${project_id}:${aliasOrId}`,
model.id
);
await NocoCache.set(`${CacheScope.MODEL}:${model.id}`, model);

9
packages/nocodb/src/lib/noco-models/ModelRoleVisibility.ts

@ -6,8 +6,8 @@ import {
CacheScope,
MetaTable
} from '../utils/globals';
import Model from './Model';
import NocoCache from '../noco-cache/NocoCache';
import View from './View';
export default class ModelRoleVisibility implements ModelRoleVisibilityType {
id?: string;
@ -137,7 +137,6 @@ export default class ModelRoleVisibility implements ModelRoleVisibilityType {
const insertObj = {
role: body.role,
disabled: body.disabled,
// fk_model_id: body.fk_model_id,
fk_view_id: body.fk_view_id,
project_id: body.project_id,
base_id: body.base_id,
@ -146,9 +145,9 @@ export default class ModelRoleVisibility implements ModelRoleVisibilityType {
};
if (!(body.project_id && body.base_id)) {
const model = await Model.getByIdOrName({ id: body.fk_model_id }, ncMeta);
insertObj.project_id = model.project_id;
insertObj.base_id = model.base_id;
const view = await View.get(body.fk_view_id, ncMeta);
insertObj.project_id = view.project_id;
insertObj.base_id = view.base_id;
}
await ncMeta.metaInsert2(

2
packages/nocodb/src/lib/noco-models/ProjectUser.ts

@ -104,8 +104,6 @@ export default class ProjectUser {
);
});
console.log(queryBuilder.toQuery());
return await queryBuilder;
}

7
packages/nocodb/src/lib/noco-models/User.ts

@ -136,4 +136,11 @@ export default class User implements UserType {
}
return user;
}
static async getByRefreshToken(refresh_token, ncMeta = Noco.ncMeta) {
const user = await ncMeta.metaGet2(null, null, MetaTable.USERS, {
refresh_token
});
return user;
}
}

3
packages/nocodb/src/lib/noco/Noco.ts

@ -351,7 +351,6 @@ export default class Noco {
projectBuilder.updateConfig(project.config);
await projectBuilder.reInit();
console.log(`Project updated: ${projectId}`);
}
break;
@ -370,7 +369,6 @@ export default class Noco {
this.ncToolApi.destroy();
this.ncToolApi.reInitialize(this.config);
// await this.init({progressCallback});
console.log(`Loaded env : ${data.req.args.env}`);
} catch (e) {
console.log(e);
}
@ -510,7 +508,6 @@ export default class Noco {
this.socketClient = client;
client.on('disconnect', () => {
console.log('Disconnected');
this.socketClient = null;
});
});

14
packages/nocodb/src/lib/noco/meta/api/apiTokenApis.ts

@ -17,11 +17,17 @@ export async function apiTokenDelete(req: Request, res: Response) {
const router = Router({ mergeParams: true });
router.get('/projects/:projectId/apiTokens', ncMetaAclMw(apiTokenList));
router.post('/projects/:projectId/apiTokens', ncMetaAclMw(apiTokenCreate));
router.get(
'/api/v1/db/meta/projects/:projectId/api-tokens',
ncMetaAclMw(apiTokenList, 'apiTokenList')
);
router.post(
'/api/v1/db/meta/projects/:projectId/api-tokens',
ncMetaAclMw(apiTokenCreate, 'apiTokenCreate')
);
router.delete(
'/projects/:projectId/apiTokens/:token',
ncMetaAclMw(apiTokenDelete)
'/api/v1/db/meta/projects/:projectId/api-tokens/:token',
ncMetaAclMw(apiTokenDelete, 'apiTokenDelete')
);
export default router;

48
packages/nocodb/src/lib/noco/meta/api/attachmentApis.ts

@ -12,12 +12,10 @@ import NcPluginMgrv2 from '../helpers/NcPluginMgrv2';
// const storageAdapter = new Local();
export async function upload(req: Request, res: Response) {
const destPath = path.join(
'nc',
'uploads',
req.params.projectId,
req.params.viewId
const filePath = sanitizeUrlPath(
req.query?.path?.toString()?.split('/') || ['']
);
const destPath = path.join('nc', 'uploads', ...filePath);
const storageAdapter = await NcPluginMgrv2.storageAdapter();
const attachments = await Promise.all(
@ -30,9 +28,9 @@ export async function upload(req: Request, res: Response) {
);
if (!url) {
url = `${(req as any).ncSiteUrl}/download/${req.params.projectId}/${
req.params.viewId
}/${fileName}`;
url = `${(req as any).ncSiteUrl}/download/${filePath.join(
'/'
)}/${fileName}`;
}
return {
@ -52,11 +50,11 @@ export async function upload(req: Request, res: Response) {
export async function fileRead(req, res) {
try {
const storageAdapter = await NcPluginMgrv2.storageAdapter();
// const type = mimetypes[path.extname(req.params.fileName).slice(1)] || 'text/plain';
// const type = mimetypes[path.extname(req.s.fileName).slice(1)] || 'text/plain';
const type =
mimetypes[
path
.extname(req.params.fileName)
.extname(req.params?.[0])
.split('/')
.pop()
.slice(1)
@ -67,9 +65,10 @@ export async function fileRead(req, res) {
path.join(
'nc',
'uploads',
req.params.projectId,
req.params.viewId,
req.params.fileName
req.params?.[0]
?.split('/')
.filter(p => p !== '..')
.join('/')
)
)
);
@ -82,15 +81,6 @@ export async function fileRead(req, res) {
}
const router = Router({ mergeParams: true });
router.post(
'/projects/:projectId/views/:viewId/upload',
multer({
storage: multer.diskStorage({})
}).any(),
ncMetaAclMw(upload)
);
router.get('/download/:projectId/:viewId/:fileName', catchError(fileRead));
router.get(/^\/dl\/([^/]+)\/([^/]+)\/(.+)$/, async (req, res) => {
try {
// const type = mimetypes[path.extname(req.params.fileName).slice(1)] || 'text/plain';
@ -122,4 +112,18 @@ router.get(/^\/dl\/([^/]+)\/([^/]+)\/(.+)$/, async (req, res) => {
res.status(404).send('Not found');
}
});
export function sanitizeUrlPath(paths) {
return paths.map(url => url.replace(/[/.?#]+/g, '_'));
}
router.post(
'/api/v1/db/storage/upload',
multer({
storage: multer.diskStorage({})
}).any(),
ncMetaAclMw(upload, 'upload')
);
router.get(/^\/download\/(.+)$/, catchError(fileRead));
export default router;

27
packages/nocodb/src/lib/noco/meta/api/auditApis.ts

@ -20,7 +20,7 @@ export async function auditRowUpdate(req: Request<any, any>, res) {
res.json(
await Audit.insert({
fk_model_id: req.body.fk_model_id,
row_id: req.body.row_id,
row_id: req.params.rowId,
op_type: AuditOperationTypes.DATA,
op_sub_type: AuditOperationSubTypes.UPDATE,
description: `Table ${model.table_name} : field ${req.body.column_name} got changed from ${req.body.prev_value} to ${req.body.value}`,
@ -58,9 +58,24 @@ export async function commentsCount(req: Request<any, any, any>, res) {
}
const router = Router({ mergeParams: true });
router.get('/audits/comments', ncMetaAclMw(commentList));
router.post('/audits/rowUpdate', ncMetaAclMw(auditRowUpdate, 'auditRowUpdate'));
router.post('/audits/comments', ncMetaAclMw(commentRow));
router.get('/audits/comments/count', ncMetaAclMw(commentsCount));
router.get('/project/:projectId/audits', ncMetaAclMw(auditList));
router.get(
'/api/v1/db/meta/audits/comments',
ncMetaAclMw(commentList, 'commentList')
);
router.post(
'/api/v1/db/meta/audits/comments',
ncMetaAclMw(commentRow, 'commentRow')
);
router.post(
'/api/v1/db/meta/audits/rows/:rowId/update',
ncMetaAclMw(auditRowUpdate, 'auditRowUpdate')
);
router.get(
'/api/v1/db/meta/audits/comments/count',
ncMetaAclMw(commentsCount, 'commentsCount')
);
router.get(
'/api/v1/db/meta/project/:projectId/audits',
ncMetaAclMw(auditList, 'auditList')
);
export default router;

4
packages/nocodb/src/lib/noco/meta/api/cacheApis.ts

@ -16,6 +16,6 @@ export async function cacheDelete(_, res) {
}
const router = Router();
router.get('/cache', catchError(cacheGet));
router.delete('/cache', catchError(cacheDelete));
router.get('/api/v1/db/meta/cache', catchError(cacheGet));
router.delete('/api/v1/db/meta/cache', catchError(cacheDelete));
export default router;

39
packages/nocodb/src/lib/noco/meta/api/columnApis.ts

@ -498,14 +498,15 @@ export async function columnAdd(req: Request, res: Response<TableType>) {
}
export async function columnSetAsPrimary(req: Request, res: Response) {
res.json(
await Model.updatePrimaryColumn(req.params.tableId, req.params.columnId)
);
const column = await Column.get({ colId: req.params.columnId });
res.json(await Model.updatePrimaryColumn(column.fk_model_id, column.id));
}
export async function columnUpdate(req: Request, res: Response<TableType>) {
const column = await Column.get({ colId: req.params.columnId });
const table = await Model.getWithInfo({
id: req.params.tableId
id: column.fk_model_id
});
const base = await Base.get(table.base_id);
@ -513,7 +514,7 @@ export async function columnUpdate(req: Request, res: Response<TableType>) {
!isVirtualCol(req.body) &&
!(await Column.checkTitleAvailable({
column_name: req.body.column_name,
fk_model_id: req.params.tableId,
fk_model_id: column.fk_model_id,
exclude_id: req.params.columnId
}))
) {
@ -522,14 +523,13 @@ export async function columnUpdate(req: Request, res: Response<TableType>) {
if (
!(await Column.checkAliasAvailable({
title: req.body.title,
fk_model_id: req.params.tableId,
fk_model_id: column.fk_model_id,
exclude_id: req.params.columnId
}))
) {
NcError.badRequest('Duplicate column alias');
}
const column = table.columns.find(c => c.id === req.params.columnId);
let colBody = req.body;
if (
[
@ -622,8 +622,9 @@ export async function columnUpdate(req: Request, res: Response<TableType>) {
}
export async function columnDelete(req: Request, res: Response<TableType>) {
const column = await Column.get({ colId: req.params.columnId });
const table = await Model.getWithInfo({
id: req.params.tableId
id: column.fk_model_id
});
const base = await Base.get(table.base_id);
@ -636,9 +637,6 @@ export async function columnDelete(req: Request, res: Response<TableType>) {
const sqlMgr = await ProjectMgrv2.getSqlMgr({ id: base.project_id });
// try {
const column: Column = table.columns.find(c => c.id === req.params.columnId);
switch (column.uidt) {
case UITypes.Lookup:
case UITypes.Rollup:
@ -900,11 +898,20 @@ const deleteHmOrBtRelation = async (
};
const router = Router({ mergeParams: true });
router.post('/tables/:tableId/columns/', ncMetaAclMw(columnAdd));
router.put('/tables/:tableId/columns/:columnId', ncMetaAclMw(columnUpdate));
router.delete('/tables/:tableId/columns/:columnId', ncMetaAclMw(columnDelete));
router.post(
'/tables/:tableId/columns/:columnId/primary',
ncMetaAclMw(columnSetAsPrimary)
'/api/v1/db/meta/tables/:tableId/columns/',
ncMetaAclMw(columnAdd, 'columnAdd')
);
router.patch(
'/api/v1/db/meta/columns/:columnId',
ncMetaAclMw(columnUpdate, 'columnUpdate')
);
router.delete(
'/api/v1/db/meta/columns/:columnId',
ncMetaAclMw(columnDelete, 'columnDelete')
);
router.post(
'/api/v1/db/meta/columns/:columnId/primary',
ncMetaAclMw(columnSetAsPrimary, 'columnSetAsPrimary')
);
export default router;

20
packages/nocodb/src/lib/noco/meta/api/dataApis/bulkDataAliasApis.ts

@ -91,24 +91,24 @@ async function getViewAndModelFromRequest(req) {
const router = Router({ mergeParams: true });
router.post(
'/bulkData/:orgs/:projectName/:tableAlias',
ncMetaAclMw(bulkDataInsert)
'/api/v1/db/data/bulk/:orgs/:projectName/:tableAlias',
ncMetaAclMw(bulkDataInsert, 'bulkDataInsert')
);
router.patch(
'/bulkData/:orgs/:projectName/:tableAlias',
ncMetaAclMw(bulkDataUpdate)
'/api/v1/db/data/bulk/:orgs/:projectName/:tableAlias',
ncMetaAclMw(bulkDataUpdate, 'bulkDataUpdate')
);
router.patch(
'/bulkData/:orgs/:projectName/:tableAlias/all',
ncMetaAclMw(bulkDataUpdateAll)
'/api/v1/db/data/bulk/:orgs/:projectName/:tableAlias/all',
ncMetaAclMw(bulkDataUpdateAll, 'bulkDataUpdateAll')
);
router.delete(
'/bulkData/:orgs/:projectName/:tableAlias',
ncMetaAclMw(bulkDataDelete)
'/api/v1/db/data/bulk/:orgs/:projectName/:tableAlias',
ncMetaAclMw(bulkDataDelete, 'bulkDataDelete')
);
router.delete(
'/bulkData/:orgs/:projectName/:tableAlias/all',
ncMetaAclMw(bulkDataDeleteAll)
'/api/v1/db/data/bulk/:orgs/:projectName/:tableAlias/all',
ncMetaAclMw(bulkDataDeleteAll, 'bulkDataDeleteAll')
);
export default router;

86
packages/nocodb/src/lib/noco/meta/api/dataApis/dataAliasApis.ts

@ -6,11 +6,10 @@ import NcConnectionMgrv2 from '../../../common/NcConnectionMgrv2';
import { PagedResponseImpl } from '../../helpers/PagedResponse';
import View from '../../../../noco-models/View';
import ncMetaAclMw from '../../helpers/ncMetaAclMw';
import Project from '../../../../noco-models/Project';
import { NcError } from '../../helpers/catchError';
import { getViewAndModelFromRequestByAliasOrId } from './helpers';
export async function dataList(req: Request, res: Response) {
const { model, view } = await getViewAndModelFromRequest(req);
async function dataList(req: Request, res: Response) {
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req);
res.json(await getDataList(model, view, req));
}
@ -36,7 +35,7 @@ async function dataCount(req: Request, res: Response) {
}
async function dataInsert(req: Request, res: Response) {
const { model, view } = await getViewAndModelFromRequest(req);
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req);
const base = await Base.get(model.base_id);
@ -50,12 +49,12 @@ async function dataInsert(req: Request, res: Response) {
}
async function dataUpdate(req: Request, res: Response) {
const { model, view } = await getViewAndModelFromRequest(req);
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req);
const base = await Base.get(model.base_id);
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base)
});
@ -63,11 +62,11 @@ async function dataUpdate(req: Request, res: Response) {
}
async function dataDelete(req: Request, res: Response) {
const { model, view } = await getViewAndModelFromRequest(req);
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req);
const base = await Base.get(model.base_id);
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base)
});
@ -106,25 +105,9 @@ async function getDataList(model, view: View, req) {
count
});
}
async function getViewAndModelFromRequest(req) {
const project = await Project.getWithInfoByTitle(req.params.projectName);
const model = await Model.getByAliasOrId({
project_id: project.id,
base_id: project.bases?.[0]?.id,
aliasOrId: req.params.tableName
});
const view =
req.params.viewName &&
(await View.getByTitleOrId({
titleOrId: req.params.viewName,
fk_model_id: model.id
}));
if (!model) NcError.notFound('Table not found');
return { model, view };
}
async function dataRead(req: Request, res: Response) {
const { model, view } = await getViewAndModelFromRequest(req);
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req);
const base = await Base.get(model.base_id);
@ -146,10 +129,33 @@ async function dataRead(req: Request, res: Response) {
const router = Router({ mergeParams: true });
router.get('/data/:orgs/:projectName/:tableName', ncMetaAclMw(dataList));
// table data crud apis
router.get(
'/data/:orgs/:projectName/:tableName/views/:viewName',
ncMetaAclMw(dataList)
'/api/v1/db/data/:orgs/:projectName/:tableName',
ncMetaAclMw(dataList, 'dataList')
);
router.get(
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId',
ncMetaAclMw(dataRead, 'dataRead')
);
router.patch(
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId',
ncMetaAclMw(dataUpdate, 'dataUpdate')
);
router.patch(
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId',
ncMetaAclMw(dataUpdate, 'dataUpdate')
);
router.get(
'/api/v1/db/data/:orgs/:projectName/:tableName',
ncMetaAclMw(dataList, 'dataList')
);
// table view data crud apis
router.get(
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName',
ncMetaAclMw(dataList, 'dataList')
);
router.get(
'/data/:orgs/:projectName/:tableName/views/:viewName/count',
@ -157,20 +163,24 @@ router.get(
);
router.post(
'/data/:orgs/:projectName/:tableName/views/:viewName',
ncMetaAclMw(dataInsert)
'/api/v1/db/data/:orgs/:projectName/:tableName',
ncMetaAclMw(dataInsert, 'dataInsert')
);
router.post(
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName',
ncMetaAclMw(dataInsert, 'dataInsert')
);
router.put(
'/data/:orgs/:projectName/:tableName/views/:viewName/:rowId',
ncMetaAclMw(dataUpdate)
router.patch(
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/:rowId',
ncMetaAclMw(dataUpdate, 'dataUpdate')
);
router.get(
'/data/:orgs/:projectName/:tableName/views/:viewName/:rowId',
ncMetaAclMw(dataRead)
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/:rowId',
ncMetaAclMw(dataRead, 'dataRead')
);
router.delete(
'/data/:orgs/:projectName/:tableName/views/:viewName/:rowId',
ncMetaAclMw(dataDelete)
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/:rowId',
ncMetaAclMw(dataDelete, 'dataDelete')
);
export default router;

33
packages/nocodb/src/lib/noco/meta/api/dataApis/dataAliasExportApis.ts

@ -0,0 +1,33 @@
import { Request, Response, Router } from 'express';
import ncMetaAclMw from '../../helpers/ncMetaAclMw';
import {
extractCsvData,
getViewAndModelFromRequestByAliasOrId
} from './helpers';
async function csvDataExport(req: Request, res: Response) {
const { view } = await getViewAndModelFromRequestByAliasOrId(req);
const { offset, elapsed, data } = await extractCsvData(view, req);
res.set({
'Access-Control-Expose-Headers': 'nc-export-offset',
'nc-export-offset': offset,
'nc-export-elapsed-time': elapsed,
'Content-Disposition': `attachment; filename="${view.title}-export.csv"`
});
res.send(data);
}
const router = Router({ mergeParams: true });
router.get(
'/api/v1/db/data/:orgs/:projectName/:tableName/export/csv',
ncMetaAclMw(csvDataExport, 'exportCsv')
);
router.get(
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/export/csv',
ncMetaAclMw(csvDataExport, 'exportCsv')
);
export default router;

288
packages/nocodb/src/lib/noco/meta/api/dataApis/dataAliasNestedApis.ts

@ -0,0 +1,288 @@
import { Request, Response, Router } from 'express';
import Model from '../../../../noco-models/Model';
import Base from '../../../../noco-models/Base';
import NcConnectionMgrv2 from '../../../common/NcConnectionMgrv2';
import { PagedResponseImpl } from '../../helpers/PagedResponse';
import ncMetaAclMw from '../../helpers/ncMetaAclMw';
import { getViewAndModelFromRequestByAliasOrId } from './helpers';
import { NcError } from '../../helpers/catchError';
export async function mmList(req: Request, res: Response, next) {
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req);
if (!model) return next(new Error('Table not found'));
const base = await Base.get(model.base_id);
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base)
});
const column = await getColumnByIdOrName(req.params.columnName, model);
const data = await baseModel.mmList(
{
colId: column.id,
parentId: req.params.rowId
},
req.query as any
);
const count: any = await baseModel.mmListCount({
colId: column.id,
parentId: req.params.rowId
});
res.json(
new PagedResponseImpl(data, {
count,
...req.query
})
);
}
export async function mmExcludedList(req: Request, res: Response, next) {
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req);
if (!model) return next(new Error('Table not found'));
const base = await Base.get(model.base_id);
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base)
});
const column = await getColumnByIdOrName(req.params.columnName, model);
const data = await baseModel.getMmChildrenExcludedList(
{
colId: column.id,
pid: req.params.rowId
},
req.query
);
const count = await baseModel.getMmChildrenExcludedListCount(
{
colId: column.id,
pid: req.params.rowId
},
req.query
);
res.json(
new PagedResponseImpl(data, {
count,
...req.query
})
);
}
export async function hmExcludedList(req: Request, res: Response, next) {
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req);
if (!model) return next(new Error('Table not found'));
const base = await Base.get(model.base_id);
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base)
});
const column = await getColumnByIdOrName(req.params.columnName, model);
const data = await baseModel.getHmChildrenExcludedList(
{
colId: column.id,
pid: req.params.rowId
},
req.query
);
const count = await baseModel.getHmChildrenExcludedListCount(
{
colId: column.id,
pid: req.params.rowId
},
req.query
);
res.json(
new PagedResponseImpl(data, {
count,
...req.query
})
);
}
export async function btExcludedList(req: Request, res: Response, next) {
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req);
if (!model) return next(new Error('Table not found'));
const base = await Base.get(model.base_id);
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base)
});
const column = await getColumnByIdOrName(req.params.columnName, model);
const data = await baseModel.getBtChildrenExcludedList(
{
colId: column.id,
cid: req.params.rowId
},
req.query
);
const count = await baseModel.getBtChildrenExcludedListCount(
{
colId: column.id,
cid: req.params.rowId
},
req.query
);
res.json(
new PagedResponseImpl(data, {
count,
...req.query
})
);
}
export async function hmList(req: Request, res: Response, next) {
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req);
if (!model) return next(new Error('Table not found'));
const base = await Base.get(model.base_id);
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base)
});
const column = await getColumnByIdOrName(req.params.columnName, model);
const data = await baseModel.hmList(
{
colId: column.id,
id: req.params.rowId
},
req.query
);
const count = await baseModel.hmListCount({
colId: column.id,
id: req.params.rowId
});
res.json(
new PagedResponseImpl(data, {
totalRows: count
} as any)
);
}
//@ts-ignore
async function relationDataDelete(req, res) {
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req);
if (!model) NcError.notFound('Table not found');
const base = await Base.get(model.base_id);
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base)
});
const column = await getColumnByIdOrName(req.params.columnName, model);
await baseModel.removeChild({
colId: column.id,
childId: req.params.refRowId,
rowId: req.params.rowId
});
res.json({ msg: 'success' });
}
//@ts-ignore
async function relationDataAdd(req, res) {
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req);
if (!model) NcError.notFound('Table not found');
const base = await Base.get(model.base_id);
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base)
});
const column = await getColumnByIdOrName(req.params.columnName, model);
await baseModel.addChild({
colId: column.id,
childId: req.params.refRowId,
rowId: req.params.rowId
});
res.json({ msg: 'success' });
}
async function getColumnByIdOrName(columnNameOrId: string, model: Model) {
const column = (await model.getColumns()).find(
c =>
c.title === columnNameOrId ||
c.id === columnNameOrId ||
c.column_name === columnNameOrId
);
if (!column)
NcError.notFound(`Column with id/name '${columnNameOrId}' is not found`);
return column;
}
const router = Router({ mergeParams: true });
router.get(
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/mm/:columnName/exclude',
ncMetaAclMw(mmExcludedList, 'mmExcludedList')
);
router.get(
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/hm/:columnName/exclude',
ncMetaAclMw(hmExcludedList, 'hmExcludedList')
);
router.get(
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/bt/:columnName/exclude',
ncMetaAclMw(btExcludedList, 'btExcludedList')
);
router.post(
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/:relationType/:columnName/:refRowId',
ncMetaAclMw(relationDataAdd, 'relationDataAdd')
);
router.delete(
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/:relationType/:columnName/:refRowId',
ncMetaAclMw(relationDataDelete, 'relationDataDelete')
);
router.get(
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/mm/:columnName',
ncMetaAclMw(mmList, 'mmList')
);
router.get(
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/hm/:columnName',
ncMetaAclMw(hmList, 'hmList')
);
export default router;

31
packages/nocodb/src/lib/noco/meta/api/dataApis/dataApis.ts

@ -20,11 +20,6 @@ export async function dataList(req: Request, res: Response, next) {
res.json(await getDataList(model, view, req));
}
// async function dataListNew(req: Request, res: Response) {
// const { model, view } = await getViewAndModelFromRequest(req);
// res.json(await getDataList(model, view, req));
// }
export async function mmList(req: Request, res: Response, next) {
const view = await View.get(req.params.viewId);
@ -554,7 +549,7 @@ const router = Router({ mergeParams: true });
// '/data/:orgs/:projectName/:tableName/views/:viewName',
// ncMetaAclMw(dataInsertNew)
// );
// router.put(
// router.patch(
// '/data/:orgs/:projectName/:tableName/views/:viewName/:rowId',
// ncMetaAclMw(dataUpdateNew)
// );
@ -563,34 +558,34 @@ const router = Router({ mergeParams: true });
// ncMetaAclMw(dataDeleteNew)
// );
router.get('/data/:viewId/', ncMetaAclMw(dataList));
router.post('/data/:viewId/', ncMetaAclMw(dataInsert));
router.get('/data/:viewId/:rowId', ncMetaAclMw(dataRead));
router.put('/data/:viewId/:rowId', ncMetaAclMw(dataUpdate));
router.delete('/data/:viewId/:rowId', ncMetaAclMw(dataDelete));
router.get('/data/:viewId/', ncMetaAclMw(dataList, 'dataList'));
router.post('/data/:viewId/', ncMetaAclMw(dataInsert, 'dataInsert'));
router.get('/data/:viewId/:rowId', ncMetaAclMw(dataRead, 'dataRead'));
router.patch('/data/:viewId/:rowId', ncMetaAclMw(dataUpdate, 'dataUpdate'));
router.delete('/data/:viewId/:rowId', ncMetaAclMw(dataDelete, 'dataDelete'));
router.get('/data/:viewId/:rowId/mm/:colId', ncMetaAclMw(mmList));
router.get('/data/:viewId/:rowId/hm/:colId', ncMetaAclMw(hmList));
router.get('/data/:viewId/:rowId/mm/:colId', ncMetaAclMw(mmList, 'mmList'));
router.get('/data/:viewId/:rowId/hm/:colId', ncMetaAclMw(hmList, 'hmList'));
router.get(
'/data/:viewId/:rowId/mm/:colId/exclude',
ncMetaAclMw(mmExcludedList)
ncMetaAclMw(mmExcludedList, 'mmExcludedList')
);
router.get(
'/data/:viewId/:rowId/hm/:colId/exclude',
ncMetaAclMw(hmExcludedList)
ncMetaAclMw(hmExcludedList, 'hmExcludedList')
);
router.get(
'/data/:viewId/:rowId/bt/:colId/exclude',
ncMetaAclMw(btExcludedList)
ncMetaAclMw(btExcludedList, 'btExcludedList')
);
router.post(
'/data/:viewId/:rowId/:relationType/:colId/:childId',
ncMetaAclMw(relationDataAdd)
ncMetaAclMw(relationDataAdd, 'relationDataAdd')
);
router.delete(
'/data/:viewId/:rowId/:relationType/:colId/:childId',
ncMetaAclMw(relationDataDelete)
ncMetaAclMw(relationDataDelete, 'relationDataDelete')
);
export default router;

179
packages/nocodb/src/lib/noco/meta/api/dataApis/helpers.ts

@ -0,0 +1,179 @@
import Project from '../../../../noco-models/Project';
import Model from '../../../../noco-models/Model';
import View from '../../../../noco-models/View';
import { NcError } from '../../helpers/catchError';
import { Request } from 'express';
import Base from '../../../../noco-models/Base';
import NcConnectionMgrv2 from '../../../common/NcConnectionMgrv2';
import { isSystemColumn, UITypes } from 'nocodb-sdk';
import { nocoExecute } from 'nc-help';
import Column from '../../../../noco-models/Column';
import LookupColumn from '../../../../noco-models/LookupColumn';
import LinkToAnotherRecordColumn from '../../../../noco-models/LinkToAnotherRecordColumn';
import papaparse from 'papaparse';
export async function getViewAndModelFromRequestByAliasOrId(
req:
| Request<{ projectName: string; tableName: string; viewName?: string }>
| Request
) {
let project = await Project.getWithInfoByTitle(req.params.projectName);
if (!project) {
project = await Project.getWithInfo(req.params.projectName);
}
const model = await Model.getByAliasOrId({
project_id: project.id,
base_id: project.bases?.[0]?.id,
aliasOrId: req.params.tableName
});
const view =
req.params.viewName &&
(await View.getByTitleOrId({
titleOrId: req.params.viewName,
fk_model_id: model.id
}));
if (!model) NcError.notFound('Table not found');
return { model, view };
}
export async function extractCsvData(view: View, req: Request) {
const base = await Base.get(view.base_id);
await view.getModelWithInfo();
await view.getColumns();
view.model.columns = view.columns
.filter(c => c.show)
.map(
c =>
new Column({ ...c, ...view.model.columnsById[c.fk_column_id] } as any)
)
.filter(column => !isSystemColumn(column) || view.show_system_fields);
const baseModel = await Model.getBaseModelSQL({
id: view.model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base)
});
let offset = +req.query.offset || 0;
const limit = 100;
// const size = +process.env.NC_EXPORT_MAX_SIZE || 1024;
const timeout = +process.env.NC_EXPORT_MAX_TIMEOUT || 5000;
const csvRows = [];
const startTime = process.hrtime();
let elapsed, temp;
for (
elapsed = 0;
elapsed < timeout;
offset += limit,
temp = process.hrtime(startTime),
elapsed = temp[0] * 1000 + temp[1] / 1000000
) {
const rows = await nocoExecute(
await baseModel.defaultResolverReq(req.query, false, false),
await baseModel.list({ ...req.query, offset, limit }),
{},
req.query
);
if (!rows?.length) {
offset = -1;
break;
}
for (const row of rows) {
const csvRow = { ...row };
for (const column of view.model.columns) {
if (isSystemColumn(column) && !view.show_system_fields) continue;
csvRow[column.title] = await serializeCellValue({
value: row[column.title],
column
});
}
csvRows.push(csvRow);
}
}
const data = papaparse.unparse(
{
fields: view.model.columns.map(c => c.title),
data: csvRows
},
{
escapeFormulae: true
}
);
return { offset, csvRows, elapsed, data };
}
export async function serializeCellValue({
value,
column
}: {
column?: Column;
value: any;
}) {
if (!column) {
return value;
}
if (!value) return value;
switch (column?.uidt) {
case UITypes.Attachment: {
let data = value;
try {
if (typeof value === 'string') {
data = JSON.parse(value);
}
} catch {}
return (data || []).map(
attachment =>
`${encodeURI(attachment.title)}(${encodeURI(attachment.url)})`
);
}
case UITypes.Lookup:
{
const colOptions = await column.getColOptions<LookupColumn>();
const lookupColumn = await colOptions.getLookupColumn();
return (
await Promise.all(
[...(Array.isArray(value) ? value : [value])].map(async v =>
serializeCellValue({
value: v,
column: lookupColumn
})
)
)
).join(', ');
}
break;
case UITypes.LinkToAnotherRecord:
{
const colOptions = await column.getColOptions<
LinkToAnotherRecordColumn
>();
const relatedModel = await colOptions.getRelatedTable();
await relatedModel.getColumns();
return [...(Array.isArray(value) ? value : [value])]
.map(v => {
return v[relatedModel.primaryValue?.title];
})
.join(', ');
}
break;
default:
if (value && typeof value === 'object') {
return JSON.stringify(value);
}
return value;
}
}

11
packages/nocodb/src/lib/noco/meta/api/dataApis/index.ts

@ -2,5 +2,14 @@ import dataApis from './dataApis';
import oldDataApis from './oldDataApis';
import dataAliasApis from './dataAliasApis';
import bulkDataAliasApis from './bulkDataAliasApis';
import dataAliasNestedApis from './dataAliasNestedApis';
import dataAliasExportApis from './dataAliasExportApis';
export { dataApis, oldDataApis, dataAliasApis, bulkDataAliasApis };
export {
dataApis,
oldDataApis,
dataAliasApis,
bulkDataAliasApis,
dataAliasNestedApis,
dataAliasExportApis
};

22
packages/nocodb/src/lib/noco/meta/api/dataApis/oldDataApis.ts

@ -125,14 +125,26 @@ async function dataRead(req: Request, res: Response) {
const router = Router({ mergeParams: true });
router.get('/nc/:projectId/api/v2/:tableName', ncMetaAclMw(dataList));
router.get(
'/nc/:projectId/api/v2/:tableName',
ncMetaAclMw(dataList, 'dataList')
);
router.post('/nc/:projectId/api/v2/:tableName', ncMetaAclMw(dataInsert));
router.get('/nc/:projectId/api/v2/:tableName/:rowId', ncMetaAclMw(dataRead));
router.put('/nc/:projectId/api/v2/:tableName/:rowId', ncMetaAclMw(dataUpdate));
router.post(
'/nc/:projectId/api/v2/:tableName',
ncMetaAclMw(dataInsert, 'dataInsert')
);
router.get(
'/nc/:projectId/api/v2/:tableName/:rowId',
ncMetaAclMw(dataRead, 'dataRead')
);
router.patch(
'/nc/:projectId/api/v2/:tableName/:rowId',
ncMetaAclMw(dataUpdate, 'dataUpdate')
);
router.delete(
'/nc/:projectId/api/v2/:tableName/:rowId',
ncMetaAclMw(dataDelete)
ncMetaAclMw(dataDelete, 'dataDelete')
);
export default router;

164
packages/nocodb/src/lib/noco/meta/api/exportApis.ts

@ -1,102 +1,11 @@
import { Request, Response, Router } from 'express';
import View from '../../../noco-models/View';
import Model from '../../../noco-models/Model';
import Base from '../../../noco-models/Base';
import NcConnectionMgrv2 from '../../common/NcConnectionMgrv2';
import { nocoExecute } from 'nc-help';
import papaparse from 'papaparse';
import { isSystemColumn, UITypes } from 'nocodb-sdk';
import Column from '../../../noco-models/Column';
import LinkToAnotherRecordColumn from '../../../noco-models/LinkToAnotherRecordColumn';
import LookupColumn from '../../../noco-models/LookupColumn';
import ncMetaAclMw from '../helpers/ncMetaAclMw';
import { extractCsvData } from './dataApis/helpers';
async function exportCsv(req: Request, res: Response, next) {
async function exportCsv(req: Request, res: Response) {
const view = await View.get(req.params.viewId);
const model = await view.getModelWithInfo();
await view.getColumns();
view.model.columns = view.columns
.filter(c => c.show)
.map(
c =>
new Column({ ...c, ...view.model.columnsById[c.fk_column_id] } as any)
)
.filter(column => !isSystemColumn(column) || view.show_system_fields);
if (!model) return next(new Error('Table not found'));
const base = await Base.get(model.base_id);
const baseModel = await Model.getBaseModelSQL({
id: model.id,
viewId: view?.id,
dbDriver: NcConnectionMgrv2.get(base)
});
const key = `${model.title}List`;
const requestObj = {
[key]: await baseModel.defaultResolverReq(req.query, false, false)
};
let offset = +req.query.offset || 0;
const limit = 100;
// const size = +process.env.NC_EXPORT_MAX_SIZE || 1024;
const timeout = +process.env.NC_EXPORT_MAX_TIMEOUT || 5000;
const csvRows = [];
const startTime = process.hrtime();
let elapsed, temp;
for (
elapsed = 0;
elapsed < timeout;
offset += limit,
temp = process.hrtime(startTime),
elapsed = temp[0] * 1000 + temp[1] / 1000000
) {
console.time('nocoExecute');
const rows = (
await nocoExecute(
requestObj,
{
[key]: async args => {
return await baseModel.list({ ...args, offset, limit });
}
},
{},
{ nested: { [key]: req.query } }
)
)?.[key];
if (!rows?.length) {
offset = -1;
break;
}
for (const row of rows) {
const csvRow = { ...row };
for (const column of view.model.columns) {
if (isSystemColumn(column) && !view.show_system_fields) continue;
csvRow[column.title] = await serializeCellValue({
value: row[column.title],
column
});
}
csvRows.push(csvRow);
}
console.timeEnd('nocoExecute');
}
const data = papaparse.unparse(
{
fields: model.columns.map(c => c.title),
data: csvRows
},
{
escapeFormulae: true
}
);
const { offset, elapsed, data } = await extractCsvData(view, req);
res.set({
'Access-Control-Expose-Headers': 'nc-export-offset',
@ -107,71 +16,6 @@ async function exportCsv(req: Request, res: Response, next) {
res.send(data);
}
async function serializeCellValue({
value,
column
}: {
column?: Column;
value: any;
}) {
if (!column) {
return value;
}
if (!value) return value;
switch (column?.uidt) {
case UITypes.Attachment: {
let data = value;
try {
if (typeof value === 'string') {
data = JSON.parse(value);
}
} catch {}
return (data || []).map(
attachment =>
`${encodeURI(attachment.title)}(${encodeURI(attachment.url)})`
);
}
case UITypes.Lookup:
{
const colOptions = await column.getColOptions<LookupColumn>();
const lookupColumn = await colOptions.getLookupColumn();
return (
await Promise.all(
[...(Array.isArray(value) ? value : [value])].map(async v =>
serializeCellValue({
value: v,
column: lookupColumn
})
)
)
).join(', ');
}
break;
case UITypes.LinkToAnotherRecord:
{
const colOptions = await column.getColOptions<
LinkToAnotherRecordColumn
>();
const relatedModel = await colOptions.getRelatedTable();
await relatedModel.getColumns();
return [...(Array.isArray(value) ? value : [value])]
.map(v => {
return v[relatedModel.primaryValue?.title];
})
.join(', ');
}
break;
default:
if (value && typeof value === 'object') {
return JSON.stringify(value);
}
return value;
}
}
const router = Router({ mergeParams: true });
router.get('/data/:viewId/export/csv', ncMetaAclMw(exportCsv));
router.get('/data/:viewId/export/csv', ncMetaAclMw(exportCsv, 'exportCsv'));
export default router;

64
packages/nocodb/src/lib/noco/meta/api/filterApis.ts

@ -85,7 +85,7 @@ export async function filterUpdate(req, res, next) {
...req.body,
fk_view_id: req.params.viewId
});
Tele.emit('evt', { evt_type: 'table:updated' });
Tele.emit('evt', { evt_type: 'filter:updated' });
res.json(filter);
} catch (e) {
console.log(e);
@ -97,7 +97,7 @@ export async function filterUpdate(req, res, next) {
export async function filterDelete(req: Request, res: Response, next) {
try {
const filter = await Filter.delete(req.params.filterId);
Tele.emit('evt', { evt_type: 'table:deleted' });
Tele.emit('evt', { evt_type: 'filter:deleted' });
res.json(filter);
} catch (e) {
console.log(e);
@ -105,14 +105,60 @@ export async function filterDelete(req: Request, res: Response, next) {
}
}
export async function hookFilterList(
req: Request<any, any, any, TableListParams>,
res: Response
) {
const filter = await Filter.rootFilterListByHook({
hookId: req.params.hookId
});
res.json(filter);
}
export async function hookFilterCreate(req: Request<any, any, TableReq>, res) {
const filter = await Filter.insert({
...req.body,
fk_hook_id: req.params.hookId
});
Tele.emit('evt', { evt_type: 'hookFilter:created' });
res.json(filter);
}
const router = Router({ mergeParams: true });
router.get('/views/:viewId/filters/', ncMetaAclMw(filterList));
router.post('/views/:viewId/filters/', ncMetaAclMw(filterCreate));
router.get('/views/:viewId/filters/:filterId', ncMetaAclMw(filterGet));
router.put('/views/:viewId/filters/:filterId', ncMetaAclMw(filterUpdate));
router.delete('/views/:viewId/filters/:filterId', ncMetaAclMw(filterDelete));
router.get(
'/views/:viewId/filters/:filterParentId/children',
ncMetaAclMw(filterChildrenRead)
'/api/v1/db/meta/views/:viewId/filters',
ncMetaAclMw(filterList, 'filterList')
);
router.post(
'/api/v1/db/meta/views/:viewId/filters',
ncMetaAclMw(filterCreate, 'filterCreate')
);
router.get(
'/api/v1/db/meta/hooks/:hookId/filters',
ncMetaAclMw(hookFilterList, 'filterList')
);
router.post(
'/api/v1/db/meta/hooks/:hookId/filters',
ncMetaAclMw(hookFilterCreate, 'filterCreate')
);
router.get(
'/api/v1/db/meta/filters/:filterId',
ncMetaAclMw(filterGet, 'filterGet')
);
router.patch(
'/api/v1/db/meta/filters/:filterId',
ncMetaAclMw(filterUpdate, 'filterUpdate')
);
router.delete(
'/api/v1/db/meta/filters/:filterId',
ncMetaAclMw(filterDelete, 'filterDelete')
);
router.get(
'/api/v1/db/meta/filters/:filterParentId/children',
ncMetaAclMw(filterChildrenRead, 'filterChildrenRead')
);
export default router;

20
packages/nocodb/src/lib/noco/meta/api/formViewApis.ts

@ -39,8 +39,20 @@ export async function formViewUpdate(req, res) {
export async function formViewDelete(req: Request, res: Response, next) {}
const router = Router({ mergeParams: true });
router.post('/tables/:tableId/forms', ncMetaAclMw(formViewCreate));
router.get('/forms/:formViewId', ncMetaAclMw(formViewGet));
router.put('/forms/:formViewId', ncMetaAclMw(formViewUpdate));
router.delete('/forms/:formViewId', ncMetaAclMw(formViewDelete));
router.post(
'/api/v1/db/meta/tables/:tableId/forms',
ncMetaAclMw(formViewCreate, 'formViewCreate')
);
router.get(
'/api/v1/db/meta/forms/:formViewId',
ncMetaAclMw(formViewGet, 'formViewGet')
);
router.patch(
'/api/v1/db/meta/forms/:formViewId',
ncMetaAclMw(formViewUpdate, 'formViewUpdate')
);
router.delete(
'/api/v1/db/meta/forms/:formViewId',
ncMetaAclMw(formViewDelete, 'formViewDelete')
);
export default router;

5
packages/nocodb/src/lib/noco/meta/api/formViewColumnApis.ts

@ -9,5 +9,8 @@ export async function columnUpdate(req: Request, res: Response) {
}
const router = Router({ mergeParams: true });
router.put('/formColumns/:formViewColumnId', ncMetaAclMw(columnUpdate));
router.patch(
'/api/v1/db/meta/form-columns/:formViewColumnId',
ncMetaAclMw(columnUpdate, 'columnUpdate')
);
export default router;

40
packages/nocodb/src/lib/noco/meta/api/galleryViewApis.ts

@ -1,29 +1,13 @@
import { Request, Response, Router } from 'express';
// @ts-ignore
import Model from '../../../noco-models/Model';
// @ts-ignore
import { PagedResponseImpl } from '../helpers/PagedResponse';
import { GalleryType, TableListType, ViewTypes } from 'nocodb-sdk';
// @ts-ignore
import ProjectMgrv2 from '../../../sqlMgr/v2/ProjectMgrv2';
// @ts-ignore
import Project from '../../../noco-models/Project';
import { GalleryType, ViewTypes } from 'nocodb-sdk';
import View from '../../../noco-models/View';
import GalleryView from '../../../noco-models/GalleryView';
import ncMetaAclMw from '../helpers/ncMetaAclMw';
import { Tele } from 'nc-help';
// @ts-ignore
export async function galleryViewGet(req: Request, res: Response<GalleryType>) {
res.json(await GalleryView.get(req.params.galleryViewId));
}
// @ts-ignore
export async function galleyViewList(
_req: Request<any, any, any>,
_res: Response<TableListType>
) {}
// @ts-ignore
export async function galleryViewCreate(req: Request<any, any>, res) {
Tele.emit('evt', { evt_type: 'vtable:created', show_as: 'gallery' });
const view = await View.insert({
@ -35,20 +19,22 @@ export async function galleryViewCreate(req: Request<any, any>, res) {
res.json(view);
}
// @ts-ignore
export async function galleryViewUpdate(req, res) {
Tele.emit('evt', { evt_type: 'view:updated', type: 'gallery' });
res.json(await GalleryView.update(req.params.galleryViewId, req.body));
}
// @ts-ignore
export async function galleyViewDelete(req: Request, res: Response, next) {}
const router = Router({ mergeParams: true });
// router.get('/', galleyViewList);
router.post('/tables/:tableId/galleries', ncMetaAclMw(galleryViewCreate));
// router.get('/:galleryViewId', galleyViewGet);
router.put('/galleries/:galleryViewId', ncMetaAclMw(galleryViewUpdate));
router.get('/galleries/:galleryViewId', ncMetaAclMw(galleryViewGet));
// router.delete('/:galleryViewId', galleyViewDelete);
router.post(
'/api/v1/db/meta/tables/:tableId/galleries',
ncMetaAclMw(galleryViewCreate, 'galleryViewCreate')
);
router.patch(
'/api/v1/db/meta/galleries/:galleryViewId',
ncMetaAclMw(galleryViewUpdate, 'galleryViewUpdate')
);
router.get(
'/api/v1/db/meta/galleries/:galleryViewId',
ncMetaAclMw(galleryViewGet, 'galleryViewGet')
);
export default router;

5
packages/nocodb/src/lib/noco/meta/api/gridViewApis.ts

@ -25,5 +25,8 @@ export async function gridViewCreate(req: Request<any, any>, res) {
}
const router = Router({ mergeParams: true });
router.post('/tables/:tableId/grids/', ncMetaAclMw(gridViewCreate));
router.post(
'/api/v1/db/meta/tables/:tableId/grids/',
ncMetaAclMw(gridViewCreate, 'gridViewCreate')
);
export default router;

10
packages/nocodb/src/lib/noco/meta/api/gridViewColumnApis.ts

@ -13,6 +13,12 @@ export async function gridColumnUpdate(req: Request, res: Response) {
}
const router = Router({ mergeParams: true });
router.get('/grid/:gridViewId/gridColumns', ncMetaAclMw(columnList));
router.put('/gridColumns/:gridViewColumnId', ncMetaAclMw(gridColumnUpdate));
router.get(
'/api/v1/db/meta/grids/:gridViewId/grid-columns',
ncMetaAclMw(columnList, 'columnList')
);
router.patch(
'/api/v1/db/meta/grid-columns/:gridViewColumnId',
ncMetaAclMw(gridColumnUpdate, 'gridColumnUpdate')
);
export default router;

27
packages/nocodb/src/lib/noco/meta/api/hookApis.ts

@ -75,13 +75,28 @@ export async function tableSampleData(req: Request, res: Response) {
}
const router = Router({ mergeParams: true });
router.get('/tables/:tableId/hooks', ncMetaAclMw(hookList));
router.post('/tables/:tableId/hooks/test', ncMetaAclMw(hookTest));
router.post('/tables/:tableId/hooks', ncMetaAclMw(hookCreate));
router.delete('/hooks/:hookId', ncMetaAclMw(hookDelete));
router.put('/hooks/:hookId', ncMetaAclMw(hookUpdate));
router.get(
'/tables/:tableId/hooks/samplePayload/:operation',
'/api/v1/db/meta/tables/:tableId/hooks',
ncMetaAclMw(hookList, 'hookList')
);
router.post(
'/api/v1/db/meta/tables/:tableId/hooks/test',
ncMetaAclMw(hookTest, 'hookTest')
);
router.post(
'/api/v1/db/meta/tables/:tableId/hooks',
ncMetaAclMw(hookCreate, 'hookCreate')
);
router.delete(
'/api/v1/db/meta/hooks/:hookId',
ncMetaAclMw(hookDelete, 'hookDelete')
);
router.patch(
'/api/v1/db/meta/hooks/:hookId',
ncMetaAclMw(hookUpdate, 'hookUpdate')
);
router.get(
'/api/v1/db/meta/tables/:tableId/hooks/samplePayload/:operation',
catchError(tableSampleData)
);
export default router;

24
packages/nocodb/src/lib/noco/meta/api/hookFilterApis.ts

@ -108,13 +108,25 @@ export async function filterDelete(req: Request, res: Response, next) {
}
const router = Router({ mergeParams: true });
router.get('/hooks/:hookId/filters/', ncMetaAclMw(filterList));
router.post('/hooks/:hookId/filters/', ncMetaAclMw(filterCreate));
router.get('/hooks/:hookId/filters/:filterId', ncMetaAclMw(filterGet));
router.put('/hooks/:hookId/filters/:filterId', ncMetaAclMw(filterUpdate));
router.delete('/hooks/:hookId/filters/:filterId', ncMetaAclMw(filterDelete));
router.get('/hooks/:hookId/filters/', ncMetaAclMw(filterList, 'filterList'));
router.post(
'/hooks/:hookId/filters/',
ncMetaAclMw(filterCreate, 'filterCreate')
);
router.get(
'/hooks/:hookId/filters/:filterId',
ncMetaAclMw(filterGet, 'filterGet')
);
router.patch(
'/hooks/:hookId/filters/:filterId',
ncMetaAclMw(filterUpdate, 'filterUpdate')
);
router.delete(
'/hooks/:hookId/filters/:filterId',
ncMetaAclMw(filterDelete, 'filterDelete')
);
router.get(
'/hooks/:hookId/filters/:filterParentId/children',
ncMetaAclMw(filterChildrenRead)
ncMetaAclMw(filterChildrenRead, 'filterChildrenRead')
);
export default router;

39
packages/nocodb/src/lib/noco/meta/api/index.ts

@ -30,6 +30,8 @@ import hookFilterApis from './hookFilterApis';
import {
bulkDataAliasApis,
dataAliasApis,
dataAliasExportApis,
dataAliasNestedApis,
dataApis,
oldDataApis
} from './dataApis';
@ -39,7 +41,8 @@ import {
publicMetaApis
} from './publicApis';
import { Tele } from 'nc-help';
import Server from 'socket.io';
import { Server } from 'socket.io';
import passport from 'passport';
export default function(router: Router, server) {
initStrategies(router);
@ -51,6 +54,8 @@ export default function(router: Router, server) {
router.use(dataApis);
router.use(bulkDataAliasApis);
router.use(dataAliasApis);
router.use(dataAliasNestedApis);
router.use(dataAliasExportApis);
router.use(oldDataApis);
router.use(sortApis);
router.use(filterApis);
@ -79,9 +84,33 @@ export default function(router: Router, server) {
userApis(router);
const io = new Server(server);
io.on('connection', socket => {
socket.on('page', args => Tele.page(args));
socket.on('event', args => Tele.event(args));
const io = new Server(server, {
cors: {
origin: '*',
allowedHeaders: ['xc-auth'],
credentials: true
}
});
io.use(function(socket, next) {
passport.authenticate(
'jwt',
{ session: false },
(_err, user, _info): any => {
if (!user) {
socket.disconnect();
return next(new Error('Unauthorized'));
}
(socket.handshake as any).user = user;
next();
}
)(socket.handshake, {}, next);
}).on('connection', socket => {
socket.on('page', args => {
Tele.page(args);
});
socket.on('event', args => {
Tele.event(args);
});
});
}

36
packages/nocodb/src/lib/noco/meta/api/metaDiffApis.ts

@ -17,6 +17,7 @@ import UITypes from '../../../sqlUi/UITypes';
import mapDefaultPrimaryValue from '../helpers/mapDefaultPrimaryValue';
import { Tele } from 'nc-help';
import getColumnUiType from '../helpers/getColumnUiType';
export enum MetaDiffType {
TABLE_NEW = 'TABLE_NEW',
TABLE_REMOVE = 'TABLE_REMOVE',
@ -730,6 +731,7 @@ async function isMMRelationAvailable(
if (col.uidt === UITypes.LinkToAnotherRecord) {
const colOpt = await col.getColOptions<LinkToAnotherRecordColumn>();
if (
colOpt &&
colOpt.type === RelationTypes.MANY_TO_MANY &&
colOpt.fk_mm_model_id === assocModel.id &&
colOpt.fk_child_column_id === colChildOpt.fk_parent_column_id &&
@ -761,7 +763,7 @@ export async function extractAndGenerateManyToManyRelations(
}
}
// todo: impl proper way to identify m2m relation
// todo: impl better method to identify m2m relation
if (belongsToCols?.length === 2 && normalColumns.length < 5) {
const modelA = await belongsToCols[0].colOptions.getRelatedTable();
const modelB = await belongsToCols[1].colOptions.getRelatedTable();
@ -819,6 +821,28 @@ export async function extractAndGenerateManyToManyRelations(
}
await Model.markAsMmTable(assocModel.id, true);
// mark has many relation associated with mm as system field in both table
for (const btCol of [belongsToCols[0], belongsToCols[1]]) {
const colOpt = await btCol.colOptions;
const model = await colOpt.getRelatedTable();
for (const col of await model.getColumns()) {
if (col.uidt !== UITypes.LinkToAnotherRecord) continue;
const colOpt1 = await col.getColOptions<LinkToAnotherRecordColumn>();
if (!colOpt1 || colOpt1.type !== RelationTypes.HAS_MANY) continue;
if (
colOpt1.fk_child_column_id !== colOpt.fk_child_column_id ||
colOpt1.fk_parent_column_id !== colOpt.fk_parent_column_id
)
continue;
await Column.markAsSystemField(col.id);
break;
}
}
} else {
if (assocModel.mm) await Model.markAsMmTable(assocModel.id, false);
}
@ -826,6 +850,12 @@ export async function extractAndGenerateManyToManyRelations(
}
const router = Router();
router.get('/projects/:projectId/metaDiff', ncMetaAclMw(metaDiff));
router.post('/projects/:projectId/metaDiff', ncMetaAclMw(metaDiffSync));
router.get(
'/api/v1/db/meta/projects/:projectId/meta-diff',
ncMetaAclMw(metaDiff, 'metaDiff')
);
router.post(
'/api/v1/db/meta/projects/:projectId/meta-diff',
ncMetaAclMw(metaDiffSync, 'metaDiffSync')
);
export default router;

4
packages/nocodb/src/lib/noco/meta/api/modelVisibilityApis.ts

@ -107,7 +107,7 @@ export async function xcVisibilityMetaGet(
const router = Router({ mergeParams: true });
router.get(
'/projects/:projectId/modelVisibility',
'/api/v1/db/meta/projects/:projectId/visibility-rules',
ncMetaAclMw(async (req, res) => {
res.json(
await xcVisibilityMetaGet(
@ -119,7 +119,7 @@ router.get(
}, 'modelVisibilityList')
);
router.post(
'/projects/:projectId/modelVisibility',
'/api/v1/db/meta/projects/:projectId/visibility-rules',
ncMetaAclMw(xcVisibilityMetaSetAll, 'modelVisibilitySet')
);
export default router;

22
packages/nocodb/src/lib/noco/meta/api/pluginApis.ts

@ -34,9 +34,21 @@ export async function isPluginActive(req: Request, res: Response) {
}
const router = Router({ mergeParams: true });
router.get('/plugins', ncMetaAclMw(pluginList));
router.post('/plugins/test', ncMetaAclMw(pluginTest));
router.get('/plugins/:pluginId', ncMetaAclMw(pluginRead));
router.put('/plugins/:pluginId', ncMetaAclMw(pluginUpdate));
router.get('/plugins/:pluginTitle/status', ncMetaAclMw(isPluginActive));
router.get('/api/v1/db/meta/plugins', ncMetaAclMw(pluginList, 'pluginList'));
router.post(
'/api/v1/db/meta/plugins/test',
ncMetaAclMw(pluginTest, 'pluginTest')
);
router.get(
'/api/v1/db/meta/plugins/:pluginId',
ncMetaAclMw(pluginRead, 'pluginRead')
);
router.patch(
'/api/v1/db/meta/plugins/:pluginId',
ncMetaAclMw(pluginUpdate, 'pluginUpdate')
);
router.get(
'/api/v1/db/meta/plugins/:pluginTitle/status',
ncMetaAclMw(isPluginActive, 'isPluginActive')
);
export default router;

27
packages/nocodb/src/lib/noco/meta/api/projectApis.ts

@ -32,7 +32,6 @@ export async function projectGet(
req: Request<any, any, any>,
res: Response<Project>
) {
console.log(req.query.page);
const project = await Project.getWithInfo(req.params.projectId);
// delete datasource connection details
@ -49,7 +48,6 @@ export async function projectList(
next
) {
try {
console.log(req.query.page);
const projects = await Project.list(req.query);
res // todo: pagination
@ -393,9 +391,24 @@ export async function projectInfoGet(req, res) {
}
export default router => {
router.get('/projects/:projectId/info', ncMetaAclMw(projectInfoGet));
router.get('/projects/:projectId', ncMetaAclMw(projectGet));
router.delete('/projects/:projectId', ncMetaAclMw(projectDelete));
router.post('/projects', ncMetaAclMw(projectCreate));
router.get('/projects', ncMetaAclMw(projectList));
router.get(
'/api/v1/db/meta/projects/:projectId/info',
ncMetaAclMw(projectInfoGet, 'projectInfoGet')
);
router.get(
'/api/v1/db/meta/projects/:projectId',
ncMetaAclMw(projectGet, 'projectGet')
);
router.delete(
'/api/v1/db/meta/projects/:projectId',
ncMetaAclMw(projectDelete, 'projectDelete')
);
router.post(
'/api/v1/db/meta/projects',
ncMetaAclMw(projectCreate, 'projectCreate')
);
router.get(
'/api/v1/db/meta/projects',
ncMetaAclMw(projectList, 'projectList')
);
};

20
packages/nocodb/src/lib/noco/meta/api/projectUserApis.ts

@ -249,14 +249,20 @@ async function resendInvite(req, res, next): Promise<any> {
}
const router = Router({ mergeParams: true });
router.get('/projects/:projectId/users', ncMetaAclMw(userList));
router.post('/projects/:projectId/users', ncMetaAclMw(userInvite));
router.put(
'/projects/:projectId/users/:userId',
ncMetaAclMw(projectUserUpdate)
router.get(
'/api/v1/db/meta/projects/:projectId/users',
ncMetaAclMw(userList, 'userList')
);
router.post(
'/api/v1/db/meta/projects/:projectId/users',
ncMetaAclMw(userInvite, 'userInvite')
);
router.patch(
'/api/v1/db/meta/projects/:projectId/users/:userId',
ncMetaAclMw(projectUserUpdate, 'projectUserUpdate')
);
router.delete(
'/projects/:projectId/users/:userId',
ncMetaAclMw(projectUserDelete)
'/api/v1/db/meta/projects/:projectId/users/:userId',
ncMetaAclMw(projectUserDelete, 'projectUserDelete')
);
export default router;

98
packages/nocodb/src/lib/noco/meta/api/publicApis/publicDataApis.ts

@ -15,15 +15,16 @@ import path from 'path';
import { nanoid } from 'nanoid';
import { mimeIcons } from '../../../../utils/mimeTypes';
import slash from 'slash';
import { sanitizeUrlPath } from '../attachmentApis';
export async function dataList(req: Request, res: Response) {
try {
const view = await View.getByUUID(req.params.publicDataUuid);
const view = await View.getByUUID(req.params.sharedViewUuid);
if (!view) NcError.notFound('Not found');
if (view.type !== ViewTypes.GRID) NcError.notFound('Not found');
if (view.password && view.password !== req.body?.password) {
if (view.password && view.password !== req.headers?.['xc-password']) {
return NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD);
}
@ -39,37 +40,22 @@ export async function dataList(req: Request, res: Response) {
dbDriver: NcConnectionMgrv2.get(base)
});
const key = `${model.title}List`;
const requestObj = {
[key]: await baseModel.defaultResolverReq(req.query)
};
const data = (
await nocoExecute(
requestObj,
{
[key]: async args => {
return await baseModel.list(args);
}
},
{},
{
nested: {
[key]: {
...req.query,
sortArr: req.body?.sorts,
filterArr: req.body?.filters
}
}
}
)
)?.[key];
const listArgs: any = { ...req.query };
try {
listArgs.filterArr = JSON.parse(listArgs.filterArrJson);
} catch (e) {}
try {
listArgs.sortArr = JSON.parse(listArgs.sortArrJson);
} catch (e) {}
const data = await nocoExecute(
await baseModel.defaultResolverReq(req.query),
await baseModel.list(listArgs),
{},
listArgs
);
const count = await baseModel.count({
...req.query,
filterArr: req.body?.filters
});
const count = await baseModel.count(listArgs);
res.json({
data: new PagedResponseImpl(data, { ...req.query, count })
@ -85,12 +71,12 @@ async function dataInsert(
res: Response,
next
) {
const view = await View.getByUUID(req.params.publicDataUuid);
const view = await View.getByUUID(req.params.sharedViewUuid);
if (!view) return next(new Error('Not found'));
if (view.type !== ViewTypes.FORM) return next(new Error('Not found'));
if (view.password && view.password !== req.body?.password) {
if (view.password && view.password !== req.headers?.['xc-password']) {
return res.status(403).json(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD);
}
@ -98,6 +84,7 @@ async function dataInsert(
id: view?.fk_model_id
});
const base = await Base.get(model.base_id);
const project = await base.getProject();
const baseModel = await Model.getBaseModelSQL({
id: model.id,
@ -137,18 +124,26 @@ async function dataInsert(
for (const file of req.files || []) {
// remove `_` prefix and `[]` suffix
const fieldName = file?.fieldname?.replace(/^_|\[\d*]$/g, '');
const filePath = sanitizeUrlPath([
'noco',
project.title,
model.title,
fieldName
]);
if (fieldName in fields && fields[fieldName].uidt === UITypes.Attachment) {
attachments[fieldName] = attachments[fieldName] || [];
const fileName = `${nanoid(6)}_${file.originalname}`;
let url = await storageAdapter.fileCreate(
slash(path.join('nc', 'uploads', base.project_id, view.id, fileName)),
slash(path.join('nc', 'uploads', ...filePath, fileName)),
file
);
if (!url) {
url = `${(req as any).ncSiteUrl}/download/${base.project_id}/${
view.id
}/${fileName}`;
url = `${(req as any).ncSiteUrl}/download/${filePath.join(
'/'
)}/${fileName}`;
}
attachments[fieldName].push({
@ -169,12 +164,12 @@ async function dataInsert(
}
async function relDataList(req, res) {
const view = await View.getByUUID(req.params.publicDataUuid);
const view = await View.getByUUID(req.params.sharedViewUuid);
if (!view) NcError.notFound('Not found');
if (view.type !== ViewTypes.FORM) NcError.notFound('Not found');
if (view.password && view.password !== req.body?.password) {
if (view.password && view.password !== req.headers?.['xc-password']) {
NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD);
}
@ -215,12 +210,12 @@ async function relDataList(req, res) {
}
export async function publicMmList(req: Request, res: Response) {
const view = await View.getByUUID(req.params.publicDataUuid);
const view = await View.getByUUID(req.params.sharedViewUuid);
if (!view) NcError.notFound('Not found');
if (view.type !== ViewTypes.GRID) NcError.notFound('Not found');
if (view.password && view.password !== req.body?.password) {
if (view.password && view.password !== req.headers?.['xc-password']) {
NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD);
}
@ -271,12 +266,12 @@ export async function publicMmList(req: Request, res: Response) {
}
export async function publicHmList(req: Request, res: Response) {
const view = await View.getByUUID(req.params.publicDataUuid);
const view = await View.getByUUID(req.params.sharedViewUuid);
if (!view) NcError.notFound('Not found');
if (view.type !== ViewTypes.GRID) NcError.notFound('Not found');
if (view.password && view.password !== req.body?.password) {
if (view.password && view.password !== req.headers?.['xc-password']) {
NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD);
}
@ -330,13 +325,16 @@ export async function publicHmList(req: Request, res: Response) {
}
const router = Router({ mergeParams: true });
router.post('/public/data/:publicDataUuid/list', catchError(dataList));
router.post(
'/public/data/:publicDataUuid/relationTable/:columnId',
router.get(
'/api/v1/db/public/shared-view/:sharedViewUuid/rows',
catchError(dataList)
);
router.get(
'/api/v1/db/public/shared-view/:sharedViewUuid/nested/:columnId',
catchError(relDataList)
);
router.post(
'/public/data/:publicDataUuid/create',
'/api/v1/db/public/shared-view/:sharedViewUuid/rows',
multer({
storage: multer.diskStorage({})
}).any(),
@ -344,11 +342,11 @@ router.post(
);
router.get(
'/public/data/:publicDataUuid/:rowId/mm/:colId',
'/api/v1/db/public/shared-view/:sharedViewUuid/rows/:rowId/mm/:colId',
catchError(publicMmList)
);
router.get(
'/public/data/:publicDataUuid/:rowId/hm/:colId',
'/api/v1/db/public/shared-view/:sharedViewUuid/rows/:rowId/hm/:colId',
catchError(publicHmList)
);

37
packages/nocodb/src/lib/noco/meta/api/publicApis/publicDataExportApis.ts

@ -17,7 +17,7 @@ async function exportCsv(req: Request, res: Response) {
if (!view) NcError.notFound('Not found');
if (view.type !== ViewTypes.GRID) NcError.notFound('Not found');
if (view.password && view.password !== req.body?.password) {
if (view.password && view.password !== req.headers?.['xc-password']) {
NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD);
}
@ -34,6 +34,15 @@ async function exportCsv(req: Request, res: Response) {
if (!model) NcError.notFound('Table not found');
const fields = req.query.fields;
const listArgs: any = { ...req.query };
try {
listArgs.filterArr = JSON.parse(listArgs.filterArrJson);
} catch (e) {}
try {
listArgs.sortArr = JSON.parse(listArgs.sortArrJson);
} catch (e) {}
const base = await Base.get(model.base_id);
const baseModel = await Model.getBaseModelSQL({
id: model.id,
@ -61,7 +70,6 @@ async function exportCsv(req: Request, res: Response) {
temp = process.hrtime(startTime),
elapsed = temp[0] * 1000 + temp[1] / 1000000
) {
console.time('nocoExecute');
const rows = (
await nocoExecute(
requestObj,
@ -71,14 +79,9 @@ async function exportCsv(req: Request, res: Response) {
}
},
{},
{
nested: {
[key]: {
...req.query,
sortArr: req.body?.sorts,
filterArr: req.body?.filters
}
[key]: listArgs
}
}
)
@ -100,12 +103,21 @@ async function exportCsv(req: Request, res: Response) {
}
csvRows.push(csvRow);
}
console.timeEnd('nocoExecute');
}
const data = papaparse.unparse(
{
fields: model.columns.map(c => c.title),
fields: model.columns
.sort((c1, c2) =>
Array.isArray(fields)
? fields.indexOf(c1.title as any) - fields.indexOf(c2.title as any)
: 0
)
.filter(
c =>
!fields || !Array.isArray(fields) || fields.includes(c.title as any)
)
.map(c => c.title),
data: csvRows
},
{
@ -188,5 +200,8 @@ async function serializeCellValue({
}
const router = Router({ mergeParams: true });
router.post('/public/data/:publicDataUuid/export/csv', catchError(exportCsv));
router.get(
'/api/v1/db/public/shared-view/:publicDataUuid/rows/export/csv',
catchError(exportCsv)
);
export default router;

24
packages/nocodb/src/lib/noco/meta/api/publicApis/publicMetaApis.ts

@ -6,16 +6,17 @@ import UITypes from '../../../../sqlUi/UITypes';
import { ErrorMessages, LinkToAnotherRecordType } from 'nocodb-sdk';
import Column from '../../../../noco-models/Column';
import Base from '../../../../noco-models/Base';
import Project from '../../../../noco-models/Project';
export async function viewMetaGet(req: Request, res: Response) {
const view: View & {
relatedMetas?: { [ket: string]: Model };
client?: string;
} = await View.getByUUID(req.params.publicDataUuid);
} = await View.getByUUID(req.params.sharedViewUuid);
if (!view) NcError.notFound('Not found');
if (view.password && view.password !== req.body?.password) {
if (view.password && view.password !== req.headers?.['xc-password']) {
NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD);
}
@ -66,7 +67,24 @@ export async function viewMetaGet(req: Request, res: Response) {
res.json(view);
}
async function publicSharedBaseGet(req, res): Promise<any> {
const project = await Project.getByUuid(req.params.sharedBaseUuid);
if (!project) {
NcError.notFound();
}
res.json({ project_id: project.id });
}
const router = Router({ mergeParams: true });
router.post('/public/meta/:publicDataUuid/', catchError(viewMetaGet));
router.get(
'/api/v1/db/public/shared-view/:sharedViewUuid/meta',
catchError(viewMetaGet)
);
router.get(
'/api/v1/db/public/shared-base/:sharedBaseUuid/meta',
catchError(publicSharedBaseGet)
);
export default router;

31
packages/nocodb/src/lib/noco/meta/api/sharedBaseApis.ts

@ -3,7 +3,7 @@ import ncMetaAclMw from '../helpers/ncMetaAclMw';
import { v4 as uuidv4 } from 'uuid';
import { Tele } from 'nc-help';
import Project from '../../../noco-models/Project';
import catchError, { NcError } from '../helpers/catchError';
import { NcError } from '../helpers/catchError';
// todo: load from config
const config = {
dashboardPath: '/nc'
@ -88,29 +88,22 @@ async function getSharedBaseLink(req, res): Promise<any> {
res.json(data);
}
async function publicSharedBaseGet(req, res): Promise<any> {
const project = await Project.getByUuid(req.params.uuid);
if (!project) {
NcError.notFound();
}
res.json({ project_id: project.id });
}
const router = Router({ mergeParams: true });
router.get('/projects/:projectId/sharedBase', ncMetaAclMw(getSharedBaseLink));
router.get(
'/api/v1/db/meta/projects/:projectId/shared',
ncMetaAclMw(getSharedBaseLink, 'getSharedBaseLink')
);
router.post(
'/projects/:projectId/sharedBase',
ncMetaAclMw(createSharedBaseLink)
'/api/v1/db/meta/projects/:projectId/shared',
ncMetaAclMw(createSharedBaseLink, 'createSharedBaseLink')
);
router.put(
'/projects/:projectId/sharedBase',
ncMetaAclMw(updateSharedBaseLink)
router.patch(
'/api/v1/db/meta/projects/:projectId/shared',
ncMetaAclMw(updateSharedBaseLink, 'updateSharedBaseLink')
);
router.delete(
'/projects/:projectId/sharedBase',
ncMetaAclMw(disableSharedBaseLink)
'/api/v1/db/meta/projects/:projectId/shared',
ncMetaAclMw(disableSharedBaseLink, 'disableSharedBaseLink')
);
router.get('/public/sharedBase/:uuid', catchError(publicSharedBaseGet));
export default router;

28
packages/nocodb/src/lib/noco/meta/api/sortApis.ts

@ -36,24 +36,34 @@ export async function sortCreate(req: Request<any, any, TableReqType>, res) {
res.json(sort);
}
// @ts-ignore
export async function sortUpdate(req, res, next) {
export async function sortUpdate(req, res) {
const sort = await Sort.update(req.params.sortId, req.body);
Tele.emit('evt', { evt_type: 'sort:updated' });
res.json(sort);
}
// @ts-ignore
export async function sortDelete(req: Request, res: Response, next) {
export async function sortDelete(req: Request, res: Response) {
Tele.emit('evt', { evt_type: 'sort:deleted' });
const sort = await Sort.delete(req.params.sortId);
res.json(sort);
}
const router = Router({ mergeParams: true });
router.get('/views/:viewId/sorts/', ncMetaAclMw(sortList));
router.post('/views/:viewId/sorts/', ncMetaAclMw(sortCreate));
router.get('/views/:viewId/sorts/:sortId', ncMetaAclMw(sortGet));
router.put('/views/:viewId/sorts/:sortId', ncMetaAclMw(sortUpdate));
router.delete('/views/:viewId/sorts/:sortId', ncMetaAclMw(sortDelete));
router.get(
'/api/v1/db/meta/views/:viewId/sorts/',
ncMetaAclMw(sortList, 'sortList')
);
router.post(
'/api/v1/db/meta/views/:viewId/sorts/',
ncMetaAclMw(sortCreate, 'sortCreate')
);
router.get('/api/v1/db/meta/sorts/:sortId', ncMetaAclMw(sortGet, 'sortGet'));
router.patch(
'/api/v1/db/meta/sorts/:sortId',
ncMetaAclMw(sortUpdate, 'sortUpdate')
);
router.delete(
'/api/v1/db/meta/sorts/:sortId',
ncMetaAclMw(sortDelete, 'sortDelete')
);
export default router;

31
packages/nocodb/src/lib/noco/meta/api/tableApis.ts

@ -183,7 +183,6 @@ export async function tableUpdate(req: Request<any, any>, res) {
export async function tableDelete(req: Request, res: Response, next) {
try {
console.log(req.params);
const table = await Model.getByIdOrName({ id: req.params.tableId });
await table.getColumns();
@ -223,10 +222,28 @@ export async function tableDelete(req: Request, res: Response, next) {
}
const router = Router({ mergeParams: true });
router.get('/projects/:projectId/:baseId/tables', ncMetaAclMw(tableList));
router.post('/projects/:projectId/:baseId/tables', ncMetaAclMw(tableCreate));
router.get('/tables/:tableId', ncMetaAclMw(tableGet));
router.put('/tables/:tableId', ncMetaAclMw(tableUpdate));
router.delete('/tables/:tableId', ncMetaAclMw(tableDelete));
router.post('/tables/:tableId/reorder', ncMetaAclMw(tableReorder));
router.get(
'/api/v1/db/meta/projects/:projectId/tables',
ncMetaAclMw(tableList, 'tableList')
);
router.post(
'/api/v1/db/meta/projects/:projectId/tables',
ncMetaAclMw(tableCreate, 'tableCreate')
);
router.get(
'/api/v1/db/meta/tables/:tableId',
ncMetaAclMw(tableGet, 'tableGet')
);
router.patch(
'/api/v1/db/meta/tables/:tableId',
ncMetaAclMw(tableUpdate, 'tableUpdate')
);
router.delete(
'/api/v1/db/meta/tables/:tableId',
ncMetaAclMw(tableDelete, 'tableDelete')
);
router.post(
'/api/v1/db/meta/tables/:tableId/reorder',
ncMetaAclMw(tableReorder, 'tableReorder')
);
export default router;

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save