mirror of https://github.com/nocodb/nocodb
659 changed files with 479 additions and 1475811 deletions
@ -1,18 +0,0 @@
|
||||
node_modules |
||||
coverage |
||||
.nyc_output |
||||
.DS_Store |
||||
*.log |
||||
.vscode |
||||
.idea |
||||
# ignore src besides src/lib/public |
||||
src |
||||
!src/lib/public |
||||
server |
||||
compiled |
||||
.awcache |
||||
.rpt2_cache |
||||
docs |
||||
help |
||||
.serverless |
||||
noco.db* |
@ -1,67 +0,0 @@
|
||||
{ |
||||
"root": true, |
||||
"parser": "@typescript-eslint/parser", |
||||
"parserOptions": { |
||||
"project": "./tsconfig.json" |
||||
}, |
||||
"env": { |
||||
"es6": true |
||||
}, |
||||
"ignorePatterns": ["node_modules", "build", "coverage", "dist", "nc"], |
||||
"plugins": ["import", "eslint-comments", "functional"], |
||||
"extends": [ |
||||
"eslint:recommended", |
||||
"plugin:eslint-comments/recommended", |
||||
"plugin:@typescript-eslint/recommended", |
||||
"plugin:import/typescript", |
||||
"plugin:prettier/recommended" |
||||
], |
||||
"globals": { |
||||
"BigInt": true, |
||||
"console": true, |
||||
"WebAssembly": true |
||||
}, |
||||
"rules": { |
||||
"@typescript-eslint/explicit-module-boundary-types": "off", |
||||
"eslint-comments/disable-enable-pair": [ |
||||
"error", |
||||
{ |
||||
"allowWholeFile": true |
||||
} |
||||
], |
||||
"eslint-comments/no-unused-disable": "error", |
||||
"sort-imports": [ |
||||
"error", |
||||
{ |
||||
"ignoreDeclarationSort": true, |
||||
"ignoreCase": true |
||||
} |
||||
], |
||||
"import/order": [ |
||||
"error", |
||||
{ |
||||
"groups": [ |
||||
"builtin", |
||||
"external", |
||||
"internal", |
||||
"parent", |
||||
"sibling", |
||||
"index", |
||||
"object", |
||||
"type" |
||||
] |
||||
} |
||||
], |
||||
"@typescript-eslint/no-this-alias": "off", |
||||
|
||||
// todo: enable |
||||
"@typescript-eslint/ban-ts-comment": "off", |
||||
"@typescript-eslint/no-explicit-any": "off", |
||||
"@typescript-eslint/no-unused-vars": "off", |
||||
"@typescript-eslint/no-var-requires": "off", |
||||
"no-useless-catch": "off", |
||||
"no-empty": "off", |
||||
"@typescript-eslint/no-empty-function": "off", |
||||
"@typescript-eslint/consistent-type-imports": "warn" |
||||
} |
||||
} |
@ -1,17 +0,0 @@
|
||||
# This file specifies files that are *not* uploaded to Google Cloud Platform |
||||
# using gcloud. It follows the same syntax as .gitignore, with the addition of |
||||
# "#!include" directives (which insert the entries of the given .gitignore-style |
||||
# file at that point). |
||||
# |
||||
# For more information, run: |
||||
# $ gcloud topic gcloudignore |
||||
# |
||||
.gcloudignore |
||||
# If you would like to upload your .git directory, .gitignore file or files |
||||
# from your .gitignore file, remove the corresponding line |
||||
# below: |
||||
.git |
||||
.gitignore |
||||
|
||||
node_modules |
||||
src |
@ -1,23 +0,0 @@
|
||||
node_modules |
||||
build |
||||
src/**.js |
||||
.idea/* |
||||
dist |
||||
coverage |
||||
.nyc_output |
||||
*.log |
||||
|
||||
yarn.lock |
||||
server |
||||
help |
||||
.serverless |
||||
projects/test/config.xc.json |
||||
xc.db* |
||||
noco.db* |
||||
/nc/ |
||||
/docker/main.js |
||||
test_meta.db |
||||
test_sakila.db |
||||
test_sakila_*.db |
||||
.env |
||||
export/** |
@ -1,43 +0,0 @@
|
||||
node_modules |
||||
coverage |
||||
.nyc_output |
||||
.DS_Store |
||||
*.log |
||||
*.json |
||||
*.db |
||||
.vscode |
||||
.idea |
||||
src |
||||
compiled |
||||
.awcache |
||||
.rpt2_cache |
||||
docs |
||||
docker |
||||
server |
||||
help |
||||
HttpTrigger |
||||
serverless |
||||
webpack.config.js |
||||
local.settings.json |
||||
host.json |
||||
serverless.yml |
||||
tsconfig.module.json |
||||
template.yml |
||||
now.json |
||||
package-lock.json |
||||
function.json |
||||
tslint.json |
||||
tsconfig.json |
||||
Dockerfile-ORACLE |
||||
Dockerfile |
||||
xc |
||||
xc.db |
||||
nc |
||||
noco.db* |
||||
xc.sh |
||||
docker-compose.yml |
||||
uploads |
||||
build |
||||
dist |
||||
tests |
||||
litestream |
@ -1,2 +0,0 @@
|
||||
# package.json is formatted by package managers, so we ignore it here |
||||
package.json |
@ -1,7 +0,0 @@
|
||||
module.exports = { |
||||
"trailingComma": "es5", |
||||
"arrowParens": "avoid", |
||||
singleQuote: true, |
||||
tabWidth: 2, |
||||
printWidth: 120 |
||||
}; |
@ -1,72 +0,0 @@
|
||||
########### |
||||
# Litestream Builder |
||||
########### |
||||
FROM golang:alpine3.14 as lt-builder |
||||
|
||||
WORKDIR /usr/src/ |
||||
|
||||
RUN apk add --no-cache git make musl-dev gcc |
||||
|
||||
# build litestream |
||||
RUN git clone https://github.com/benbjohnson/litestream.git litestream |
||||
RUN cd litestream ; go install ./cmd/litestream |
||||
|
||||
RUN cp $GOPATH/bin/litestream /usr/src/lt |
||||
|
||||
|
||||
|
||||
########### |
||||
# Builder |
||||
########### |
||||
FROM node:16.17.0-alpine3.15 as builder |
||||
WORKDIR /usr/src/app |
||||
|
||||
# install node-gyp dependencies |
||||
RUN apk add --no-cache python3 make g++ |
||||
|
||||
# Copy application dependency manifests to the container image. |
||||
# A wildcard is used to ensure both package.json AND package-lock.json are copied. |
||||
# Copying this separately prevents re-running npm ci on every code change. |
||||
COPY ./package*.json ./ |
||||
COPY ./docker/main.js ./docker/main.js |
||||
#COPY ./docker/start.sh /usr/src/appEntry/start.sh |
||||
COPY ./docker/start-litestream.sh /usr/src/appEntry/start.sh |
||||
COPY ./src/lib/public/css/*.css ./docker/public/css/ |
||||
COPY ./src/lib/public/js/*.js ./docker/public/js/ |
||||
COPY ./src/lib/public/favicon.ico ./docker/public/ |
||||
|
||||
# install production dependencies, |
||||
# reduce node_module size with modclean & removing sqlite deps, |
||||
# package built code into app.tar.gz & add execute permission to start.sh |
||||
RUN npm ci --omit=dev --quiet \ |
||||
&& npx modclean --patterns="default:*" --ignore="nc-lib-gui/**,dayjs/**,express-status-monitor/**,@azure/msal-node/dist/**" --run \ |
||||
&& rm -rf ./node_modules/sqlite3/deps \ |
||||
&& tar -czf ../appEntry/app.tar.gz ./* \ |
||||
&& chmod +x /usr/src/appEntry/start.sh |
||||
|
||||
########## |
||||
# Runner |
||||
########## |
||||
FROM alpine:3.15 |
||||
WORKDIR /usr/src/app |
||||
|
||||
ENV NC_DOCKER 0.6 |
||||
ENV NODE_ENV production |
||||
ENV PORT 8080 |
||||
ENV NC_TOOL_DIR=/usr/app/data/ |
||||
|
||||
RUN apk --update --no-cache add \ |
||||
nodejs \ |
||||
tar \ |
||||
dumb-init |
||||
|
||||
# Copy litestream binary build |
||||
COPY --from=lt-builder /usr/src/lt /usr/src/appEntry/litestream |
||||
# Copy packaged production code & main entry file |
||||
COPY --from=builder /usr/src/appEntry/ /usr/src/appEntry/ |
||||
|
||||
EXPOSE 8080 |
||||
ENTRYPOINT ["/usr/bin/dumb-init", "--"] |
||||
|
||||
# Start Nocodb |
||||
CMD ["/usr/src/appEntry/start.sh"] |
@ -1,109 +0,0 @@
|
||||
FROM alpine:latest |
||||
|
||||
#ENV LD_LIBRARY_PATH=/lib |
||||
|
||||
|
||||
ENV PORT 8080 |
||||
ENV NODE_ENV=dev |
||||
|
||||
# the client version we will download from bumpx repo |
||||
ENV CLIENT_FILENAME instantclient-basic-linux.x64-12.1.0.1.0.zip |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Create and change to the app directory. |
||||
WORKDIR /usr/src/appTemp |
||||
|
||||
# Copy application dependency manifests to the container image. |
||||
# A wildcard is used to ensure both package.json AND package-lock.json are copied. |
||||
# Copying this separately prevents re-running npm install on every code change. |
||||
COPY ./docker/main.js ./docker/main.js |
||||
COPY ./package*.json ./ |
||||
|
||||
|
||||
COPY ./docker/start.sh /usr/src/appEntry/start.sh |
||||
|
||||
RUN apk --update --no-cache add \ |
||||
nodejs \ |
||||
nodejs-npm \ |
||||
tar |
||||
|
||||
|
||||
# Install production dependencies. |
||||
RUN npm install --cache=/usr/src/app/cache --production && rm -rf /usr/src/app/cache && rm -rf /root/.npm |
||||
|
||||
RUN apk del nodejs-npm |
||||
|
||||
|
||||
#RUN zip ./ –r ../appEntry/mydir.zip |
||||
RUN tar -czf ../appEntry/app.tar.gz ./* ; rm -rf ./* |
||||
|
||||
# |
||||
#RUN wget https://download.oracle.com/otn_software/linux/instantclient/193000/instantclient-basic-linux.x64-19.3.0.0.0dbru.zip && \ |
||||
# unzip instantclient-basic-linux.x64-19.3.0.0.0dbru.zip && \ |
||||
# cp -r instantclient_19_3/* /lib && \ |
||||
# rm -rf instantclient-basic-linux.x64-19.3.0.0.0dbru.zip && \ |
||||
# apk add libaio && \ |
||||
# apk add --update libaio libnsl libc6-compat |
||||
|
||||
|
||||
|
||||
# |
||||
## ln -s /lib/libnsl.so.2 /lib/libnsl.so.1 ;\ |
||||
# |
||||
#RUN ln -s /lib64/* /lib ;\ |
||||
# ln -s /lib/libnsl.so.2 /usr/lib/libnsl.so.1 ;\ |
||||
# ln -s /lib/libc.so /lib/libresolv.so.2 ;\ |
||||
# ln -s /usr/lib/libnsl.so.2 /usr/lib/libnsl.so.1 |
||||
## ln -s /lib/libclntsh.so.12.1 /lib/libclntsh.so ; |
||||
|
||||
|
||||
#RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories && \ |
||||
#apk add --update libaio libnsl && \ |
||||
#ln -s /usr/lib/libnsl.so.2 /lib/libnsl.so.1 |
||||
|
||||
# Bug fix for segfault ( Convert PT_GNU_STACK program header into PT_PAX_FLAGS ) |
||||
#RUN apk --update --no-cache add paxctl \ |
||||
# && paxctl -cm $(which node) |
||||
|
||||
|
||||
|
||||
# work in this directory |
||||
WORKDIR /opt/oracle/lib |
||||
|
||||
# take advantage of this repo to easily download the client (use it at your own risk) |
||||
ADD https://github.com/bumpx/oracle-instantclient/raw/master/${CLIENT_FILENAME} . |
||||
|
||||
# we need libaio and libnsl, the latter is only available as package in the edge repository |
||||
RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories && \ |
||||
apk add --update libaio libnsl && \ |
||||
ln -s /usr/lib/libnsl.so.2 /usr/lib/libnsl.so.1 |
||||
|
||||
# unzip the necessary libraries, create the base symlink and remove the zip file |
||||
RUN LIBS="*/libociei.so */libons.so */libnnz12.so */libclntshcore.so.12.1 */libclntsh.so.12.1" && \ |
||||
unzip ${CLIENT_FILENAME} ${LIBS} && \ |
||||
for lib in ${LIBS}; do mv ${lib} /usr/lib; done && \ |
||||
ln -s /usr/lib/libclntsh.so.12.1 /usr/lib/libclntsh.so && \ |
||||
rm ${CLIENT_FILENAME} |
||||
|
||||
|
||||
|
||||
|
||||
# Create and change to the app directory. |
||||
WORKDIR /usr/src/app |
||||
|
||||
|
||||
# Run the web service on container startup. |
||||
#CMD [ "node", "docker/index.js" ] |
||||
ENTRYPOINT ["/bin/sh", "/usr/src/appEntry/start.sh"] |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,55 +0,0 @@
|
||||
########### |
||||
# Builder |
||||
########### |
||||
FROM node:16.17.0-alpine3.15 as builder |
||||
WORKDIR /usr/src/app |
||||
|
||||
# install node-gyp dependencies |
||||
RUN apk add --no-cache python3 make g++ |
||||
|
||||
# Copy application dependency manifests to the container image. |
||||
# A wildcard is used to ensure both package.json AND package-lock.json are copied. |
||||
# Copying this separately prevents re-running npm ci on every code change. |
||||
COPY ./package*.json ./ |
||||
COPY ./docker/nc-gui/ ./docker/nc-gui/ |
||||
COPY ./docker/main.js ./docker/index.js |
||||
COPY ./docker/start-local.sh /usr/src/appEntry/start.sh |
||||
COPY ./src/lib/public/css/*.css ./docker/public/css/ |
||||
COPY ./src/lib/public/js/*.js ./docker/public/js/ |
||||
COPY ./src/lib/public/favicon.ico ./docker/public/ |
||||
|
||||
# install production dependencies, |
||||
# reduce node_module size with modclean & removing sqlite deps, |
||||
# package built code into app.tar.gz & add execute permission to start.sh |
||||
RUN npm ci --omit=dev --quiet \ |
||||
&& npx modclean --patterns="default:*" --ignore="nc-lib-gui/**,dayjs/**,express-status-monitor/**,@azure/msal-node/dist/**" --run \ |
||||
&& rm -rf ./node_modules/sqlite3/deps \ |
||||
&& tar -czf ../appEntry/app.tar.gz ./* \ |
||||
&& chmod +x /usr/src/appEntry/start.sh |
||||
|
||||
########## |
||||
# Runner |
||||
########## |
||||
FROM alpine:3.15 |
||||
WORKDIR /usr/src/app |
||||
|
||||
ENV NC_DOCKER 0.6 |
||||
ENV NODE_ENV production |
||||
ENV PORT 8080 |
||||
ENV NC_TOOL_DIR=/usr/app/data/ |
||||
|
||||
RUN apk --update --no-cache add \ |
||||
nodejs \ |
||||
tar \ |
||||
dumb-init \ |
||||
curl \ |
||||
jq |
||||
|
||||
# Copy packaged production code & main entry file |
||||
COPY --from=builder /usr/src/appEntry/ /usr/src/appEntry/ |
||||
|
||||
EXPOSE 8080 |
||||
ENTRYPOINT ["/usr/bin/dumb-init", "--"] |
||||
|
||||
# Start Nocodb |
||||
CMD ["/usr/src/appEntry/start.sh"] |
@ -1,661 +0,0 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE |
||||
Version 3, 19 November 2007 |
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> |
||||
Everyone is permitted to copy and distribute verbatim copies |
||||
of this license document, but changing it is not allowed. |
||||
|
||||
Preamble |
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for |
||||
software and other kinds of works, specifically designed to ensure |
||||
cooperation with the community in the case of network server software. |
||||
|
||||
The licenses for most software and other practical works are designed |
||||
to take away your freedom to share and change the works. By contrast, |
||||
our General Public Licenses are intended to guarantee your freedom to |
||||
share and change all versions of a program--to make sure it remains free |
||||
software for all its users. |
||||
|
||||
When we speak of free software, we are referring to freedom, not |
||||
price. Our General Public Licenses are designed to make sure that you |
||||
have the freedom to distribute copies of free software (and charge for |
||||
them if you wish), that you receive source code or can get it if you |
||||
want it, that you can change the software or use pieces of it in new |
||||
free programs, and that you know you can do these things. |
||||
|
||||
Developers that use our General Public Licenses protect your rights |
||||
with two steps: (1) assert copyright on the software, and (2) offer |
||||
you this License which gives you legal permission to copy, distribute |
||||
and/or modify the software. |
||||
|
||||
A secondary benefit of defending all users' freedom is that |
||||
improvements made in alternate versions of the program, if they |
||||
receive widespread use, become available for other developers to |
||||
incorporate. Many developers of free software are heartened and |
||||
encouraged by the resulting cooperation. However, in the case of |
||||
software used on network servers, this result may fail to come about. |
||||
The GNU General Public License permits making a modified version and |
||||
letting the public access it on a server without ever releasing its |
||||
source code to the public. |
||||
|
||||
The GNU Affero General Public License is designed specifically to |
||||
ensure that, in such cases, the modified source code becomes available |
||||
to the community. It requires the operator of a network server to |
||||
provide the source code of the modified version running there to the |
||||
users of that server. Therefore, public use of a modified version, on |
||||
a publicly accessible server, gives the public access to the source |
||||
code of the modified version. |
||||
|
||||
An older license, called the Affero General Public License and |
||||
published by Affero, was designed to accomplish similar goals. This is |
||||
a different license, not a version of the Affero GPL, but Affero has |
||||
released a new version of the Affero GPL which permits relicensing under |
||||
this license. |
||||
|
||||
The precise terms and conditions for copying, distribution and |
||||
modification follow. |
||||
|
||||
TERMS AND CONDITIONS |
||||
|
||||
0. Definitions. |
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License. |
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of |
||||
works, such as semiconductor masks. |
||||
|
||||
"The Program" refers to any copyrightable work licensed under this |
||||
License. Each licensee is addressed as "you". "Licensees" and |
||||
"recipients" may be individuals or organizations. |
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work |
||||
in a fashion requiring copyright permission, other than the making of an |
||||
exact copy. The resulting work is called a "modified version" of the |
||||
earlier work or a work "based on" the earlier work. |
||||
|
||||
A "covered work" means either the unmodified Program or a work based |
||||
on the Program. |
||||
|
||||
To "propagate" a work means to do anything with it that, without |
||||
permission, would make you directly or secondarily liable for |
||||
infringement under applicable copyright law, except executing it on a |
||||
computer or modifying a private copy. Propagation includes copying, |
||||
distribution (with or without modification), making available to the |
||||
public, and in some countries other activities as well. |
||||
|
||||
To "convey" a work means any kind of propagation that enables other |
||||
parties to make or receive copies. Mere interaction with a user through |
||||
a computer network, with no transfer of a copy, is not conveying. |
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices" |
||||
to the extent that it includes a convenient and prominently visible |
||||
feature that (1) displays an appropriate copyright notice, and (2) |
||||
tells the user that there is no warranty for the work (except to the |
||||
extent that warranties are provided), that licensees may convey the |
||||
work under this License, and how to view a copy of this License. If |
||||
the interface presents a list of user commands or options, such as a |
||||
menu, a prominent item in the list meets this criterion. |
||||
|
||||
1. Source Code. |
||||
|
||||
The "source code" for a work means the preferred form of the work |
||||
for making modifications to it. "Object code" means any non-source |
||||
form of a work. |
||||
|
||||
A "Standard Interface" means an interface that either is an official |
||||
standard defined by a recognized standards body, or, in the case of |
||||
interfaces specified for a particular programming language, one that |
||||
is widely used among developers working in that language. |
||||
|
||||
The "System Libraries" of an executable work include anything, other |
||||
than the work as a whole, that (a) is included in the normal form of |
||||
packaging a Major Component, but which is not part of that Major |
||||
Component, and (b) serves only to enable use of the work with that |
||||
Major Component, or to implement a Standard Interface for which an |
||||
implementation is available to the public in source code form. A |
||||
"Major Component", in this context, means a major essential component |
||||
(kernel, window system, and so on) of the specific operating system |
||||
(if any) on which the executable work runs, or a compiler used to |
||||
produce the work, or an object code interpreter used to run it. |
||||
|
||||
The "Corresponding Source" for a work in object code form means all |
||||
the source code needed to generate, install, and (for an executable |
||||
work) run the object code and to modify the work, including scripts to |
||||
control those activities. However, it does not include the work's |
||||
System Libraries, or general-purpose tools or generally available free |
||||
programs which are used unmodified in performing those activities but |
||||
which are not part of the work. For example, Corresponding Source |
||||
includes interface definition files associated with source files for |
||||
the work, and the source code for shared libraries and dynamically |
||||
linked subprograms that the work is specifically designed to require, |
||||
such as by intimate data communication or control flow between those |
||||
subprograms and other parts of the work. |
||||
|
||||
The Corresponding Source need not include anything that users |
||||
can regenerate automatically from other parts of the Corresponding |
||||
Source. |
||||
|
||||
The Corresponding Source for a work in source code form is that |
||||
same work. |
||||
|
||||
2. Basic Permissions. |
||||
|
||||
All rights granted under this License are granted for the term of |
||||
copyright on the Program, and are irrevocable provided the stated |
||||
conditions are met. This License explicitly affirms your unlimited |
||||
permission to run the unmodified Program. The output from running a |
||||
covered work is covered by this License only if the output, given its |
||||
content, constitutes a covered work. This License acknowledges your |
||||
rights of fair use or other equivalent, as provided by copyright law. |
||||
|
||||
You may make, run and propagate covered works that you do not |
||||
convey, without conditions so long as your license otherwise remains |
||||
in force. You may convey covered works to others for the sole purpose |
||||
of having them make modifications exclusively for you, or provide you |
||||
with facilities for running those works, provided that you comply with |
||||
the terms of this License in conveying all material for which you do |
||||
not control copyright. Those thus making or running the covered works |
||||
for you must do so exclusively on your behalf, under your direction |
||||
and control, on terms that prohibit them from making any copies of |
||||
your copyrighted material outside their relationship with you. |
||||
|
||||
Conveying under any other circumstances is permitted solely under |
||||
the conditions stated below. Sublicensing is not allowed; section 10 |
||||
makes it unnecessary. |
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law. |
||||
|
||||
No covered work shall be deemed part of an effective technological |
||||
measure under any applicable law fulfilling obligations under article |
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or |
||||
similar laws prohibiting or restricting circumvention of such |
||||
measures. |
||||
|
||||
When you convey a covered work, you waive any legal power to forbid |
||||
circumvention of technological measures to the extent such circumvention |
||||
is effected by exercising rights under this License with respect to |
||||
the covered work, and you disclaim any intention to limit operation or |
||||
modification of the work as a means of enforcing, against the work's |
||||
users, your or third parties' legal rights to forbid circumvention of |
||||
technological measures. |
||||
|
||||
4. Conveying Verbatim Copies. |
||||
|
||||
You may convey verbatim copies of the Program's source code as you |
||||
receive it, in any medium, provided that you conspicuously and |
||||
appropriately publish on each copy an appropriate copyright notice; |
||||
keep intact all notices stating that this License and any |
||||
non-permissive terms added in accord with section 7 apply to the code; |
||||
keep intact all notices of the absence of any warranty; and give all |
||||
recipients a copy of this License along with the Program. |
||||
|
||||
You may charge any price or no price for each copy that you convey, |
||||
and you may offer support or warranty protection for a fee. |
||||
|
||||
5. Conveying Modified Source Versions. |
||||
|
||||
You may convey a work based on the Program, or the modifications to |
||||
produce it from the Program, in the form of source code under the |
||||
terms of section 4, provided that you also meet all of these conditions: |
||||
|
||||
a) The work must carry prominent notices stating that you modified |
||||
it, and giving a relevant date. |
||||
|
||||
b) The work must carry prominent notices stating that it is |
||||
released under this License and any conditions added under section |
||||
7. This requirement modifies the requirement in section 4 to |
||||
"keep intact all notices". |
||||
|
||||
c) You must license the entire work, as a whole, under this |
||||
License to anyone who comes into possession of a copy. This |
||||
License will therefore apply, along with any applicable section 7 |
||||
additional terms, to the whole of the work, and all its parts, |
||||
regardless of how they are packaged. This License gives no |
||||
permission to license the work in any other way, but it does not |
||||
invalidate such permission if you have separately received it. |
||||
|
||||
d) If the work has interactive user interfaces, each must display |
||||
Appropriate Legal Notices; however, if the Program has interactive |
||||
interfaces that do not display Appropriate Legal Notices, your |
||||
work need not make them do so. |
||||
|
||||
A compilation of a covered work with other separate and independent |
||||
works, which are not by their nature extensions of the covered work, |
||||
and which are not combined with it such as to form a larger program, |
||||
in or on a volume of a storage or distribution medium, is called an |
||||
"aggregate" if the compilation and its resulting copyright are not |
||||
used to limit the access or legal rights of the compilation's users |
||||
beyond what the individual works permit. Inclusion of a covered work |
||||
in an aggregate does not cause this License to apply to the other |
||||
parts of the aggregate. |
||||
|
||||
6. Conveying Non-Source Forms. |
||||
|
||||
You may convey a covered work in object code form under the terms |
||||
of sections 4 and 5, provided that you also convey the |
||||
machine-readable Corresponding Source under the terms of this License, |
||||
in one of these ways: |
||||
|
||||
a) Convey the object code in, or embodied in, a physical product |
||||
(including a physical distribution medium), accompanied by the |
||||
Corresponding Source fixed on a durable physical medium |
||||
customarily used for software interchange. |
||||
|
||||
b) Convey the object code in, or embodied in, a physical product |
||||
(including a physical distribution medium), accompanied by a |
||||
written offer, valid for at least three years and valid for as |
||||
long as you offer spare parts or customer support for that product |
||||
model, to give anyone who possesses the object code either (1) a |
||||
copy of the Corresponding Source for all the software in the |
||||
product that is covered by this License, on a durable physical |
||||
medium customarily used for software interchange, for a price no |
||||
more than your reasonable cost of physically performing this |
||||
conveying of source, or (2) access to copy the |
||||
Corresponding Source from a network server at no charge. |
||||
|
||||
c) Convey individual copies of the object code with a copy of the |
||||
written offer to provide the Corresponding Source. This |
||||
alternative is allowed only occasionally and noncommercially, and |
||||
only if you received the object code with such an offer, in accord |
||||
with subsection 6b. |
||||
|
||||
d) Convey the object code by offering access from a designated |
||||
place (gratis or for a charge), and offer equivalent access to the |
||||
Corresponding Source in the same way through the same place at no |
||||
further charge. You need not require recipients to copy the |
||||
Corresponding Source along with the object code. If the place to |
||||
copy the object code is a network server, the Corresponding Source |
||||
may be on a different server (operated by you or a third party) |
||||
that supports equivalent copying facilities, provided you maintain |
||||
clear directions next to the object code saying where to find the |
||||
Corresponding Source. Regardless of what server hosts the |
||||
Corresponding Source, you remain obligated to ensure that it is |
||||
available for as long as needed to satisfy these requirements. |
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided |
||||
you inform other peers where the object code and Corresponding |
||||
Source of the work are being offered to the general public at no |
||||
charge under subsection 6d. |
||||
|
||||
A separable portion of the object code, whose source code is excluded |
||||
from the Corresponding Source as a System Library, need not be |
||||
included in conveying the object code work. |
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any |
||||
tangible personal property which is normally used for personal, family, |
||||
or household purposes, or (2) anything designed or sold for incorporation |
||||
into a dwelling. In determining whether a product is a consumer product, |
||||
doubtful cases shall be resolved in favor of coverage. For a particular |
||||
product received by a particular user, "normally used" refers to a |
||||
typical or common use of that class of product, regardless of the status |
||||
of the particular user or of the way in which the particular user |
||||
actually uses, or expects or is expected to use, the product. A product |
||||
is a consumer product regardless of whether the product has substantial |
||||
commercial, industrial or non-consumer uses, unless such uses represent |
||||
the only significant mode of use of the product. |
||||
|
||||
"Installation Information" for a User Product means any methods, |
||||
procedures, authorization keys, or other information required to install |
||||
and execute modified versions of a covered work in that User Product from |
||||
a modified version of its Corresponding Source. The information must |
||||
suffice to ensure that the continued functioning of the modified object |
||||
code is in no case prevented or interfered with solely because |
||||
modification has been made. |
||||
|
||||
If you convey an object code work under this section in, or with, or |
||||
specifically for use in, a User Product, and the conveying occurs as |
||||
part of a transaction in which the right of possession and use of the |
||||
User Product is transferred to the recipient in perpetuity or for a |
||||
fixed term (regardless of how the transaction is characterized), the |
||||
Corresponding Source conveyed under this section must be accompanied |
||||
by the Installation Information. But this requirement does not apply |
||||
if neither you nor any third party retains the ability to install |
||||
modified object code on the User Product (for example, the work has |
||||
been installed in ROM). |
||||
|
||||
The requirement to provide Installation Information does not include a |
||||
requirement to continue to provide support service, warranty, or updates |
||||
for a work that has been modified or installed by the recipient, or for |
||||
the User Product in which it has been modified or installed. Access to a |
||||
network may be denied when the modification itself materially and |
||||
adversely affects the operation of the network or violates the rules and |
||||
protocols for communication across the network. |
||||
|
||||
Corresponding Source conveyed, and Installation Information provided, |
||||
in accord with this section must be in a format that is publicly |
||||
documented (and with an implementation available to the public in |
||||
source code form), and must require no special password or key for |
||||
unpacking, reading or copying. |
||||
|
||||
7. Additional Terms. |
||||
|
||||
"Additional permissions" are terms that supplement the terms of this |
||||
License by making exceptions from one or more of its conditions. |
||||
Additional permissions that are applicable to the entire Program shall |
||||
be treated as though they were included in this License, to the extent |
||||
that they are valid under applicable law. If additional permissions |
||||
apply only to part of the Program, that part may be used separately |
||||
under those permissions, but the entire Program remains governed by |
||||
this License without regard to the additional permissions. |
||||
|
||||
When you convey a copy of a covered work, you may at your option |
||||
remove any additional permissions from that copy, or from any part of |
||||
it. (Additional permissions may be written to require their own |
||||
removal in certain cases when you modify the work.) You may place |
||||
additional permissions on material, added by you to a covered work, |
||||
for which you have or can give appropriate copyright permission. |
||||
|
||||
Notwithstanding any other provision of this License, for material you |
||||
add to a covered work, you may (if authorized by the copyright holders of |
||||
that material) supplement the terms of this License with terms: |
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the |
||||
terms of sections 15 and 16 of this License; or |
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or |
||||
author attributions in that material or in the Appropriate Legal |
||||
Notices displayed by works containing it; or |
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or |
||||
requiring that modified versions of such material be marked in |
||||
reasonable ways as different from the original version; or |
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or |
||||
authors of the material; or |
||||
|
||||
e) Declining to grant rights under trademark law for use of some |
||||
trade names, trademarks, or service marks; or |
||||
|
||||
f) Requiring indemnification of licensors and authors of that |
||||
material by anyone who conveys the material (or modified versions of |
||||
it) with contractual assumptions of liability to the recipient, for |
||||
any liability that these contractual assumptions directly impose on |
||||
those licensors and authors. |
||||
|
||||
All other non-permissive additional terms are considered "further |
||||
restrictions" within the meaning of section 10. If the Program as you |
||||
received it, or any part of it, contains a notice stating that it is |
||||
governed by this License along with a term that is a further |
||||
restriction, you may remove that term. If a license document contains |
||||
a further restriction but permits relicensing or conveying under this |
||||
License, you may add to a covered work material governed by the terms |
||||
of that license document, provided that the further restriction does |
||||
not survive such relicensing or conveying. |
||||
|
||||
If you add terms to a covered work in accord with this section, you |
||||
must place, in the relevant source files, a statement of the |
||||
additional terms that apply to those files, or a notice indicating |
||||
where to find the applicable terms. |
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the |
||||
form of a separately written license, or stated as exceptions; |
||||
the above requirements apply either way. |
||||
|
||||
8. Termination. |
||||
|
||||
You may not propagate or modify a covered work except as expressly |
||||
provided under this License. Any attempt otherwise to propagate or |
||||
modify it is void, and will automatically terminate your rights under |
||||
this License (including any patent licenses granted under the third |
||||
paragraph of section 11). |
||||
|
||||
However, if you cease all violation of this License, then your |
||||
license from a particular copyright holder is reinstated (a) |
||||
provisionally, unless and until the copyright holder explicitly and |
||||
finally terminates your license, and (b) permanently, if the copyright |
||||
holder fails to notify you of the violation by some reasonable means |
||||
prior to 60 days after the cessation. |
||||
|
||||
Moreover, your license from a particular copyright holder is |
||||
reinstated permanently if the copyright holder notifies you of the |
||||
violation by some reasonable means, this is the first time you have |
||||
received notice of violation of this License (for any work) from that |
||||
copyright holder, and you cure the violation prior to 30 days after |
||||
your receipt of the notice. |
||||
|
||||
Termination of your rights under this section does not terminate the |
||||
licenses of parties who have received copies or rights from you under |
||||
this License. If your rights have been terminated and not permanently |
||||
reinstated, you do not qualify to receive new licenses for the same |
||||
material under section 10. |
||||
|
||||
9. Acceptance Not Required for Having Copies. |
||||
|
||||
You are not required to accept this License in order to receive or |
||||
run a copy of the Program. Ancillary propagation of a covered work |
||||
occurring solely as a consequence of using peer-to-peer transmission |
||||
to receive a copy likewise does not require acceptance. However, |
||||
nothing other than this License grants you permission to propagate or |
||||
modify any covered work. These actions infringe copyright if you do |
||||
not accept this License. Therefore, by modifying or propagating a |
||||
covered work, you indicate your acceptance of this License to do so. |
||||
|
||||
10. Automatic Licensing of Downstream Recipients. |
||||
|
||||
Each time you convey a covered work, the recipient automatically |
||||
receives a license from the original licensors, to run, modify and |
||||
propagate that work, subject to this License. You are not responsible |
||||
for enforcing compliance by third parties with this License. |
||||
|
||||
An "entity transaction" is a transaction transferring control of an |
||||
organization, or substantially all assets of one, or subdividing an |
||||
organization, or merging organizations. If propagation of a covered |
||||
work results from an entity transaction, each party to that |
||||
transaction who receives a copy of the work also receives whatever |
||||
licenses to the work the party's predecessor in interest had or could |
||||
give under the previous paragraph, plus a right to possession of the |
||||
Corresponding Source of the work from the predecessor in interest, if |
||||
the predecessor has it or can get it with reasonable efforts. |
||||
|
||||
You may not impose any further restrictions on the exercise of the |
||||
rights granted or affirmed under this License. For example, you may |
||||
not impose a license fee, royalty, or other charge for exercise of |
||||
rights granted under this License, and you may not initiate litigation |
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that |
||||
any patent claim is infringed by making, using, selling, offering for |
||||
sale, or importing the Program or any portion of it. |
||||
|
||||
11. Patents. |
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this |
||||
License of the Program or a work on which the Program is based. The |
||||
work thus licensed is called the contributor's "contributor version". |
||||
|
||||
A contributor's "essential patent claims" are all patent claims |
||||
owned or controlled by the contributor, whether already acquired or |
||||
hereafter acquired, that would be infringed by some manner, permitted |
||||
by this License, of making, using, or selling its contributor version, |
||||
but do not include claims that would be infringed only as a |
||||
consequence of further modification of the contributor version. For |
||||
purposes of this definition, "control" includes the right to grant |
||||
patent sublicenses in a manner consistent with the requirements of |
||||
this License. |
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free |
||||
patent license under the contributor's essential patent claims, to |
||||
make, use, sell, offer for sale, import and otherwise run, modify and |
||||
propagate the contents of its contributor version. |
||||
|
||||
In the following three paragraphs, a "patent license" is any express |
||||
agreement or commitment, however denominated, not to enforce a patent |
||||
(such as an express permission to practice a patent or covenant not to |
||||
sue for patent infringement). To "grant" such a patent license to a |
||||
party means to make such an agreement or commitment not to enforce a |
||||
patent against the party. |
||||
|
||||
If you convey a covered work, knowingly relying on a patent license, |
||||
and the Corresponding Source of the work is not available for anyone |
||||
to copy, free of charge and under the terms of this License, through a |
||||
publicly available network server or other readily accessible means, |
||||
then you must either (1) cause the Corresponding Source to be so |
||||
available, or (2) arrange to deprive yourself of the benefit of the |
||||
patent license for this particular work, or (3) arrange, in a manner |
||||
consistent with the requirements of this License, to extend the patent |
||||
license to downstream recipients. "Knowingly relying" means you have |
||||
actual knowledge that, but for the patent license, your conveying the |
||||
covered work in a country, or your recipient's use of the covered work |
||||
in a country, would infringe one or more identifiable patents in that |
||||
country that you have reason to believe are valid. |
||||
|
||||
If, pursuant to or in connection with a single transaction or |
||||
arrangement, you convey, or propagate by procuring conveyance of, a |
||||
covered work, and grant a patent license to some of the parties |
||||
receiving the covered work authorizing them to use, propagate, modify |
||||
or convey a specific copy of the covered work, then the patent license |
||||
you grant is automatically extended to all recipients of the covered |
||||
work and works based on it. |
||||
|
||||
A patent license is "discriminatory" if it does not include within |
||||
the scope of its coverage, prohibits the exercise of, or is |
||||
conditioned on the non-exercise of one or more of the rights that are |
||||
specifically granted under this License. You may not convey a covered |
||||
work if you are a party to an arrangement with a third party that is |
||||
in the business of distributing software, under which you make payment |
||||
to the third party based on the extent of your activity of conveying |
||||
the work, and under which the third party grants, to any of the |
||||
parties who would receive the covered work from you, a discriminatory |
||||
patent license (a) in connection with copies of the covered work |
||||
conveyed by you (or copies made from those copies), or (b) primarily |
||||
for and in connection with specific products or compilations that |
||||
contain the covered work, unless you entered into that arrangement, |
||||
or that patent license was granted, prior to 28 March 2007. |
||||
|
||||
Nothing in this License shall be construed as excluding or limiting |
||||
any implied license or other defenses to infringement that may |
||||
otherwise be available to you under applicable patent law. |
||||
|
||||
12. No Surrender of Others' Freedom. |
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or |
||||
otherwise) that contradict the conditions of this License, they do not |
||||
excuse you from the conditions of this License. If you cannot convey a |
||||
covered work so as to satisfy simultaneously your obligations under this |
||||
License and any other pertinent obligations, then as a consequence you may |
||||
not convey it at all. For example, if you agree to terms that obligate you |
||||
to collect a royalty for further conveying from those to whom you convey |
||||
the Program, the only way you could satisfy both those terms and this |
||||
License would be to refrain entirely from conveying the Program. |
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License. |
||||
|
||||
Notwithstanding any other provision of this License, if you modify the |
||||
Program, your modified version must prominently offer all users |
||||
interacting with it remotely through a computer network (if your version |
||||
supports such interaction) an opportunity to receive the Corresponding |
||||
Source of your version by providing access to the Corresponding Source |
||||
from a network server at no charge, through some standard or customary |
||||
means of facilitating copying of software. This Corresponding Source |
||||
shall include the Corresponding Source for any work covered by version 3 |
||||
of the GNU General Public License that is incorporated pursuant to the |
||||
following paragraph. |
||||
|
||||
Notwithstanding any other provision of this License, you have |
||||
permission to link or combine any covered work with a work licensed |
||||
under version 3 of the GNU General Public License into a single |
||||
combined work, and to convey the resulting work. The terms of this |
||||
License will continue to apply to the part which is the covered work, |
||||
but the work with which it is combined will remain governed by version |
||||
3 of the GNU General Public License. |
||||
|
||||
14. Revised Versions of this License. |
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of |
||||
the GNU Affero General Public License from time to time. Such new versions |
||||
will be similar in spirit to the present version, but may differ in detail to |
||||
address new problems or concerns. |
||||
|
||||
Each version is given a distinguishing version number. If the |
||||
Program specifies that a certain numbered version of the GNU Affero General |
||||
Public License "or any later version" applies to it, you have the |
||||
option of following the terms and conditions either of that numbered |
||||
version or of any later version published by the Free Software |
||||
Foundation. If the Program does not specify a version number of the |
||||
GNU Affero General Public License, you may choose any version ever published |
||||
by the Free Software Foundation. |
||||
|
||||
If the Program specifies that a proxy can decide which future |
||||
versions of the GNU Affero General Public License can be used, that proxy's |
||||
public statement of acceptance of a version permanently authorizes you |
||||
to choose that version for the Program. |
||||
|
||||
Later license versions may give you additional or different |
||||
permissions. However, no additional obligations are imposed on any |
||||
author or copyright holder as a result of your choosing to follow a |
||||
later version. |
||||
|
||||
15. Disclaimer of Warranty. |
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY |
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT |
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY |
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, |
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM |
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF |
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION. |
||||
|
||||
16. Limitation of Liability. |
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING |
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS |
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY |
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE |
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF |
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD |
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), |
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF |
||||
SUCH DAMAGES. |
||||
|
||||
17. Interpretation of Sections 15 and 16. |
||||
|
||||
If the disclaimer of warranty and limitation of liability provided |
||||
above cannot be given local legal effect according to their terms, |
||||
reviewing courts shall apply local law that most closely approximates |
||||
an absolute waiver of all civil liability in connection with the |
||||
Program, unless a warranty or assumption of liability accompanies a |
||||
copy of the Program in return for a fee. |
||||
|
||||
END OF TERMS AND CONDITIONS |
||||
|
||||
How to Apply These Terms to Your New Programs |
||||
|
||||
If you develop a new program, and you want it to be of the greatest |
||||
possible use to the public, the best way to achieve this is to make it |
||||
free software which everyone can redistribute and change under these terms. |
||||
|
||||
To do so, attach the following notices to the program. It is safest |
||||
to attach them to the start of each source file to most effectively |
||||
state the exclusion of warranty; and each file should have at least |
||||
the "copyright" line and a pointer to where the full notice is found. |
||||
|
||||
<one line to give the program's name and a brief idea of what it does.> |
||||
Copyright (C) <year> <name of author> |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU Affero General Public License as published |
||||
by the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU Affero General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU Affero General Public License |
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
|
||||
Also add information on how to contact you by electronic and paper mail. |
||||
|
||||
If your software can interact with users remotely through a computer |
||||
network, you should also make sure that it provides a way for users to |
||||
get its source. For example, if your program is a web application, its |
||||
interface could display a "Source" link that leads users to an archive |
||||
of the code. There are many ways you could offer source, and different |
||||
solutions will be better for different programs; see section 13 for the |
||||
specific requirements. |
||||
|
||||
You should also get your employer (if you work as a programmer) or school, |
||||
if any, to sign a "copyright disclaimer" for the program, if necessary. |
||||
For more information on this, and how to apply and follow the GNU AGPL, see |
||||
<http://www.gnu.org/licenses/>. |
@ -1,22 +0,0 @@
|
||||
# Nocodb |
||||
|
||||
## Running locally |
||||
|
||||
Even though this package is a backend project, you can still visit the dashboard as it includes ``nc-lib-gui``. |
||||
|
||||
```sh |
||||
npm install |
||||
npm run watch:run |
||||
# open localhost:8080/dashboard in browser |
||||
``` |
||||
|
||||
As ``nc-lib-gui`` is hosted in the npm registry, for local development, you should run ``nc-gui`` separately. |
||||
|
||||
If you wish to combine the frontend and backend together in your local development environment, you may use ``packages/nc-lib-gui`` as a local dependency by updating the ``packages/nocodb/package.json`` to |
||||
|
||||
```json |
||||
"nc-lib-gui": "file:../nc-lib-gui" |
||||
``` |
||||
|
||||
In this case, whenever there are any changes made in the frontend, you need to run ``npm run build:copy`` under ``packages/nc-gui/``. |
||||
|
@ -1,453 +0,0 @@
|
||||
version: "2.2" |
||||
|
||||
services: |
||||
# db55: |
||||
# image: mysql:5.5 |
||||
# restart: always |
||||
# environment: |
||||
# MYSQL_ROOT_PASSWORD: password |
||||
# ports: |
||||
# - 3355:3306 |
||||
# volumes: |
||||
# - ./mysql-sakila-db:/docker-entrypoint-initdb.d |
||||
# db56: |
||||
# image: mysql:5.6 |
||||
# restart: always |
||||
# environment: |
||||
# MYSQL_ROOT_PASSWORD: password |
||||
# ports: |
||||
# - 3356:3306 |
||||
# volumes: |
||||
# - ./mysql-sakila-db:/docker-entrypoint-initdb.d |
||||
# db57: |
||||
# image: mysql:5.7 |
||||
# restart: always |
||||
# environment: |
||||
# MYSQL_ROOT_PASSWORD: password |
||||
# ports: |
||||
# - 3357:3306 |
||||
# volumes: |
||||
# - ./tests/mysql-sakila-db:/docker-entrypoint-initdb.d |
||||
# healthcheck: |
||||
# test: "/etc/init.d/mysql status" |
||||
# interval: 1s |
||||
# retries: 240 |
||||
db8032: |
||||
image: mysql:8.0.32 |
||||
restart: always |
||||
environment: |
||||
MYSQL_ROOT_PASSWORD: password |
||||
ports: |
||||
- 3380:3306 |
||||
volumes: |
||||
- ./tests/mysql-sakila-db:/docker-entrypoint-initdb.d |
||||
healthcheck: |
||||
test: "/etc/init.d/mysql status" |
||||
interval: 1s |
||||
retries: 240 |
||||
# maria102: |
||||
# image: mariadb:10.2 |
||||
# restart: always |
||||
# environment: |
||||
# MYSQL_ROOT_PASSWORD: password |
||||
# ports: |
||||
# - 3412:3306 |
||||
# volumes: |
||||
# - ./mysql-sakila-db:/docker-entrypoint-initdb.d |
||||
# maria103: |
||||
# image: mariadb:10.3 |
||||
# restart: always |
||||
# environment: |
||||
# MYSQL_ROOT_PASSWORD: password |
||||
# ports: |
||||
# - 3413:3306 |
||||
# volumes: |
||||
# - ./mysql-sakila-db:/docker-entrypoint-initdb.d |
||||
# maria104: |
||||
# image: mariadb:10.4 |
||||
# restart: always |
||||
# environment: |
||||
# MYSQL_ROOT_PASSWORD: password |
||||
# ports: |
||||
# - 3414:3306 |
||||
# volumes: |
||||
# - ./mysql-sakila-db:/docker-entrypoint-initdb.d |
||||
# healthcheck: |
||||
# test: '/usr/bin/mysql --user=root --password=password --database=sakila --execute "SELECT count(*) FROM payment;"' |
||||
# interval: 10s |
||||
# timeout: 3s |
||||
# retries: 10 |
||||
# ERROR 1832 (HY000) at line 23: Cannot change column 'manager_staff_id': used in a foreign key constraint 'fk_store_staff' |
||||
# pg9.4: |
||||
# image: postgres:9.4 |
||||
# restart: always |
||||
# environment: |
||||
# POSTGRES_PASSWORD: password |
||||
# ports: |
||||
# - 5494:5432 |
||||
# volumes: |
||||
# - ./pg-sakila-db:/docker-entrypoint-initdb.d |
||||
# pg9.5: |
||||
# image: postgres:9.5 |
||||
# restart: always |
||||
# environment: |
||||
# POSTGRES_PASSWORD: password |
||||
# ports: |
||||
# - 5495:5432 |
||||
# volumes: |
||||
# - ./pg-sakila-db:/docker-entrypoint-initdb.d |
||||
pg147: |
||||
image: postgres:14.7 |
||||
restart: always |
||||
environment: |
||||
POSTGRES_PASSWORD: password |
||||
ports: |
||||
- 5496:5432 |
||||
volumes: |
||||
- ./tests/pg-sakila-db:/docker-entrypoint-initdb.d |
||||
healthcheck: |
||||
test: [ "CMD-SHELL", "pg_isready -U postgres" ] |
||||
interval: 10s |
||||
timeout: 5s |
||||
retries: 5 |
||||
# pg10: |
||||
# image: postgres:10 |
||||
# restart: always |
||||
# environment: |
||||
# POSTGRES_PASSWORD: password |
||||
# ports: |
||||
# - 5410:5432 |
||||
# volumes: |
||||
# - ./pg-sakila-db:/docker-entrypoint-initdb.d |
||||
# pg11: |
||||
# image: postgres:11 |
||||
# restart: always |
||||
# environment: |
||||
# POSTGRES_PASSWORD: password |
||||
# ports: |
||||
# - 5411:5432 |
||||
# volumes: |
||||
# - ./pg-sakila-db:/docker-entrypoint-initdb.d |
||||
# INFO: not working sqlite |
||||
# sqlite3: |
||||
# image: nouchka/sqlite3:latest |
||||
# restart: always |
||||
# command: sqlite3 /root/db/01-sqlite-sakila-schema.sql |
||||
# # environment: |
||||
# # MYSQL_ROOT_PASSWORD: password |
||||
# stdin_open: true |
||||
# tty: true |
||||
# # ports: |
||||
# # - 3414:3306 |
||||
# volumes: |
||||
# - ./sqlite-sakila-db:/root/db/ |
||||
# # - ./sqlite-sakila-db:/root/db/ |
||||
mssql: |
||||
image: mcr.microsoft.com/mssql/server:2019-CU11-ubuntu-20.04 |
||||
# restart: always |
||||
environment: |
||||
ACCEPT_EULA: Y |
||||
SA_PASSWORD: Password123. |
||||
ports: |
||||
- 1433:1433 |
||||
volumes: |
||||
- ./tests/sql-server-sakila-db:/scripts/ |
||||
command: |
||||
- /bin/bash |
||||
- -c |
||||
- | |
||||
echo "Starting to import - - - - - - - -" |
||||
# Launch MSSQL and send to background |
||||
/opt/mssql/bin/sqlservr & |
||||
# Wait 30 seconds for it to be available |
||||
# (lame, I know, but there's no nc available to start prodding network ports) |
||||
sleep 30 |
||||
/opt/mssql-tools/bin/sqlcmd -U sa -P $$SA_PASSWORD -l 30 -i /scripts/01-sql-server-sakila-schema.sql |
||||
/opt/mssql-tools/bin/sqlcmd -U sa -P $$SA_PASSWORD -l 30 -i /scripts/02-sql-server-sakila-insert-data.sql |
||||
# So that the container doesn't shut down, sleep this thread |
||||
echo "DONE inserting - - - - - - - -" |
||||
python -m SimpleHTTPServer 80 |
||||
sleep infinity |
||||
# healthcheck: |
||||
# test: curl -sS http://localhost || exit 1 |
||||
# interval: 15s |
||||
# retries: 200 |
||||
# oracle18: |
||||
# image: quillbuilduser/oracle-18-xe |
||||
# restart: always |
||||
# environment: |
||||
# - IMPORT_FROM_VOLUME=true |
||||
# ports: |
||||
# - 1521:1521 |
||||
# - 8080:8080 |
||||
# volumes: |
||||
# - ./oracle-sakila-db:/docker-entrypoint-initdb.d |
||||
# oracle12c: |
||||
# image: andersjanmyr/oracle-12c-extended |
||||
# restart: always |
||||
# environment: |
||||
# - IMPORT_FROM_VOLUME=true |
||||
# ports: |
||||
# - 1521:1521 |
||||
# - 8080:8080 |
||||
# volumes: |
||||
# - ./oracle-sakila-db:/docker-entrypoint-initdb.d |
||||
# oracle11g: |
||||
# image: oracleinanutshell/oracle-xe-11g |
||||
# restart: always |
||||
# environment: |
||||
# - ORACLE_DISABLE_ASYNCH_IO=true |
||||
# - ORACLE_ALLOW_REMOTE=true |
||||
# ports: |
||||
# - 1521:1521 |
||||
# volumes: |
||||
# - ./oracle-sakila-db:/docker-entrypoint-initdb.d |
||||
|
||||
# knexPG: |
||||
# image: node |
||||
# depends_on: |
||||
# - pg10 |
||||
# volumes: |
||||
# - ./:/home/app |
||||
# environment: |
||||
# DOCKER_DB_HOST: pg10 |
||||
# DOCKER_DB_PORT: 5432 |
||||
# DOCKERIZE_VERSION: v0.6.1 |
||||
# command: |
||||
# - /bin/bash |
||||
# - -c |
||||
# - | |
||||
# echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" |
||||
# chmod +x /home/app/dockerize.sh && /home/app/dockerize.sh && cd /home/app/ && npm i && npm test |
||||
# knexOracle11g: |
||||
# image: node |
||||
# depends_on: |
||||
# - oracle11g |
||||
# volumes: |
||||
# - ./:/home/app |
||||
# - ./instantclient_18_5:/opt/oracle/instantclient_18_5 |
||||
# environment: |
||||
# DOCKER_DB_HOST: oracle11g |
||||
# DOCKER_DB_PORT: 1521 |
||||
# DOCKERIZE_VERSION: v0.6.1 |
||||
# command: |
||||
# - /bin/bash |
||||
# - -c |
||||
# - | |
||||
# echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" |
||||
# apt update -y |
||||
# apt install libaio1 libaio-dev -y |
||||
# echo /opt/oracle/instantclient_18_5 > /etc/ld.so.conf.d/oracle-instantclient.conf |
||||
# ldconfig |
||||
# mkdir -p /opt/oracle/instantclient_18_5/network/admin |
||||
# chmod +x /home/app/dockerize-oracle.sh && /home/app/dockerize-oracle.sh && cd /home/app/ && npm i && npm test |
||||
# knexOracle12c: |
||||
# image: node |
||||
# depends_on: |
||||
# - oracle12c |
||||
# volumes: |
||||
# - ./:/home/app |
||||
# - ./instantclient_18_5:/opt/oracle/instantclient_18_5 |
||||
# environment: |
||||
# DOCKER_DB_HOST: oracle12c |
||||
# DOCKER_DB_PORT: 1521 |
||||
# DOCKERIZE_VERSION: v0.6.1 |
||||
# command: |
||||
# - /bin/bash |
||||
# - -c |
||||
# - | |
||||
# echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" |
||||
# apt update -y |
||||
# apt install libaio1 libaio-dev -y |
||||
# echo /opt/oracle/instantclient_18_5 > /etc/ld.so.conf.d/oracle-instantclient.conf |
||||
# ldconfig |
||||
# mkdir -p /opt/oracle/instantclient_18_5/network/admin |
||||
# chmod +x /home/app/dockerize-oracle.sh && /home/app/dockerize-oracle.sh && cd /home/app/ && npm i && npm test |
||||
# knexOracle18: |
||||
# image: node |
||||
# depends_on: |
||||
# - oracle18 |
||||
# volumes: |
||||
# - ./:/home/app |
||||
# - ./instantclient_18_5:/opt/oracle/instantclient_18_5 |
||||
# environment: |
||||
# DOCKER_DB_HOST: oracle18 |
||||
# DOCKER_DB_PORT: 1521 |
||||
# DOCKERIZE_VERSION: v0.6.1 |
||||
# command: |
||||
# - /bin/bash |
||||
# - -c |
||||
# - | |
||||
# echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" |
||||
# apt update -y |
||||
# apt install libaio1 libaio-dev -y |
||||
# echo /opt/oracle/instantclient_18_5 > /etc/ld.so.conf.d/oracle-instantclient.conf |
||||
# ldconfig |
||||
# mkdir -p /opt/oracle/instantclient_18_5/network/admin |
||||
# chmod +x /home/app/dockerize-oracle.sh && /home/app/dockerize-oracle.sh && cd /home/app/ && npm i && npm test |
||||
# knexMSSQL: |
||||
# image: node |
||||
# depends_on: |
||||
# - mssql |
||||
# volumes: |
||||
# - ./:/home/app |
||||
# environment: |
||||
# DOCKER_DB_HOST: mssql |
||||
# DOCKER_DB_PORT: 1433 |
||||
# DOCKERIZE_VERSION: v0.6.1 |
||||
# command: |
||||
# - /bin/bash |
||||
# - -c |
||||
# - | |
||||
# echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" |
||||
# chmod +x /home/app/dockerize-mssql.sh && /home/app/dockerize-mssql.sh && cd /home/app/ && npm i && npm test |
||||
# knexMySQL: |
||||
# image: node |
||||
# depends_on: |
||||
# - db57 |
||||
# volumes: |
||||
# - ./:/home/app |
||||
# environment: |
||||
# DOCKER_DB_HOST: db57 |
||||
# DOCKER_DB_PORT: 3306 |
||||
# DOCKERIZE_VERSION: v0.6.1 |
||||
# command: |
||||
# - /bin/bash |
||||
# - -c |
||||
# - | |
||||
# echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" |
||||
# chmod +x /home/app/dockerize.sh && /home/app/dockerize.sh && cd /home/app/ && npm i && npm test |
||||
xc-test-mysql: |
||||
image: node:12.22.1-slim |
||||
depends_on: |
||||
db57: |
||||
condition: service_healthy |
||||
volumes: |
||||
- ./:/home/app |
||||
environment: |
||||
NODE_ENV: test |
||||
DATABASE_URL: mysql://root:password@db57:3306/sakila |
||||
command: |
||||
- /bin/bash |
||||
- -c |
||||
- | |
||||
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" |
||||
cd /home/app/ && npm i && npm run test:rest |
||||
|
||||
xc-test-pg: |
||||
image: node:12.22.1-slim |
||||
depends_on: |
||||
pg147: |
||||
condition: service_healthy |
||||
volumes: |
||||
- ./:/home/app |
||||
environment: |
||||
NODE_ENV: test |
||||
DATABASE_URL: postgres://postgres:password@pg147:5432/postgres |
||||
command: |
||||
- /bin/bash |
||||
- -c |
||||
- | |
||||
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" |
||||
cd /home/app/ && npm i && npm run test:rest |
||||
|
||||
xc-test-mssql: |
||||
image: node:12.22.1-slim |
||||
depends_on: |
||||
- mssql |
||||
volumes: |
||||
- ./:/home/app |
||||
environment: |
||||
NODE_ENV: test |
||||
DATABASE_URL: mssql://sa:Password123.@mssql:1433/sakila |
||||
DOCKER_DB_HOST: mssql |
||||
DOCKERIZE_VERSION: v0.6.1 |
||||
command: |
||||
- /bin/bash |
||||
- -c |
||||
- | |
||||
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"# |
||||
chmod +x /home/app/tests/dockerize-mssql.sh && /home/app/tests/dockerize-mssql.sh && cd /home/app/ && npm i && npm run test:rest |
||||
|
||||
xc-test-sqlite: |
||||
image: node:12.22.1-slim |
||||
volumes: |
||||
- ./:/home/app |
||||
environment: |
||||
NODE_ENV: test |
||||
DATABASE_URL: sqlite:////home/sakila.db |
||||
command: |
||||
- /bin/bash |
||||
- -c |
||||
- | |
||||
cp /home/app/tests/sqlite-dump/sakila.db /home/sakila.db |
||||
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"# |
||||
cd /home/app/ && npm i && npm run test:rest |
||||
|
||||
|
||||
xc-test-gql-mysql: |
||||
image: node:12.22.1-slim |
||||
depends_on: |
||||
db57: |
||||
condition: service_healthy |
||||
volumes: |
||||
- ./:/home/app |
||||
environment: |
||||
NODE_ENV: test |
||||
DATABASE_URL: mysql://root:password@db57:3306/sakila |
||||
command: |
||||
- /bin/bash |
||||
- -c |
||||
- | |
||||
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" |
||||
cd /home/app/ && npm i && npm run test:graphql |
||||
|
||||
xc-test-gql-pg: |
||||
image: node:12.22.1-slim |
||||
depends_on: |
||||
pg147: |
||||
condition: service_healthy |
||||
volumes: |
||||
- ./:/home/app |
||||
environment: |
||||
NODE_ENV: test |
||||
DATABASE_URL: postgres://postgres:password@pg147:5432/postgres |
||||
command: |
||||
- /bin/bash |
||||
- -c |
||||
- | |
||||
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" |
||||
cd /home/app/ && npm i && npm run test:graphql |
||||
|
||||
xc-test-gql-mssql: |
||||
image: node:12.22.1-slim |
||||
depends_on: |
||||
- mssql |
||||
volumes: |
||||
- ./:/home/app |
||||
environment: |
||||
NODE_ENV: test |
||||
DATABASE_URL: mssql://sa:Password123.@mssql:1433/sakila |
||||
DOCKER_DB_HOST: mssql |
||||
DOCKERIZE_VERSION: v0.6.1 |
||||
command: |
||||
- /bin/bash |
||||
- -c |
||||
- | |
||||
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"# |
||||
chmod +x /home/app/tests/dockerize-mssql.sh && /home/app/tests/dockerize-mssql.sh && cd /home/app/ && npm i && npm run test:graphql |
||||
|
||||
xc-test-gql-sqlite: |
||||
image: node:12.22.1-slim |
||||
volumes: |
||||
- ./:/home/app |
||||
environment: |
||||
NODE_ENV: test |
||||
DATABASE_URL: sqlite:////home/sakila.db |
||||
command: |
||||
- /bin/bash |
||||
- -c |
||||
- | |
||||
cp /home/app/tests/sqlite-dump/sakila.db /home/sakila.db |
||||
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"# |
||||
cd /home/app/ && npm i && npm run test:graphql |
@ -1,16 +0,0 @@
|
||||
const Noco = require("../build/main/lib/Noco").default; |
||||
const express = require('express'); |
||||
const cors = require('cors'); |
||||
const server = express(); |
||||
server.enable('trust proxy'); |
||||
server.use(cors()); |
||||
|
||||
|
||||
server.set('view engine', 'ejs'); |
||||
|
||||
|
||||
(async () => { |
||||
const httpServer = server.listen(process.env.PORT || 8080, async () => { |
||||
server.use(await Noco.init({}, httpServer, server)); |
||||
}) |
||||
})().catch(e => console.log(e)) |
@ -1,32 +0,0 @@
|
||||
#!/bin/sh |
||||
|
||||
FILE="/usr/src/app/package.json" |
||||
#sleep 5 |
||||
|
||||
if [ ! -z "${NC_TOOL_DIR}" ]; then |
||||
mkdir -p $NC_TOOL_DIR |
||||
fi |
||||
|
||||
if [[ ! -z "${AWS_ACCESS_KEY_ID}" && ! -z "${AWS_SECRET_ACCESS_KEY}" && ! -z "${AWS_BUCKET}" && ! -z "${AWS_BUCKET_PATH}" ]]; then |
||||
|
||||
if [ -f "${NC_TOOL_DIR}noco.db" ] |
||||
then |
||||
rm "${NC_TOOL_DIR}noco.db" |
||||
rm "${NC_TOOL_DIR}noco.db-shm" |
||||
rm "${NC_TOOL_DIR}noco.db-wal" |
||||
fi |
||||
|
||||
/usr/src/appEntry/litestream restore -o "${NC_TOOL_DIR}noco.db" s3://$AWS_BUCKET/$AWS_BUCKET_PATH; |
||||
if [ ! -f "${NC_TOOL_DIR}noco.db" ] |
||||
then |
||||
touch "${NC_TOOL_DIR}noco.db" |
||||
fi |
||||
/usr/src/appEntry/litestream replicate "${NC_TOOL_DIR}noco.db" s3://$AWS_BUCKET/$AWS_BUCKET_PATH & |
||||
fi |
||||
|
||||
if [ ! -f "$FILE" ] |
||||
then |
||||
tar -xzf /usr/src/appEntry/app.tar.gz -C /usr/src/app/ |
||||
fi |
||||
|
||||
node docker/main.js |
@ -1,14 +0,0 @@
|
||||
#!/bin/sh |
||||
|
||||
FILE="/usr/src/app/package.json" |
||||
|
||||
if [ ! -z "${NC_TOOL_DIR}" ]; then |
||||
mkdir -p $NC_TOOL_DIR |
||||
fi |
||||
|
||||
if [ ! -f "$FILE" ] |
||||
then |
||||
tar -xzf /usr/src/appEntry/app.tar.gz -C /usr/src/app/ |
||||
fi |
||||
|
||||
node docker/index.js |
@ -1,10 +0,0 @@
|
||||
#!/bin/sh |
||||
|
||||
FILE="/usr/src/app/package.json" |
||||
|
||||
if [ ! -f "$FILE" ] |
||||
then |
||||
tar -xzf /usr/src/appEntry/app.tar.gz -C /usr/src/app/ |
||||
fi |
||||
|
||||
node docker/main.js |
@ -1,38 +0,0 @@
|
||||
const nodeExternals = require('webpack-node-externals'); |
||||
// const CopyPlugin = require('copy-webpack-plugin');
|
||||
const webpack = require('webpack') |
||||
const TerserPlugin = require('terser-webpack-plugin'); |
||||
// const JavaScriptObfuscator = require('webpack-obfuscator');
|
||||
|
||||
module.exports = { |
||||
entry: './docker/index.js', |
||||
module: { |
||||
rules: [ |
||||
], |
||||
}, |
||||
resolve: { |
||||
extensions: ['.js'], |
||||
}, |
||||
output: { |
||||
path: require('path').resolve("./docker"), |
||||
filename: "main.js", |
||||
library: 'libs', |
||||
libraryTarget: 'umd', |
||||
globalObject: "typeof self !== 'undefined' ? self : this" |
||||
}, |
||||
optimization: { |
||||
minimize: true, //Update this to true or false
|
||||
minimizer: [new TerserPlugin()], |
||||
nodeEnv:false |
||||
}, |
||||
externals: [nodeExternals()], |
||||
plugins: [ |
||||
new webpack.EnvironmentPlugin([ |
||||
'EE' |
||||
]), |
||||
], |
||||
target: 'node', |
||||
node: { |
||||
__dirname: false, |
||||
}, |
||||
}; |
@ -1,100 +0,0 @@
|
||||
FROM golang:alpine3.14 as lt |
||||
|
||||
WORKDIR /usr/src/ |
||||
|
||||
RUN apk add --no-cache git make musl-dev gcc |
||||
|
||||
# build litestream |
||||
RUN git clone https://github.com/benbjohnson/litestream.git litestream |
||||
RUN cd litestream ; go install ./cmd/litestream |
||||
|
||||
RUN cp $GOPATH/bin/litestream /usr/src/lt |
||||
|
||||
|
||||
|
||||
FROM node:12 as builder |
||||
WORKDIR /usr/src/app |
||||
|
||||
# Copy application dependency manifests to the container image. |
||||
# A wildcard is used to ensure both package.json AND package-lock.json are copied. |
||||
# Copying this separately prevents re-running npm ci on every code change. |
||||
COPY ./package*.json ./ |
||||
COPY ./docker/main.js ./docker/main.js |
||||
#COPY ./docker/start.sh /usr/src/appEntry/start.sh |
||||
COPY ./docker/start-litestream.sh /usr/src/appEntry/start.sh |
||||
# install production dependencies, |
||||
# reduce node_module size with modclean & removing sqlite deps, |
||||
# package built code into app.tar.gz & add execute permission to start.sh |
||||
RUN npm ci --production --quiet |
||||
RUN npx modclean --patterns="default:*" --ignore="nc-lib-gui/**,dayjs/**,express-status-monitor/**" --run |
||||
RUN rm -rf ./node_modules/sqlite3/deps |
||||
RUN tar -czf ../appEntry/app.tar.gz ./* |
||||
RUN chmod +x /usr/src/appEntry/start.sh |
||||
|
||||
|
||||
|
||||
|
||||
FROM alpine:3.14 |
||||
|
||||
#ENV AWS_ACCESS_KEY_ID= |
||||
#ENV AWS_SECRET_ACCESS_KEY= |
||||
#ENV AWS_BUCKET= |
||||
|
||||
|
||||
|
||||
#WORKDIR /usr/src/ |
||||
# |
||||
## Install go lang |
||||
#RUN apk add --no-cache git make musl-dev go |
||||
# |
||||
## Configure Go |
||||
#ENV GOROOT /usr/lib/go |
||||
#ENV GOPATH /go |
||||
#ENV PATH /go/bin:$PATH |
||||
# |
||||
#RUN mkdir -p ${GOPATH}/src ${GOPATH}/bin |
||||
# |
||||
## build litestream |
||||
# |
||||
#RUN git clone https://github.com/benbjohnson/litestream.git litestream |
||||
#RUN cd litestream ; go install ./cmd/litestream |
||||
|
||||
|
||||
# Bug fix for segfault ( Convert PT_GNU_STACK program header into PT_PAX_FLAGS ) |
||||
#RUN apk --update --no-cache add paxctl \ |
||||
# && paxctl -cm $(which node) |
||||
|
||||
WORKDIR /usr/src/app |
||||
|
||||
ENV NC_DOCKER 0.6 |
||||
ENV PORT 8080 |
||||
ENV NC_TOOL_DIR=/usr/app/data/ |
||||
|
||||
|
||||
# Copy application dependency manifests to the container image. |
||||
# A wildcard is used to ensure both package.json AND package-lock.json are copied. |
||||
# Copying this separately prevents re-running npm install on every code change. |
||||
#COPY ./build/ ./build/ |
||||
#COPY ./docker/main.js ./docker/main.js |
||||
#COPY ./package.json ./ |
||||
|
||||
RUN apk --update --no-cache add \ |
||||
nodejs \ |
||||
tar |
||||
|
||||
# Copy litestream binary build |
||||
COPY --from=lt /usr/src/lt /usr/src/appEntry/litestream |
||||
# Copy packaged production code & main entry file |
||||
COPY --from=builder /usr/src/appEntry/ /usr/src/appEntry/ |
||||
|
||||
|
||||
# Run the web service on container startup. |
||||
#CMD [ "node", "docker/index.js" ] |
||||
ENTRYPOINT ["sh", "/usr/src/appEntry/start.sh"] |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,191 +0,0 @@
|
||||
{ |
||||
"name": "nocodb", |
||||
"version": "0.106.1", |
||||
"description": "NocoDB Backend", |
||||
"main": "dist/bundle.js", |
||||
"author": { |
||||
"name": "NocoDB Inc", |
||||
"url": "https://nocodb.com/" |
||||
}, |
||||
"homepage": "https://github.com/nocodb/nocodb", |
||||
"repository": { |
||||
"type": "git", |
||||
"url": "https://github.com/nocodb/nocodb.git" |
||||
}, |
||||
"bugs": { |
||||
"url": "https://github.com/nocodb/nocodb/issues" |
||||
}, |
||||
"license": "AGPL-3.0-or-later", |
||||
"keywords": [], |
||||
"scripts": { |
||||
"build": "run-s clean && run-p build:*", |
||||
"build:obfuscate": "EE=true webpack --config webpack.config.js", |
||||
"build:main": "tsc -p tsconfig.json", |
||||
"build:module": "tsc -p tsconfig.module.json", |
||||
"obfuscate:build:publish": "npm run build:obfuscate && npm publish .", |
||||
"fix": "run-s fix:*", |
||||
"fix:prettier": "prettier \"src/**/*.ts\" --write", |
||||
"lint": "eslint src --ext .ts --fix", |
||||
"test": "cross-env TS_NODE_PROJECT=tsconfig.json mocha -r ts-node/register src/__tests__/**/*.test.ts --recursive", |
||||
"unit-test": "cross-env TS_NODE_PROJECT=tsconfig.json mocha --require ts-node/register 'src/__tests__/unit/**/*.test.ts' --recursive --check-leaks --exit", |
||||
"local:test:unit": "cross-env TS_NODE_PROJECT=./tests/unit/tsconfig.json mocha -r ts-node/register tests/unit/index.test.ts --recursive --timeout 300000 --exit --delay", |
||||
"test:unit": "cross-env TS_NODE_PROJECT=./tests/unit/tsconfig.json mocha -r ts-node/register tests/unit/index.test.ts --recursive --timeout 300000 --exit --delay", |
||||
"test:unit:pg": "cp tests/unit/.pg.env tests/unit/.env; cross-env TS_NODE_PROJECT=./tests/unit/tsconfig.json mocha -r ts-node/register tests/unit/index.test.ts --recursive --timeout 300000 --exit --delay", |
||||
"test:lint": "tslint --project . && prettier \"src/**/*.ts\" --list-different", |
||||
"watch": "run-s clean build:main && run-p \"build:main -- -w\" \"test:unit -- --watch\"", |
||||
"clean": "trash build src/test", |
||||
"docker:build": "EE=\"true-xc-test\" webpack --config docker/webpack.config.js", |
||||
"watch:build": "nodemon -e ts,js -w ./src -x npm run build", |
||||
"watch:run": "cross-env NC_DISABLE_TELE1=true EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/docker --log-error --project tsconfig.json\"", |
||||
"watch:run:playwright": "rm -f ./test_noco.db; cross-env DATABASE_URL=sqlite:./test_noco.db PLAYWRIGHT_TEST=true NC_DISABLE_TELE=true EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/testDocker --log-error --project tsconfig.json\"", |
||||
"watch:run:playwright:quick": "rm -f ./test_noco.db; cp ../../tests/playwright/fixtures/noco_0_91_7.db ./test_noco.db; cross-env DATABASE_URL=sqlite:./test_noco.db NC_DISABLE_TELE=true EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/docker --log-error --project tsconfig.json\"", |
||||
"watch:run:playwright:pg:cyquick": "rm -f ./test_noco.db; cp ../../tests/playwright/fixtures/noco_0_91_7.db ./test_noco.db; cross-env NC_DISABLE_TELE=true EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/dockerRunPG_CyQuick.ts --log-error --project tsconfig.json\"", |
||||
"watch:run:mysql": "cross-env NC_DISABLE_TELE=true EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/dockerRunMysql --log-error --project tsconfig.json\"", |
||||
"watch:run:pg": "cross-env NC_DISABLE_TELE=true EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/dockerRunPG --log-error --project tsconfig.json\"", |
||||
"run": "ts-node src/run/docker", |
||||
"watch:try": "nodemon -e ts,js -w ./src -x \"ts-node src/run/try --log-error --project tsconfig.json\"", |
||||
"example:docker": "ts-node src/run/docker.ts", |
||||
"redoc": "nodemon -e ts,json -w ./src/schema -x \"ts-node src/run/redoc --log-error --project tsconfig.json\"" |
||||
}, |
||||
"engines": { |
||||
"node": ">=8.9" |
||||
}, |
||||
"dependencies": { |
||||
"@google-cloud/storage": "^5.7.2", |
||||
"@graphql-tools/merge": "^6.0.12", |
||||
"@sentry/node": "^6.3.5", |
||||
"airtable": "^0.11.3", |
||||
"ajv": "^8.12.0", |
||||
"ajv-formats": "^2.1.1", |
||||
"archiver": "^5.0.2", |
||||
"auto-bind": "^4.0.0", |
||||
"aws-sdk": "^2.829.0", |
||||
"axios": "^0.21.1", |
||||
"bcryptjs": "^2.4.3", |
||||
"body-parser": "^1.19.0", |
||||
"boxen": "^5.0.0", |
||||
"bullmq": "^1.81.1", |
||||
"clear": "^0.1.0", |
||||
"colors": "1.4.0", |
||||
"compare-versions": "^5.0.1", |
||||
"cookie-parser": "^1.4.5", |
||||
"cors": "^2.8.5", |
||||
"cron": "^1.8.2", |
||||
"crypto-js": "^4.0.0", |
||||
"dataloader": "^2.0.0", |
||||
"dayjs": "^1.8.34", |
||||
"debug": "^4.2.0", |
||||
"dotenv": "^8.2.0", |
||||
"ejs": "^3.1.3", |
||||
"emittery": "^0.7.1", |
||||
"express": "^4.17.1", |
||||
"express-graphql": "^0.11.0", |
||||
"extract-zip": "^2.0.1", |
||||
"fast-levenshtein": "^2.0.6", |
||||
"fs-extra": "^9.0.1", |
||||
"glob": "^7.1.6", |
||||
"graphql": "^15.3.0", |
||||
"graphql-depth-limit": "^1.1.0", |
||||
"graphql-type-json": "^0.3.2", |
||||
"handlebars": "^4.7.6", |
||||
"import-fresh": "^3.2.1", |
||||
"inflection": "^1.12.0", |
||||
"ioredis": "^4.28.5", |
||||
"ioredis-mock": "^7.1.0", |
||||
"is-docker": "^2.2.1", |
||||
"isomorphic-dompurify": "^0.19.0", |
||||
"jsep": "^1.3.6", |
||||
"jsonfile": "^6.1.0", |
||||
"jsonwebtoken": "^9.0.0", |
||||
"knex": "2.2.0", |
||||
"lodash": "^4.17.19", |
||||
"lru-cache": "^6.0.0", |
||||
"mailersend": "^1.1.0", |
||||
"minio": "^7.0.18", |
||||
"mkdirp": "^2.1.3", |
||||
"morgan": "^1.10.0", |
||||
"mssql": "^6.2.0", |
||||
"multer": "^1.4.2", |
||||
"mysql2": "^2.2.5", |
||||
"nanoid": "^3.1.20", |
||||
"nc-help": "0.2.87", |
||||
"nc-lib-gui": "0.106.1", |
||||
"nc-plugin": "file:../nc-plugin", |
||||
"ncp": "^2.0.0", |
||||
"nocodb-sdk": "file:../nocodb-sdk", |
||||
"nodemailer": "^6.4.10", |
||||
"object-hash": "^3.0.0", |
||||
"os-locale": "^5.0.0", |
||||
"papaparse": "^5.3.1", |
||||
"parse-database-url": "^0.3.0", |
||||
"passport": "^0.4.1", |
||||
"passport-auth-token": "^1.0.1", |
||||
"passport-custom": "^1.1.1", |
||||
"passport-github": "^1.1.0", |
||||
"passport-google-oauth20": "^2.0.0", |
||||
"passport-jwt": "^4.0.0", |
||||
"passport-local": "^1.0.0", |
||||
"pg": "^8.3.0", |
||||
"request": "^2.88.2", |
||||
"request-ip": "^2.1.3", |
||||
"rmdir": "^1.2.0", |
||||
"slash": "^3.0.0", |
||||
"socket.io": "^4.4.1", |
||||
"sqlite3": "5.1.2", |
||||
"tedious": "^15.0.0", |
||||
"tinycolor2": "^1.4.2", |
||||
"twilio": "^3.55.1", |
||||
"unique-names-generator": "^4.3.1", |
||||
"uuid": "^8.2.0", |
||||
"validator": "^13.1.1", |
||||
"xc-core-ts": "^0.1.0", |
||||
"xlsx": "^0.18.5" |
||||
}, |
||||
"devDependencies": { |
||||
"@types/chai": "^4.2.12", |
||||
"@types/express": "^4.17.7", |
||||
"@types/inflection": "^1.5.28", |
||||
"@types/lru-cache": "^5.1.0", |
||||
"@types/minio": "^7.0.7", |
||||
"@types/mkdirp": "^1.0.2", |
||||
"@types/mocha": "^8.0.1", |
||||
"@types/node": "^18.0.0", |
||||
"@types/nodemailer": "^6.4.0", |
||||
"@types/supertest": "^2.0.10", |
||||
"@types/tinycolor2": "^1.4.3", |
||||
"@typescript-eslint/eslint-plugin": "^4.0.1", |
||||
"@typescript-eslint/parser": "^4.0.1", |
||||
"chai": "^4.2.0", |
||||
"copy-webpack-plugin": "^6.4.0", |
||||
"cross-env": "^7.0.3", |
||||
"cz-conventional-changelog": "^2.1.0", |
||||
"eslint": "^7.8.0", |
||||
"eslint-config-prettier": "^6.15.0", |
||||
"eslint-plugin-eslint-comments": "^3.2.0", |
||||
"eslint-plugin-functional": "^3.0.2", |
||||
"eslint-plugin-import": "^2.25.2", |
||||
"eslint-plugin-prettier": "^4.0.0", |
||||
"mocha": "^10.1.0", |
||||
"nodemon": "^2.0.7", |
||||
"npm-run-all": "^4.1.5", |
||||
"prettier": "^2.7.1", |
||||
"supertest": "^4.0.2", |
||||
"terser-webpack-plugin": "^4.1.0", |
||||
"trash-cli": "^3.0.0", |
||||
"ts-loader": "^8.0.5", |
||||
"ts-node": "^8.10.2", |
||||
"typescript": "^4.0.3", |
||||
"webpack": "^4.44.1", |
||||
"webpack-cli": "^3.3.12", |
||||
"webpack-node-externals": "^2.5.2", |
||||
"webpack-obfuscator": "^1.12.0" |
||||
}, |
||||
"config": { |
||||
"commitizen": { |
||||
"path": "cz-conventional-changelog" |
||||
} |
||||
}, |
||||
"prettier": { |
||||
"singleQuote": true |
||||
} |
||||
} |
@ -1,15 +0,0 @@
|
||||
export default interface IEmailAdapter { |
||||
init(): Promise<any>; |
||||
mailSend(mail: XcEmail): Promise<any>; |
||||
test(email): Promise<boolean>; |
||||
} |
||||
|
||||
interface XcEmail { |
||||
// from?:string;
|
||||
to: string; |
||||
subject: string; |
||||
html?: string; |
||||
text?: string; |
||||
} |
||||
|
||||
export { XcEmail }; |
@ -1,17 +0,0 @@
|
||||
export default interface IStorageAdapter { |
||||
init(): Promise<any>; |
||||
fileCreate(destPath: string, file: XcFile): Promise<any>; |
||||
fileDelete(filePath: string): Promise<any>; |
||||
fileRead(filePath: string): Promise<any>; |
||||
test(): Promise<boolean>; |
||||
} |
||||
|
||||
interface XcFile { |
||||
originalname: string; |
||||
path: string; |
||||
mimetype: string; |
||||
size: number | string; |
||||
buffer?: any; |
||||
} |
||||
|
||||
export { XcFile }; |
@ -1,8 +0,0 @@
|
||||
export default interface XcDynamicChanges { |
||||
onTableCreate(tn: string): Promise<void>; |
||||
onTableUpdate(changeObj: any): Promise<void>; |
||||
onTableDelete(tn: string): Promise<void>; |
||||
onTableRename(oldTableName: string, newTableName: string): Promise<void>; |
||||
onHandlerCodeUpdate(tn: string): Promise<void>; |
||||
onMetaUpdate(tn: string): Promise<void>; |
||||
} |
@ -1,2 +0,0 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export default interface XcMetaMgr {} |
@ -1,293 +0,0 @@
|
||||
import type { Handler } from 'express'; |
||||
import type * as e from 'express'; |
||||
import type { Knex } from 'knex'; |
||||
|
||||
export interface Route { |
||||
path: string; |
||||
type: RouteType | string; |
||||
handler: Array<Handler | string>; |
||||
acl: { |
||||
[key: string]: boolean; |
||||
}; |
||||
disabled?: boolean; |
||||
functions?: string[]; |
||||
} |
||||
|
||||
export enum RouteType { |
||||
GET = 'get', |
||||
POST = 'post', |
||||
PUT = 'put', |
||||
PATCH = 'patch', |
||||
DELETE = 'delete', |
||||
HEAD = 'head', |
||||
OPTIONS = 'options', |
||||
} |
||||
|
||||
type InflectionTypes = |
||||
| 'pluralize' |
||||
| 'singularize' |
||||
| 'inflect' |
||||
| 'camelize' |
||||
| 'underscore' |
||||
| 'humanize' |
||||
| 'capitalize' |
||||
| 'dasherize' |
||||
| 'titleize' |
||||
| 'demodulize' |
||||
| 'tableize' |
||||
| 'classify' |
||||
| 'foreign_key' |
||||
| 'ordinalize' |
||||
| 'transform' |
||||
| 'none'; |
||||
|
||||
export interface DbConfig extends Knex.Config { |
||||
client: string; |
||||
|
||||
connection: Knex.StaticConnectionConfig | Knex.Config | any; |
||||
|
||||
meta: { |
||||
dbAlias: string; |
||||
|
||||
metaTables?: 'db' | 'file'; |
||||
tn?: string; |
||||
models?: { |
||||
disabled: boolean; |
||||
}; |
||||
|
||||
routes?: { |
||||
disabled: boolean; |
||||
}; |
||||
|
||||
hooks?: { |
||||
disabled: boolean; |
||||
}; |
||||
|
||||
migrations?: { |
||||
disabled: boolean; |
||||
name: 'nc_evolutions'; |
||||
}; |
||||
|
||||
api: { |
||||
type: 'rest' | 'graphql' | 'grpc'; |
||||
prefix: string; |
||||
swagger?: boolean; |
||||
graphiql?: boolean; |
||||
graphqlDepthLimit?: number; |
||||
}; |
||||
|
||||
allSchemas?: boolean; |
||||
|
||||
ignoreTables?: string[]; |
||||
readonly?: boolean; |
||||
|
||||
query?: { |
||||
print?: boolean; |
||||
explain?: boolean; |
||||
measure?: boolean; |
||||
}; |
||||
reset?: boolean; |
||||
dbtype?: 'vitess' | string; |
||||
pluralize?: boolean; |
||||
inflection?: { |
||||
tn?: InflectionTypes; |
||||
cn?: InflectionTypes; |
||||
}; |
||||
}; |
||||
} |
||||
|
||||
// Refer : https://www.npmjs.com/package/jsonwebtoken
|
||||
interface JwtOptions { |
||||
algorithm?: string; |
||||
expiresIn?: string | number; |
||||
notBefore?: string | number; |
||||
audience?: string; |
||||
issuer?: string; |
||||
jwtid?: any; |
||||
subject?: string; |
||||
noTimestamp?: any; |
||||
header?: any; |
||||
keyid?: any; |
||||
} |
||||
|
||||
export interface AuthConfig { |
||||
jwt?: { |
||||
secret: string; |
||||
[key: string]: any; |
||||
dbAlias?: string; |
||||
options?: JwtOptions; |
||||
}; |
||||
masterKey?: { |
||||
secret: string; |
||||
}; |
||||
middleware?: { |
||||
url: string; |
||||
}; |
||||
disabled?: boolean; |
||||
} |
||||
|
||||
export interface MiddlewareConfig { |
||||
handler?: (...args: any[]) => any; |
||||
} |
||||
|
||||
export interface ACLConfig { |
||||
roles?: string[]; |
||||
defaultRoles?: string[]; |
||||
} |
||||
|
||||
export interface MailerConfig { |
||||
[key: string]: any; |
||||
} |
||||
|
||||
export interface ServerlessConfig { |
||||
aws?: { |
||||
lambda: boolean; |
||||
}; |
||||
gcp?: { |
||||
cloudFunction: boolean; |
||||
}; |
||||
azure?: { |
||||
cloudFunctionApp: boolean; |
||||
}; |
||||
zeit?: { |
||||
now: boolean; |
||||
}; |
||||
alibaba?: { |
||||
functionCompute: boolean; |
||||
}; |
||||
serverlessFramework?: { |
||||
http: boolean; |
||||
}; |
||||
} |
||||
|
||||
export interface NcGui { |
||||
path?: string; |
||||
disabled?: boolean; |
||||
favicon?: string; |
||||
logo?: string; |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
export interface NcConfig { |
||||
title?: string; |
||||
version?: string; |
||||
|
||||
envs: { |
||||
[key: string]: { |
||||
db: DbConfig[]; |
||||
api?: any; |
||||
publicUrl?: string; |
||||
}; |
||||
}; |
||||
|
||||
// dbs: DbConfig[];
|
||||
|
||||
auth?: AuthConfig; |
||||
middleware?: MiddlewareConfig[]; |
||||
acl?: ACLConfig; |
||||
port?: number; |
||||
host?: string; |
||||
cluster?: number; |
||||
|
||||
mailer?: MailerConfig; |
||||
make?: () => NcConfig; |
||||
serverless?: ServerlessConfig; |
||||
|
||||
toolDir?: string; |
||||
env?: 'production' | 'dev' | 'test' | string; |
||||
workingEnv?: string; |
||||
|
||||
seedsFolder?: string | string[]; |
||||
queriesFolder?: string | string[]; |
||||
apisFolder?: string | string[]; |
||||
projectType?: 'rest' | 'graphql' | 'grpc'; |
||||
type?: 'mvc' | 'package' | 'docker'; |
||||
language?: 'ts' | 'js'; |
||||
meta?: { |
||||
db?: any; |
||||
}; |
||||
api?: any; |
||||
gui?: NcGui; |
||||
try?: boolean; |
||||
|
||||
dashboardPath?: string; |
||||
|
||||
prefix?: string; |
||||
publicUrl?: string; |
||||
} |
||||
|
||||
export interface Event { |
||||
title: string; |
||||
tn: string; |
||||
url; |
||||
headers; |
||||
operation; |
||||
event; |
||||
retry; |
||||
max; |
||||
interval; |
||||
timeout; |
||||
} |
||||
|
||||
export interface Acl { |
||||
[role: string]: |
||||
| { |
||||
create: boolean | ColumnAcl; |
||||
[key: string]: boolean | ColumnAcl; |
||||
} |
||||
| boolean |
||||
| any; |
||||
} |
||||
|
||||
export interface ColumnAcl { |
||||
columns: { |
||||
[cn: string]: boolean; |
||||
}; |
||||
assign?: { |
||||
[cn: string]: any; |
||||
}; |
||||
} |
||||
|
||||
export interface Acls { |
||||
[tn: string]: Acl; |
||||
} |
||||
|
||||
export enum ServerlessType { |
||||
AWS_LAMBDA = 'AWS_LAMBDA', |
||||
GCP_FUNCTION = 'GCP_FUNCTION', |
||||
AZURE_FUNCTION_APP = 'AZURE_FUNCTION_APP', |
||||
ALIYUN = 'ALIYUN', |
||||
ZEIT = 'ZEIT', |
||||
LYRID = 'LYRID', |
||||
SERVERLESS = 'SERVERLESS', |
||||
} |
||||
|
||||
export class Result { |
||||
public code: any; |
||||
public message: string; |
||||
public data: any; |
||||
|
||||
constructor(code = 0, message = '', data = {}) { |
||||
this.code = code; |
||||
this.message = message; |
||||
this.data = data; |
||||
} |
||||
} |
||||
|
||||
enum HTTPType { |
||||
GET = 'get', |
||||
POST = 'post', |
||||
PUT = 'put', |
||||
DELETE = 'delete', |
||||
PATCH = 'patch', |
||||
HEAD = 'head', |
||||
OPTIONS = 'options', |
||||
} |
||||
|
||||
export interface XcRoute { |
||||
httpType: HTTPType; |
||||
path: string; |
||||
handler: e.Handler; |
||||
dbAlias?: string; |
||||
isCustom?: boolean; |
||||
} |
@ -1,559 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/ban-types */ |
||||
import fs from 'fs'; |
||||
import path from 'path'; |
||||
import { promisify } from 'util'; |
||||
import * as Sentry from '@sentry/node'; |
||||
import bodyParser from 'body-parser'; |
||||
import clear from 'clear'; |
||||
import cookieParser from 'cookie-parser'; |
||||
import debug from 'debug'; |
||||
import * as express from 'express'; |
||||
import importFresh from 'import-fresh'; |
||||
import morgan from 'morgan'; |
||||
import NcToolGui from 'nc-lib-gui'; |
||||
import requestIp from 'request-ip'; |
||||
import { v4 as uuidv4 } from 'uuid'; |
||||
import { T } from 'nc-help'; |
||||
import mkdirp from 'mkdirp'; |
||||
import { NC_LICENSE_KEY } from './constants'; |
||||
import Migrator from './db/sql-migrator/lib/KnexMigrator'; |
||||
import Store from './models/Store'; |
||||
import NcConfigFactory from './utils/NcConfigFactory'; |
||||
import NcProjectBuilderCE from './v1-legacy/NcProjectBuilder'; |
||||
import NcProjectBuilderEE from './v1-legacy/NcProjectBuilderEE'; |
||||
import NcMetaImplCE from './meta/NcMetaIOImpl'; |
||||
import NcMetaImplEE from './meta/NcMetaIOImplEE'; |
||||
import RestAuthCtrlCE from './v1-legacy/rest/RestAuthCtrl'; |
||||
import RestAuthCtrlEE from './v1-legacy/rest/RestAuthCtrlEE'; |
||||
import MetaAPILogger from './meta/MetaAPILogger'; |
||||
import NcUpgrader from './version-upgrader/NcUpgrader'; |
||||
import NocoCache from './cache/NocoCache'; |
||||
import registerMetaApis from './meta/api'; |
||||
import NcPluginMgrv2 from './meta/helpers/NcPluginMgrv2'; |
||||
import User from './models/User'; |
||||
import weAreHiring from './utils/weAreHiring'; |
||||
import getInstance from './utils/getInstance'; |
||||
import initAdminFromEnv from './services/user/initAdminFromEnv'; |
||||
import type * as http from 'http'; |
||||
import type NcMetaMgrv2 from './meta/NcMetaMgrv2'; |
||||
import type { RestApiBuilder } from './v1-legacy/rest/RestApiBuilder'; |
||||
import type NcMetaMgrEE from './meta/NcMetaMgrEE'; |
||||
import type NcMetaMgrCE from './meta/NcMetaMgr'; |
||||
import type NcMetaIO from './meta/NcMetaIO'; |
||||
import type { GqlApiBuilder } from './v1-legacy/gql/GqlApiBuilder'; |
||||
import type { NcConfig } from '../interface/config'; |
||||
import type { Router } from 'express'; |
||||
|
||||
const log = debug('nc:app'); |
||||
require('dotenv').config(); |
||||
|
||||
const NcProjectBuilder = process.env.EE |
||||
? NcProjectBuilderEE |
||||
: NcProjectBuilderCE; |
||||
|
||||
export default class Noco { |
||||
private static _this: Noco; |
||||
private static ee: boolean; |
||||
|
||||
public static get dashboardUrl(): string { |
||||
let siteUrl = `http://localhost:${process.env.PORT || 8080}`; |
||||
// if (Noco._this?.config?.envs?.[Noco._this?.env]?.publicUrl) {
|
||||
// siteUrl = Noco._this?.config?.envs?.[Noco._this?.env]?.publicUrl;
|
||||
// }
|
||||
if (Noco._this?.config?.envs?.['_noco']?.publicUrl) { |
||||
siteUrl = Noco._this?.config?.envs?.['_noco']?.publicUrl; |
||||
} |
||||
|
||||
return `${siteUrl}${Noco._this?.config?.dashboardPath}`; |
||||
} |
||||
|
||||
public static async init( |
||||
args?: { |
||||
progressCallback?: Function; |
||||
registerRoutes?: Function; |
||||
registerGql?: Function; |
||||
registerContext?: Function; |
||||
afterMetaMigrationInit?: Function; |
||||
}, |
||||
server?: http.Server, |
||||
app?: express.Express |
||||
): Promise<Router> { |
||||
if (Noco._this) { |
||||
return Noco._this.router; |
||||
} |
||||
Noco._this = new Noco(); |
||||
return Noco._this.init(args, server, app); |
||||
} |
||||
|
||||
private static config: NcConfig; |
||||
public readonly router: express.Router; |
||||
public readonly projectRouter: express.Router; |
||||
public static _ncMeta: NcMetaIO; |
||||
public readonly metaMgr: NcMetaMgrEE | NcMetaMgrCE; |
||||
public readonly metaMgrv2: NcMetaMgrv2; |
||||
public env: string; |
||||
|
||||
public projectBuilders: Array<NcProjectBuilderCE | NcProjectBuilderEE> = []; |
||||
private apiBuilders: Array<RestApiBuilder | GqlApiBuilder> = []; |
||||
private ncToolApi; |
||||
private config: NcConfig; |
||||
private requestContext: any; |
||||
|
||||
constructor() { |
||||
process.env.PORT = process.env.PORT || '8080'; |
||||
// todo: move
|
||||
process.env.NC_VERSION = '0105004'; |
||||
|
||||
// if env variable NC_MINIMAL_DBS is set, then disable project creation with external sources
|
||||
if (process.env.NC_MINIMAL_DBS) { |
||||
process.env.NC_CONNECT_TO_EXTERNAL_DB_DISABLED = 'true'; |
||||
} |
||||
|
||||
this.router = express.Router(); |
||||
this.projectRouter = express.Router(); |
||||
|
||||
/******************* prints : start *******************/ |
||||
// this.sumTable = new Table({
|
||||
// head: ['#DBs', '#Tables',
|
||||
// '#GQL\nServers', '#REST\nServers',
|
||||
// '#APIs',
|
||||
// 'Time\ntaken',
|
||||
// // 'If avg manual effort\nper api = 15 minutes\nand\nAPI developer salary = $76k'
|
||||
// ].map(v => colors.green(v))
|
||||
// , colWidths: [10, 12, 9, 9, 12, 12]
|
||||
// });
|
||||
// this.table = new Table({
|
||||
// colWidths: [4, 8, 8, 20, 9, 7, 35, 9],
|
||||
// head: ['#', 'DB\nType', 'API\nType', 'Database', '#Tables', '#APIs', 'APIs URL', 'Time\ntaken'].map(v => colors.green(v))
|
||||
// });
|
||||
clear(); |
||||
/******************* prints : end *******************/ |
||||
} |
||||
|
||||
public async init( |
||||
args?: { |
||||
progressCallback?: Function; |
||||
registerRoutes?: Function; |
||||
registerGql?: Function; |
||||
registerContext?: Function; |
||||
afterMetaMigrationInit?: Function; |
||||
}, |
||||
server?: http.Server, |
||||
_app?: express.Express |
||||
) { |
||||
/* prepare config */ |
||||
Noco.config = this.config = await NcConfigFactory.make(); |
||||
|
||||
/******************* setup : start *******************/ |
||||
this.env = '_noco'; //process.env['NODE_ENV'] || this.config.workingEnv || 'dev';
|
||||
this.config.workingEnv = this.env; |
||||
|
||||
this.config.type = 'docker'; |
||||
if (!this.config.toolDir) { |
||||
this.config.toolDir = process.cwd(); |
||||
} |
||||
|
||||
// this.ncToolApi = new NcToolGui(this.config);
|
||||
// if (server) {
|
||||
// server.set('view engine', 'ejs');
|
||||
// }
|
||||
|
||||
const NcMetaImpl = process.env.EE ? NcMetaImplEE : NcMetaImplCE; |
||||
// const NcMetaMgr = process.env.EE ? NcMetaMgrEE : NcMetaMgrCE;
|
||||
|
||||
Noco._ncMeta = new NcMetaImpl(this, this.config); |
||||
// this.metaMgr = new NcMetaMgr(this, this.config, Noco._ncMeta);
|
||||
// this.metaMgrv2 = new NcMetaMgrv2(this, this.config, Noco._ncMeta);
|
||||
|
||||
/******************* setup : end *******************/ |
||||
|
||||
// @ts-ignore
|
||||
const { |
||||
progressCallback, |
||||
// registerRoutes,
|
||||
// registerContext,
|
||||
// registerGql
|
||||
} = args || {}; |
||||
|
||||
log('Initializing app'); |
||||
|
||||
// create tool directory if missing
|
||||
await mkdirp(this.config.toolDir); |
||||
|
||||
this.initSentry(); |
||||
NocoCache.init(); |
||||
|
||||
// this.apiInfInfoList = [];
|
||||
//
|
||||
// this.startTime = Date.now();
|
||||
|
||||
if (!this.config.try) { |
||||
await NcConfigFactory.metaDbCreateIfNotExist(this.config); |
||||
await this.syncMigration(); |
||||
} |
||||
|
||||
await Noco._ncMeta.metaInit(); |
||||
await Noco.loadEEState(); |
||||
await this.initJwt(); |
||||
await initAdminFromEnv(); |
||||
|
||||
await NcUpgrader.upgrade({ ncMeta: Noco._ncMeta }); |
||||
|
||||
if (args?.afterMetaMigrationInit) { |
||||
await args.afterMetaMigrationInit(); |
||||
} |
||||
|
||||
/******************* Middlewares : start *******************/ |
||||
this.router.use((req: any, _res, next) => { |
||||
req.nc = this.requestContext; |
||||
req.ncSiteUrl = |
||||
this.config?.envs?.[this.env]?.publicUrl || |
||||
this.config?.publicUrl || |
||||
req.protocol + '://' + req.get('host'); |
||||
req.ncFullUrl = req.protocol + '://' + req.get('host') + req.originalUrl; |
||||
next(); |
||||
}); |
||||
|
||||
// to get ip addresses
|
||||
this.router.use(requestIp.mw()); |
||||
this.router.use(cookieParser()); |
||||
this.router.use( |
||||
bodyParser.json({ |
||||
limit: process.env.NC_REQUEST_BODY_SIZE || '50mb', |
||||
}) |
||||
); |
||||
this.router.use(morgan('tiny')); |
||||
this.router.use(express.static(path.join(__dirname, './public'))); |
||||
|
||||
this.router.use((req: any, _res, next) => { |
||||
req.ncProjectId = req?.query?.project_id || req?.body?.project_id; |
||||
next(); |
||||
}); |
||||
/* this.router.use(this.config.dashboardPath, (req: any, _res, next) => { |
||||
req.ncProjectId = req?.body?.project_id; |
||||
next(); |
||||
})*/ |
||||
this.router.use(`/nc/:project_id/*`, (req: any, _res, next) => { |
||||
req.ncProjectId = req.ncProjectId || req.params.project_id; |
||||
next(); |
||||
}); |
||||
this.router.use(MetaAPILogger.mw); |
||||
|
||||
/******************* Middlewares : end *******************/ |
||||
|
||||
// await this.initProjectBuilders();
|
||||
|
||||
// const runTimeHandler = this.handleRuntimeChanges(progressCallback);
|
||||
|
||||
// this.ncToolApi.addListener(runTimeHandler);
|
||||
// this.metaMgr.setListener(runTimeHandler);
|
||||
// this.metaMgrv2.setListener(runTimeHandler);
|
||||
// await this.metaMgr.initHandler(this.router);
|
||||
// await this.metaMgrv2.initHandler(this.router);
|
||||
|
||||
await NcPluginMgrv2.init(Noco.ncMeta); |
||||
registerMetaApis(this.router, server); |
||||
|
||||
// this.router.use(
|
||||
// this.config.dashboardPath,
|
||||
// await this.ncToolApi.expressMiddleware()
|
||||
// );
|
||||
this.router.use(NcToolGui.expressMiddleware(this.config.dashboardPath)); |
||||
this.router.get('/', (_req, res) => |
||||
res.redirect(this.config.dashboardPath) |
||||
); |
||||
|
||||
this.initSentryErrorHandler(); |
||||
|
||||
/* catch error */ |
||||
this.router.use((err, _req, res, next) => { |
||||
if (err) { |
||||
return res.status(400).json({ msg: err.message }); |
||||
} |
||||
next(); |
||||
}); |
||||
T.init({ |
||||
instance: getInstance, |
||||
}); |
||||
T.emit('evt_app_started', await User.count()); |
||||
console.log(`App started successfully.\nVisit -> ${Noco.dashboardUrl}`); |
||||
weAreHiring(); |
||||
return this.router; |
||||
} |
||||
|
||||
private initSentryErrorHandler() { |
||||
if (process.env.NC_SENTRY_DSN) { |
||||
this.router.use(Sentry.Handlers.errorHandler()); |
||||
} |
||||
} |
||||
|
||||
private initSentry() { |
||||
if (process.env.NC_SENTRY_DSN) { |
||||
Sentry.init({ dsn: process.env.NC_SENTRY_DSN }); |
||||
|
||||
// The request handler must be the first middleware on the app
|
||||
this.router.use(Sentry.Handlers.requestHandler()); |
||||
} |
||||
} |
||||
|
||||
async initServerless() {} |
||||
|
||||
public getBuilders(): Array<RestApiBuilder | GqlApiBuilder> { |
||||
return this.apiBuilders; |
||||
} |
||||
|
||||
public getConfig(): NcConfig { |
||||
return this.config; |
||||
} |
||||
|
||||
public getToolDir(): string { |
||||
return this.getConfig()?.toolDir; |
||||
} |
||||
|
||||
public addToContext(context: any) { |
||||
this.requestContext = context; |
||||
} |
||||
|
||||
protected handleRuntimeChanges(_progressCallback: Function) { |
||||
return async (data): Promise<any> => { |
||||
switch (data?.req?.api) { |
||||
case 'projectCreateByWeb': |
||||
case 'projectCreateByOneClick': |
||||
case 'projectCreateByWebWithXCDB': |
||||
{ |
||||
// || data?.req?.args?.project?.title || data?.req?.args?.title
|
||||
const project = await Noco.ncMeta.projectGetById(data?.res?.id); |
||||
const builder = new NcProjectBuilder(this, this.config, project); |
||||
this.projectBuilders.push(builder); |
||||
await builder.init(true); |
||||
} |
||||
break; |
||||
// create project builder for newly imported project
|
||||
// duplicated code - projectCreateByWeb
|
||||
case 'xcMetaTablesImportZipToLocalFsAndDb': |
||||
{ |
||||
if (data.req?.freshImport) { |
||||
const project = await Noco.ncMeta.projectGetById( |
||||
data?.req?.project_id |
||||
); |
||||
const builder = new NcProjectBuilder(this, this.config, project); |
||||
this.projectBuilders.push(builder); |
||||
await builder.init(true); |
||||
} else { |
||||
const projectBuilder = this.projectBuilders.find( |
||||
(pb) => pb.id == data.req?.project_id |
||||
); |
||||
return projectBuilder?.handleRunTimeChanges(data); |
||||
} |
||||
} |
||||
break; |
||||
|
||||
case 'projectUpdateByWeb': |
||||
{ |
||||
const projectId = data.req?.project_id; |
||||
const project = await Noco._ncMeta.projectGetById( |
||||
data?.req?.project_id |
||||
); |
||||
const projectBuilder = this.projectBuilders.find( |
||||
(pb) => pb.id === projectId |
||||
); |
||||
|
||||
projectBuilder.updateConfig(project.config); |
||||
await projectBuilder.reInit(); |
||||
} |
||||
break; |
||||
|
||||
case 'projectChangeEnv': |
||||
try { |
||||
this.config = importFresh( |
||||
path.join(process.cwd(), 'config.xc.json') |
||||
) as NcConfig; |
||||
this.config.toolDir = this.config.toolDir || process.cwd(); |
||||
Noco._ncMeta.setConfig(this.config); |
||||
this.metaMgr.setConfig(this.config); |
||||
Object.assign(process.env, { |
||||
NODE_ENV: (this.env = this.config.workingEnv), |
||||
}); |
||||
this.router.stack.splice(0, this.router.stack.length); |
||||
this.ncToolApi.destroy(); |
||||
this.ncToolApi.reInitialize(this.config); |
||||
// await this.init({progressCallback});
|
||||
} catch (e) { |
||||
console.log(e); |
||||
} |
||||
break; |
||||
|
||||
default: { |
||||
const projectBuilder = this.projectBuilders.find( |
||||
(pb) => pb.id == data.req?.project_id |
||||
); |
||||
return projectBuilder?.handleRunTimeChanges(data); |
||||
} |
||||
} |
||||
}; |
||||
} |
||||
|
||||
protected async initProjectBuilders() { |
||||
// @ts-ignore
|
||||
const RestAuthCtrl = process.env.EE ? RestAuthCtrlEE : RestAuthCtrlCE; |
||||
|
||||
this.projectBuilders.splice(0, this.projectBuilders.length); |
||||
|
||||
// await new RestAuthCtrl(
|
||||
// this as any,
|
||||
// Noco._ncMeta?.knex,
|
||||
// this.config?.meta?.db,
|
||||
// this.config,
|
||||
// Noco._ncMeta
|
||||
// ).init();
|
||||
|
||||
this.router.use(this.projectRouter); |
||||
const projects = await Noco._ncMeta.projectList(); |
||||
|
||||
for (const project of projects) { |
||||
const projectBuilder = new NcProjectBuilder(this, this.config, project); |
||||
this.projectBuilders.push(projectBuilder); |
||||
} |
||||
let i = 0; |
||||
for (const builder of this.projectBuilders) { |
||||
if ( |
||||
projects[i].status === 'started' || |
||||
projects[i].status === 'starting' |
||||
) { |
||||
await builder.init(); |
||||
} |
||||
i++; |
||||
} |
||||
} |
||||
|
||||
private async syncMigration(): Promise<void> { |
||||
if ( |
||||
this.config?.toolDir |
||||
// && !('NC_MIGRATIONS_DISABLED' in process.env)
|
||||
) { |
||||
const dbs = this.config?.envs?.[this.env]?.db; |
||||
|
||||
if (!dbs || !dbs.length) { |
||||
log( |
||||
`'${this.env}' environment doesn't have any database configuration.` |
||||
); |
||||
return; |
||||
} |
||||
|
||||
for (const connectionConfig of dbs) { |
||||
log( |
||||
`Migrations start >> ${connectionConfig?.connection?.['database']} (${connectionConfig.meta?.dbAlias})` |
||||
); |
||||
|
||||
try { |
||||
/* Update database migrations */ |
||||
const migrator = new Migrator(); |
||||
|
||||
/* initialize migration if folder doesn't exist */ |
||||
const migrationFolder = path.join( |
||||
this.config.toolDir, |
||||
'server', |
||||
'tool', |
||||
connectionConfig.meta.dbAlias, |
||||
'migrations' |
||||
); |
||||
if (!(await promisify(fs.exists)(migrationFolder))) { |
||||
await migrator.init({ |
||||
folder: this.config?.toolDir, |
||||
env: this.env, |
||||
dbAlias: connectionConfig.meta.dbAlias, |
||||
}); |
||||
} |
||||
|
||||
await migrator.sync({ |
||||
folder: this.config?.toolDir, |
||||
env: this.env, |
||||
dbAlias: connectionConfig.meta.dbAlias, |
||||
}); |
||||
|
||||
await migrator.migrationsUp({ |
||||
folder: this.config?.toolDir, |
||||
env: this.env, |
||||
dbAlias: connectionConfig.meta.dbAlias, |
||||
migrationSteps: 99999, |
||||
sqlContentMigrate: 1, |
||||
}); |
||||
|
||||
log( |
||||
`Migrations end << ${connectionConfig?.connection?.['database']} (${connectionConfig.meta?.dbAlias})` |
||||
); |
||||
} catch (e) { |
||||
log( |
||||
`Migrations Failed !! ${connectionConfig?.connection?.['database']} (${connectionConfig.meta?.dbAlias})` |
||||
); |
||||
console.log(e); |
||||
// throw e;
|
||||
} |
||||
} |
||||
} else { |
||||
log( |
||||
'Warning : ignoring migrations on boot since tools directory not defined' |
||||
); |
||||
} |
||||
} |
||||
|
||||
private async initJwt(): Promise<any> { |
||||
if (this.config?.auth?.jwt) { |
||||
if (!this.config.auth.jwt.secret) { |
||||
let secret = ( |
||||
await Noco._ncMeta.metaGet('', '', 'nc_store', { |
||||
key: 'nc_auth_jwt_secret', |
||||
}) |
||||
)?.value; |
||||
if (!secret) { |
||||
await Noco._ncMeta.metaInsert('', '', 'nc_store', { |
||||
key: 'nc_auth_jwt_secret', |
||||
value: (secret = uuidv4()), |
||||
}); |
||||
} |
||||
this.config.auth.jwt.secret = secret; |
||||
} |
||||
|
||||
this.config.auth.jwt.options = this.config.auth.jwt.options || {}; |
||||
if (!this.config.auth.jwt.options?.expiresIn) { |
||||
this.config.auth.jwt.options.expiresIn = |
||||
process.env.NC_JWT_EXPIRES_IN ?? '10h'; |
||||
} |
||||
} |
||||
let serverId = ( |
||||
await Noco._ncMeta.metaGet('', '', 'nc_store', { |
||||
key: 'nc_server_id', |
||||
}) |
||||
)?.value; |
||||
if (!serverId) { |
||||
await Noco._ncMeta.metaInsert('', '', 'nc_store', { |
||||
key: 'nc_server_id', |
||||
value: (serverId = T.id), |
||||
}); |
||||
} |
||||
process.env.NC_SERVER_UUID = serverId; |
||||
} |
||||
|
||||
public static get ncMeta(): NcMetaIO { |
||||
return this._ncMeta; |
||||
} |
||||
|
||||
public get ncMeta(): NcMetaIO { |
||||
return Noco._ncMeta; |
||||
} |
||||
|
||||
public static getConfig(): NcConfig { |
||||
return Noco.config; |
||||
} |
||||
|
||||
public static isEE(): boolean { |
||||
return Noco.ee; |
||||
} |
||||
|
||||
public static async loadEEState(): Promise<boolean> { |
||||
try { |
||||
return (Noco.ee = !!(await Store.get(NC_LICENSE_KEY))?.value); |
||||
} catch {} |
||||
return (Noco.ee = false); |
||||
} |
||||
} |
@ -1,31 +0,0 @@
|
||||
export default abstract class CacheMgr { |
||||
public abstract get(key: string, type: string): Promise<any>; |
||||
public abstract set(key: string, value: any): Promise<any>; |
||||
public abstract del(key: string): Promise<any>; |
||||
public abstract getAll(pattern: string): Promise<any[]>; |
||||
public abstract delAll(scope: string, pattern: string): Promise<any[]>; |
||||
public abstract getList( |
||||
scope: string, |
||||
list: string[] |
||||
): Promise<{ |
||||
list: any[]; |
||||
isNoneList: boolean; |
||||
}>; |
||||
public abstract setList( |
||||
scope: string, |
||||
subListKeys: string[], |
||||
list: any[] |
||||
): Promise<boolean>; |
||||
public abstract deepDel( |
||||
scope: string, |
||||
key: string, |
||||
direction: string |
||||
): Promise<boolean>; |
||||
public abstract appendToList( |
||||
scope: string, |
||||
subListKeys: string[], |
||||
key: string |
||||
): Promise<boolean>; |
||||
public abstract destroy(): Promise<boolean>; |
||||
public abstract export(): Promise<any>; |
||||
} |
@ -1,112 +0,0 @@
|
||||
import { CacheGetType } from '../utils/globals'; |
||||
import RedisCacheMgr from './RedisCacheMgr'; |
||||
import RedisMockCacheMgr from './RedisMockCacheMgr'; |
||||
import type CacheMgr from './CacheMgr'; |
||||
|
||||
export default class NocoCache { |
||||
private static client: CacheMgr; |
||||
private static cacheDisabled: boolean; |
||||
private static prefix: string; |
||||
|
||||
public static init() { |
||||
this.cacheDisabled = (process.env.NC_DISABLE_CACHE || false) === 'true'; |
||||
if (this.cacheDisabled) { |
||||
return; |
||||
} |
||||
if (process.env.NC_REDIS_URL) { |
||||
this.client = new RedisCacheMgr(process.env.NC_REDIS_URL); |
||||
} else { |
||||
this.client = new RedisMockCacheMgr(); |
||||
} |
||||
|
||||
// TODO(cache): fetch orgs once it's implemented
|
||||
const orgs = 'noco'; |
||||
this.prefix = `nc:${orgs}`; |
||||
} |
||||
|
||||
public static async set(key, value): Promise<boolean> { |
||||
if (this.cacheDisabled) return Promise.resolve(true); |
||||
return this.client.set(`${this.prefix}:${key}`, value); |
||||
} |
||||
|
||||
public static async get(key, type): Promise<any> { |
||||
if (this.cacheDisabled) { |
||||
if (type === CacheGetType.TYPE_ARRAY) return Promise.resolve([]); |
||||
else if (type === CacheGetType.TYPE_OBJECT) return Promise.resolve(null); |
||||
return Promise.resolve(null); |
||||
} |
||||
return this.client.get(`${this.prefix}:${key}`, type); |
||||
} |
||||
|
||||
public static async getAll(pattern: string): Promise<any[]> { |
||||
if (this.cacheDisabled) return Promise.resolve([]); |
||||
return this.client.getAll(`${this.prefix}:${pattern}`); |
||||
} |
||||
|
||||
public static async del(key): Promise<boolean> { |
||||
if (this.cacheDisabled) return Promise.resolve(true); |
||||
return this.client.del(`${this.prefix}:${key}`); |
||||
} |
||||
|
||||
public static async delAll(scope: string, pattern: string): Promise<any[]> { |
||||
if (this.cacheDisabled) return Promise.resolve([]); |
||||
return this.client.delAll(scope, pattern); |
||||
} |
||||
|
||||
public static async getList( |
||||
scope: string, |
||||
subKeys: string[] |
||||
): Promise<{ |
||||
list: any[]; |
||||
isNoneList: boolean; |
||||
}> { |
||||
if (this.cacheDisabled) |
||||
return Promise.resolve({ |
||||
list: [], |
||||
isNoneList: false, |
||||
}); |
||||
return this.client.getList(scope, subKeys); |
||||
} |
||||
|
||||
public static async setList( |
||||
scope: string, |
||||
subListKeys: string[], |
||||
list: any[] |
||||
): Promise<boolean> { |
||||
if (this.cacheDisabled) return Promise.resolve(true); |
||||
return this.client.setList(scope, subListKeys, list); |
||||
} |
||||
|
||||
public static async deepDel( |
||||
scope: string, |
||||
key: string, |
||||
direction: string |
||||
): Promise<boolean> { |
||||
if (this.cacheDisabled) return Promise.resolve(true); |
||||
return this.client.deepDel(scope, key, direction); |
||||
} |
||||
|
||||
public static async appendToList( |
||||
scope: string, |
||||
subListKeys: string[], |
||||
|
||||
key: string |
||||
): Promise<boolean> { |
||||
if (this.cacheDisabled) return Promise.resolve(true); |
||||
return this.client.appendToList( |
||||
scope, |
||||
subListKeys, |
||||
`${this.prefix}:${key}` |
||||
); |
||||
} |
||||
|
||||
public static async destroy(): Promise<boolean> { |
||||
if (this.cacheDisabled) return Promise.resolve(true); |
||||
return this.client.destroy(); |
||||
} |
||||
|
||||
public static async export(): Promise<any> { |
||||
if (this.cacheDisabled) return Promise.resolve({}); |
||||
return this.client.export(); |
||||
} |
||||
} |
@ -1,278 +0,0 @@
|
||||
import debug from 'debug'; |
||||
import Redis from 'ioredis'; |
||||
import { CacheDelDirection, CacheGetType, CacheScope } from '../utils/globals'; |
||||
import CacheMgr from './CacheMgr'; |
||||
|
||||
const log = debug('nc:cache'); |
||||
|
||||
export default class RedisCacheMgr extends CacheMgr { |
||||
client: any; |
||||
prefix: string; |
||||
|
||||
constructor(config: any) { |
||||
super(); |
||||
this.client = new Redis(config); |
||||
// flush the existing db with selected key (Default: 0)
|
||||
this.client.flushdb(); |
||||
|
||||
// TODO(cache): fetch orgs once it's implemented
|
||||
const orgs = 'noco'; |
||||
this.prefix = `nc:${orgs}`; |
||||
} |
||||
|
||||
// avoid circular structure to JSON
|
||||
getCircularReplacer = () => { |
||||
const seen = new WeakSet(); |
||||
return (_, value) => { |
||||
if (typeof value === 'object' && value !== null) { |
||||
if (seen.has(value)) { |
||||
return; |
||||
} |
||||
seen.add(value); |
||||
} |
||||
return value; |
||||
}; |
||||
}; |
||||
|
||||
// @ts-ignore
|
||||
async del(key: string): Promise<any> { |
||||
log(`RedisCacheMgr::del: deleting key ${key}`); |
||||
return this.client.del(key); |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
async get(key: string, type: string, config?: any): Promise<any> { |
||||
log(`RedisCacheMgr::get: getting key ${key} with type ${type}`); |
||||
if (type === CacheGetType.TYPE_ARRAY) { |
||||
return this.client.smembers(key); |
||||
} else if (type === CacheGetType.TYPE_OBJECT) { |
||||
const res = await this.client.get(key); |
||||
try { |
||||
const o = JSON.parse(res); |
||||
if (typeof o === 'object') { |
||||
if ( |
||||
o && |
||||
Object.keys(o).length === 0 && |
||||
Object.getPrototypeOf(o) === Object.prototype |
||||
) { |
||||
log(`RedisCacheMgr::get: object is empty!`); |
||||
} |
||||
return Promise.resolve(o); |
||||
} |
||||
} catch (e) { |
||||
return Promise.resolve(res); |
||||
} |
||||
return Promise.resolve(res); |
||||
} else if (type === CacheGetType.TYPE_STRING) { |
||||
return await this.client.get(key); |
||||
} |
||||
log(`Invalid CacheGetType: ${type}`); |
||||
return Promise.resolve(false); |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
async set(key: string, value: any): Promise<any> { |
||||
if (typeof value !== 'undefined' && value) { |
||||
log(`RedisCacheMgr::set: setting key ${key} with value ${value}`); |
||||
if (typeof value === 'object') { |
||||
if (Array.isArray(value) && value.length) { |
||||
return this.client.sadd(key, value); |
||||
} |
||||
return this.client.set( |
||||
key, |
||||
JSON.stringify(value, this.getCircularReplacer()) |
||||
); |
||||
} |
||||
return this.client.set(key, value); |
||||
} else { |
||||
log(`RedisCacheMgr::set: value is empty for ${key}. Skipping ...`); |
||||
return Promise.resolve(true); |
||||
} |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
async getAll(pattern: string): Promise<any> { |
||||
return this.client.hgetall(pattern); |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
async delAll(scope: string, pattern: string): Promise<any[]> { |
||||
// Example: nc:<orgs>:model:*:<id>
|
||||
const keys = await this.client.keys(`${this.prefix}:${scope}:${pattern}`); |
||||
log( |
||||
`RedisCacheMgr::delAll: deleting all keys with pattern ${this.prefix}:${scope}:${pattern}` |
||||
); |
||||
await Promise.all( |
||||
keys.map(async (k) => { |
||||
await this.deepDel(scope, k, CacheDelDirection.CHILD_TO_PARENT); |
||||
}) |
||||
); |
||||
return Promise.all( |
||||
keys.map(async (k) => { |
||||
await this.del(k); |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async getList( |
||||
scope: string, |
||||
subKeys: string[] |
||||
): Promise<{ |
||||
list: any[]; |
||||
isNoneList: boolean; |
||||
}> { |
||||
// remove null from arrays
|
||||
subKeys = subKeys.filter((k) => k); |
||||
// e.g. key = nc:<orgs>:<scope>:<project_id_1>:<base_id_1>:list
|
||||
const key = |
||||
subKeys.length === 0 |
||||
? `${this.prefix}:${scope}:list` |
||||
: `${this.prefix}:${scope}:${subKeys.join(':')}:list`; |
||||
// e.g. arr = ["nc:<orgs>:<scope>:<model_id_1>", "nc:<orgs>:<scope>:<model_id_2>"]
|
||||
const arr = (await this.get(key, CacheGetType.TYPE_ARRAY)) || []; |
||||
log(`RedisCacheMgr::getList: getting list with key ${key}`); |
||||
|
||||
const isNoneList = arr.length && arr[0] === 'NONE'; |
||||
|
||||
if (isNoneList) { |
||||
return Promise.resolve({ |
||||
list: [], |
||||
isNoneList, |
||||
}); |
||||
} |
||||
|
||||
return { |
||||
list: await Promise.all( |
||||
arr.map(async (k) => await this.get(k, CacheGetType.TYPE_OBJECT)) |
||||
), |
||||
isNoneList, |
||||
}; |
||||
} |
||||
|
||||
async setList( |
||||
scope: string, |
||||
subListKeys: string[], |
||||
list: any[] |
||||
): Promise<boolean> { |
||||
// remove null from arrays
|
||||
subListKeys = subListKeys.filter((k) => k); |
||||
// construct key for List
|
||||
// e.g. nc:<orgs>:<scope>:<project_id_1>:<base_id_1>:list
|
||||
const listKey = |
||||
subListKeys.length === 0 |
||||
? `${this.prefix}:${scope}:list` |
||||
: `${this.prefix}:${scope}:${subListKeys.join(':')}:list`; |
||||
if (!list.length) { |
||||
// Set NONE here so that it won't hit the DB on each page load
|
||||
return this.set(listKey, ['NONE']); |
||||
} |
||||
// fetch existing list
|
||||
const listOfGetKeys = |
||||
(await this.get(listKey, CacheGetType.TYPE_ARRAY)) || []; |
||||
for (const o of list) { |
||||
// construct key for Get
|
||||
// e.g. nc:<orgs>:<scope>:<model_id_1>
|
||||
let getKey = `${this.prefix}:${scope}:${o.id}`; |
||||
// special case - MODEL_ROLE_VISIBILITY
|
||||
if (scope === CacheScope.MODEL_ROLE_VISIBILITY) { |
||||
getKey = `${this.prefix}:${scope}:${o.id}:${o.role}`; |
||||
} |
||||
// set Get Key
|
||||
log(`RedisCacheMgr::setList: setting key ${getKey}`); |
||||
await this.set(getKey, JSON.stringify(o, this.getCircularReplacer())); |
||||
// push Get Key to List
|
||||
listOfGetKeys.push(getKey); |
||||
} |
||||
// set List Key
|
||||
log(`RedisCacheMgr::setList: setting list with key ${listKey}`); |
||||
return this.set(listKey, listOfGetKeys); |
||||
} |
||||
|
||||
async deepDel( |
||||
scope: string, |
||||
key: string, |
||||
direction: string |
||||
): Promise<boolean> { |
||||
key = `${this.prefix}:${key}`; |
||||
log(`RedisCacheMgr::deepDel: choose direction ${direction}`); |
||||
if (direction === CacheDelDirection.CHILD_TO_PARENT) { |
||||
// given a child key, delete all keys in corresponding parent lists
|
||||
const scopeList = await this.client.keys(`${this.prefix}:${scope}*list`); |
||||
for (const listKey of scopeList) { |
||||
// get target list
|
||||
let list = (await this.get(listKey, CacheGetType.TYPE_ARRAY)) || []; |
||||
if (!list.length) { |
||||
continue; |
||||
} |
||||
// remove target Key
|
||||
list = list.filter((k) => k !== key); |
||||
// delete list
|
||||
log(`RedisCacheMgr::deepDel: remove listKey ${listKey}`); |
||||
await this.del(listKey); |
||||
if (list.length) { |
||||
// set target list
|
||||
log(`RedisCacheMgr::deepDel: set key ${listKey}`); |
||||
await this.del(listKey); |
||||
await this.set(listKey, list); |
||||
} |
||||
} |
||||
log(`RedisCacheMgr::deepDel: remove key ${key}`); |
||||
return await this.del(key); |
||||
} else if (direction === CacheDelDirection.PARENT_TO_CHILD) { |
||||
// given a list key, delete all the children
|
||||
const listOfChildren = await this.get(key, CacheGetType.TYPE_ARRAY); |
||||
// delete each child key
|
||||
await Promise.all(listOfChildren.map(async (k) => await this.del(k))); |
||||
// delete list key
|
||||
return await this.del(key); |
||||
} else { |
||||
log(`Invalid deepDel direction found : ${direction}`); |
||||
return Promise.resolve(false); |
||||
} |
||||
} |
||||
|
||||
async appendToList( |
||||
scope: string, |
||||
subListKeys: string[], |
||||
key: string |
||||
): Promise<boolean> { |
||||
// remove null from arrays
|
||||
subListKeys = subListKeys.filter((k) => k); |
||||
// e.g. key = nc:<orgs>:<scope>:<project_id_1>:<base_id_1>:list
|
||||
const listKey = |
||||
subListKeys.length === 0 |
||||
? `${this.prefix}:${scope}:list` |
||||
: `${this.prefix}:${scope}:${subListKeys.join(':')}:list`; |
||||
log(`RedisCacheMgr::appendToList: append key ${key} to ${listKey}`); |
||||
let list = (await this.get(listKey, CacheGetType.TYPE_ARRAY)) || []; |
||||
if (list.length && list[0] === 'NONE') { |
||||
list = []; |
||||
await this.del(listKey); |
||||
} |
||||
list.push(key); |
||||
return this.set(listKey, list); |
||||
} |
||||
|
||||
async destroy(): Promise<boolean> { |
||||
log('RedisCacheMgr::destroy: destroy redis'); |
||||
return this.client.flushdb(); |
||||
} |
||||
|
||||
async export(): Promise<any> { |
||||
log('RedisCacheMgr::export: export data'); |
||||
const data = await this.client.keys('*'); |
||||
const res = {}; |
||||
return await Promise.all( |
||||
data.map(async (k) => { |
||||
res[k] = await this.get( |
||||
k, |
||||
k.slice(-4) === 'list' |
||||
? CacheGetType.TYPE_ARRAY |
||||
: CacheGetType.TYPE_OBJECT |
||||
); |
||||
}) |
||||
).then(() => { |
||||
return res; |
||||
}); |
||||
} |
||||
} |
@ -1,277 +0,0 @@
|
||||
import debug from 'debug'; |
||||
import Redis from 'ioredis-mock'; |
||||
import { CacheDelDirection, CacheGetType, CacheScope } from '../utils/globals'; |
||||
import CacheMgr from './CacheMgr'; |
||||
|
||||
const log = debug('nc:cache'); |
||||
|
||||
export default class RedisMockCacheMgr extends CacheMgr { |
||||
client: any; |
||||
prefix: string; |
||||
|
||||
constructor() { |
||||
super(); |
||||
this.client = new Redis(); |
||||
// flush the existing db with selected key (Default: 0)
|
||||
this.client.flushdb(); |
||||
|
||||
// TODO(cache): fetch orgs once it's implemented
|
||||
const orgs = 'noco'; |
||||
this.prefix = `nc:${orgs}`; |
||||
} |
||||
|
||||
// avoid circular structure to JSON
|
||||
getCircularReplacer = () => { |
||||
const seen = new WeakSet(); |
||||
return (_, value) => { |
||||
if (typeof value === 'object' && value !== null) { |
||||
if (seen.has(value)) { |
||||
return; |
||||
} |
||||
seen.add(value); |
||||
} |
||||
return value; |
||||
}; |
||||
}; |
||||
|
||||
// @ts-ignore
|
||||
async del(key: string): Promise<any> { |
||||
log(`RedisMockCacheMgr::del: deleting key ${key}`); |
||||
return this.client.del(key); |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
async get(key: string, type: string, config?: any): Promise<any> { |
||||
log(`RedisMockCacheMgr::get: getting key ${key} with type ${type}`); |
||||
if (type === CacheGetType.TYPE_ARRAY) { |
||||
return this.client.smembers(key); |
||||
} else if (type === CacheGetType.TYPE_OBJECT) { |
||||
const res = await this.client.get(key); |
||||
try { |
||||
const o = JSON.parse(res); |
||||
if (typeof o === 'object') { |
||||
if ( |
||||
o && |
||||
Object.keys(o).length === 0 && |
||||
Object.getPrototypeOf(o) === Object.prototype |
||||
) { |
||||
log(`RedisMockCacheMgr::get: object is empty!`); |
||||
} |
||||
return Promise.resolve(o); |
||||
} |
||||
} catch (e) { |
||||
return Promise.resolve(res); |
||||
} |
||||
return Promise.resolve(res); |
||||
} else if (type === CacheGetType.TYPE_STRING) { |
||||
return await this.client.get(key); |
||||
} |
||||
log(`Invalid CacheGetType: ${type}`); |
||||
return Promise.resolve(false); |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
async set(key: string, value: any): Promise<any> { |
||||
if (typeof value !== 'undefined' && value) { |
||||
log(`RedisMockCacheMgr::set: setting key ${key} with value ${value}`); |
||||
if (typeof value === 'object') { |
||||
if (Array.isArray(value) && value.length) { |
||||
return this.client.sadd(key, value); |
||||
} |
||||
return this.client.set( |
||||
key, |
||||
JSON.stringify(value, this.getCircularReplacer()) |
||||
); |
||||
} |
||||
return this.client.set(key, value); |
||||
} else { |
||||
log(`RedisMockCacheMgr::set: value is empty for ${key}. Skipping ...`); |
||||
return Promise.resolve(true); |
||||
} |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
async getAll(pattern: string): Promise<any> { |
||||
return this.client.hgetall(pattern); |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
async delAll(scope: string, pattern: string): Promise<any[]> { |
||||
// Example: nc:<orgs>:model:*:<id>
|
||||
const keys = await this.client.keys(`${this.prefix}:${scope}:${pattern}`); |
||||
log( |
||||
`RedisMockCacheMgr::delAll: deleting all keys with pattern ${this.prefix}:${scope}:${pattern}` |
||||
); |
||||
await Promise.all( |
||||
keys.map(async (k) => { |
||||
await this.deepDel(scope, k, CacheDelDirection.CHILD_TO_PARENT); |
||||
}) |
||||
); |
||||
return Promise.all( |
||||
keys.map(async (k) => { |
||||
await this.del(k); |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async getList( |
||||
scope: string, |
||||
subKeys: string[] |
||||
): Promise<{ |
||||
list: any[]; |
||||
isNoneList: boolean; |
||||
}> { |
||||
// remove null from arrays
|
||||
subKeys = subKeys.filter((k) => k); |
||||
// e.g. key = nc:<orgs>:<scope>:<project_id_1>:<base_id_1>:list
|
||||
const key = |
||||
subKeys.length === 0 |
||||
? `${this.prefix}:${scope}:list` |
||||
: `${this.prefix}:${scope}:${subKeys.join(':')}:list`; |
||||
// e.g. arr = ["nc:<orgs>:<scope>:<model_id_1>", "nc:<orgs>:<scope>:<model_id_2>"]
|
||||
const arr = (await this.get(key, CacheGetType.TYPE_ARRAY)) || []; |
||||
log(`RedisMockCacheMgr::getList: getting list with key ${key}`); |
||||
const isNoneList = arr.length && arr[0] === 'NONE'; |
||||
|
||||
if (isNoneList) { |
||||
return Promise.resolve({ |
||||
list: [], |
||||
isNoneList, |
||||
}); |
||||
} |
||||
|
||||
return { |
||||
list: await Promise.all( |
||||
arr.map(async (k) => await this.get(k, CacheGetType.TYPE_OBJECT)) |
||||
), |
||||
isNoneList, |
||||
}; |
||||
} |
||||
|
||||
async setList( |
||||
scope: string, |
||||
subListKeys: string[], |
||||
list: any[] |
||||
): Promise<boolean> { |
||||
// remove null from arrays
|
||||
subListKeys = subListKeys.filter((k) => k); |
||||
// construct key for List
|
||||
// e.g. nc:<orgs>:<scope>:<project_id_1>:<base_id_1>:list
|
||||
const listKey = |
||||
subListKeys.length === 0 |
||||
? `${this.prefix}:${scope}:list` |
||||
: `${this.prefix}:${scope}:${subListKeys.join(':')}:list`; |
||||
if (!list.length) { |
||||
// Set NONE here so that it won't hit the DB on each page load
|
||||
return this.set(listKey, ['NONE']); |
||||
} |
||||
// fetch existing list
|
||||
const listOfGetKeys = |
||||
(await this.get(listKey, CacheGetType.TYPE_ARRAY)) || []; |
||||
for (const o of list) { |
||||
// construct key for Get
|
||||
// e.g. nc:<orgs>:<scope>:<model_id_1>
|
||||
let getKey = `${this.prefix}:${scope}:${o.id}`; |
||||
// special case - MODEL_ROLE_VISIBILITY
|
||||
if (scope === CacheScope.MODEL_ROLE_VISIBILITY) { |
||||
getKey = `${this.prefix}:${scope}:${o.id}:${o.role}`; |
||||
} |
||||
// set Get Key
|
||||
log(`RedisMockCacheMgr::setList: setting key ${getKey}`); |
||||
await this.set(getKey, JSON.stringify(o, this.getCircularReplacer())); |
||||
// push Get Key to List
|
||||
listOfGetKeys.push(getKey); |
||||
} |
||||
// set List Key
|
||||
log(`RedisMockCacheMgr::setList: setting list with key ${listKey}`); |
||||
return this.set(listKey, listOfGetKeys); |
||||
} |
||||
|
||||
async deepDel( |
||||
scope: string, |
||||
key: string, |
||||
direction: string |
||||
): Promise<boolean> { |
||||
key = `${this.prefix}:${key}`; |
||||
log(`RedisMockCacheMgr::deepDel: choose direction ${direction}`); |
||||
if (direction === CacheDelDirection.CHILD_TO_PARENT) { |
||||
// given a child key, delete all keys in corresponding parent lists
|
||||
const scopeList = await this.client.keys(`${this.prefix}:${scope}*list`); |
||||
for (const listKey of scopeList) { |
||||
// get target list
|
||||
let list = (await this.get(listKey, CacheGetType.TYPE_ARRAY)) || []; |
||||
if (!list.length) { |
||||
continue; |
||||
} |
||||
// remove target Key
|
||||
list = list.filter((k) => k !== key); |
||||
// delete list
|
||||
log(`RedisMockCacheMgr::deepDel: remove listKey ${listKey}`); |
||||
await this.del(listKey); |
||||
if (list.length) { |
||||
// set target list
|
||||
log(`RedisMockCacheMgr::deepDel: set key ${listKey}`); |
||||
await this.del(listKey); |
||||
await this.set(listKey, list); |
||||
} |
||||
} |
||||
log(`RedisMockCacheMgr::deepDel: remove key ${key}`); |
||||
return await this.del(key); |
||||
} else if (direction === CacheDelDirection.PARENT_TO_CHILD) { |
||||
// given a list key, delete all the children
|
||||
const listOfChildren = await this.get(key, CacheGetType.TYPE_ARRAY); |
||||
// delete each child key
|
||||
await Promise.all(listOfChildren.map(async (k) => await this.del(k))); |
||||
// delete list key
|
||||
return await this.del(key); |
||||
} else { |
||||
log(`Invalid deepDel direction found : ${direction}`); |
||||
return Promise.resolve(false); |
||||
} |
||||
} |
||||
|
||||
async appendToList( |
||||
scope: string, |
||||
subListKeys: string[], |
||||
key: string |
||||
): Promise<boolean> { |
||||
// remove null from arrays
|
||||
subListKeys = subListKeys.filter((k) => k); |
||||
// e.g. key = nc:<orgs>:<scope>:<project_id_1>:<base_id_1>:list
|
||||
const listKey = |
||||
subListKeys.length === 0 |
||||
? `${this.prefix}:${scope}:list` |
||||
: `${this.prefix}:${scope}:${subListKeys.join(':')}:list`; |
||||
log(`RedisMockCacheMgr::appendToList: append key ${key} to ${listKey}`); |
||||
let list = (await this.get(listKey, CacheGetType.TYPE_ARRAY)) || []; |
||||
if (list.length && list[0] === 'NONE') { |
||||
list = []; |
||||
await this.del(listKey); |
||||
} |
||||
list.push(key); |
||||
return this.set(listKey, list); |
||||
} |
||||
|
||||
async destroy(): Promise<boolean> { |
||||
log('RedisMockCacheMgr::destroy: destroy redis'); |
||||
return this.client.flushdb(); |
||||
} |
||||
|
||||
async export(): Promise<any> { |
||||
log('RedisMockCacheMgr::export: export data'); |
||||
const data = await this.client.keys('*'); |
||||
const res = {}; |
||||
return await Promise.all( |
||||
data.map(async (k) => { |
||||
res[k] = await this.get( |
||||
k, |
||||
k.slice(-4) === 'list' |
||||
? CacheGetType.TYPE_ARRAY |
||||
: CacheGetType.TYPE_OBJECT |
||||
); |
||||
}) |
||||
).then(() => { |
||||
return res; |
||||
}); |
||||
} |
||||
} |
@ -1,4 +0,0 @@
|
||||
export const NC_LICENSE_KEY = 'nc-license-key'; |
||||
export const NC_APP_SETTINGS = 'nc-app-settings'; |
||||
export const NC_ATTACHMENT_FIELD_SIZE = |
||||
+process.env['NC_ATTACHMENT_FIELD_SIZE'] || 20 * 1024 * 1024; // 20 MB
|
@ -1,36 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw'; |
||||
import { swaggerService } from '../../services'; |
||||
import getSwaggerHtml from './swaggerHtml'; |
||||
import getRedocHtml from './redocHtml'; |
||||
|
||||
async function swaggerJson(req, res) { |
||||
const swagger = await swaggerService.swaggerJson({ |
||||
projectId: req.params.projectId, |
||||
siteUrl: req.ncSiteUrl, |
||||
}); |
||||
|
||||
res.json(swagger); |
||||
} |
||||
|
||||
function swaggerHtml(_, res) { |
||||
res.send(getSwaggerHtml({ ncSiteUrl: process.env.NC_PUBLIC_URL || '' })); |
||||
} |
||||
|
||||
function redocHtml(_, res) { |
||||
res.send(getRedocHtml({ ncSiteUrl: process.env.NC_PUBLIC_URL || '' })); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
// todo: auth
|
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/swagger.json', |
||||
ncMetaAclMw(swaggerJson, 'swaggerJson') |
||||
); |
||||
|
||||
router.get('/api/v1/db/meta/projects/:projectId/swagger', swaggerHtml); |
||||
|
||||
router.get('/api/v1/db/meta/projects/:projectId/redoc', redocHtml); |
||||
|
||||
export default router; |
@ -1,93 +0,0 @@
|
||||
export default ({ |
||||
ncSiteUrl, |
||||
}: { |
||||
ncSiteUrl: string; |
||||
}): string => `<!DOCTYPE html>
|
||||
<html> |
||||
<head> |
||||
<title>NocoDB API Documentation</title> |
||||
<!-- needed for adaptive design --> |
||||
<meta charset="utf-8"/> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1"> |
||||
<link href="${ncSiteUrl}/css/fonts.montserrat.css" rel="stylesheet"> |
||||
<!-- |
||||
Redoc doesn't change outer page styles |
||||
--> |
||||
<style> |
||||
body { |
||||
margin: 0; |
||||
padding: 0; |
||||
} |
||||
</style> |
||||
</head> |
||||
<body> |
||||
<div id="redoc"></div> |
||||
<script src="${ncSiteUrl}/js/redoc.standalone.min.js"></script> |
||||
<script> |
||||
let initialLocalStorage = {} |
||||
|
||||
try { |
||||
initialLocalStorage = JSON.parse(localStorage.getItem('nocodb-gui-v2') || '{}'); |
||||
} catch (e) { |
||||
console.error('Failed to parse local storage', e); |
||||
} |
||||
|
||||
const xhttp = new XMLHttpRequest(); |
||||
|
||||
xhttp.open("GET", "./swagger.json"); |
||||
xhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); |
||||
xhttp.setRequestHeader("xc-auth", initialLocalStorage && initialLocalStorage.token); |
||||
|
||||
xhttp.onload = function () { |
||||
const swaggerJson = this.responseText; |
||||
const swagger = JSON.parse(swaggerJson); |
||||
Redoc.init(swagger, { |
||||
scrollYOffset: 50 |
||||
}, document.getElementById('redoc')) |
||||
}; |
||||
|
||||
xhttp.send(); |
||||
</script> |
||||
<script> |
||||
console.log('%c🚀 We are Hiring!!! 🚀%c\\n%cJoin the forces http://careers.nocodb.com', 'color:#1348ba;font-size:3rem;padding:20px;', 'display:none', 'font-size:1.5rem;padding:20px') |
||||
const linkEl = document.createElement('a') |
||||
linkEl.setAttribute('href', "http://careers.nocodb.com") |
||||
linkEl.setAttribute('target', '_blank') |
||||
linkEl.setAttribute('class', 'we-are-hiring') |
||||
linkEl.innerHTML = '🚀 We are Hiring!!! 🚀' |
||||
const styleEl = document.createElement('style'); |
||||
styleEl.innerHTML = \` |
||||
.we-are-hiring { |
||||
position: fixed; |
||||
bottom: 50px; |
||||
right: -250px; |
||||
opacity: 0; |
||||
background: orange; |
||||
border-radius: 4px; |
||||
padding: 19px; |
||||
z-index: 200; |
||||
text-decoration: none;
|
||||
text-transform: uppercase; |
||||
color: black; |
||||
transition: 1s opacity, 1s right; |
||||
display: block; |
||||
font-weight: bold; |
||||
}
|
||||
|
||||
.we-are-hiring.active { |
||||
opacity: 1; |
||||
right:25px; |
||||
} |
||||
|
||||
@media only screen and (max-width: 600px) { |
||||
.we-are-hiring { |
||||
display: none; |
||||
} |
||||
} |
||||
\` |
||||
document.body.appendChild(linkEl, document.body.firstChild) |
||||
document.body.appendChild(styleEl, document.body.firstChild) |
||||
setTimeout(() => linkEl.classList.add('active'), 2000) |
||||
</script> |
||||
</body> |
||||
</html>`;
|
@ -1,87 +0,0 @@
|
||||
export default ({ |
||||
ncSiteUrl, |
||||
}: { |
||||
ncSiteUrl: string; |
||||
}): string => `<!DOCTYPE html>
|
||||
<html> |
||||
<head> |
||||
<title>NocoDB : API Docs</title> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui"> |
||||
<link rel="shortcut icon" href="${ncSiteUrl}/favicon.ico" /> |
||||
<link rel="stylesheet" href="${ncSiteUrl}/css/swagger-ui-bundle.4.5.2.min.css"/> |
||||
<script src="${ncSiteUrl}/js/swagger-ui-bundle.4.5.2.min.js"></script> |
||||
</head> |
||||
<body> |
||||
<div id="app"></div> |
||||
<script> |
||||
|
||||
let initialLocalStorage = {} |
||||
|
||||
try { |
||||
initialLocalStorage = JSON.parse(localStorage.getItem('nocodb-gui-v2') || '{}'); |
||||
} catch (e) { |
||||
console.error('Failed to parse local storage', e); |
||||
} |
||||
|
||||
var xmlhttp = new XMLHttpRequest(); // new HttpRequest instance
|
||||
xmlhttp.open("GET", "./swagger.json"); |
||||
xmlhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); |
||||
xmlhttp.setRequestHeader("xc-auth", initialLocalStorage && initialLocalStorage.token); |
||||
xmlhttp.onload = function () { |
||||
|
||||
const ui = SwaggerUIBundle({ |
||||
// url: ,
|
||||
spec: JSON.parse(xmlhttp.responseText), |
||||
dom_id: '#app', |
||||
presets: [ |
||||
SwaggerUIBundle.presets.apis, |
||||
SwaggerUIBundle.SwaggerUIStandalonePreset |
||||
], |
||||
}) |
||||
} |
||||
xmlhttp.send(); |
||||
|
||||
|
||||
console.log('%c🚀 We are Hiring!!! 🚀%c\\n%cJoin the forces http://careers.nocodb.com', 'color:#1348ba;font-size:3rem;padding:20px;', 'display:none', 'font-size:1.5rem;padding:20px'); |
||||
const linkEl = document.createElement('a') |
||||
linkEl.setAttribute('href', "http://careers.nocodb.com") |
||||
linkEl.setAttribute('target', '_blank') |
||||
linkEl.setAttribute('class', 'we-are-hiring') |
||||
linkEl.innerHTML = '🚀 We are Hiring!!! 🚀' |
||||
const styleEl = document.createElement('style'); |
||||
styleEl.innerHTML = \` |
||||
.we-are-hiring { |
||||
position: fixed; |
||||
bottom: 50px; |
||||
right: -250px; |
||||
opacity: 0; |
||||
background: orange; |
||||
border-radius: 4px; |
||||
padding: 19px; |
||||
z-index: 200; |
||||
text-decoration: none;
|
||||
text-transform: uppercase; |
||||
color: black; |
||||
transition: 1s opacity, 1s right; |
||||
display: block; |
||||
font-weight: bold; |
||||
}
|
||||
|
||||
.we-are-hiring.active { |
||||
opacity: 1; |
||||
right:25px; |
||||
} |
||||
|
||||
@media only screen and (max-width: 600px) { |
||||
.we-are-hiring { |
||||
display: none; |
||||
} |
||||
} |
||||
\` |
||||
document.body.appendChild(linkEl, document.body.firstChild) |
||||
document.body.appendChild(styleEl, document.body.firstChild) |
||||
setTimeout(() => linkEl.classList.add('active'), 2000) |
||||
</script> |
||||
</body> |
||||
</html> |
||||
`;
|
@ -1,55 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; |
||||
import { apiTokenService } from '../services'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
export async function apiTokenList(req: Request, res: Response) { |
||||
res.json( |
||||
new PagedResponseImpl( |
||||
await apiTokenService.apiTokenList({ userId: req['user'].id }) |
||||
) |
||||
); |
||||
} |
||||
|
||||
export async function apiTokenCreate(req: Request, res: Response) { |
||||
res.json( |
||||
await apiTokenService.apiTokenCreate({ |
||||
tokenBody: req.body, |
||||
userId: req['user'].id, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function apiTokenDelete(req: Request, res: Response) { |
||||
res.json( |
||||
await apiTokenService.apiTokenDelete({ |
||||
token: req.params.token, |
||||
user: req['user'], |
||||
}) |
||||
); |
||||
} |
||||
|
||||
// todo: add reset token api to regenerate token
|
||||
|
||||
// deprecated apis
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/api-tokens', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(apiTokenList, 'apiTokenList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/api-tokens', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(apiTokenCreate, 'apiTokenCreate') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/projects/:projectId/api-tokens/:token', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(apiTokenDelete, 'apiTokenDelete') |
||||
); |
||||
|
||||
export default router; |
@ -1,128 +0,0 @@
|
||||
import path from 'path'; |
||||
import { Router } from 'express'; |
||||
import multer from 'multer'; |
||||
import { OrgUserRoles, ProjectRoles } from 'nocodb-sdk'; |
||||
import Noco from '../Noco'; |
||||
import { MetaTable } from '../utils/globals'; |
||||
import extractProjectIdAndAuthenticate from '../meta/helpers/extractProjectIdAndAuthenticate'; |
||||
import catchError, { NcError } from '../meta/helpers/catchError'; |
||||
import { NC_ATTACHMENT_FIELD_SIZE } from '../constants'; |
||||
import { getCacheMiddleware } from '../meta/api/helpers'; |
||||
import { attachmentService } from '../services'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
const isUploadAllowedMw = async (req: Request, _res: Response, next: any) => { |
||||
if (!req['user']?.id) { |
||||
if (!req['user']?.isPublicBase) { |
||||
NcError.unauthorized('Unauthorized'); |
||||
} |
||||
} |
||||
|
||||
try { |
||||
// check user is super admin or creator
|
||||
if ( |
||||
req['user'].roles?.includes(OrgUserRoles.SUPER_ADMIN) || |
||||
req['user'].roles?.includes(OrgUserRoles.CREATOR) || |
||||
req['user'].roles?.includes(ProjectRoles.EDITOR) || |
||||
// if viewer then check at-least one project have editor or higher role
|
||||
// todo: cache
|
||||
!!(await Noco.ncMeta |
||||
.knex(MetaTable.PROJECT_USERS) |
||||
.where(function () { |
||||
this.where('roles', ProjectRoles.OWNER); |
||||
this.orWhere('roles', ProjectRoles.CREATOR); |
||||
this.orWhere('roles', ProjectRoles.EDITOR); |
||||
}) |
||||
.andWhere('fk_user_id', req['user'].id) |
||||
.first()) |
||||
) |
||||
return next(); |
||||
} catch {} |
||||
NcError.badRequest('Upload not allowed'); |
||||
}; |
||||
|
||||
export async function upload(req: Request, res: Response) { |
||||
const attachments = await attachmentService.upload({ |
||||
files: (req as any).files, |
||||
path: req.query?.path as string, |
||||
}); |
||||
|
||||
res.json(attachments); |
||||
} |
||||
|
||||
export async function uploadViaURL(req: Request, res: Response) { |
||||
const attachments = await attachmentService.uploadViaURL({ |
||||
urls: req.body, |
||||
path: req.query?.path as string, |
||||
}); |
||||
|
||||
res.json(attachments); |
||||
} |
||||
|
||||
export async function fileRead(req, res) { |
||||
try { |
||||
const { img, type } = await attachmentService.fileRead({ |
||||
path: path.join('nc', 'uploads', req.params?.[0]), |
||||
}); |
||||
|
||||
res.writeHead(200, { 'Content-Type': type }); |
||||
res.end(img, 'binary'); |
||||
} catch (e) { |
||||
console.log(e); |
||||
res.status(404).send('Not found'); |
||||
} |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.get( |
||||
/^\/dl\/([^/]+)\/([^/]+)\/(.+)$/, |
||||
getCacheMiddleware(), |
||||
async (req, res) => { |
||||
try { |
||||
const { img, type } = await attachmentService.fileRead({ |
||||
path: path.join( |
||||
'nc', |
||||
req.params[0], |
||||
req.params[1], |
||||
'uploads', |
||||
...req.params[2].split('/') |
||||
), |
||||
}); |
||||
|
||||
res.writeHead(200, { 'Content-Type': type }); |
||||
res.end(img, 'binary'); |
||||
} catch (e) { |
||||
res.status(404).send('Not found'); |
||||
} |
||||
} |
||||
); |
||||
|
||||
router.post( |
||||
'/api/v1/db/storage/upload', |
||||
multer({ |
||||
storage: multer.diskStorage({}), |
||||
limits: { |
||||
fieldSize: NC_ATTACHMENT_FIELD_SIZE, |
||||
}, |
||||
}).any(), |
||||
[ |
||||
extractProjectIdAndAuthenticate, |
||||
catchError(isUploadAllowedMw), |
||||
catchError(upload), |
||||
] |
||||
); |
||||
|
||||
router.post( |
||||
'/api/v1/db/storage/upload-by-url', |
||||
|
||||
[ |
||||
extractProjectIdAndAuthenticate, |
||||
catchError(isUploadAllowedMw), |
||||
catchError(uploadViaURL), |
||||
] |
||||
); |
||||
|
||||
router.get(/^\/download\/(.+)$/, getCacheMiddleware(), catchError(fileRead)); |
||||
|
||||
export default router; |
@ -1,99 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import Audit from '../models/Audit'; |
||||
import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { auditService } from '../services'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
export async function commentRow(req: Request<any, any>, res) { |
||||
res.json( |
||||
await auditService.commentRow({ |
||||
rowId: req.params.rowId, |
||||
user: (req as any).user, |
||||
body: req.body, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function auditRowUpdate(req: Request<any, any>, res) { |
||||
res.json( |
||||
await auditService.auditRowUpdate({ |
||||
rowId: req.params.rowId, |
||||
body: req.body, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function commentList(req: Request<any, any, any>, res) { |
||||
res.json( |
||||
new PagedResponseImpl(await auditService.commentList({ query: req.query })) |
||||
); |
||||
} |
||||
|
||||
export async function commentUpdate(req, res) { |
||||
res.json( |
||||
await auditService.commentUpdate({ |
||||
auditId: req.params.auditId, |
||||
userEmail: req?.session?.passport?.user.email, |
||||
body: req.body, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function auditList(req: Request, res: Response) { |
||||
res.json( |
||||
new PagedResponseImpl( |
||||
await auditService.auditList({ |
||||
query: req.query, |
||||
projectId: req.params.projectId, |
||||
}), |
||||
{ |
||||
count: await Audit.projectAuditCount(req.params.projectId), |
||||
...req.query, |
||||
} |
||||
) |
||||
); |
||||
} |
||||
|
||||
export async function commentsCount(req: Request<any, any, any>, res) { |
||||
res.json( |
||||
await auditService.commentsCount({ |
||||
fk_model_id: req.query.fk_model_id as string, |
||||
ids: req.query.ids as string[], |
||||
}) |
||||
); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.get( |
||||
'/api/v1/db/meta/audits/comments', |
||||
ncMetaAclMw(commentList, 'commentList') |
||||
); |
||||
|
||||
router.post( |
||||
'/api/v1/db/meta/audits/comments', |
||||
ncMetaAclMw(commentRow, 'commentRow') |
||||
); |
||||
|
||||
router.patch( |
||||
'/api/v1/db/meta/audits/:auditId/comment', |
||||
ncMetaAclMw(commentUpdate, 'commentUpdate') |
||||
); |
||||
|
||||
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/projects/:projectId/audits', |
||||
ncMetaAclMw(auditList, 'auditList') |
||||
); |
||||
|
||||
export default router; |
@ -1,89 +0,0 @@
|
||||
import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import { baseService } from '../services'; |
||||
import type Base from '../models/Base'; |
||||
import type { BaseListType } from 'nocodb-sdk'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
async function baseGet(req: Request<any>, res: Response<Base>) { |
||||
const base = await baseService.baseGetWithConfig({ |
||||
baseId: req.params.baseId, |
||||
}); |
||||
|
||||
res.json(base); |
||||
} |
||||
|
||||
async function baseUpdate(req: Request<any, any, any>, res: Response<any>) { |
||||
const base = await baseService.baseUpdate({ |
||||
baseId: req.params.baseId, |
||||
base: req.body, |
||||
projectId: req.params.projectId, |
||||
}); |
||||
res.json(base); |
||||
} |
||||
|
||||
async function baseList( |
||||
req: Request<any, any, any>, |
||||
res: Response<BaseListType> |
||||
) { |
||||
const bases = await baseService.baseList({ |
||||
projectId: req.params.projectId, |
||||
}); |
||||
|
||||
res.json( |
||||
new PagedResponseImpl(bases, { |
||||
count: bases.length, |
||||
limit: bases.length, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function baseDelete( |
||||
req: Request<any, any, any>, |
||||
res: Response<any> |
||||
) { |
||||
const result = await baseService.baseDelete({ |
||||
baseId: req.params.baseId, |
||||
}); |
||||
res.json(result); |
||||
} |
||||
|
||||
async function baseCreate(req: Request<any, any>, res) { |
||||
const base = await baseService.baseCreate({ |
||||
projectId: req.params.projectId, |
||||
base: req.body, |
||||
}); |
||||
|
||||
res.json(base); |
||||
} |
||||
|
||||
const initRoutes = (router) => { |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/bases/:baseId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(baseGet, 'baseGet') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/projects/:projectId/bases/:baseId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(baseUpdate, 'baseUpdate') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/projects/:projectId/bases/:baseId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(baseDelete, 'baseDelete') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/bases', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(baseCreate, 'baseCreate') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/bases', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(baseList, 'baseList') |
||||
); |
||||
}; |
||||
|
||||
export default initRoutes; |
@ -1,21 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import catchError from '../meta/helpers/catchError'; |
||||
import { cacheService } from '../services'; |
||||
|
||||
export async function cacheGet(_, res) { |
||||
const data = await cacheService.cacheGet(); |
||||
res.set({ |
||||
'Content-Type': 'application/json', |
||||
'Content-Disposition': `attachment; filename="cache-export.json"`, |
||||
}); |
||||
res.send(JSON.stringify(data)); |
||||
} |
||||
|
||||
export async function cacheDelete(_, res) { |
||||
return res.json(await cacheService.cacheDelete()); |
||||
} |
||||
|
||||
const router = Router(); |
||||
router.get('/api/v1/db/meta/cache', catchError(cacheGet)); |
||||
router.delete('/api/v1/db/meta/cache', catchError(cacheDelete)); |
||||
export default router; |
@ -1,78 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { columnService } from '../services'; |
||||
import type { ColumnReqType, TableType, UITypes } from 'nocodb-sdk'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
export async function columnGet(req: Request, res: Response) { |
||||
res.json(await columnService.columnGet({ columnId: req.params.columnId })); |
||||
} |
||||
|
||||
export async function columnAdd( |
||||
req: Request<any, any, ColumnReqType & { uidt: UITypes }>, |
||||
res: Response<TableType> |
||||
) { |
||||
res.json( |
||||
await columnService.columnAdd({ |
||||
tableId: req.params.tableId, |
||||
column: req.body, |
||||
req, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function columnSetAsPrimary(req: Request, res: Response) { |
||||
res.json( |
||||
await columnService.columnSetAsPrimary({ columnId: req.params.columnId }) |
||||
); |
||||
} |
||||
|
||||
export async function columnUpdate(req: Request, res: Response<TableType>) { |
||||
res.json( |
||||
await columnService.columnUpdate({ |
||||
columnId: req.params.columnId, |
||||
column: req.body, |
||||
req, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function columnDelete(req: Request, res: Response<TableType>) { |
||||
res.json( |
||||
await columnService.columnDelete({ columnId: req.params.columnId, req }) |
||||
); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.post( |
||||
'/api/v1/db/meta/tables/:tableId/columns/', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(columnAdd, 'columnAdd') |
||||
); |
||||
|
||||
router.patch( |
||||
'/api/v1/db/meta/columns/:columnId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(columnUpdate, 'columnUpdate') |
||||
); |
||||
|
||||
router.delete( |
||||
'/api/v1/db/meta/columns/:columnId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(columnDelete, 'columnDelete') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/meta/columns/:columnId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(columnGet, 'columnGet') |
||||
); |
||||
|
||||
router.post( |
||||
'/api/v1/db/meta/columns/:columnId/primary', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(columnSetAsPrimary, 'columnSetAsPrimary') |
||||
); |
||||
export default router; |
@ -1,93 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import { bulkDataService } from '../../services'; |
||||
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw'; |
||||
import apiMetrics from '../../meta/helpers/apiMetrics'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
async function bulkDataInsert(req: Request, res: Response) { |
||||
res.json( |
||||
await bulkDataService.bulkDataInsert({ |
||||
body: req.body, |
||||
cookie: req, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function bulkDataUpdate(req: Request, res: Response) { |
||||
res.json( |
||||
await bulkDataService.bulkDataUpdate({ |
||||
body: req.body, |
||||
cookie: req, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
// todo: Integrate with filterArrJson bulkDataUpdateAll
|
||||
async function bulkDataUpdateAll(req: Request, res: Response) { |
||||
res.json( |
||||
await bulkDataService.bulkDataUpdateAll({ |
||||
body: req.body, |
||||
cookie: req, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
query: req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function bulkDataDelete(req: Request, res: Response) { |
||||
res.json( |
||||
await bulkDataService.bulkDataDelete({ |
||||
body: req.body, |
||||
cookie: req, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
// todo: Integrate with filterArrJson bulkDataDeleteAll
|
||||
async function bulkDataDeleteAll(req: Request, res: Response) { |
||||
res.json( |
||||
await bulkDataService.bulkDataDeleteAll({ |
||||
// cookie: req,
|
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
query: req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.post( |
||||
'/api/v1/db/data/bulk/:orgs/:projectName/:tableName', |
||||
apiMetrics, |
||||
ncMetaAclMw(bulkDataInsert, 'bulkDataInsert') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/data/bulk/:orgs/:projectName/:tableName', |
||||
apiMetrics, |
||||
ncMetaAclMw(bulkDataUpdate, 'bulkDataUpdate') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/data/bulk/:orgs/:projectName/:tableName/all', |
||||
apiMetrics, |
||||
ncMetaAclMw(bulkDataUpdateAll, 'bulkDataUpdateAll') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/data/bulk/:orgs/:projectName/:tableName', |
||||
apiMetrics, |
||||
ncMetaAclMw(bulkDataDelete, 'bulkDataDelete') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/data/bulk/:orgs/:projectName/:tableName/all', |
||||
apiMetrics, |
||||
ncMetaAclMw(bulkDataDeleteAll, 'bulkDataDeleteAll') |
||||
); |
||||
|
||||
export default router; |
@ -1,192 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import { dataService } from '../../services'; |
||||
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw'; |
||||
import apiMetrics from '../../meta/helpers/apiMetrics'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
export async function dataList(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.dataListByViewId({ |
||||
viewId: req.params.viewId, |
||||
query: req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function mmList(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.mmList({ |
||||
viewId: req.params.viewId, |
||||
colId: req.params.colId, |
||||
rowId: req.params.rowId, |
||||
query: req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function mmExcludedList(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.mmExcludedList({ |
||||
viewId: req.params.viewId, |
||||
colId: req.params.colId, |
||||
rowId: req.params.rowId, |
||||
query: req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function hmExcludedList(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.hmExcludedList({ |
||||
viewId: req.params.viewId, |
||||
colId: req.params.colId, |
||||
rowId: req.params.rowId, |
||||
query: req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function btExcludedList(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.btExcludedList({ |
||||
viewId: req.params.viewId, |
||||
colId: req.params.colId, |
||||
rowId: req.params.rowId, |
||||
query: req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function hmList(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.hmList({ |
||||
viewId: req.params.viewId, |
||||
colId: req.params.colId, |
||||
rowId: req.params.rowId, |
||||
query: req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function dataRead(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.dataReadByViewId({ |
||||
viewId: req.params.viewId, |
||||
rowId: req.params.rowId, |
||||
query: req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function dataInsert(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.dataInsertByViewId({ |
||||
viewId: req.params.viewId, |
||||
body: req.body, |
||||
cookie: req, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function dataUpdate(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.dataUpdateByViewId({ |
||||
viewId: req.params.viewId, |
||||
rowId: req.params.rowId, |
||||
body: req.body, |
||||
cookie: req, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function dataDelete(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.dataDeleteByViewId({ |
||||
viewId: req.params.viewId, |
||||
rowId: req.params.rowId, |
||||
cookie: req, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function relationDataDelete(req, res) { |
||||
await dataService.relationDataDelete({ |
||||
viewId: req.params.viewId, |
||||
colId: req.params.colId, |
||||
childId: req.params.childId, |
||||
rowId: req.params.rowId, |
||||
cookie: req, |
||||
}); |
||||
|
||||
res.json({ msg: 'The relation data has been deleted successfully' }); |
||||
} |
||||
|
||||
async function relationDataAdd(req, res) { |
||||
await dataService.relationDataAdd({ |
||||
viewId: req.params.viewId, |
||||
colId: req.params.colId, |
||||
childId: req.params.childId, |
||||
rowId: req.params.rowId, |
||||
cookie: req, |
||||
}); |
||||
|
||||
res.json({ msg: 'The relation data has been created successfully' }); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.get('/data/:viewId/', apiMetrics, ncMetaAclMw(dataList, 'dataList')); |
||||
router.post( |
||||
'/data/:viewId/', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataInsert, 'dataInsert') |
||||
); |
||||
router.get( |
||||
'/data/:viewId/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataRead, 'dataRead') |
||||
); |
||||
router.patch( |
||||
'/data/:viewId/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataUpdate, 'dataUpdate') |
||||
); |
||||
router.delete( |
||||
'/data/:viewId/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataDelete, 'dataDelete') |
||||
); |
||||
|
||||
router.get( |
||||
'/data/:viewId/:rowId/mm/:colId', |
||||
apiMetrics, |
||||
ncMetaAclMw(mmList, 'mmList') |
||||
); |
||||
router.get( |
||||
'/data/:viewId/:rowId/hm/:colId', |
||||
apiMetrics, |
||||
ncMetaAclMw(hmList, 'hmList') |
||||
); |
||||
|
||||
router.get( |
||||
'/data/:viewId/:rowId/mm/:colId/exclude', |
||||
ncMetaAclMw(mmExcludedList, 'mmExcludedList') |
||||
); |
||||
router.get( |
||||
'/data/:viewId/:rowId/hm/:colId/exclude', |
||||
ncMetaAclMw(hmExcludedList, 'hmExcludedList') |
||||
); |
||||
router.get( |
||||
'/data/:viewId/:rowId/bt/:colId/exclude', |
||||
ncMetaAclMw(btExcludedList, 'btExcludedList') |
||||
); |
||||
|
||||
router.post( |
||||
'/data/:viewId/:rowId/:relationType/:colId/:childId', |
||||
ncMetaAclMw(relationDataAdd, 'relationDataAdd') |
||||
); |
||||
router.delete( |
||||
'/data/:viewId/:rowId/:relationType/:colId/:childId', |
||||
ncMetaAclMw(relationDataDelete, 'relationDataDelete') |
||||
); |
||||
export default router; |
@ -1,260 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import { dataService } from '../../services'; |
||||
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw'; |
||||
import apiMetrics from '../../meta/helpers/apiMetrics'; |
||||
import { parseHrtimeToSeconds } from '../../meta/api/helpers'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
// todo: Handle the error case where view doesnt belong to model
|
||||
async function dataList(req: Request, res: Response) { |
||||
const startTime = process.hrtime(); |
||||
const responseData = await dataService.dataList({ |
||||
query: req.query, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
viewName: req.params.viewName, |
||||
}); |
||||
const elapsedSeconds = parseHrtimeToSeconds(process.hrtime(startTime)); |
||||
res.setHeader('xc-db-response', elapsedSeconds); |
||||
res.json(responseData); |
||||
} |
||||
|
||||
async function dataFindOne(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.dataFindOne({ |
||||
query: req.query, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
viewName: req.params.viewName, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function dataGroupBy(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.dataGroupBy({ |
||||
query: req.query, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
viewName: req.params.viewName, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function dataCount(req: Request, res: Response) { |
||||
const countResult = await dataService.dataCount({ |
||||
query: req.query, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
viewName: req.params.viewName, |
||||
}); |
||||
|
||||
res.json(countResult); |
||||
} |
||||
|
||||
async function dataInsert(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.dataInsert({ |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
viewName: req.params.viewName, |
||||
body: req.body, |
||||
cookie: req, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function dataUpdate(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.dataUpdate({ |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
viewName: req.params.viewName, |
||||
body: req.body, |
||||
cookie: req, |
||||
rowId: req.params.rowId, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function dataDelete(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.dataDelete({ |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
viewName: req.params.viewName, |
||||
cookie: req, |
||||
rowId: req.params.rowId, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function dataRead(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.dataRead({ |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
viewName: req.params.viewName, |
||||
rowId: req.params.rowId, |
||||
query: req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function dataExist(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.dataExist({ |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
viewName: req.params.viewName, |
||||
rowId: req.params.rowId, |
||||
query: req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
// todo: Handle the error case where view doesnt belong to model
|
||||
async function groupedDataList(req: Request, res: Response) { |
||||
const startTime = process.hrtime(); |
||||
const groupedData = await dataService.groupedDataList({ |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
viewName: req.params.viewName, |
||||
query: req.query, |
||||
columnId: req.params.columnId, |
||||
}); |
||||
const elapsedSeconds = parseHrtimeToSeconds(process.hrtime(startTime)); |
||||
res.setHeader('xc-db-response', elapsedSeconds); |
||||
res.json(groupedData); |
||||
} |
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
// table data crud apis
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataList, 'dataList') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/find-one', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataFindOne, 'dataFindOne') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/groupby', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataGroupBy, 'dataGroupBy') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/group/:columnId', |
||||
apiMetrics, |
||||
ncMetaAclMw(groupedDataList, 'groupedDataList') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/exist', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataExist, 'dataExist') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/count', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataCount, 'dataCount') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/count', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataCount, 'dataCount') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataRead, 'dataRead') |
||||
); |
||||
|
||||
router.patch( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataUpdate, 'dataUpdate') |
||||
); |
||||
|
||||
router.delete( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataDelete, 'dataDelete') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataList, 'dataList') |
||||
); |
||||
|
||||
// table view data crud apis
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataList, 'dataList') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/find-one', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataFindOne, 'dataFindOne') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/groupby', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataGroupBy, 'dataGroupBy') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/group/:columnId', |
||||
apiMetrics, |
||||
ncMetaAclMw(groupedDataList, 'groupedDataList') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/:rowId/exist', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataExist, 'dataExist') |
||||
); |
||||
|
||||
router.post( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataInsert, 'dataInsert') |
||||
); |
||||
|
||||
router.post( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataInsert, 'dataInsert') |
||||
); |
||||
|
||||
router.patch( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataUpdate, 'dataUpdate') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataRead, 'dataRead') |
||||
); |
||||
|
||||
router.delete( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataDelete, 'dataDelete') |
||||
); |
||||
|
||||
export default router; |
@ -1,73 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import * as XLSX from 'xlsx'; |
||||
import apiMetrics from '../../meta/helpers/apiMetrics'; |
||||
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw'; |
||||
import { View } from '../../models'; |
||||
import { extractCsvData, extractXlsxData } from '../../services/dbData/helpers'; |
||||
import { getViewAndModelFromRequestByAliasOrId } from './helpers'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
async function excelDataExport(req: Request, res: Response) { |
||||
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); |
||||
let targetView = view; |
||||
if (!targetView) { |
||||
targetView = await View.getDefaultView(model.id); |
||||
} |
||||
const { offset, elapsed, data } = await extractXlsxData(targetView, req); |
||||
const wb = XLSX.utils.book_new(); |
||||
XLSX.utils.book_append_sheet(wb, data, targetView.title); |
||||
const buf = XLSX.write(wb, { type: 'base64', bookType: 'xlsx' }); |
||||
res.set({ |
||||
'Access-Control-Expose-Headers': 'nc-export-offset', |
||||
'nc-export-offset': offset, |
||||
'nc-export-elapsed-time': elapsed, |
||||
'Content-Disposition': `attachment; filename="${encodeURI( |
||||
targetView.title |
||||
)}-export.xlsx"`,
|
||||
}); |
||||
res.end(buf); |
||||
} |
||||
|
||||
async function csvDataExport(req: Request, res: Response) { |
||||
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); |
||||
let targetView = view; |
||||
if (!targetView) { |
||||
targetView = await View.getDefaultView(model.id); |
||||
} |
||||
const { offset, elapsed, data } = await extractCsvData(targetView, req); |
||||
|
||||
res.set({ |
||||
'Access-Control-Expose-Headers': 'nc-export-offset', |
||||
'nc-export-offset': offset, |
||||
'nc-export-elapsed-time': elapsed, |
||||
'Content-Disposition': `attachment; filename="${encodeURI( |
||||
targetView.title |
||||
)}-export.csv"`,
|
||||
}); |
||||
res.send(data); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/export/csv', |
||||
apiMetrics, |
||||
ncMetaAclMw(csvDataExport, 'exportCsv') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/export/csv', |
||||
apiMetrics, |
||||
ncMetaAclMw(csvDataExport, 'exportCsv') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/export/excel', |
||||
apiMetrics, |
||||
ncMetaAclMw(excelDataExport, 'exportExcel') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/export/excel', |
||||
apiMetrics, |
||||
ncMetaAclMw(excelDataExport, 'exportExcel') |
||||
); |
||||
|
||||
export default router; |
@ -1,138 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw'; |
||||
import apiMetrics from '../../meta/helpers/apiMetrics'; |
||||
import { dataAliasNestedService } from '../../services'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
// todo: handle case where the given column is not ltar
|
||||
export async function mmList(req: Request, res: Response) { |
||||
res.json( |
||||
await dataAliasNestedService.mmList({ |
||||
query: req.query, |
||||
columnName: req.params.columnName, |
||||
rowId: req.params.rowId, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function mmExcludedList(req: Request, res: Response) { |
||||
res.json( |
||||
await dataAliasNestedService.mmExcludedList({ |
||||
query: req.query, |
||||
columnName: req.params.columnName, |
||||
rowId: req.params.rowId, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function hmExcludedList(req: Request, res: Response) { |
||||
res.json( |
||||
await dataAliasNestedService.hmExcludedList({ |
||||
query: req.query, |
||||
columnName: req.params.columnName, |
||||
rowId: req.params.rowId, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function btExcludedList(req: Request, res: Response) { |
||||
res.json( |
||||
await dataAliasNestedService.btExcludedList({ |
||||
query: req.query, |
||||
columnName: req.params.columnName, |
||||
rowId: req.params.rowId, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
// todo: handle case where the given column is not ltar
|
||||
export async function hmList(req: Request, res: Response) { |
||||
res.json( |
||||
await dataAliasNestedService.hmList({ |
||||
query: req.query, |
||||
columnName: req.params.columnName, |
||||
rowId: req.params.rowId, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
//@ts-ignore
|
||||
async function relationDataRemove(req, res) { |
||||
await dataAliasNestedService.relationDataRemove({ |
||||
columnName: req.params.columnName, |
||||
rowId: req.params.rowId, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
cookie: req, |
||||
refRowId: req.params.refRowId, |
||||
}); |
||||
|
||||
res.json({ msg: 'The relation data has been deleted successfully' }); |
||||
} |
||||
|
||||
//@ts-ignore
|
||||
// todo: Give proper error message when reference row is already related and handle duplicate ref row id in hm
|
||||
async function relationDataAdd(req, res) { |
||||
await dataAliasNestedService.relationDataAdd({ |
||||
columnName: req.params.columnName, |
||||
rowId: req.params.rowId, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
cookie: req, |
||||
refRowId: req.params.refRowId, |
||||
}); |
||||
|
||||
res.json({ msg: 'The relation data has been created successfully' }); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/mm/:columnName/exclude', |
||||
apiMetrics, |
||||
ncMetaAclMw(mmExcludedList, 'mmExcludedList') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/hm/:columnName/exclude', |
||||
apiMetrics, |
||||
ncMetaAclMw(hmExcludedList, 'hmExcludedList') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/bt/:columnName/exclude', |
||||
apiMetrics, |
||||
ncMetaAclMw(btExcludedList, 'btExcludedList') |
||||
); |
||||
|
||||
router.post( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/:relationType/:columnName/:refRowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(relationDataAdd, 'relationDataAdd') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/:relationType/:columnName/:refRowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(relationDataRemove, 'relationDataRemove') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/mm/:columnName', |
||||
apiMetrics, |
||||
ncMetaAclMw(mmList, 'mmList') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/hm/:columnName', |
||||
apiMetrics, |
||||
ncMetaAclMw(hmList, 'hmList') |
||||
); |
||||
|
||||
export default router; |
@ -1,269 +0,0 @@
|
||||
import { isSystemColumn, UITypes } from 'nocodb-sdk'; |
||||
import * as XLSX from 'xlsx'; |
||||
import papaparse from 'papaparse'; |
||||
import { NcError } from '../../meta/helpers/catchError'; |
||||
import Project from '../../models/Project'; |
||||
import Model from '../../models/Model'; |
||||
import View from '../../models/View'; |
||||
import Base from '../../models/Base'; |
||||
import NcConnectionMgrv2 from '../../utils/common/NcConnectionMgrv2'; |
||||
import Column from '../../models/Column'; |
||||
import { dataService } from '../../services'; |
||||
import type LookupColumn from '../../models/LookupColumn'; |
||||
import type LinkToAnotherRecordColumn from '../../models/LinkToAnotherRecordColumn'; |
||||
import type { Request } from 'express'; |
||||
|
||||
export async function getViewAndModelFromRequestByAliasOrId( |
||||
req: |
||||
| Request<{ projectName: string; tableName: string; viewName?: string }> |
||||
| Request |
||||
) { |
||||
const project = await Project.getWithInfoByTitleOrId(req.params.projectName); |
||||
|
||||
const model = await Model.getByAliasOrId({ |
||||
project_id: project.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 extractXlsxData(param: { |
||||
view: View; |
||||
query: any; |
||||
siteUrl: string; |
||||
}) { |
||||
const { view, query, siteUrl } = param; |
||||
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: await NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const { offset, dbRows, elapsed } = await dataService.getDbRows({ |
||||
baseModel, |
||||
view, |
||||
query, |
||||
siteUrl, |
||||
}); |
||||
|
||||
const fields = query.fields as string[]; |
||||
|
||||
const data = XLSX.utils.json_to_sheet(dbRows, { header: fields }); |
||||
|
||||
return { offset, dbRows, elapsed, data }; |
||||
} |
||||
|
||||
export async function extractCsvData(view: View, req: Request) { |
||||
const base = await Base.get(view.base_id); |
||||
const fields = req.query.fields; |
||||
|
||||
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: await NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const { offset, dbRows, elapsed } = await dataService.getDbRows({ |
||||
baseModel, |
||||
view, |
||||
query: req.query, |
||||
siteUrl: (req as any).ncSiteUrl, |
||||
}); |
||||
|
||||
const data = papaparse.unparse( |
||||
{ |
||||
fields: view.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: dbRows, |
||||
}, |
||||
{ |
||||
escapeFormulae: true, |
||||
} |
||||
); |
||||
|
||||
return { offset, dbRows, elapsed, data }; |
||||
} |
||||
//
|
||||
// async function getDbRows(baseModel, view: View, req: Request) {
|
||||
// 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 dbRows = [];
|
||||
// const startTime = process.hrtime();
|
||||
// let elapsed, temp;
|
||||
//
|
||||
// const listArgs: any = { ...req.query };
|
||||
// try {
|
||||
// listArgs.filterArr = JSON.parse(listArgs.filterArrJson);
|
||||
// } catch (e) {}
|
||||
// try {
|
||||
// listArgs.sortArr = JSON.parse(listArgs.sortArrJson);
|
||||
// } catch (e) {}
|
||||
//
|
||||
// for (
|
||||
// elapsed = 0;
|
||||
// elapsed < timeout;
|
||||
// offset += limit,
|
||||
// temp = process.hrtime(startTime),
|
||||
// elapsed = temp[0] * 1000 + temp[1] / 1000000
|
||||
// ) {
|
||||
// const rows = await nocoExecute(
|
||||
// await getAst({
|
||||
// query: req.query,
|
||||
// includePkByDefault: false,
|
||||
// model: view.model,
|
||||
// view,
|
||||
// }),
|
||||
// await baseModel.list({ ...listArgs, offset, limit }),
|
||||
// {},
|
||||
// req.query
|
||||
// );
|
||||
//
|
||||
// if (!rows?.length) {
|
||||
// offset = -1;
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// for (const row of rows) {
|
||||
// const dbRow = { ...row };
|
||||
//
|
||||
// for (const column of view.model.columns) {
|
||||
// if (isSystemColumn(column) && !view.show_system_fields) continue;
|
||||
// dbRow[column.title] = await serializeCellValue({
|
||||
// value: row[column.title],
|
||||
// column,
|
||||
// siteUrl: req['ncSiteUrl'],
|
||||
// });
|
||||
// }
|
||||
// dbRows.push(dbRow);
|
||||
// }
|
||||
// }
|
||||
// return { offset, dbRows, elapsed };
|
||||
// }
|
||||
|
||||
export async function serializeCellValue({ |
||||
value, |
||||
column, |
||||
siteUrl, |
||||
}: { |
||||
column?: Column; |
||||
value: any; |
||||
siteUrl: string; |
||||
}) { |
||||
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.path ? `${siteUrl}/${attachment.path}` : 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, |
||||
siteUrl, |
||||
}) |
||||
) |
||||
) |
||||
).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.displayValue?.title]; |
||||
}) |
||||
.join(', '); |
||||
} |
||||
break; |
||||
default: |
||||
if (value && typeof value === 'object') { |
||||
return JSON.stringify(value); |
||||
} |
||||
return value; |
||||
} |
||||
} |
||||
|
||||
export 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; |
||||
} |
@ -1,15 +0,0 @@
|
||||
import dataController from './data.ctl'; |
||||
import oldDataController from './oldData.ctl'; |
||||
import dataAliasController from './dataAlias.ctl'; |
||||
import bulkDataAliasController from './bulkDataAlias.ctl'; |
||||
import dataAliasNestedController from './dataAliasNested.ctl'; |
||||
import dataAliasExportController from './dataAliasExport.ctl'; |
||||
|
||||
export { |
||||
dataController, |
||||
oldDataController, |
||||
dataAliasController, |
||||
bulkDataAliasController, |
||||
dataAliasNestedController, |
||||
dataAliasExportController, |
||||
}; |
@ -1,179 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import { nocoExecute } from 'nc-help'; |
||||
import Model from '../../models/Model'; |
||||
import Base from '../../models/Base'; |
||||
import NcConnectionMgrv2 from '../../utils/common/NcConnectionMgrv2'; |
||||
import View from '../../models/View'; |
||||
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw'; |
||||
import Project from '../../models/Project'; |
||||
import { NcError } from '../../meta/helpers/catchError'; |
||||
import apiMetrics from '../../meta/helpers/apiMetrics'; |
||||
import getAst from '../../db/sql-data-mapper/lib/sql/helpers/getAst'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
export async function dataList(req: Request, res: Response) { |
||||
const { model, view } = await getViewAndModelFromRequest(req); |
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: await NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const { ast } = await getAst({ |
||||
query: req.query, |
||||
model, |
||||
view, |
||||
}); |
||||
|
||||
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( |
||||
ast, |
||||
await baseModel.list(listArgs), |
||||
{}, |
||||
listArgs |
||||
); |
||||
|
||||
res.json(data); |
||||
} |
||||
export async function dataCount(req: Request, res: Response) { |
||||
const { model, view } = await getViewAndModelFromRequest(req); |
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: await NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const listArgs: any = { ...req.query }; |
||||
try { |
||||
listArgs.filterArr = JSON.parse(listArgs.filterArrJson); |
||||
} catch (e) {} |
||||
|
||||
const count = await baseModel.count(listArgs); |
||||
|
||||
res.json({ |
||||
count, |
||||
}); |
||||
} |
||||
|
||||
async function dataInsert(req: Request, res: Response) { |
||||
const { model, view } = await getViewAndModelFromRequest(req); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: await NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
res.json(await baseModel.insert(req.body, null, req)); |
||||
} |
||||
|
||||
async function dataUpdate(req: Request, res: Response) { |
||||
const { model, view } = await getViewAndModelFromRequest(req); |
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view.id, |
||||
dbDriver: await NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
res.json(await baseModel.updateByPk(req.params.rowId, req.body, null, req)); |
||||
} |
||||
|
||||
async function dataDelete(req: Request, res: Response) { |
||||
const { model, view } = await getViewAndModelFromRequest(req); |
||||
const base = await Base.get(model.base_id); |
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view.id, |
||||
dbDriver: await NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
res.json(await baseModel.delByPk(req.params.rowId, null, req)); |
||||
} |
||||
|
||||
async function getViewAndModelFromRequest(req) { |
||||
const project = await Project.getWithInfo(req.params.projectId); |
||||
const model = await Model.getByAliasOrId({ |
||||
project_id: project.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 base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: await NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const { ast } = await getAst({ |
||||
query: req.query, |
||||
model, |
||||
view, |
||||
}); |
||||
|
||||
res.json( |
||||
await nocoExecute(ast, await baseModel.readByPk(req.params.rowId), {}, {}) |
||||
); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.get( |
||||
'/nc/:projectId/api/v1/:tableName', |
||||
ncMetaAclMw(dataList, 'dataList') |
||||
); |
||||
|
||||
router.get( |
||||
'/nc/:projectId/api/v1/:tableName/count', |
||||
ncMetaAclMw(dataCount, 'dataCount') |
||||
); |
||||
|
||||
router.post( |
||||
'/nc/:projectId/api/v1/:tableName', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataInsert, 'dataInsert') |
||||
); |
||||
router.get( |
||||
'/nc/:projectId/api/v1/:tableName/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataRead, 'dataRead') |
||||
); |
||||
router.patch( |
||||
'/nc/:projectId/api/v1/:tableName/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataUpdate, 'dataUpdate') |
||||
); |
||||
router.delete( |
||||
'/nc/:projectId/api/v1/:tableName/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataDelete, 'dataDelete') |
||||
); |
||||
|
||||
export default router; |
@ -1,24 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import View from '../models/View'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { extractCsvData } from './dbData/helpers'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
async function exportCsv(req: Request, res: Response) { |
||||
const view = await View.get(req.params.viewId); |
||||
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="${encodeURI( |
||||
view.title |
||||
)}-export.csv"`,
|
||||
}); |
||||
res.send(data); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get('/data/:viewId/export/csv', ncMetaAclMw(exportCsv, 'exportCsv')); |
||||
export default router; |
@ -1,19 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw'; |
||||
import { exportService } from '../../services'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
export async function exportBase(req: Request, res: Response) { |
||||
res.json( |
||||
await exportService.exportBase({ baseId: req.params.baseId, path: req.body.path }) |
||||
); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.post( |
||||
'/api/v1/db/meta/export/:projectId/:baseId', |
||||
ncMetaAclMw(exportBase, 'exportBase') |
||||
); |
||||
|
||||
export default router; |
@ -1,39 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw'; |
||||
import { importService } from '../../services'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
export async function importModels(req: Request, res: Response) { |
||||
const { body, ...rest } = req; |
||||
res.json( |
||||
await importService.importModels({ |
||||
user: (req as any).user, |
||||
projectId: req.params.projectId, |
||||
baseId: req.params.baseId, |
||||
data: Array.isArray(body) ? body : body.models, |
||||
req: rest, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function importBase(req: Request, res: Response) { |
||||
const { body, ...rest } = req; |
||||
res.json( |
||||
await importService.importBase({ |
||||
user: (req as any).user, |
||||
projectId: req.params.projectId, |
||||
baseId: req.params.baseId, |
||||
src: body.src, |
||||
req: rest, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.post( |
||||
'/api/v1/db/meta/import/:projectId/:baseId', |
||||
ncMetaAclMw(importBase, 'importBase') |
||||
); |
||||
|
||||
export default router; |
@ -1,7 +0,0 @@
|
||||
import exportController from './export.ctl'; |
||||
import importController from './import.ctl'; |
||||
|
||||
export default { |
||||
exportController, |
||||
importController, |
||||
}; |
@ -1,119 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; |
||||
import { filterService } from '../services'; |
||||
import type { FilterListType, FilterReqType } from 'nocodb-sdk'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
export async function filterGet(req: Request, res: Response) { |
||||
res.json(await filterService.filterGet({ filterId: req.params.filterId })); |
||||
} |
||||
|
||||
export async function filterList(req: Request, res: Response<FilterListType>) { |
||||
res.json( |
||||
new PagedResponseImpl( |
||||
await filterService.filterList({ |
||||
viewId: req.params.viewId, |
||||
}) |
||||
) |
||||
); |
||||
} |
||||
|
||||
export async function filterChildrenRead(req: Request, res: Response) { |
||||
res.json( |
||||
new PagedResponseImpl( |
||||
await filterService.filterChildrenList({ |
||||
filterId: req.params.filterParentId, |
||||
}) |
||||
) |
||||
); |
||||
} |
||||
|
||||
export async function filterCreate(req: Request<any, any, FilterReqType>, res) { |
||||
const filter = await filterService.filterCreate({ |
||||
filter: req.body, |
||||
viewId: req.params.viewId, |
||||
}); |
||||
res.json(filter); |
||||
} |
||||
|
||||
export async function filterUpdate(req, res) { |
||||
const filter = await filterService.filterUpdate({ |
||||
filterId: req.params.filterId, |
||||
filter: req.body, |
||||
}); |
||||
res.json(filter); |
||||
} |
||||
|
||||
export async function filterDelete(req: Request, res: Response) { |
||||
const filter = await filterService.filterDelete({ |
||||
filterId: req.params.filterId, |
||||
}); |
||||
res.json(filter); |
||||
} |
||||
|
||||
export async function hookFilterList(req: Request, res: Response) { |
||||
res.json( |
||||
new PagedResponseImpl( |
||||
await filterService.hookFilterList({ |
||||
hookId: req.params.hookId, |
||||
}) |
||||
) |
||||
); |
||||
} |
||||
|
||||
export async function hookFilterCreate( |
||||
req: Request<any, any, FilterReqType>, |
||||
res |
||||
) { |
||||
const filter = await filterService.hookFilterCreate({ |
||||
filter: req.body, |
||||
hookId: req.params.hookId, |
||||
}); |
||||
res.json(filter); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/meta/views/:viewId/filters', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterList, 'filterList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/views/:viewId/filters', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterCreate, 'filterCreate') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/meta/hooks/:hookId/filters', |
||||
ncMetaAclMw(hookFilterList, 'filterList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/hooks/:hookId/filters', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(hookFilterCreate, 'filterCreate') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/meta/filters/:filterId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterGet, 'filterGet') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/filters/:filterId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterUpdate, 'filterUpdate') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/filters/:filterId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterDelete, 'filterDelete') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/filters/:filterParentId/children', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterChildrenRead, 'filterChildrenRead') |
||||
); |
||||
export default router; |
@ -1,137 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import catchError from '../meta/helpers/catchError'; |
||||
import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import { hookService } from '../services'; |
||||
import type { HookListType, HookLogListType, HookType } from 'nocodb-sdk'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
export async function hookList( |
||||
req: Request<any, any, any>, |
||||
res: Response<HookListType> |
||||
) { |
||||
res.json( |
||||
new PagedResponseImpl( |
||||
await hookService.hookList({ tableId: req.params.tableId }) |
||||
) |
||||
); |
||||
} |
||||
|
||||
export async function hookCreate( |
||||
req: Request<any, HookType>, |
||||
res: Response<HookType> |
||||
) { |
||||
const hook = await hookService.hookCreate({ |
||||
hook: req.body, |
||||
tableId: req.params.tableId, |
||||
}); |
||||
res.json(hook); |
||||
} |
||||
|
||||
export async function hookDelete( |
||||
req: Request<any, HookType>, |
||||
res: Response<any> |
||||
) { |
||||
res.json(await hookService.hookDelete({ hookId: req.params.hookId })); |
||||
} |
||||
|
||||
export async function hookUpdate( |
||||
req: Request<any, HookType>, |
||||
res: Response<HookType> |
||||
) { |
||||
res.json( |
||||
await hookService.hookUpdate({ hookId: req.params.hookId, hook: req.body }) |
||||
); |
||||
} |
||||
|
||||
export async function hookTest(req: Request<any, any>, res: Response) { |
||||
try { |
||||
await hookService.hookTest({ |
||||
hookTest: { |
||||
...req.body, |
||||
payload: { |
||||
...req.body.payload, |
||||
user: (req as any)?.user, |
||||
}, |
||||
}, |
||||
tableId: req.params.tableId, |
||||
}); |
||||
res.json({ msg: 'The hook has been tested successfully' }); |
||||
} catch (e) { |
||||
console.error(e); |
||||
throw e; |
||||
} |
||||
} |
||||
|
||||
export async function tableSampleData(req: Request, res: Response) { |
||||
res.json( |
||||
await hookService.tableSampleData({ |
||||
tableId: req.params.tableId, |
||||
operation: req.params.operation as HookType['operation'], |
||||
version: req.params.version as HookType['version'], |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function hookLogList( |
||||
req: Request<any, any, any>, |
||||
res: Response<HookLogListType> |
||||
) { |
||||
res.json( |
||||
new PagedResponseImpl( |
||||
await hookService.hookLogList({ |
||||
query: req.query, |
||||
hookId: req.params.hookId, |
||||
}), |
||||
{ |
||||
...req.query, |
||||
count: await hookService.hookLogCount({ |
||||
hookId: req.params.hookId, |
||||
}), |
||||
} |
||||
) |
||||
); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.get( |
||||
'/api/v1/db/meta/tables/:tableId/hooks', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(hookList, 'hookList') |
||||
); |
||||
|
||||
router.post( |
||||
'/api/v1/db/meta/tables/:tableId/hooks/test', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(hookTest, 'hookTest') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/tables/:tableId/hooks', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(hookCreate, 'hookCreate') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/hooks/:hookId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(hookDelete, 'hookDelete') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/hooks/:hookId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(hookUpdate, 'hookUpdate') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/tables/:tableId/hooks/samplePayload/:operation/:version', |
||||
metaApiMetrics, |
||||
catchError(tableSampleData) |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/meta/hooks/:hookId/logs', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(hookLogList, 'hookLogList') |
||||
); |
||||
|
||||
export default router; |
@ -1,54 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import { metaDiffService } from '../services'; |
||||
|
||||
export async function metaDiff(req, res) { |
||||
res.json(await metaDiffService.metaDiff({ projectId: req.params.projectId })); |
||||
} |
||||
|
||||
export async function baseMetaDiff(req, res) { |
||||
res.json( |
||||
await metaDiffService.baseMetaDiff({ |
||||
baseId: req.params.baseId, |
||||
projectId: req.params.projectId, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function metaDiffSync(req, res) { |
||||
await metaDiffService.metaDiffSync({ projectId: req.params.projectId }); |
||||
res.json({ msg: 'The meta has been synchronized successfully' }); |
||||
} |
||||
|
||||
export async function baseMetaDiffSync(req, res) { |
||||
await metaDiffService.baseMetaDiffSync({ |
||||
projectId: req.params.projectId, |
||||
baseId: req.params.baseId, |
||||
}); |
||||
|
||||
res.json({ msg: 'The base meta has been synchronized successfully' }); |
||||
} |
||||
|
||||
const router = Router(); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/meta-diff', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(metaDiff, 'metaDiff') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/meta-diff', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(metaDiffSync, 'metaDiffSync') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/meta-diff/:baseId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(baseMetaDiff, 'baseMetaDiff') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/meta-diff/:baseId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(baseMetaDiffSync, 'baseMetaDiffSync') |
||||
); |
||||
export default router; |
@ -1,34 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import { modelVisibilityService } from '../services'; |
||||
|
||||
async function xcVisibilityMetaSetAll(req, res) { |
||||
await modelVisibilityService.xcVisibilityMetaSetAll({ |
||||
visibilityRule: req.body, |
||||
projectId: req.params.projectId, |
||||
}); |
||||
|
||||
res.json({ msg: 'UI ACL has been created successfully' }); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/visibility-rules', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(async (req, res) => { |
||||
res.json( |
||||
await modelVisibilityService.xcVisibilityMetaGet({ |
||||
projectId: req.params.projectId, |
||||
includeM2M: |
||||
req.query.includeM2M === true || req.query.includeM2M === 'true', |
||||
}) |
||||
); |
||||
}, 'modelVisibilityList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/visibility-rules', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(xcVisibilityMetaSetAll, 'modelVisibilitySet') |
||||
); |
||||
export default router; |
@ -1,34 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import { OrgUserRoles } from 'nocodb-sdk'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { orgLicenseService } from '../services'; |
||||
|
||||
async function licenseGet(_req, res) { |
||||
res.json(await orgLicenseService.licenseGet()); |
||||
} |
||||
|
||||
async function licenseSet(req, res) { |
||||
await orgLicenseService.licenseSet({ key: req.body.key }); |
||||
res.json({ msg: 'The license key has been saved' }); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/license', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(licenseGet, 'licenseGet', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
router.post( |
||||
'/api/v1/license', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(licenseSet, 'licenseSet', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
|
||||
export default router; |
@ -1,64 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import { getConditionalHandler } from '../meta/helpers/getHandler'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { orgTokenService, orgTokenServiceEE } from '../services'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
async function apiTokenList(req, res) { |
||||
res.json( |
||||
await getConditionalHandler( |
||||
orgTokenService.apiTokenList, |
||||
orgTokenServiceEE.apiTokenListEE |
||||
)({ |
||||
query: req.query, |
||||
user: req['user'], |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function apiTokenCreate(req: Request, res: Response) { |
||||
res.json( |
||||
await orgTokenService.apiTokenCreate({ |
||||
apiToken: req.body, |
||||
user: req['user'], |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function apiTokenDelete(req: Request, res: Response) { |
||||
res.json( |
||||
await orgTokenService.apiTokenDelete({ |
||||
token: req.params.token, |
||||
user: req['user'], |
||||
}) |
||||
); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.get( |
||||
'/api/v1/tokens', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(apiTokenList, 'apiTokenList', { |
||||
// allowedRoles: [OrgUserRoles.SUPER],
|
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
router.post( |
||||
'/api/v1/tokens', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(apiTokenCreate, 'apiTokenCreate', { |
||||
// allowedRoles: [OrgUserRoles.SUPER],
|
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
router.delete( |
||||
'/api/v1/tokens/:token', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(apiTokenDelete, 'apiTokenDelete', { |
||||
// allowedRoles: [OrgUserRoles.SUPER],
|
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
export default router; |
@ -1,162 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import { OrgUserRoles } from 'nocodb-sdk'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; |
||||
import { orgUserService } from '../services'; |
||||
import { User } from '../models'; |
||||
|
||||
async function userList(req, res) { |
||||
res.json( |
||||
new PagedResponseImpl( |
||||
await orgUserService.userList({ |
||||
query: req.query, |
||||
}), |
||||
{ |
||||
...req.query, |
||||
count: await User.count(req.query), |
||||
} |
||||
) |
||||
); |
||||
} |
||||
|
||||
async function userUpdate(req, res) { |
||||
res.json( |
||||
await orgUserService.userUpdate({ |
||||
user: req.body, |
||||
userId: req.params.userId, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function userDelete(req, res) { |
||||
await orgUserService.userDelete({ |
||||
userId: req.params.userId, |
||||
}); |
||||
res.json({ msg: 'The user has been deleted successfully' }); |
||||
} |
||||
|
||||
async function userAdd(req, res) { |
||||
const result = await orgUserService.userAdd({ |
||||
user: req.body, |
||||
req, |
||||
projectId: req.params.projectId, |
||||
}); |
||||
|
||||
res.json(result); |
||||
} |
||||
|
||||
async function userSettings(_req, res): Promise<any> { |
||||
await orgUserService.userSettings({}); |
||||
res.json({}); |
||||
} |
||||
|
||||
async function userInviteResend(req, res): Promise<any> { |
||||
await orgUserService.userInviteResend({ |
||||
userId: req.params.userId, |
||||
req, |
||||
}); |
||||
|
||||
res.json({ msg: 'The invitation has been sent to the user' }); |
||||
} |
||||
|
||||
async function generateResetUrl(req, res) { |
||||
const result = await orgUserService.generateResetUrl({ |
||||
siteUrl: req.ncSiteUrl, |
||||
userId: req.params.userId, |
||||
}); |
||||
|
||||
res.json(result); |
||||
} |
||||
|
||||
async function appSettingsGet(_req, res) { |
||||
const settings = await orgUserService.appSettingsGet(); |
||||
res.json(settings); |
||||
} |
||||
|
||||
async function appSettingsSet(req, res) { |
||||
await orgUserService.appSettingsSet({ |
||||
settings: req.body, |
||||
}); |
||||
|
||||
res.json({ msg: 'The app settings have been saved' }); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/users', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(userList, 'userList', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
router.patch( |
||||
'/api/v1/users/:userId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(userUpdate, 'userUpdate', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
router.delete( |
||||
'/api/v1/users/:userId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(userDelete, 'userDelete', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
router.post( |
||||
'/api/v1/users', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(userAdd, 'userAdd', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
router.post( |
||||
'/api/v1/users/settings', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(userSettings, 'userSettings', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
router.post( |
||||
'/api/v1/users/:userId/resend-invite', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(userInviteResend, 'userInviteResend', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
|
||||
router.post( |
||||
'/api/v1/users/:userId/generate-reset-url', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(generateResetUrl, 'generateResetUrl', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/app-settings', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(appSettingsGet, 'appSettingsGet', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
|
||||
router.post( |
||||
'/api/v1/app-settings', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(appSettingsSet, 'appSettingsSet', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
|
||||
export default router; |
@ -1,75 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import { pluginService } from '../services'; |
||||
import type { PluginType } from 'nocodb-sdk'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
export async function pluginList(_req: Request, res: Response) { |
||||
res.json(new PagedResponseImpl(await pluginService.pluginList())); |
||||
} |
||||
|
||||
export async function pluginTest(req: Request<any, any>, res: Response) { |
||||
res.json(await pluginService.pluginTest({ body: req.body })); |
||||
} |
||||
|
||||
export async function pluginRead(req: Request, res: Response) { |
||||
res.json(await pluginService.pluginRead({ pluginId: req.params.pluginId })); |
||||
} |
||||
|
||||
export async function pluginUpdate( |
||||
req: Request<any, any, PluginType>, |
||||
res: Response |
||||
) { |
||||
const plugin = await pluginService.pluginUpdate({ |
||||
pluginId: req.params.pluginId, |
||||
plugin: req.body, |
||||
}); |
||||
res.json(plugin); |
||||
} |
||||
|
||||
export async function isPluginActive(req: Request, res: Response) { |
||||
res.json( |
||||
await pluginService.isPluginActive({ pluginTitle: req.params.pluginTitle }) |
||||
); |
||||
} |
||||
|
||||
const blockInCloudMw = (_req, res, next) => { |
||||
if (process.env.NC_CLOUD === 'true') { |
||||
res.status(403).send('Not allowed'); |
||||
} else next(); |
||||
}; |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/meta/plugins', |
||||
blockInCloudMw, |
||||
metaApiMetrics, |
||||
ncMetaAclMw(pluginList, 'pluginList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/plugins/test', |
||||
metaApiMetrics, |
||||
blockInCloudMw, |
||||
ncMetaAclMw(pluginTest, 'pluginTest') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/plugins/:pluginId', |
||||
metaApiMetrics, |
||||
blockInCloudMw, |
||||
ncMetaAclMw(pluginRead, 'pluginRead') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/plugins/:pluginId', |
||||
metaApiMetrics, |
||||
blockInCloudMw, |
||||
ncMetaAclMw(pluginUpdate, 'pluginUpdate') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/plugins/:pluginTitle/status', |
||||
metaApiMetrics, |
||||
blockInCloudMw, |
||||
ncMetaAclMw(isPluginActive, 'isPluginActive') |
||||
); |
||||
export default router; |
@ -1,168 +0,0 @@
|
||||
import { T } from 'nc-help'; |
||||
import isDocker from 'is-docker'; |
||||
import { packageVersion } from '../utils/packageVersion'; |
||||
import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; |
||||
import NcConnectionMgrv2 from '../utils/common/NcConnectionMgrv2'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import ProjectUser from '../models/ProjectUser'; |
||||
import Noco from '../Noco'; |
||||
import Project from '../models/Project'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import Filter from '../models/Filter'; |
||||
import { projectService } from '../services'; |
||||
import type { ProjectListType } from 'nocodb-sdk'; |
||||
import type { ProjectType } from 'nocodb-sdk'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
// // Project CRUD
|
||||
|
||||
export async function projectGet( |
||||
req: Request<any, any, any>, |
||||
res: Response<Project> |
||||
) { |
||||
const project = await projectService.getProjectWithInfo({ |
||||
projectId: req.params.projectId, |
||||
}); |
||||
|
||||
projectService.sanitizeProject(project); |
||||
|
||||
res.json(project); |
||||
} |
||||
|
||||
export async function projectUpdate( |
||||
req: Request<any, any, any>, |
||||
res: Response<ProjectListType> |
||||
) { |
||||
const project = await projectService.projectUpdate({ |
||||
projectId: req.params.projectId, |
||||
project: req.body, |
||||
}); |
||||
|
||||
res.json(project); |
||||
} |
||||
|
||||
export async function projectList( |
||||
req: Request<any> & { user: { id: string; roles: string } }, |
||||
res: Response<ProjectListType> |
||||
) { |
||||
const projects = await projectService.projectList({ |
||||
user: req.user, |
||||
query: req.query, |
||||
}); |
||||
|
||||
res.json( |
||||
new PagedResponseImpl(projects as ProjectType[], { |
||||
count: projects.length, |
||||
limit: projects.length, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function projectDelete(req: Request<any>, res: Response<boolean>) { |
||||
const deleted = await projectService.projectSoftDelete({ |
||||
projectId: req.params.projectId, |
||||
}); |
||||
|
||||
res.json(deleted); |
||||
} |
||||
|
||||
async function projectCreate(req: Request<any, any>, res) { |
||||
const project = await projectService.projectCreate({ |
||||
project: req.body, |
||||
user: req['user'], |
||||
}); |
||||
|
||||
res.json(project); |
||||
} |
||||
|
||||
export async function projectInfoGet(_req, res) { |
||||
res.json({ |
||||
Node: process.version, |
||||
Arch: process.arch, |
||||
Platform: process.platform, |
||||
Docker: isDocker(), |
||||
RootDB: Noco.getConfig()?.meta?.db?.client, |
||||
PackageVersion: packageVersion, |
||||
}); |
||||
} |
||||
|
||||
export async function projectCost(req, res) { |
||||
let cost = 0; |
||||
const project = await Project.getWithInfo(req.params.projectId); |
||||
|
||||
for (const base of project.bases) { |
||||
const sqlClient = await NcConnectionMgrv2.getSqlClient(base); |
||||
const userCount = await ProjectUser.getUsersCount(req.query); |
||||
const recordCount = (await sqlClient.totalRecords())?.data.TotalRecords; |
||||
|
||||
if (recordCount > 100000) { |
||||
// 36,000 or $79/user/month
|
||||
cost = Math.max(36000, 948 * userCount); |
||||
} else if (recordCount > 50000) { |
||||
// $36,000 or $50/user/month
|
||||
cost = Math.max(36000, 600 * userCount); |
||||
} else if (recordCount > 10000) { |
||||
// $240/user/yr
|
||||
cost = Math.min(240 * userCount, 36000); |
||||
} else if (recordCount > 1000) { |
||||
// $120/user/yr
|
||||
cost = Math.min(120 * userCount, 36000); |
||||
} |
||||
} |
||||
|
||||
T.event({ |
||||
event: 'a:project:cost', |
||||
data: { |
||||
cost, |
||||
}, |
||||
}); |
||||
|
||||
res.json({ cost }); |
||||
} |
||||
|
||||
export async function hasEmptyOrNullFilters(req, res) { |
||||
res.json(await Filter.hasEmptyOrNullFilters(req.params.projectId)); |
||||
} |
||||
|
||||
export default (router) => { |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/info', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectInfoGet, 'projectInfoGet') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectGet, 'projectGet') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/projects/:projectId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectUpdate, 'projectUpdate') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/cost', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectCost, 'projectCost') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/projects/:projectId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectDelete, 'projectDelete') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectCreate, 'projectCreate') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/projects', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectList, 'projectList') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/has-empty-or-null-filters', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(hasEmptyOrNullFilters, 'hasEmptyOrNullFilters') |
||||
); |
||||
}; |
@ -1,86 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import { projectUserService } from '../services'; |
||||
|
||||
async function userList(req, res) { |
||||
res.json({ |
||||
users: await projectUserService.userList({ |
||||
projectId: req.params.projectId, |
||||
query: req.query, |
||||
}), |
||||
}); |
||||
} |
||||
|
||||
async function userInvite(req, res): Promise<any> { |
||||
res.json( |
||||
await projectUserService.userInvite({ |
||||
projectId: req.params.projectId, |
||||
projectUser: req.body, |
||||
req, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function projectUserUpdate(req, res): Promise<any> { |
||||
await projectUserService.projectUserUpdate({ |
||||
projectUser: req.body, |
||||
projectId: req.params.projectId, |
||||
userId: req.params.userId, |
||||
req, |
||||
}); |
||||
res.json({ |
||||
msg: 'The user has been updated successfully', |
||||
}); |
||||
} |
||||
|
||||
async function projectUserDelete(req, res): Promise<any> { |
||||
await projectUserService.projectUserDelete({ |
||||
projectId: req.params.projectId, |
||||
userId: req.params.userId, |
||||
req, |
||||
}); |
||||
res.json({ |
||||
msg: 'The user has been deleted successfully', |
||||
}); |
||||
} |
||||
|
||||
async function projectUserInviteResend(req, res): Promise<any> { |
||||
await projectUserService.projectUserInviteResend({ |
||||
projectId: req.params.projectId, |
||||
userId: req.params.userId, |
||||
projectUser: req.body, |
||||
req, |
||||
}); |
||||
res.json({ |
||||
msg: 'The invitation has been sent to the user', |
||||
}); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/users', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(userList, 'userList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/users', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(userInvite, 'userInvite') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/projects/:projectId/users/:userId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectUserUpdate, 'projectUserUpdate') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/projects/:projectId/users/:userId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectUserDelete, 'projectUserDelete') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/users/:userId/resend-invite', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectUserInviteResend, 'projectUserInviteResend') |
||||
); |
||||
export default router; |
@ -1,9 +0,0 @@
|
||||
import publicDataController from './publicData.ctl'; |
||||
import publicDataExportController from './publicDataExport.ctl'; |
||||
import publicMetaController from './publicMeta.ctl'; |
||||
|
||||
export { |
||||
publicDataController, |
||||
publicDataExportController, |
||||
publicMetaController, |
||||
}; |
@ -1,107 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import multer from 'multer'; |
||||
import { NC_ATTACHMENT_FIELD_SIZE } from '../../constants'; |
||||
import catchError from '../../meta/helpers/catchError'; |
||||
import { publicDataService } from '../../services'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
export async function dataList(req: Request, res: Response) { |
||||
const pagedResponse = await publicDataService.dataList({ |
||||
query: req.query, |
||||
password: req.headers?.['xc-password'] as string, |
||||
sharedViewUuid: req.params.sharedViewUuid, |
||||
}); |
||||
res.json(pagedResponse); |
||||
} |
||||
|
||||
// todo: Handle the error case where view doesnt belong to model
|
||||
async function groupedDataList(req: Request, res: Response) { |
||||
const groupedData = await publicDataService.groupedDataList({ |
||||
query: req.query, |
||||
password: req.headers?.['xc-password'] as string, |
||||
sharedViewUuid: req.params.sharedViewUuid, |
||||
groupColumnId: req.params.columnId, |
||||
}); |
||||
res.json(groupedData); |
||||
} |
||||
|
||||
async function dataInsert(req: Request & { files: any[] }, res: Response) { |
||||
const insertResult = await publicDataService.dataInsert({ |
||||
sharedViewUuid: req.params.sharedViewUuid, |
||||
password: req.headers?.['xc-password'] as string, |
||||
body: req.body?.data, |
||||
siteUrl: (req as any).ncSiteUrl, |
||||
// req.files is enriched by multer
|
||||
files: req.files, |
||||
}); |
||||
|
||||
res.json(insertResult); |
||||
} |
||||
|
||||
async function relDataList(req, res) { |
||||
const pagedResponse = await publicDataService.relDataList({ |
||||
query: req.query, |
||||
password: req.headers?.['xc-password'] as string, |
||||
sharedViewUuid: req.params.sharedViewUuid, |
||||
columnId: req.params.columnId, |
||||
}); |
||||
|
||||
res.json(pagedResponse); |
||||
} |
||||
|
||||
export async function publicMmList(req: Request, res: Response) { |
||||
const paginatedResponse = await publicDataService.publicMmList({ |
||||
query: req.query, |
||||
password: req.headers?.['xc-password'] as string, |
||||
sharedViewUuid: req.params.sharedViewUuid, |
||||
columnId: req.params.columnId, |
||||
rowId: req.params.rowId, |
||||
}); |
||||
res.json(paginatedResponse); |
||||
} |
||||
|
||||
export async function publicHmList(req: Request, res: Response) { |
||||
const paginatedResponse = await publicDataService.publicHmList({ |
||||
query: req.query, |
||||
password: req.headers?.['xc-password'] as string, |
||||
sharedViewUuid: req.params.sharedViewUuid, |
||||
columnId: req.params.columnId, |
||||
rowId: req.params.rowId, |
||||
}); |
||||
res.json(paginatedResponse); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/public/shared-view/:sharedViewUuid/rows', |
||||
catchError(dataList) |
||||
); |
||||
router.get( |
||||
'/api/v1/db/public/shared-view/:sharedViewUuid/group/:columnId', |
||||
catchError(groupedDataList) |
||||
); |
||||
router.get( |
||||
'/api/v1/db/public/shared-view/:sharedViewUuid/nested/:columnId', |
||||
catchError(relDataList) |
||||
); |
||||
router.post( |
||||
'/api/v1/db/public/shared-view/:sharedViewUuid/rows', |
||||
multer({ |
||||
storage: multer.diskStorage({}), |
||||
limits: { |
||||
fieldSize: NC_ATTACHMENT_FIELD_SIZE, |
||||
}, |
||||
}).any(), |
||||
catchError(dataInsert) |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/public/shared-view/:sharedViewUuid/rows/:rowId/mm/:columnId', |
||||
catchError(publicMmList) |
||||
); |
||||
router.get( |
||||
'/api/v1/db/public/shared-view/:sharedViewUuid/rows/:rowId/hm/:columnId', |
||||
catchError(publicHmList) |
||||
); |
||||
|
||||
export default router; |
@ -1,261 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import * as XLSX from 'xlsx'; |
||||
import { nocoExecute } from 'nc-help'; |
||||
import papaparse from 'papaparse'; |
||||
import { ErrorMessages, isSystemColumn, UITypes, ViewTypes } from 'nocodb-sdk'; |
||||
import getAst from '../../db/sql-data-mapper/lib/sql/helpers/getAst'; |
||||
import catchError, { NcError } from '../../meta/helpers/catchError'; |
||||
import { Base, Column, Model, View } from '../../models'; |
||||
import NcConnectionMgrv2 from '../../utils/common/NcConnectionMgrv2'; |
||||
import type { LinkToAnotherRecordColumn, LookupColumn } from '../../models'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
async function exportExcel(req: Request, res: Response) { |
||||
const view = await View.getByUUID(req.params.publicDataUuid); |
||||
if (!view) NcError.notFound('Not found'); |
||||
if ( |
||||
view.type !== ViewTypes.GRID && |
||||
view.type !== ViewTypes.KANBAN && |
||||
view.type !== ViewTypes.GALLERY && |
||||
view.type !== ViewTypes.MAP |
||||
) |
||||
NcError.notFound('Not found'); |
||||
|
||||
if (view.password && view.password !== req.headers?.['xc-password']) { |
||||
NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD); |
||||
} |
||||
|
||||
const model = await view.getModelWithInfo(); |
||||
|
||||
await view.getColumns(); |
||||
|
||||
const { offset, dbRows, elapsed } = await getDbRows(model, view, req); |
||||
|
||||
const fields = req.query.fields as string[]; |
||||
|
||||
const data = XLSX.utils.json_to_sheet( |
||||
dbRows.map((o: Record<string, any>) => |
||||
Object.fromEntries(fields.map((f) => [f, o[f]])) |
||||
), |
||||
{ header: fields } |
||||
); |
||||
|
||||
const wb = XLSX.utils.book_new(); |
||||
|
||||
XLSX.utils.book_append_sheet(wb, data, view.title); |
||||
|
||||
const buf = XLSX.write(wb, { type: 'base64', bookType: 'xlsx' }); |
||||
|
||||
res.set({ |
||||
'Access-Control-Expose-Headers': 'nc-export-offset', |
||||
'nc-export-offset': offset, |
||||
'nc-export-elapsed-time': elapsed, |
||||
'Content-Disposition': `attachment; filename="${encodeURI( |
||||
view.title |
||||
)}-export.xlsx"`,
|
||||
}); |
||||
res.end(buf); |
||||
} |
||||
|
||||
async function exportCsv(req: Request, res: Response) { |
||||
const view = await View.getByUUID(req.params.publicDataUuid); |
||||
const fields = req.query.fields; |
||||
|
||||
if (!view) NcError.notFound('Not found'); |
||||
if ( |
||||
view.type !== ViewTypes.GRID && |
||||
view.type !== ViewTypes.KANBAN && |
||||
view.type !== ViewTypes.GALLERY && |
||||
view.type !== ViewTypes.MAP |
||||
) |
||||
NcError.notFound('Not found'); |
||||
|
||||
if (view.password && view.password !== req.headers?.['xc-password']) { |
||||
NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD); |
||||
} |
||||
|
||||
const model = await view.getModelWithInfo(); |
||||
await view.getColumns(); |
||||
|
||||
const { offset, dbRows, elapsed } = await getDbRows(model, view, req); |
||||
|
||||
const data = papaparse.unparse( |
||||
{ |
||||
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: dbRows, |
||||
}, |
||||
{ |
||||
escapeFormulae: true, |
||||
} |
||||
); |
||||
|
||||
res.set({ |
||||
'Access-Control-Expose-Headers': 'nc-export-offset', |
||||
'nc-export-offset': offset, |
||||
'nc-export-elapsed-time': elapsed, |
||||
'Content-Disposition': `attachment; filename="${encodeURI( |
||||
view.title |
||||
)}-export.csv"`,
|
||||
}); |
||||
res.send(data); |
||||
} |
||||
|
||||
async function getDbRows(model, view: View, req: Request) { |
||||
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) NcError.notFound('Table not found'); |
||||
|
||||
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, |
||||
viewId: view?.id, |
||||
dbDriver: await NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const { ast } = await getAst({ |
||||
query: req.query, |
||||
model, |
||||
view, |
||||
includePkByDefault: 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 dbRows = []; |
||||
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( |
||||
ast, |
||||
await baseModel.list({ ...listArgs, offset, limit }), |
||||
{}, |
||||
listArgs |
||||
); |
||||
|
||||
if (!rows?.length) { |
||||
offset = -1; |
||||
break; |
||||
} |
||||
|
||||
for (const row of rows) { |
||||
const dbRow = { ...row }; |
||||
|
||||
for (const column of view.model.columns) { |
||||
dbRow[column.title] = await serializeCellValue({ |
||||
value: row[column.title], |
||||
column, |
||||
}); |
||||
} |
||||
dbRows.push(dbRow); |
||||
} |
||||
} |
||||
return { offset, dbRows, elapsed }; |
||||
} |
||||
|
||||
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.displayValue?.title]; |
||||
}) |
||||
.join(', '); |
||||
} |
||||
break; |
||||
default: |
||||
if (value && typeof value === 'object') { |
||||
return JSON.stringify(value); |
||||
} |
||||
return value; |
||||
} |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/public/shared-view/:publicDataUuid/rows/export/csv', |
||||
catchError(exportCsv) |
||||
); |
||||
router.get( |
||||
'/api/v1/db/public/shared-view/:publicDataUuid/rows/export/excel', |
||||
catchError(exportExcel) |
||||
); |
||||
export default router; |
@ -1,32 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import catchError from '../../meta/helpers/catchError'; |
||||
import { publicMetaService } from '../../services'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
export async function viewMetaGet(req: Request, res: Response) { |
||||
res.json( |
||||
await publicMetaService.viewMetaGet({ |
||||
password: req.headers?.['xc-password'] as string, |
||||
sharedViewUuid: req.params.sharedViewUuid, |
||||
}) |
||||
); |
||||
} |
||||
async function publicSharedBaseGet(req, res): Promise<any> { |
||||
res.json( |
||||
await publicMetaService.publicSharedBaseGet({ |
||||
sharedBaseUuid: req.params.sharedBaseUuid, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
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; |
@ -1,61 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { sharedBaseService } from '../services'; |
||||
|
||||
async function createSharedBaseLink(req, res): Promise<any> { |
||||
const sharedBase = await sharedBaseService.createSharedBaseLink({ |
||||
projectId: req.params.projectId, |
||||
roles: req.body?.roles, |
||||
password: req.body?.password, |
||||
siteUrl: req.ncSiteUrl, |
||||
}); |
||||
|
||||
res.json(sharedBase); |
||||
} |
||||
|
||||
async function updateSharedBaseLink(req, res): Promise<any> { |
||||
const sharedBase = await sharedBaseService.updateSharedBaseLink({ |
||||
projectId: req.params.projectId, |
||||
roles: req.body?.roles, |
||||
password: req.body?.password, |
||||
siteUrl: req.ncSiteUrl, |
||||
}); |
||||
|
||||
res.json(sharedBase); |
||||
} |
||||
|
||||
async function disableSharedBaseLink(req, res): Promise<any> { |
||||
const sharedBase = await sharedBaseService.disableSharedBaseLink({ |
||||
projectId: req.params.projectId, |
||||
}); |
||||
|
||||
res.json(sharedBase); |
||||
} |
||||
|
||||
async function getSharedBaseLink(req, res): Promise<any> { |
||||
const sharedBase = await sharedBaseService.getSharedBaseLink({ |
||||
projectId: req.params.projectId, |
||||
siteUrl: req.ncSiteUrl, |
||||
}); |
||||
|
||||
res.json(sharedBase); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/shared', |
||||
ncMetaAclMw(getSharedBaseLink, 'getSharedBaseLink') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/shared', |
||||
ncMetaAclMw(createSharedBaseLink, 'createSharedBaseLink') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/projects/:projectId/shared', |
||||
ncMetaAclMw(updateSharedBaseLink, 'updateSharedBaseLink') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/projects/:projectId/shared', |
||||
ncMetaAclMw(disableSharedBaseLink, 'disableSharedBaseLink') |
||||
); |
||||
export default router; |
@ -1,80 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import { sortService } from '../services'; |
||||
import type { SortListType, SortReqType } from 'nocodb-sdk'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
export async function sortList( |
||||
req: Request<any, any, any>, |
||||
res: Response<SortListType> |
||||
) { |
||||
res.json( |
||||
new PagedResponseImpl( |
||||
await sortService.sortList({ |
||||
viewId: req.params.viewId, |
||||
}) |
||||
) |
||||
); |
||||
} |
||||
|
||||
export async function sortCreate(req: Request<any, any, SortReqType>, res) { |
||||
const sort = await sortService.sortCreate({ |
||||
sort: req.body, |
||||
viewId: req.params.viewId, |
||||
}); |
||||
res.json(sort); |
||||
} |
||||
|
||||
export async function sortUpdate(req, res) { |
||||
const sort = await sortService.sortUpdate({ |
||||
sortId: req.params.sortId, |
||||
sort: req.body, |
||||
}); |
||||
res.json(sort); |
||||
} |
||||
|
||||
export async function sortDelete(req: Request, res: Response) { |
||||
const sort = await sortService.sortDelete({ |
||||
sortId: req.params.sortId, |
||||
}); |
||||
res.json(sort); |
||||
} |
||||
|
||||
export async function sortGet(req: Request, res: Response) { |
||||
const sort = await sortService.sortGet({ |
||||
sortId: req.params.sortId, |
||||
}); |
||||
res.json(sort); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/meta/views/:viewId/sorts/', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(sortList, 'sortList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/views/:viewId/sorts/', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(sortCreate, 'sortCreate') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/meta/sorts/:sortId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(sortGet, 'sortGet') |
||||
); |
||||
|
||||
router.patch( |
||||
'/api/v1/db/meta/sorts/:sortId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(sortUpdate, 'sortUpdate') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/sorts/:sortId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(sortDelete, 'sortDelete') |
||||
); |
||||
export default router; |
@ -1,141 +0,0 @@
|
||||
// import { Queue } from 'bullmq';
|
||||
// import axios from 'axios';
|
||||
import catchError, { NcError } from '../../meta/helpers/catchError'; |
||||
import NocoJobs from '../../jobs/NocoJobs'; |
||||
import { SyncSource } from '../../models'; |
||||
import Noco from '../../Noco'; |
||||
import { syncService, userService } from '../../services'; |
||||
import type { Server } from 'socket.io'; |
||||
import type { Request, Router } from 'express'; |
||||
import type { AirtableSyncConfig } from '../../services/sync/helpers/job'; |
||||
|
||||
const AIRTABLE_IMPORT_JOB = 'AIRTABLE_IMPORT_JOB'; |
||||
const AIRTABLE_PROGRESS_JOB = 'AIRTABLE_PROGRESS_JOB'; |
||||
|
||||
enum SyncStatus { |
||||
PROGRESS = 'PROGRESS', |
||||
COMPLETED = 'COMPLETED', |
||||
FAILED = 'FAILED', |
||||
} |
||||
|
||||
export default ( |
||||
router: Router, |
||||
sv: Server, |
||||
jobs: { [id: string]: { last_message: any } } |
||||
) => { |
||||
// add importer job handler and progress notification job handler
|
||||
NocoJobs.jobsMgr.addJobWorker( |
||||
AIRTABLE_IMPORT_JOB, |
||||
syncService.airtableImportJob |
||||
); |
||||
NocoJobs.jobsMgr.addJobWorker( |
||||
AIRTABLE_PROGRESS_JOB, |
||||
({ payload, progress }) => { |
||||
sv.to(payload?.id).emit('progress', { |
||||
msg: progress?.msg, |
||||
level: progress?.level, |
||||
status: progress?.status, |
||||
}); |
||||
|
||||
if (payload?.id in jobs) { |
||||
jobs[payload?.id].last_message = { |
||||
msg: progress?.msg, |
||||
level: progress?.level, |
||||
status: progress?.status, |
||||
}; |
||||
} |
||||
} |
||||
); |
||||
|
||||
NocoJobs.jobsMgr.addProgressCbk(AIRTABLE_IMPORT_JOB, (payload, progress) => { |
||||
NocoJobs.jobsMgr.add(AIRTABLE_PROGRESS_JOB, { |
||||
payload, |
||||
progress: { |
||||
msg: progress?.msg, |
||||
level: progress?.level, |
||||
status: progress?.status, |
||||
}, |
||||
}); |
||||
}); |
||||
NocoJobs.jobsMgr.addSuccessCbk(AIRTABLE_IMPORT_JOB, (payload) => { |
||||
NocoJobs.jobsMgr.add(AIRTABLE_PROGRESS_JOB, { |
||||
payload, |
||||
progress: { |
||||
msg: 'Complete!', |
||||
status: SyncStatus.COMPLETED, |
||||
}, |
||||
}); |
||||
delete jobs[payload?.id]; |
||||
}); |
||||
NocoJobs.jobsMgr.addFailureCbk(AIRTABLE_IMPORT_JOB, (payload, error: any) => { |
||||
NocoJobs.jobsMgr.add(AIRTABLE_PROGRESS_JOB, { |
||||
payload, |
||||
progress: { |
||||
msg: error?.message || 'Failed due to some internal error', |
||||
status: SyncStatus.FAILED, |
||||
}, |
||||
}); |
||||
delete jobs[payload?.id]; |
||||
}); |
||||
|
||||
router.post( |
||||
'/api/v1/db/meta/import/airtable', |
||||
catchError((req, res) => { |
||||
NocoJobs.jobsMgr.add(AIRTABLE_IMPORT_JOB, { |
||||
id: req.query.id, |
||||
...req.body, |
||||
}); |
||||
res.json({}); |
||||
}) |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/syncs/:syncId/trigger', |
||||
catchError(async (req: Request, res) => { |
||||
if (req.params.syncId in jobs) { |
||||
NcError.badRequest('Sync already in progress'); |
||||
} |
||||
|
||||
const syncSource = await SyncSource.get(req.params.syncId); |
||||
|
||||
const user = await syncSource.getUser(); |
||||
const token = userService.genJwt(user, Noco.getConfig()); |
||||
|
||||
// Treat default baseUrl as siteUrl from req object
|
||||
let baseURL = (req as any).ncSiteUrl; |
||||
|
||||
// if environment value avail use it
|
||||
// or if it's docker construct using `PORT`
|
||||
if (process.env.NC_DOCKER) { |
||||
baseURL = `http://localhost:${process.env.PORT || 8080}`; |
||||
} |
||||
|
||||
setTimeout(() => { |
||||
NocoJobs.jobsMgr.add<AirtableSyncConfig>(AIRTABLE_IMPORT_JOB, { |
||||
id: req.params.syncId, |
||||
...(syncSource?.details || {}), |
||||
projectId: syncSource.project_id, |
||||
baseId: syncSource.base_id, |
||||
authToken: token, |
||||
baseURL, |
||||
user: user, |
||||
}); |
||||
}, 1000); |
||||
|
||||
jobs[req.params.syncId] = { |
||||
last_message: { |
||||
msg: 'Sync started', |
||||
}, |
||||
}; |
||||
res.json({}); |
||||
}) |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/syncs/:syncId/abort', |
||||
catchError(async (req: Request, res) => { |
||||
if (req.params.syncId in jobs) { |
||||
delete jobs[req.params.syncId]; |
||||
} |
||||
res.json({}); |
||||
}) |
||||
); |
||||
}; |
@ -1,4 +0,0 @@
|
||||
import importController from './import.ctl'; |
||||
import syncSourceController from './sync.ctl'; |
||||
|
||||
export { importController, syncSourceController }; |
@ -1,69 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw'; |
||||
import { syncService } from '../../services'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
export async function syncSourceList(req: Request, res: Response) { |
||||
res.json( |
||||
await syncService.syncSourceList({ |
||||
projectId: req.params.projectId, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function syncCreate(req: Request, res: Response) { |
||||
res.json( |
||||
await syncService.syncCreate({ |
||||
projectId: req.params.projectId, |
||||
baseId: req.params.baseId, |
||||
userId: (req as any).user.id, |
||||
syncPayload: req.body, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function syncDelete(req: Request, res: Response<any>) { |
||||
res.json( |
||||
await syncService.syncDelete({ |
||||
syncId: req.params.syncId, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function syncUpdate(req: Request, res: Response) { |
||||
res.json( |
||||
await syncService.syncUpdate({ |
||||
syncId: req.params.syncId, |
||||
syncPayload: req.body, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/syncs', |
||||
ncMetaAclMw(syncSourceList, 'syncSourceList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/syncs', |
||||
ncMetaAclMw(syncCreate, 'syncSourceCreate') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/syncs/:baseId', |
||||
ncMetaAclMw(syncSourceList, 'syncSourceList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/syncs/:baseId', |
||||
ncMetaAclMw(syncCreate, 'syncSourceCreate') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/syncs/:syncId', |
||||
ncMetaAclMw(syncDelete, 'syncSourceDelete') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/syncs/:syncId', |
||||
ncMetaAclMw(syncUpdate, 'syncSourceUpdate') |
||||
); |
||||
|
||||
export default router; |
@ -1,111 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; |
||||
import { tableService } from '../services'; |
||||
import type { TableListType, TableReqType, TableType } from 'nocodb-sdk'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
export async function tableList(req: Request, res: Response<TableListType>) { |
||||
res.json( |
||||
new PagedResponseImpl( |
||||
await tableService.getAccessibleTables({ |
||||
projectId: req.params.projectId, |
||||
baseId: req.params.baseId, |
||||
includeM2M: req.query?.includeM2M === 'true', |
||||
roles: (req as any).session?.passport?.user?.roles, |
||||
}) |
||||
) |
||||
); |
||||
} |
||||
|
||||
export async function tableCreate(req: Request<any, any, TableReqType>, res) { |
||||
const result = await tableService.tableCreate({ |
||||
projectId: req.params.projectId, |
||||
baseId: req.params.baseId, |
||||
table: req.body, |
||||
user: (req as any).session?.passport?.user, |
||||
}); |
||||
|
||||
res.json(result); |
||||
} |
||||
|
||||
export async function tableGet(req: Request, res: Response<TableType>) { |
||||
const table = await tableService.getTableWithAccessibleViews({ |
||||
tableId: req.params.tableId, |
||||
user: (req as any).session?.passport?.user, |
||||
}); |
||||
|
||||
res.json(table); |
||||
} |
||||
|
||||
export async function tableDelete(req: Request, res: Response) { |
||||
const result = await tableService.tableDelete({ |
||||
tableId: req.params.tableId, |
||||
user: (req as any).session?.passport?.user, |
||||
req, |
||||
}); |
||||
|
||||
res.json(result); |
||||
} |
||||
|
||||
export async function tableReorder(req: Request, res: Response) { |
||||
res.json( |
||||
await tableService.reorderTable({ |
||||
tableId: req.params.tableId, |
||||
order: req.body.order, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function tableUpdate(req: Request<any, any>, res) { |
||||
await tableService.tableUpdate({ |
||||
tableId: req.params.tableId, |
||||
table: req.body, |
||||
projectId: (req as any).ncProjectId, |
||||
}); |
||||
res.json({ msg: 'The table has been updated successfully' }); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/tables', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(tableList, 'tableList') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/:baseId/tables', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(tableList, 'tableList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/tables', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(tableCreate, 'tableCreate') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/:baseId/tables', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(tableCreate, 'tableCreate') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/tables/:tableId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(tableGet, 'tableGet') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/tables/:tableId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(tableUpdate, 'tableUpdate') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/tables/:tableId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(tableDelete, 'tableDelete') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/tables/:tableId/reorder', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(tableReorder, 'tableReorder') |
||||
); |
||||
export default router; |
@ -1,20 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import { TestResetService } from '../services/test/TestResetService'; |
||||
import type { Request } from 'express'; |
||||
|
||||
export async function reset(req: Request<any, any>, res) { |
||||
const service = new TestResetService({ |
||||
parallelId: req.body.parallelId, |
||||
dbType: req.body.dbType, |
||||
isEmptyProject: req.body.isEmptyProject, |
||||
workerId: req.body.workerId, |
||||
}); |
||||
|
||||
res.json(await service.process()); |
||||
} |
||||
|
||||
const router = Router(); |
||||
|
||||
router.post('/api/v1/meta/test/reset', reset); |
||||
|
||||
export default router; |
@ -1,3 +0,0 @@
|
||||
import userController from './user.ctl'; |
||||
|
||||
export { userController }; |
@ -1,332 +0,0 @@
|
||||
import { promisify } from 'util'; |
||||
import { OrgUserRoles } from 'nocodb-sdk'; |
||||
import { Strategy as CustomStrategy } from 'passport-custom'; |
||||
import passport from 'passport'; |
||||
import passportJWT from 'passport-jwt'; |
||||
import { Strategy as AuthTokenStrategy } from 'passport-auth-token'; |
||||
import { Strategy as GoogleStrategy } from 'passport-google-oauth20'; |
||||
import bcrypt from 'bcryptjs'; |
||||
import NocoCache from '../../cache/NocoCache'; |
||||
import { ApiToken, Plugin, Project, ProjectUser, User } from '../../models'; |
||||
import Noco from '../../Noco'; |
||||
import { CacheGetType, CacheScope } from '../../utils/globals'; |
||||
import { userService } from '../../services'; |
||||
|
||||
const ExtractJwt = passportJWT.ExtractJwt; |
||||
const JwtStrategy = passportJWT.Strategy; |
||||
|
||||
const jwtOptions = { |
||||
jwtFromRequest: ExtractJwt.fromHeader('xc-auth'), |
||||
}; |
||||
|
||||
const PassportLocalStrategy = require('passport-local').Strategy; |
||||
|
||||
export function initStrategies(router): void { |
||||
passport.use( |
||||
'authtoken', |
||||
new AuthTokenStrategy( |
||||
{ headerFields: ['xc-token'], passReqToCallback: true }, |
||||
(req, token, done) => { |
||||
ApiToken.getByToken(token) |
||||
.then((apiToken) => { |
||||
if (!apiToken) { |
||||
return done({ msg: 'Invalid token' }); |
||||
} |
||||
|
||||
if (!apiToken.fk_user_id) return done(null, { roles: 'editor' }); |
||||
User.get(apiToken.fk_user_id) |
||||
.then((user) => { |
||||
user['is_api_token'] = true; |
||||
if (req.ncProjectId) { |
||||
ProjectUser.get(req.ncProjectId, user.id) |
||||
.then(async (projectUser) => { |
||||
user.roles = projectUser?.roles || user.roles; |
||||
user.roles = |
||||
user.roles === 'owner' ? 'owner,creator' : user.roles; |
||||
// + (user.roles ? `,${user.roles}` : '');
|
||||
// todo : cache
|
||||
// await NocoCache.set(`${CacheScope.USER}:${key}`, user);
|
||||
done(null, user); |
||||
}) |
||||
.catch((e) => done(e)); |
||||
} else { |
||||
return done(null, user); |
||||
} |
||||
}) |
||||
.catch((e) => { |
||||
console.log(e); |
||||
done({ msg: 'User not found' }); |
||||
}); |
||||
}) |
||||
.catch((e) => { |
||||
console.log(e); |
||||
done({ msg: 'Invalid token' }); |
||||
}); |
||||
} |
||||
) |
||||
); |
||||
|
||||
passport.serializeUser(function ( |
||||
{ |
||||
id, |
||||
email, |
||||
email_verified, |
||||
roles: _roles, |
||||
provider, |
||||
firstname, |
||||
lastname, |
||||
isAuthorized, |
||||
isPublicBase, |
||||
token_version, |
||||
}, |
||||
done |
||||
) { |
||||
const roles = (_roles || '') |
||||
.split(',') |
||||
.reduce((obj, role) => Object.assign(obj, { [role]: true }), {}); |
||||
if (roles.owner) { |
||||
roles.creator = true; |
||||
} |
||||
done(null, { |
||||
isAuthorized, |
||||
isPublicBase, |
||||
id, |
||||
email, |
||||
email_verified, |
||||
provider, |
||||
firstname, |
||||
lastname, |
||||
roles, |
||||
token_version, |
||||
}); |
||||
}); |
||||
|
||||
passport.deserializeUser(function (user, done) { |
||||
done(null, user); |
||||
}); |
||||
|
||||
passport.use( |
||||
new JwtStrategy( |
||||
{ |
||||
secretOrKey: Noco.getConfig().auth.jwt.secret, |
||||
...jwtOptions, |
||||
passReqToCallback: true, |
||||
...Noco.getConfig().auth.jwt.options, |
||||
}, |
||||
async (req, jwtPayload, done) => { |
||||
// todo: improve this
|
||||
if ( |
||||
req.ncProjectId && |
||||
jwtPayload.roles?.split(',').includes(OrgUserRoles.SUPER_ADMIN) |
||||
) { |
||||
return User.getByEmail(jwtPayload?.email).then(async (user) => { |
||||
return done(null, { |
||||
...user, |
||||
roles: `owner,creator,${OrgUserRoles.SUPER_ADMIN}`, |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
const keyVals = [jwtPayload?.email]; |
||||
if (req.ncProjectId) { |
||||
keyVals.push(req.ncProjectId); |
||||
} |
||||
const key = keyVals.join('___'); |
||||
const cachedVal = await NocoCache.get( |
||||
`${CacheScope.USER}:${key}`, |
||||
CacheGetType.TYPE_OBJECT |
||||
); |
||||
|
||||
if (cachedVal) { |
||||
if ( |
||||
!cachedVal.token_version || |
||||
!jwtPayload.token_version || |
||||
cachedVal.token_version !== jwtPayload.token_version |
||||
) { |
||||
return done(new Error('Token Expired. Please login again.')); |
||||
} |
||||
return done(null, cachedVal); |
||||
} |
||||
|
||||
User.getByEmail(jwtPayload?.email) |
||||
.then(async (user) => { |
||||
if ( |
||||
!user.token_version || |
||||
!jwtPayload.token_version || |
||||
user.token_version !== jwtPayload.token_version |
||||
) { |
||||
return done(new Error('Token Expired. Please login again.')); |
||||
} |
||||
if (req.ncProjectId) { |
||||
// this.xcMeta
|
||||
// .metaGet(req.ncProjectId, null, 'nc_projects_users', {
|
||||
// user_id: user?.id
|
||||
// })
|
||||
|
||||
ProjectUser.get(req.ncProjectId, user.id) |
||||
.then(async (projectUser) => { |
||||
user.roles = projectUser?.roles || user.roles; |
||||
user.roles = |
||||
user.roles === 'owner' ? 'owner,creator' : user.roles; |
||||
// + (user.roles ? `,${user.roles}` : '');
|
||||
|
||||
await NocoCache.set(`${CacheScope.USER}:${key}`, user); |
||||
done(null, user); |
||||
}) |
||||
.catch((e) => done(e)); |
||||
} else { |
||||
// const roles = projectUser?.roles ? JSON.parse(projectUser.roles) : {guest: true};
|
||||
if (user) { |
||||
await NocoCache.set(`${CacheScope.USER}:${key}`, user); |
||||
return done(null, user); |
||||
} else { |
||||
return done(new Error('User not found')); |
||||
} |
||||
} |
||||
}) |
||||
.catch((err) => { |
||||
return done(err); |
||||
}); |
||||
} |
||||
) |
||||
); |
||||
|
||||
passport.use( |
||||
new PassportLocalStrategy( |
||||
{ |
||||
usernameField: 'email', |
||||
session: false, |
||||
}, |
||||
async (email, password, done) => { |
||||
try { |
||||
const user = await User.getByEmail(email); |
||||
if (!user) { |
||||
return done({ msg: `Email ${email} is not registered!` }); |
||||
} |
||||
|
||||
if (!user.salt) { |
||||
return done({ |
||||
msg: `Please sign up with the invite token first or reset the password by clicking Forgot your password.`, |
||||
}); |
||||
} |
||||
|
||||
const hashedPassword = await promisify(bcrypt.hash)( |
||||
password, |
||||
user.salt |
||||
); |
||||
if (user.password !== hashedPassword) { |
||||
return done({ msg: `Password not valid!` }); |
||||
} else { |
||||
return done(null, user); |
||||
} |
||||
} catch (e) { |
||||
done(e); |
||||
} |
||||
} |
||||
) |
||||
); |
||||
|
||||
passport.use( |
||||
'baseView', |
||||
new CustomStrategy(async (req: any, callback) => { |
||||
let user; |
||||
if (req.headers['xc-shared-base-id']) { |
||||
// const cacheKey = `nc_shared_bases||${req.headers['xc-shared-base-id']}`;
|
||||
|
||||
let sharedProject = null; |
||||
|
||||
if (!sharedProject) { |
||||
sharedProject = await Project.getByUuid( |
||||
req.headers['xc-shared-base-id'] |
||||
); |
||||
} |
||||
user = { |
||||
roles: sharedProject?.roles, |
||||
}; |
||||
} |
||||
|
||||
callback(null, user); |
||||
}) |
||||
); |
||||
|
||||
// mostly copied from older code
|
||||
Plugin.getPluginByTitle('Google').then((googlePlugin) => { |
||||
if (googlePlugin && googlePlugin.input) { |
||||
const settings = JSON.parse(googlePlugin.input); |
||||
process.env.NC_GOOGLE_CLIENT_ID = settings.client_id; |
||||
process.env.NC_GOOGLE_CLIENT_SECRET = settings.client_secret; |
||||
} |
||||
|
||||
if ( |
||||
process.env.NC_GOOGLE_CLIENT_ID && |
||||
process.env.NC_GOOGLE_CLIENT_SECRET |
||||
) { |
||||
const googleAuthParamsOrig = GoogleStrategy.prototype.authorizationParams; |
||||
GoogleStrategy.prototype.authorizationParams = (options: any) => { |
||||
const params = googleAuthParamsOrig.call(this, options); |
||||
|
||||
if (options.state) { |
||||
params.state = options.state; |
||||
} |
||||
|
||||
return params; |
||||
}; |
||||
|
||||
const clientConfig = { |
||||
clientID: process.env.NC_GOOGLE_CLIENT_ID, |
||||
clientSecret: process.env.NC_GOOGLE_CLIENT_SECRET, |
||||
// todo: update url
|
||||
callbackURL: 'http://localhost:3000', |
||||
passReqToCallback: true, |
||||
}; |
||||
|
||||
const googleStrategy = new GoogleStrategy( |
||||
clientConfig, |
||||
async (req, _accessToken, _refreshToken, profile, done) => { |
||||
const email = profile.emails[0].value; |
||||
|
||||
User.getByEmail(email) |
||||
.then(async (user) => { |
||||
if (user) { |
||||
// if project id defined extract project level roles
|
||||
if (req.ncProjectId) { |
||||
ProjectUser.get(req.ncProjectId, user.id) |
||||
.then(async (projectUser) => { |
||||
user.roles = projectUser?.roles || user.roles; |
||||
user.roles = |
||||
user.roles === 'owner' ? 'owner,creator' : user.roles; |
||||
// + (user.roles ? `,${user.roles}` : '');
|
||||
|
||||
done(null, user); |
||||
}) |
||||
.catch((e) => done(e)); |
||||
} else { |
||||
return done(null, user); |
||||
} |
||||
// if user not found create new user if allowed
|
||||
// or return error
|
||||
} else { |
||||
const salt = await promisify(bcrypt.genSalt)(10); |
||||
const user = await userService.registerNewUserIfAllowed({ |
||||
firstname: null, |
||||
lastname: null, |
||||
email_verification_token: null, |
||||
email: profile.emails[0].value, |
||||
password: '', |
||||
salt, |
||||
}); |
||||
return done(null, user); |
||||
} |
||||
}) |
||||
.catch((err) => { |
||||
return done(err); |
||||
}); |
||||
} |
||||
); |
||||
|
||||
passport.use(googleStrategy); |
||||
} |
||||
}); |
||||
|
||||
router.use(passport.initialize()); |
||||
} |
@ -1,70 +0,0 @@
|
||||
export default `<!DOCTYPE html>
|
||||
<html> |
||||
<head> |
||||
<title>NocoDB - Verify Email</title> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui"> |
||||
<link href="<%- ncPublicUrl %>/css/fonts.roboto.css" rel="stylesheet"> |
||||
<link href="<%- ncPublicUrl %>/css/materialdesignicons.5.x.min.css" rel="stylesheet"> |
||||
<link href="<%- ncPublicUrl %>/css/vuetify.2.x.min.css" rel="stylesheet"> |
||||
<script src="<%- ncPublicUrl %>/js/vue.2.6.14.min.js"></script> |
||||
</head> |
||||
<body> |
||||
<div id="app"> |
||||
<v-app> |
||||
<v-container> |
||||
<v-row class="justify-center"> |
||||
<v-col class="col-12 col-md-6"> |
||||
<v-alert v-if="valid" type="success"> |
||||
Email verified successfully! |
||||
</v-alert> |
||||
<v-alert v-else-if="errMsg" type="error"> |
||||
{{errMsg}} |
||||
</v-alert> |
||||
|
||||
<template v-else> |
||||
|
||||
<v-skeleton-loader type="heading"></v-skeleton-loader> |
||||
|
||||
</template> |
||||
</v-col> |
||||
</v-row> |
||||
</v-container> |
||||
</v-app> |
||||
</div> |
||||
<script src="<%- ncPublicUrl %>/js/vuetify.2.x.min.js"></script> |
||||
<script src="<%- ncPublicUrl %>/js/axios.0.19.2.min.js"></script> |
||||
|
||||
<script> |
||||
var app = new Vue({ |
||||
el: '#app', |
||||
vuetify: new Vuetify(), |
||||
data: { |
||||
valid: null, |
||||
errMsg: null, |
||||
validForm: false, |
||||
token: <%- token %>, |
||||
greeting: 'Password Reset', |
||||
formdata: { |
||||
password: '', |
||||
newPassword: '' |
||||
}, |
||||
success: false |
||||
}, |
||||
methods: {}, |
||||
async created() { |
||||
try { |
||||
const valid = (await axios.post('<%- baseUrl %>/api/v1/auth/email/validate/' + this.token)).data; |
||||
this.valid = !!valid; |
||||
} catch (e) { |
||||
this.valid = false; |
||||
if(e.response && e.response.data && e.response.data.msg){ |
||||
this.errMsg = e.response.data.msg; |
||||
}else{ |
||||
this.errMsg = 'Some error occurred'; |
||||
} |
||||
} |
||||
} |
||||
}) |
||||
</script> |
||||
</body> |
||||
</html>`;
|
@ -1,108 +0,0 @@
|
||||
export default `<!DOCTYPE html>
|
||||
<html> |
||||
<head> |
||||
<title>NocoDB - Reset Password</title> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui"> |
||||
<link href="<%- ncPublicUrl %>/css/fonts.roboto.css" rel="stylesheet"> |
||||
<link href="<%- ncPublicUrl %>/css/materialdesignicons.5.x.min.css" rel="stylesheet"> |
||||
<link href="<%- ncPublicUrl %>/css/vuetify.2.x.min.css" rel="stylesheet"> |
||||
<script src="<%- ncPublicUrl %>/js/vue.2.6.14.min.js"></script> |
||||
</head> |
||||
<body> |
||||
<div id="app"> |
||||
<v-app> |
||||
<v-container> |
||||
<v-row class="justify-center"> |
||||
<v-col class="col-12 col-md-6"> |
||||
<v-alert v-if="success" type="success"> |
||||
Password reset successful! |
||||
</v-alert> |
||||
<template v-else> |
||||
|
||||
<v-form ref="form" v-model="validForm" v-if="valid === true" ref="formType" class="ma-auto" |
||||
lazy-validation> |
||||
|
||||
|
||||
<v-text-field |
||||
name="input-10-2" |
||||
label="New password" |
||||
type="password" |
||||
v-model="formdata.password" |
||||
:rules="[v => !!v || 'Password is required']" |
||||
></v-text-field> |
||||
|
||||
<v-text-field |
||||
name="input-10-2" |
||||
type="password" |
||||
label="Confirm new password" |
||||
v-model="formdata.newPassword" |
||||
:rules="[v => !!v || 'Password is required', v => v === formdata.password || 'Password mismatch']" |
||||
></v-text-field> |
||||
|
||||
<v-btn |
||||
:disabled="!validForm" |
||||
large |
||||
@click="resetPassword" |
||||
> |
||||
RESET PASSWORD |
||||
</v-btn> |
||||
|
||||
</v-form> |
||||
<div v-else-if="valid === false">Not a valid url</div> |
||||
<div v-else> |
||||
<v-skeleton-loader type="actions"></v-skeleton-loader> |
||||
</div> |
||||
</template> |
||||
</v-col> |
||||
</v-row> |
||||
</v-container> |
||||
</v-app> |
||||
</div> |
||||
<script src="<%- ncPublicUrl %>/js/vuetify.2.x.min.js"></script> |
||||
<script src="<%- ncPublicUrl %>/js/axios.0.19.2.min.js"></script> |
||||
|
||||
<script> |
||||
var app = new Vue({ |
||||
el: '#app', |
||||
vuetify: new Vuetify(), |
||||
data: { |
||||
valid: null, |
||||
validForm: false, |
||||
token: <%- token %>, |
||||
greeting: 'Password Reset', |
||||
formdata: { |
||||
password: '', |
||||
newPassword: '' |
||||
}, |
||||
success: false |
||||
}, |
||||
methods: { |
||||
async resetPassword() { |
||||
if (this.$refs.form.validate()) { |
||||
try { |
||||
const res = await axios.post('<%- baseUrl %>api/v1/db/auth/password/reset/' + this.token, { |
||||
...this.formdata |
||||
}); |
||||
this.success = true; |
||||
} catch (e) { |
||||
if (e.response && e.response.data && e.response.data.msg) { |
||||
alert('Failed to reset password: ' + e.response.data.msg) |
||||
} else { |
||||
alert('Some error occurred') |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
async created() { |
||||
try { |
||||
const valid = (await axios.post('<%- baseUrl %>api/v1/db/auth/token/validate/' + this.token)).data; |
||||
this.valid = !!valid; |
||||
} catch (e) { |
||||
this.valid = false; |
||||
} |
||||
} |
||||
}) |
||||
</script> |
||||
</body> |
||||
</html>`;
|
@ -1,171 +0,0 @@
|
||||
export default `<!doctype html>
|
||||
<html> |
||||
<head> |
||||
<meta name="viewport" content="width=device-width"> |
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> |
||||
<title>Simple Transactional Email</title> |
||||
<style> |
||||
@media only screen and (max-width: 620px) { |
||||
table[class=body] h1 { |
||||
font-size: 28px !important; |
||||
margin-bottom: 10px !important; |
||||
} |
||||
|
||||
table[class=body] p, |
||||
table[class=body] ul, |
||||
table[class=body] ol, |
||||
table[class=body] td, |
||||
table[class=body] span, |
||||
table[class=body] a { |
||||
font-size: 16px !important; |
||||
} |
||||
|
||||
table[class=body] .wrapper, |
||||
table[class=body] .article { |
||||
padding: 10px !important; |
||||
} |
||||
|
||||
table[class=body] .content { |
||||
padding: 0 !important; |
||||
} |
||||
|
||||
table[class=body] .container { |
||||
padding: 0 !important; |
||||
width: 100% !important; |
||||
} |
||||
|
||||
table[class=body] .main { |
||||
border-left-width: 0 !important; |
||||
border-radius: 0 !important; |
||||
border-right-width: 0 !important; |
||||
} |
||||
|
||||
table[class=body] .btn table { |
||||
width: 100% !important; |
||||
} |
||||
|
||||
table[class=body] .btn a { |
||||
width: 100% !important; |
||||
} |
||||
|
||||
table[class=body] .img-responsive { |
||||
height: auto !important; |
||||
max-width: 100% !important; |
||||
width: auto !important; |
||||
} |
||||
} |
||||
@media all { |
||||
.ExternalClass { |
||||
width: 100%; |
||||
} |
||||
|
||||
.ExternalClass, |
||||
.ExternalClass p, |
||||
.ExternalClass span, |
||||
.ExternalClass font, |
||||
.ExternalClass td, |
||||
.ExternalClass div { |
||||
line-height: 100%; |
||||
} |
||||
|
||||
.apple-link a { |
||||
color: inherit !important; |
||||
font-family: inherit !important; |
||||
font-size: inherit !important; |
||||
font-weight: inherit !important; |
||||
line-height: inherit !important; |
||||
text-decoration: none !important; |
||||
} |
||||
|
||||
#MessageViewBody a { |
||||
color: inherit; |
||||
text-decoration: none; |
||||
font-size: inherit; |
||||
font-family: inherit; |
||||
font-weight: inherit; |
||||
line-height: inherit; |
||||
} |
||||
|
||||
.btn-primary table td:hover { |
||||
background-color: #34495e !important; |
||||
} |
||||
|
||||
.btn-primary a:hover { |
||||
background-color: #34495e !important; |
||||
border-color: #34495e !important; |
||||
} |
||||
} |
||||
</style> |
||||
</head> |
||||
<body class="" style="background-color: #f6f6f6; font-family: sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;"> |
||||
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">This is preheader text. Some clients will show this text as a preview.</span> |
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #f6f6f6; width: 100%;" width="100%" bgcolor="#f6f6f6"> |
||||
<tr> |
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top"> </td> |
||||
<td class="container" style="font-family: sans-serif; font-size: 14px; vertical-align: top; display: block; max-width: 580px; padding: 10px; width: 580px; margin: 0 auto;" width="580" valign="top"> |
||||
<div class="content" style="box-sizing: border-box; display: block; margin: 0 auto; max-width: 580px; padding: 10px;"> |
||||
|
||||
<!-- START CENTERED WHITE CONTAINER --> |
||||
<table role="presentation" class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background: #ffffff; border-radius: 3px; width: 100%;" width="100%"> |
||||
|
||||
<!-- START MAIN CONTENT AREA --> |
||||
<tr> |
||||
<td class="wrapper" style="font-family: sans-serif; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 20px;" valign="top"> |
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%"> |
||||
<tr> |
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top"> |
||||
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;">Hi,</p> |
||||
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;">To change your NocoDB account password click the following link.</p> |
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; box-sizing: border-box; width: 100%;" width="100%"> |
||||
<tbody> |
||||
<tr> |
||||
<td align="left" style="font-family: sans-serif; font-size: 14px; vertical-align: top; padding-bottom: 15px;" valign="top"> |
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;"> |
||||
<tbody> |
||||
<tr> |
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top; border-radius: 5px; text-align: center; background-color: #3498db;" valign="top" align="center" bgcolor="#1088ff"> <a href="<%- resetLink %>" target="_blank" style="border: solid 1px rgb(23, 139, 255); border-radius: 5px; box-sizing: border-box; cursor: pointer; display: inline-block; font-size: 14px; font-weight: bold; margin: 0; padding: 12px 25px; text-decoration: none; text-transform: capitalize; background-color: rgb(23, 139, 255); border-color: #3498db; color: #ffffff;">Reset Password</a> </td> |
||||
</tr> |
||||
</tbody> |
||||
</table> |
||||
</td> |
||||
</tr> |
||||
</tbody> |
||||
</table> |
||||
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;">Thanks regards NocoDB.</p> |
||||
</td> |
||||
</tr> |
||||
</table> |
||||
</td> |
||||
</tr> |
||||
|
||||
<!-- END MAIN CONTENT AREA --> |
||||
</table> |
||||
<!-- END CENTERED WHITE CONTAINER --> |
||||
|
||||
<!-- START FOOTER --> |
||||
<div class="footer" style="clear: both; margin-top: 10px; text-align: center; width: 100%;"> |
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%"> |
||||
<tr> |
||||
<td class="content-block" style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; color: #999999; font-size: 12px; text-align: center;" valign="top" align="center"> |
||||
<span class="apple-link" style="color: #999999; font-size: 12px; text-align: center;"></span> |
||||
<!-- <br> Don't like these emails? <a href="http://i.imgur.com/CScmqnj.gif">Unsubscribe</a>.--> |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td class="content-block powered-by" style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; color: #999999; font-size: 12px; text-align: center;" valign="top" align="center"> |
||||
<a href="http://nocodb.com/">NocoDB</a> |
||||
<!-- Powered by <a href="http://htmlemail.io">HTMLemail</a>.--> |
||||
</td> |
||||
</tr> |
||||
</table> |
||||
</div> |
||||
<!-- END FOOTER --> |
||||
|
||||
</div> |
||||
</td> |
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top"> </td> |
||||
</tr> |
||||
</table> |
||||
</body> |
||||
</html> |
||||
`;
|
@ -1,208 +0,0 @@
|
||||
export default `<!doctype html>
|
||||
<html> |
||||
<head> |
||||
<meta name="viewport" content="width=device-width"> |
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> |
||||
<title>Simple Transactional Email</title> |
||||
<style> |
||||
@media only screen and (max-width: 620px) { |
||||
table[class=body] h1 { |
||||
font-size: 28px !important; |
||||
margin-bottom: 10px !important; |
||||
} |
||||
|
||||
table[class=body] p, |
||||
table[class=body] ul, |
||||
table[class=body] ol, |
||||
table[class=body] td, |
||||
table[class=body] span, |
||||
table[class=body] a { |
||||
font-size: 16px !important; |
||||
} |
||||
|
||||
table[class=body] .wrapper, |
||||
table[class=body] .article { |
||||
padding: 10px !important; |
||||
} |
||||
|
||||
table[class=body] .content { |
||||
padding: 0 !important; |
||||
} |
||||
|
||||
table[class=body] .container { |
||||
padding: 0 !important; |
||||
width: 100% !important; |
||||
} |
||||
|
||||
table[class=body] .main { |
||||
border-left-width: 0 !important; |
||||
border-radius: 0 !important; |
||||
border-right-width: 0 !important; |
||||
} |
||||
|
||||
table[class=body] .btn table { |
||||
width: 100% !important; |
||||
} |
||||
|
||||
table[class=body] .btn a { |
||||
width: 100% !important; |
||||
} |
||||
|
||||
table[class=body] .img-responsive { |
||||
height: auto !important; |
||||
max-width: 100% !important; |
||||
width: auto !important; |
||||
} |
||||
} |
||||
|
||||
@media all { |
||||
.ExternalClass { |
||||
width: 100%; |
||||
} |
||||
|
||||
.ExternalClass, |
||||
.ExternalClass p, |
||||
.ExternalClass span, |
||||
.ExternalClass font, |
||||
.ExternalClass td, |
||||
.ExternalClass div { |
||||
line-height: 100%; |
||||
} |
||||
|
||||
.apple-link a { |
||||
color: inherit !important; |
||||
font-family: inherit !important; |
||||
font-size: inherit !important; |
||||
font-weight: inherit !important; |
||||
line-height: inherit !important; |
||||
text-decoration: none !important; |
||||
} |
||||
|
||||
#MessageViewBody a { |
||||
color: inherit; |
||||
text-decoration: none; |
||||
font-size: inherit; |
||||
font-family: inherit; |
||||
font-weight: inherit; |
||||
line-height: inherit; |
||||
} |
||||
|
||||
.btn-primary table td:hover { |
||||
background-color: #34495e !important; |
||||
} |
||||
|
||||
.btn-primary a:hover { |
||||
background-color: #34495e !important; |
||||
border-color: #34495e !important; |
||||
} |
||||
} |
||||
</style> |
||||
</head> |
||||
<body class="" |
||||
style="background-color: #f6f6f6; font-family: sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;"> |
||||
<span class="preheader" |
||||
style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">This is preheader text. Some clients will show this text as a preview.</span> |
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body" |
||||
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #f6f6f6; width: 100%;" |
||||
width="100%" bgcolor="#f6f6f6"> |
||||
<tr> |
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top"> </td> |
||||
<td class="container" |
||||
style="font-family: sans-serif; font-size: 14px; vertical-align: top; display: block; max-width: 580px; padding: 10px; width: 580px; margin: 0 auto;" |
||||
width="580" valign="top"> |
||||
<div class="content" |
||||
style="box-sizing: border-box; display: block; margin: 0 auto; max-width: 580px; padding: 10px;"> |
||||
|
||||
<!-- START CENTERED WHITE CONTAINER --> |
||||
<table role="presentation" class="main" |
||||
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background: #ffffff; border-radius: 3px; width: 100%;" |
||||
width="100%"> |
||||
|
||||
<!-- START MAIN CONTENT AREA --> |
||||
<tr> |
||||
<td class="wrapper" |
||||
style="font-family: sans-serif; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 20px;" |
||||
valign="top"> |
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" |
||||
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" |
||||
width="100%"> |
||||
<tr> |
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" |
||||
valign="top"> |
||||
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;"> |
||||
Hi,</p> |
||||
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;"> |
||||
I invited you to be "<%- roles -%>" of the NocoDB project "<%- projectName %>". |
||||
Click the button below to to accept my invitation.</p> |
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" |
||||
class="btn btn-primary" |
||||
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; box-sizing: border-box; width: 100%;" |
||||
width="100%"> |
||||
<tbody> |
||||
<tr> |
||||
<td align="left" |
||||
style="font-family: sans-serif; font-size: 14px; vertical-align: top; padding-bottom: 15px;" |
||||
valign="top"> |
||||
<table role="presentation" border="0" cellpadding="0" |
||||
cellspacing="0" |
||||
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;"> |
||||
<tbody> |
||||
<tr> |
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top; border-radius: 5px; text-align: center; background-color: #3498db;" |
||||
valign="top" align="center" bgcolor="#1088ff"><a |
||||
href="<%- signupLink %>" target="_blank" |
||||
style="border: solid 1px rgb(23, 139, 255); border-radius: 5px; box-sizing: border-box; cursor: pointer; display: inline-block; font-size: 14px; font-weight: bold; margin: 0; padding: 12px 25px; text-decoration: none; text-transform: capitalize; background-color: rgb(23, 139, 255); border-color: #3498db; color: #ffffff;">Signup</a> |
||||
</td> |
||||
</tr> |
||||
</tbody> |
||||
</table> |
||||
</td> |
||||
</tr> |
||||
</tbody> |
||||
</table> |
||||
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;"> |
||||
Thanks regards <%- adminEmail %>.</p> |
||||
</td> |
||||
</tr> |
||||
</table> |
||||
</td> |
||||
</tr> |
||||
|
||||
<!-- END MAIN CONTENT AREA --> |
||||
</table> |
||||
<!-- END CENTERED WHITE CONTAINER --> |
||||
|
||||
<!-- START FOOTER --> |
||||
<div class="footer" style="clear: both; margin-top: 10px; text-align: center; width: 100%;"> |
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" |
||||
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" |
||||
width="100%"> |
||||
<tr> |
||||
<td class="content-block" |
||||
style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; color: #999999; font-size: 12px; text-align: center;" |
||||
valign="top" align="center"> |
||||
<span class="apple-link" |
||||
style="color: #999999; font-size: 12px; text-align: center;"></span> |
||||
<!-- <br> Don't like these emails? <a href="http://i.imgur.com/CScmqnj.gif">Unsubscribe</a>.--> |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td class="content-block powered-by" |
||||
style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; color: #999999; font-size: 12px; text-align: center;" |
||||
valign="top" align="center"> |
||||
<a href="http://nocodb.com/">NocoDB</a> |
||||
<!-- Powered by <a href="http://htmlemail.io">HTMLemail</a>.--> |
||||
</td> |
||||
</tr> |
||||
</table> |
||||
</div> |
||||
<!-- END FOOTER --> |
||||
|
||||
</div> |
||||
</td> |
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top"> </td> |
||||
</tr> |
||||
</table> |
||||
</body> |
||||
</html> |
||||
`;
|
@ -1,207 +0,0 @@
|
||||
export default `<!doctype html>
|
||||
<html> |
||||
<head> |
||||
<meta name="viewport" content="width=device-width"> |
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> |
||||
<title>Simple Transactional Email</title> |
||||
<style> |
||||
@media only screen and (max-width: 620px) { |
||||
table[class=body] h1 { |
||||
font-size: 28px !important; |
||||
margin-bottom: 10px !important; |
||||
} |
||||
|
||||
table[class=body] p, |
||||
table[class=body] ul, |
||||
table[class=body] ol, |
||||
table[class=body] td, |
||||
table[class=body] span, |
||||
table[class=body] a { |
||||
font-size: 16px !important; |
||||
} |
||||
|
||||
table[class=body] .wrapper, |
||||
table[class=body] .article { |
||||
padding: 10px !important; |
||||
} |
||||
|
||||
table[class=body] .content { |
||||
padding: 0 !important; |
||||
} |
||||
|
||||
table[class=body] .container { |
||||
padding: 0 !important; |
||||
width: 100% !important; |
||||
} |
||||
|
||||
table[class=body] .main { |
||||
border-left-width: 0 !important; |
||||
border-radius: 0 !important; |
||||
border-right-width: 0 !important; |
||||
} |
||||
|
||||
table[class=body] .btn table { |
||||
width: 100% !important; |
||||
} |
||||
|
||||
table[class=body] .btn a { |
||||
width: 100% !important; |
||||
} |
||||
|
||||
table[class=body] .img-responsive { |
||||
height: auto !important; |
||||
max-width: 100% !important; |
||||
width: auto !important; |
||||
} |
||||
} |
||||
|
||||
@media all { |
||||
.ExternalClass { |
||||
width: 100%; |
||||
} |
||||
|
||||
.ExternalClass, |
||||
.ExternalClass p, |
||||
.ExternalClass span, |
||||
.ExternalClass font, |
||||
.ExternalClass td, |
||||
.ExternalClass div { |
||||
line-height: 100%; |
||||
} |
||||
|
||||
.apple-link a { |
||||
color: inherit !important; |
||||
font-family: inherit !important; |
||||
font-size: inherit !important; |
||||
font-weight: inherit !important; |
||||
line-height: inherit !important; |
||||
text-decoration: none !important; |
||||
} |
||||
|
||||
#MessageViewBody a { |
||||
color: inherit; |
||||
text-decoration: none; |
||||
font-size: inherit; |
||||
font-family: inherit; |
||||
font-weight: inherit; |
||||
line-height: inherit; |
||||
} |
||||
|
||||
.btn-primary table td:hover { |
||||
background-color: #34495e !important; |
||||
} |
||||
|
||||
.btn-primary a:hover { |
||||
background-color: #34495e !important; |
||||
border-color: #34495e !important; |
||||
} |
||||
} |
||||
</style> |
||||
</head> |
||||
<body class="" |
||||
style="background-color: #f6f6f6; font-family: sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;"> |
||||
<span class="preheader" |
||||
style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">This is preheader text. Some clients will show this text as a preview.</span> |
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body" |
||||
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #f6f6f6; width: 100%;" |
||||
width="100%" bgcolor="#f6f6f6"> |
||||
<tr> |
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top"> </td> |
||||
<td class="container" |
||||
style="font-family: sans-serif; font-size: 14px; vertical-align: top; display: block; max-width: 580px; padding: 10px; width: 580px; margin: 0 auto;" |
||||
width="580" valign="top"> |
||||
<div class="content" |
||||
style="box-sizing: border-box; display: block; margin: 0 auto; max-width: 580px; padding: 10px;"> |
||||
|
||||
<!-- START CENTERED WHITE CONTAINER --> |
||||
<table role="presentation" class="main" |
||||
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background: #ffffff; border-radius: 3px; width: 100%;" |
||||
width="100%"> |
||||
|
||||
<!-- START MAIN CONTENT AREA --> |
||||
<tr> |
||||
<td class="wrapper" |
||||
style="font-family: sans-serif; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 20px;" |
||||
valign="top"> |
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" |
||||
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" |
||||
width="100%"> |
||||
<tr> |
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" |
||||
valign="top"> |
||||
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;"> |
||||
Hi,</p> |
||||
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;"> |
||||
Please verify your email address by clicking the following button.</p> |
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" |
||||
class="btn btn-primary" |
||||
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; box-sizing: border-box; width: 100%;" |
||||
width="100%"> |
||||
<tbody> |
||||
<tr> |
||||
<td align="left" |
||||
style="font-family: sans-serif; font-size: 14px; vertical-align: top; padding-bottom: 15px;" |
||||
valign="top"> |
||||
<table role="presentation" border="0" cellpadding="0" |
||||
cellspacing="0" |
||||
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;"> |
||||
<tbody> |
||||
<tr> |
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top; border-radius: 5px; text-align: center; background-color: #3498db;" |
||||
valign="top" align="center" bgcolor="#1088ff"><a |
||||
href="<%- verifyLink %>" target="_blank" |
||||
style="border: solid 1px rgb(23, 139, 255); border-radius: 5px; box-sizing: border-box; cursor: pointer; display: inline-block; font-size: 14px; font-weight: bold; margin: 0; padding: 12px 25px; text-decoration: none; text-transform: capitalize; background-color: rgb(23, 139, 255); border-color: #3498db; color: #ffffff;">Verify</a> |
||||
</td> |
||||
</tr> |
||||
</tbody> |
||||
</table> |
||||
</td> |
||||
</tr> |
||||
</tbody> |
||||
</table> |
||||
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;"> |
||||
Thanks regards NocoDB.</p> |
||||
</td> |
||||
</tr> |
||||
</table> |
||||
</td> |
||||
</tr> |
||||
|
||||
<!-- END MAIN CONTENT AREA --> |
||||
</table> |
||||
<!-- END CENTERED WHITE CONTAINER --> |
||||
|
||||
<!-- START FOOTER --> |
||||
<div class="footer" style="clear: both; margin-top: 10px; text-align: center; width: 100%;"> |
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" |
||||
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" |
||||
width="100%"> |
||||
<tr> |
||||
<td class="content-block" |
||||
style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; color: #999999; font-size: 12px; text-align: center;" |
||||
valign="top" align="center"> |
||||
<span class="apple-link" |
||||
style="color: #999999; font-size: 12px; text-align: center;"></span> |
||||
<!-- <br> Don't like these emails? <a href="http://i.imgur.com/CScmqnj.gif">Unsubscribe</a>.--> |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td class="content-block powered-by" |
||||
style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; color: #999999; font-size: 12px; text-align: center;" |
||||
valign="top" align="center"> |
||||
<a href="http://nocodb.com/">NocoDB</a> |
||||
<!-- Powered by <a href="http://htmlemail.io">HTMLemail</a>.--> |
||||
</td> |
||||
</tr> |
||||
</table> |
||||
</div> |
||||
<!-- END FOOTER --> |
||||
|
||||
</div> |
||||
</td> |
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top"> </td> |
||||
</tr> |
||||
</table> |
||||
</body> |
||||
</html> |
||||
`;
|
@ -1,285 +0,0 @@
|
||||
import { promisify } from 'util'; |
||||
import { AuditOperationSubTypes, AuditOperationTypes } from 'nocodb-sdk'; |
||||
import * as ejs from 'ejs'; |
||||
import passport from 'passport'; |
||||
import catchError, { NcError } from '../../meta/helpers/catchError'; |
||||
import extractProjectIdAndAuthenticate from '../../meta/helpers/extractProjectIdAndAuthenticate'; |
||||
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw'; |
||||
import { Audit, User } from '../../models'; |
||||
import Noco from '../../Noco'; |
||||
import { userService } from '../../services'; |
||||
import { setTokenCookie } from '../../services/user'; |
||||
import type { Request } from 'express'; |
||||
|
||||
export async function signup(req: Request<any, any>, res): Promise<any> { |
||||
res.json( |
||||
await userService.signup({ |
||||
body: req.body, |
||||
req, |
||||
res, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function refreshToken(req: Request<any, any>, res): Promise<any> { |
||||
res.json( |
||||
await userService.refreshToken({ |
||||
body: req.body, |
||||
req, |
||||
res, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function successfulSignIn({ |
||||
user, |
||||
err, |
||||
info, |
||||
req, |
||||
res, |
||||
auditDescription, |
||||
}) { |
||||
try { |
||||
if (!user || !user.email) { |
||||
if (err) { |
||||
return res.status(400).send(err); |
||||
} |
||||
if (info) { |
||||
return res.status(400).send(info); |
||||
} |
||||
return res.status(400).send({ msg: 'Your signin has failed' }); |
||||
} |
||||
|
||||
await promisify((req as any).login.bind(req))(user); |
||||
|
||||
const refreshToken = userService.randomTokenString(); |
||||
|
||||
if (!user.token_version) { |
||||
user.token_version = userService.randomTokenString(); |
||||
} |
||||
|
||||
await User.update(user.id, { |
||||
refresh_token: refreshToken, |
||||
email: user.email, |
||||
token_version: user.token_version, |
||||
}); |
||||
setTokenCookie(res, refreshToken); |
||||
|
||||
await Audit.insert({ |
||||
op_type: AuditOperationTypes.AUTHENTICATION, |
||||
op_sub_type: AuditOperationSubTypes.SIGNIN, |
||||
user: user.email, |
||||
ip: req.clientIp, |
||||
description: auditDescription, |
||||
}); |
||||
|
||||
res.json({ |
||||
token: userService.genJwt(user, Noco.getConfig()), |
||||
} as any); |
||||
} catch (e) { |
||||
console.log(e); |
||||
throw e; |
||||
} |
||||
} |
||||
|
||||
async function signin(req, res, next) { |
||||
passport.authenticate( |
||||
'local', |
||||
{ session: false }, |
||||
async (err, user, info): Promise<any> => |
||||
await successfulSignIn({ |
||||
user, |
||||
err, |
||||
info, |
||||
req, |
||||
res, |
||||
auditDescription: 'User has signed in successfully', |
||||
}) |
||||
)(req, res, next); |
||||
} |
||||
|
||||
async function signout(req: Request<any, any>, res): Promise<any> { |
||||
res.json( |
||||
await userService.signout({ |
||||
req, |
||||
res, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function googleSignin(req, res, next) { |
||||
passport.authenticate( |
||||
'google', |
||||
{ |
||||
session: false, |
||||
callbackURL: req.ncSiteUrl + Noco.getConfig().dashboardPath, |
||||
}, |
||||
async (err, user, info): Promise<any> => |
||||
await successfulSignIn({ |
||||
user, |
||||
err, |
||||
info, |
||||
req, |
||||
res, |
||||
auditDescription: 'User has signed in successfully using Google Auth ', |
||||
}) |
||||
)(req, res, next); |
||||
} |
||||
|
||||
async function me(req, res): Promise<any> { |
||||
res.json(req?.session?.passport?.user ?? {}); |
||||
} |
||||
|
||||
async function passwordChange(req: Request<any, any>, res): Promise<any> { |
||||
if (!(req as any).isAuthenticated()) { |
||||
NcError.forbidden('Not allowed'); |
||||
} |
||||
|
||||
await userService.passwordChange({ |
||||
user: req['user'], |
||||
req, |
||||
body: req.body, |
||||
}); |
||||
|
||||
res.json({ msg: 'Password has been updated successfully' }); |
||||
} |
||||
|
||||
async function passwordForgot(req: Request<any, any>, res): Promise<any> { |
||||
await userService.passwordForgot({ |
||||
siteUrl: (req as any).ncSiteUrl, |
||||
body: req.body, |
||||
req, |
||||
}); |
||||
|
||||
res.json({ msg: 'Please check your email to reset the password' }); |
||||
} |
||||
|
||||
async function tokenValidate(req, res): Promise<any> { |
||||
await userService.tokenValidate({ |
||||
token: req.params.tokenId, |
||||
}); |
||||
res.json({ msg: 'Token has been validated successfully' }); |
||||
} |
||||
|
||||
async function passwordReset(req, res): Promise<any> { |
||||
await userService.passwordReset({ |
||||
token: req.params.tokenId, |
||||
body: req.body, |
||||
req, |
||||
}); |
||||
|
||||
res.json({ msg: 'Password has been reset successfully' }); |
||||
} |
||||
|
||||
async function emailVerification(req, res): Promise<any> { |
||||
await userService.emailVerification({ |
||||
token: req.params.tokenId, |
||||
req, |
||||
}); |
||||
|
||||
res.json({ msg: 'Email has been verified successfully' }); |
||||
} |
||||
|
||||
async function renderPasswordReset(req, res): Promise<any> { |
||||
try { |
||||
res.send( |
||||
ejs.render((await import('./ui/auth/resetPassword')).default, { |
||||
ncPublicUrl: process.env.NC_PUBLIC_URL || '', |
||||
token: JSON.stringify(req.params.tokenId), |
||||
baseUrl: `/`, |
||||
}) |
||||
); |
||||
} catch (e) { |
||||
return res.status(400).json({ msg: e.message }); |
||||
} |
||||
} |
||||
|
||||
const mapRoutes = (router) => { |
||||
// todo: old api - /auth/signup?tool=1
|
||||
router.post('/auth/user/signup', catchError(signup)); |
||||
router.post('/auth/user/signin', catchError(signin)); |
||||
router.get('/auth/user/me', extractProjectIdAndAuthenticate, catchError(me)); |
||||
router.post('/auth/password/forgot', catchError(passwordForgot)); |
||||
router.post('/auth/token/validate/:tokenId', catchError(tokenValidate)); |
||||
router.post('/auth/password/reset/:tokenId', catchError(passwordReset)); |
||||
router.post('/auth/email/validate/:tokenId', catchError(emailVerification)); |
||||
router.post( |
||||
'/user/password/change', |
||||
ncMetaAclMw(passwordChange, 'passwordChange') |
||||
); |
||||
router.post('/auth/token/refresh', catchError(refreshToken)); |
||||
|
||||
/* Google auth apis */ |
||||
|
||||
router.post(`/auth/google/genTokenByCode`, catchError(googleSignin)); |
||||
|
||||
router.get('/auth/google', (req: any, res, next) => |
||||
passport.authenticate('google', { |
||||
scope: ['profile', 'email'], |
||||
state: req.query.state, |
||||
callbackURL: req.ncSiteUrl + Noco.getConfig().dashboardPath, |
||||
})(req, res, next) |
||||
); |
||||
|
||||
// deprecated APIs
|
||||
router.post('/api/v1/db/auth/user/signup', catchError(signup)); |
||||
router.post('/api/v1/db/auth/user/signin', catchError(signin)); |
||||
router.get( |
||||
'/api/v1/db/auth/user/me', |
||||
extractProjectIdAndAuthenticate, |
||||
catchError(me) |
||||
); |
||||
router.post('/api/v1/db/auth/password/forgot', catchError(passwordForgot)); |
||||
router.post( |
||||
'/api/v1/db/auth/token/validate/:tokenId', |
||||
catchError(tokenValidate) |
||||
); |
||||
router.post( |
||||
'/api/v1/db/auth/password/reset/:tokenId', |
||||
catchError(passwordReset) |
||||
); |
||||
router.post( |
||||
'/api/v1/db/auth/email/validate/:tokenId', |
||||
catchError(emailVerification) |
||||
); |
||||
router.post( |
||||
'/api/v1/db/auth/password/change', |
||||
ncMetaAclMw(passwordChange, 'passwordChange') |
||||
); |
||||
router.post('/api/v1/db/auth/token/refresh', catchError(refreshToken)); |
||||
router.get( |
||||
'/api/v1/db/auth/password/reset/:tokenId', |
||||
catchError(renderPasswordReset) |
||||
); |
||||
|
||||
// new API
|
||||
router.post('/api/v1/auth/user/signup', catchError(signup)); |
||||
router.post('/api/v1/auth/user/signin', catchError(signin)); |
||||
router.post('/api/v1/auth/user/signout', catchError(signout)); |
||||
router.get( |
||||
'/api/v1/auth/user/me', |
||||
extractProjectIdAndAuthenticate, |
||||
catchError(me) |
||||
); |
||||
router.post('/api/v1/auth/password/forgot', catchError(passwordForgot)); |
||||
router.post( |
||||
'/api/v1/auth/token/validate/:tokenId', |
||||
catchError(tokenValidate) |
||||
); |
||||
router.post( |
||||
'/api/v1/auth/password/reset/:tokenId', |
||||
catchError(passwordReset) |
||||
); |
||||
router.post( |
||||
'/api/v1/auth/email/validate/:tokenId', |
||||
catchError(emailVerification) |
||||
); |
||||
router.post( |
||||
'/api/v1/auth/password/change', |
||||
ncMetaAclMw(passwordChange, 'passwordChange') |
||||
); |
||||
router.post('/api/v1/auth/token/refresh', catchError(refreshToken)); |
||||
// respond with password reset page
|
||||
router.get('/auth/password/reset/:tokenId', catchError(renderPasswordReset)); |
||||
}; |
||||
export default mapRoutes; |
@ -1,58 +0,0 @@
|
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import catchError from '../meta/helpers/catchError'; |
||||
import { utilService } from '../services'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
export async function testConnection(req: Request, res: Response) { |
||||
res.json(await utilService.testConnection({ body: req.body })); |
||||
} |
||||
|
||||
export async function appInfo(req: Request, res: Response) { |
||||
res.json( |
||||
await utilService.appInfo({ |
||||
req: { |
||||
ncSiteUrl: (req as any).ncSiteUrl, |
||||
}, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function versionInfo(_req: Request, res: Response) { |
||||
res.json(await utilService.versionInfo()); |
||||
} |
||||
|
||||
export async function appHealth(_: Request, res: Response) { |
||||
res.json(await utilService.appHealth()); |
||||
} |
||||
|
||||
export async function axiosRequestMake(req: Request, res: Response) { |
||||
res.json(await utilService.axiosRequestMake({ body: req.body })); |
||||
} |
||||
|
||||
export async function aggregatedMetaInfo(_req: Request, res: Response) { |
||||
res.json(await utilService.aggregatedMetaInfo()); |
||||
} |
||||
|
||||
export async function urlToDbConfig(req: Request, res: Response) { |
||||
res.json( |
||||
await utilService.urlToDbConfig({ |
||||
body: req.body, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export default (router) => { |
||||
router.post( |
||||
'/api/v1/db/meta/connection/test', |
||||
ncMetaAclMw(testConnection, 'testConnection') |
||||
); |
||||
router.get('/api/v1/db/meta/nocodb/info', catchError(appInfo)); |
||||
router.post('/api/v1/db/meta/axiosRequestMake', catchError(axiosRequestMake)); |
||||
router.get('/api/v1/version', catchError(versionInfo)); |
||||
router.get('/api/v1/health', catchError(appHealth)); |
||||
router.post('/api/v1/url_to_config', catchError(urlToDbConfig)); |
||||
router.get( |
||||
'/api/v1/aggregated-meta-info', |
||||
ncMetaAclMw(aggregatedMetaInfo, 'aggregatedMetaInfo') |
||||
); |
||||
}; |
@ -1,137 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import { viewService } from '../services'; |
||||
import type { View } from '../models'; |
||||
import type { Request, Response } from 'express'; |
||||
|
||||
// @ts-ignore
|
||||
export async function viewGet(req: Request, res: Response) {} |
||||
|
||||
export async function viewList(req: Request<any, any, any>, res: Response) { |
||||
res.json( |
||||
new PagedResponseImpl( |
||||
await viewService.viewList({ |
||||
tableId: req.params.tableId, |
||||
user: (req as any).session?.passport?.user, |
||||
}) |
||||
) |
||||
); |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
export async function shareView( |
||||
req: Request<any, any, any>, |
||||
res: Response<View> |
||||
) { |
||||
res.json(await viewService.shareView({ viewId: req.params.viewId })); |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
export async function viewCreate(req: Request<any, any>, res, next) {} |
||||
|
||||
// @ts-ignore
|
||||
export async function viewUpdate(req, res) { |
||||
const result = await viewService.viewUpdate({ |
||||
viewId: req.params.viewId, |
||||
view: req.body, |
||||
}); |
||||
res.json(result); |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
export async function viewDelete(req: Request, res: Response, next) { |
||||
const result = await viewService.viewDelete({ viewId: req.params.viewId }); |
||||
res.json(result); |
||||
} |
||||
|
||||
async function shareViewUpdate(req: Request<any, any>, res) { |
||||
res.json( |
||||
await viewService.shareViewUpdate({ |
||||
viewId: req.params.viewId, |
||||
sharedView: req.body, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function shareViewDelete(req: Request<any, any>, res) { |
||||
res.json(await viewService.shareViewDelete({ viewId: req.params.viewId })); |
||||
} |
||||
|
||||
async function showAllColumns(req: Request<any, any>, res) { |
||||
res.json( |
||||
await viewService.showAllColumns({ |
||||
viewId: req.params.viewId, |
||||
ignoreIds: <string[]>(req.query?.ignoreIds || []), |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function hideAllColumns(req: Request<any, any>, res) { |
||||
res.json( |
||||
await viewService.hideAllColumns({ |
||||
viewId: req.params.viewId, |
||||
ignoreIds: <string[]>(req.query?.ignoreIds || []), |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function shareViewList(req: Request<any, any>, res) { |
||||
res.json( |
||||
new PagedResponseImpl( |
||||
await viewService.shareViewList({ |
||||
tableId: req.params.tableId, |
||||
}) |
||||
) |
||||
); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/meta/tables/:tableId/views', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(viewList, 'viewList') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/views/:viewId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(viewUpdate, 'viewUpdate') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/views/:viewId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(viewDelete, 'viewDelete') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/views/:viewId/show-all', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(showAllColumns, 'showAllColumns') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/views/:viewId/hide-all', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(hideAllColumns, 'hideAllColumns') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/meta/tables/:tableId/share', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(shareViewList, 'shareViewList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/views/:viewId/share', |
||||
ncMetaAclMw(shareView, 'shareView') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/views/:viewId/share', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(shareViewUpdate, 'shareViewUpdate') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/views/:viewId/share', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(shareViewDelete, 'shareViewDelete') |
||||
); |
||||
|
||||
export default router; |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue