Merge remote-tracking branch 'upstream/develop' into develop_backend_yds
# Conflicts: # .idea/misc.xml # .idea/modules.xml
7
.gitignore
vendored
@@ -62,12 +62,17 @@ $RECYCLE.BIN/
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
.idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
.idea/.gitignore
|
||||
.idea/misc.xml
|
||||
.idea/modules.xml
|
||||
.idea/vcs.xml
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
|
||||
8
.idea/.gitignore
generated
vendored
@@ -1,8 +0,0 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
17
.idea/aws.xml
generated
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="accountSettings">
|
||||
<option name="activeProfile" value="profile:default" />
|
||||
<option name="activeRegion" value="ap-northeast-2" />
|
||||
<option name="recentlyUsedProfiles">
|
||||
<list>
|
||||
<option value="profile:default" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="recentlyUsedRegions">
|
||||
<list>
|
||||
<option value="ap-northeast-2" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
7
.idea/prettier.xml
generated
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PrettierConfiguration">
|
||||
<option name="myRunOnSave" value="true" />
|
||||
<option name="myRunOnReformat" value="true" />
|
||||
</component>
|
||||
</project>
|
||||
9
.idea/vanillameta.iml
generated
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
6
.idea/vcs.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
15
.idea/webResources.xml
generated
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="WebResourcesPaths">
|
||||
<contentEntries>
|
||||
<entry url="file://$PROJECT_DIR$/frontend-web">
|
||||
<entryData>
|
||||
<resourceRoots>
|
||||
<path value="file://$PROJECT_DIR$/frontend-web/public" />
|
||||
<path value="file://$PROJECT_DIR$/frontend-web/storybook-static" />
|
||||
</resourceRoots>
|
||||
</entryData>
|
||||
</entry>
|
||||
</contentEntries>
|
||||
</component>
|
||||
</project>
|
||||
@@ -8,7 +8,7 @@ intro
|
||||
[How to Use](https://github.com/godyuo/Algorithm/edit/main/README.md#How%20to%20Use)<br/>
|
||||
[Development Status]()<br/>
|
||||
[Things that helped contribute]()<br/>
|
||||
[Wiki]()<br/>
|
||||
[Wiki](https://github.com/vanillabrain/vanillameta/wiki)<br/>
|
||||
|
||||
## How to Use
|
||||
```
|
||||
|
||||
@@ -7,3 +7,4 @@ DB_NAME='vanillameta'
|
||||
|
||||
CORS_ORIGIN='*'
|
||||
|
||||
api propertyfile fix
|
||||
24
backend-api/Dockerfile
Normal file
@@ -0,0 +1,24 @@
|
||||
FROM ubuntu:18.04
|
||||
|
||||
RUN apt-get -qq update
|
||||
RUN apt-get -qq upgrade --yes
|
||||
RUN apt-get -qq install curl --yes
|
||||
RUN curl -sL https://deb.nodesource.com/setup_14.x | bash -
|
||||
RUN apt-get -qq install nodejs --yes
|
||||
|
||||
RUN apt-get install chromium-browser --yes
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
|
||||
COPY . .
|
||||
COPY tsconfig.json .
|
||||
COPY tsconfig.build.json .
|
||||
|
||||
RUN npm run build
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["npm", "run", "start"]
|
||||
7
backend-api/db/conf.d/my.cnf
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
|
||||
[client]
|
||||
default-character-set = utf8mb4
|
||||
|
||||
[mysql]
|
||||
default-character-set = utf8mb4
|
||||
131
backend-api/docker-compose.yml
Normal file
@@ -0,0 +1,131 @@
|
||||
version: "3.7"
|
||||
services:
|
||||
main:
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: ./Dockerfile
|
||||
ports:
|
||||
- "4000:4000"
|
||||
networks:
|
||||
- vanillameta
|
||||
depends_on:
|
||||
- mysql
|
||||
# - pg
|
||||
links:
|
||||
- "mysql:mysqldb"
|
||||
# - "pg"
|
||||
env_file:
|
||||
- .env
|
||||
- .env.dev
|
||||
restart: always
|
||||
|
||||
mysql:
|
||||
container_name: vanillameta_mysql
|
||||
image: mysql
|
||||
ports:
|
||||
- "3306:3306"
|
||||
networks:
|
||||
- vanillameta
|
||||
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
|
||||
MYSQL_CHARSET: utf8mb4
|
||||
MYSQL_DATABASE: ${DB_NAME}
|
||||
MYSQL_USER: ${DB_USERNAME}
|
||||
MYSQL_PASSWORD: ${DB_PASSWORD}
|
||||
TZ: Asia/Seoul
|
||||
restart: always
|
||||
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
||||
--default_authentication_plugin=mysql_native_password
|
||||
|
||||
volumes:
|
||||
- mysql:/var/lib/mysql
|
||||
- ./db/conf.d:/etc/mysql/conf.d
|
||||
|
||||
# mariadb:
|
||||
# container_name: vanillameta_mariadb
|
||||
# extra_hosts:
|
||||
# - "host.docker.internal:host-gateway"
|
||||
# image: mariadb
|
||||
# ports:
|
||||
# - "3308:3308"
|
||||
# networks:
|
||||
# - vanillameta
|
||||
# environment:
|
||||
# MYSQL_ROOT_PASSWORD: 'strongpassword'
|
||||
# MYSQL_CHARSET: utf8mb4
|
||||
# MYSQL_DATABASE: ${DB_NAME}
|
||||
# MYSQL_USER: ${DB_USERNAME}
|
||||
# MYSQL_PASSWORD: ${DB_PASSWORD}
|
||||
# TZ: Asia/Seoul
|
||||
# restart: always
|
||||
# volumes:
|
||||
# - ./db:/var/lib/maria
|
||||
|
||||
# pg:
|
||||
# container_name: vanillameta_pg
|
||||
# image: postgres
|
||||
# ports:
|
||||
# - "5432:5432"
|
||||
# networks:
|
||||
# - vanillameta
|
||||
# environment:
|
||||
# POSTGRES_ROOT_PASSWORD: 'strongpassword'
|
||||
# POSTGRES_CHARSET: utf8mb4
|
||||
# POSTGRES_DATABASE: ${DB_NAME}
|
||||
# POSTGRES_USER: ${DB_USERNAME}
|
||||
# POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
# POSTGRES_HOST_AUTH_METHOD: "trust"
|
||||
# TZ: Asia/Seoul
|
||||
# restart: always
|
||||
# # command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
||||
# volumes:
|
||||
# - mysql:/var/lib/mysql
|
||||
# - ./db/conf.d:/etc/porstgressql/data
|
||||
|
||||
|
||||
# oracledb:
|
||||
# container_name: vanillameta_oracle
|
||||
# image: oracleinanutshell/oracle-xe-11g
|
||||
# ports:
|
||||
# - "1521:1521"
|
||||
# networks:
|
||||
# - vanillameta
|
||||
# environment:
|
||||
# ORACLE_CHARSET: utf8mb4
|
||||
# ORACLE_DATABASE: ${DB_NAME}
|
||||
# ORACLE_USER: ${DB_USERNAME}
|
||||
# ORACLE_PASSWORD: ${DB_PASSWORD}
|
||||
# TZ: Asia/Seoul
|
||||
# restart: always
|
||||
# # command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
||||
# volumes:
|
||||
# - mysql:/var/lib/mysql
|
||||
# - ./db/conf.d:/etc/porstgressql/data
|
||||
|
||||
|
||||
#
|
||||
# mssql:
|
||||
# container_name: vanillameta_mssql
|
||||
# image: mcr.microsoft.com/azure-sql-edge
|
||||
# ports:
|
||||
# - "1433:1433"
|
||||
# networks:
|
||||
# - vanillameta
|
||||
# environment:
|
||||
# ACCEPT_EULA: "Y"
|
||||
# MSSQL_SA_PASSWORD: ${DB_PASSWORD}
|
||||
# MSSQL_AGENT_ENABLED: "true"
|
||||
# restart: always
|
||||
# command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
||||
#
|
||||
# volumes:
|
||||
# - ./vanillameta_mssqldata:/var/opt/mssql
|
||||
#
|
||||
# - ./db/conf.d:/etc/mysql/conf.d
|
||||
|
||||
volumes:
|
||||
mysql:
|
||||
networks:
|
||||
vanillameta:
|
||||
|
||||
BIN
backend-api/instantclient-basic-macos.x64-19.8.0.0.0dbru.dmg
Normal file
289
backend-api/package-lock.json
generated
@@ -6332,7 +6332,8 @@
|
||||
"camelcase": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
|
||||
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
|
||||
"dev": true
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001408",
|
||||
@@ -6560,6 +6561,7 @@
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
|
||||
"integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"restore-cursor": "^3.1.0"
|
||||
}
|
||||
@@ -6616,7 +6618,8 @@
|
||||
"cli-spinners": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz",
|
||||
"integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw=="
|
||||
"integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==",
|
||||
"dev": true
|
||||
},
|
||||
"cli-sprintf-format": {
|
||||
"version": "1.1.1",
|
||||
@@ -6676,7 +6679,8 @@
|
||||
"clone": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
|
||||
"integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg=="
|
||||
"integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
|
||||
"dev": true
|
||||
},
|
||||
"clone-response": {
|
||||
"version": "1.0.3",
|
||||
@@ -7223,11 +7227,6 @@
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"decamelize": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="
|
||||
},
|
||||
"decode-uri-component": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
|
||||
@@ -7454,6 +7453,7 @@
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz",
|
||||
"integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"clone": "^1.0.2"
|
||||
}
|
||||
@@ -7805,6 +7805,11 @@
|
||||
"es5-ext": "~0.10.46"
|
||||
}
|
||||
},
|
||||
"dylib-node": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/dylib-node/-/dylib-node-1.0.10.tgz",
|
||||
"integrity": "sha512-bjsSQJgDz8Iqd0avdq1UmIgL46Ip+WfqJ6Y4AlNGBnZVlMzktQOrwuWYesqRaOTdtGjm8PijjATa8EZBzvgk4g=="
|
||||
},
|
||||
"ecc-jsbn": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
|
||||
@@ -8138,7 +8143,8 @@
|
||||
"escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
|
||||
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||
"dev": true
|
||||
},
|
||||
"escodegen": {
|
||||
"version": "1.14.3",
|
||||
@@ -8707,11 +8713,6 @@
|
||||
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
|
||||
"integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g=="
|
||||
},
|
||||
"faker": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/faker/-/faker-4.1.0.tgz",
|
||||
"integrity": "sha512-ILKg69P6y/D8/wSmDXw35Ly0re8QzQ8pMfBCflsGiZG2ZjMUNLYNexA6lz5pkmJlepVdsiDFUxYAzPQ9/+iGLA=="
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
@@ -10556,7 +10557,8 @@
|
||||
"is-interactive": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
|
||||
"integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w=="
|
||||
"integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
|
||||
"dev": true
|
||||
},
|
||||
"is-lambda": {
|
||||
"version": "1.0.1",
|
||||
@@ -11767,9 +11769,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": {
|
||||
"version": "9.4.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz",
|
||||
"integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw=="
|
||||
"version": "9.4.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz",
|
||||
"integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.4",
|
||||
@@ -12037,6 +12039,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"knex-db2": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/knex-db2/-/knex-db2-1.0.0.tgz",
|
||||
"integrity": "sha512-fdY62nGTppX50NIJgqJ7vsPaNf3LXZZ/2cTJVIxb8LZEwab3qsrtFhHFA9zgo9/E6u7/q1MV7IpS4/iyzuSfAg==",
|
||||
"requires": {
|
||||
"bluebird": "^3.5.2"
|
||||
}
|
||||
},
|
||||
"knex-schema-inspector": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/knex-schema-inspector/-/knex-schema-inspector-2.0.4.tgz",
|
||||
@@ -12663,7 +12673,8 @@
|
||||
"mimic-fn": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
|
||||
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
|
||||
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
|
||||
"dev": true
|
||||
},
|
||||
"mimic-response": {
|
||||
"version": "1.0.1",
|
||||
@@ -12879,7 +12890,8 @@
|
||||
"mute-stream": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
|
||||
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA=="
|
||||
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
|
||||
"dev": true
|
||||
},
|
||||
"mysql": {
|
||||
"version": "2.18.1",
|
||||
@@ -13076,14 +13088,6 @@
|
||||
"integrity": "sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==",
|
||||
"dev": true
|
||||
},
|
||||
"nestjs-seeder": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/nestjs-seeder/-/nestjs-seeder-0.2.0.tgz",
|
||||
"integrity": "sha512-HPvFgDITpY2a1MG73VD06QVQM6jjpqqaoPmExg0LHV7II9o5gDV1DdcMpkjSRKR90aLbv2FMsbdtII7t/M9zeA==",
|
||||
"requires": {
|
||||
"faker": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"netmask": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz",
|
||||
@@ -13459,6 +13463,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"odbc": {
|
||||
"version": "2.4.6",
|
||||
"resolved": "https://registry.npmjs.org/odbc/-/odbc-2.4.6.tgz",
|
||||
"integrity": "sha512-LAXah1LrwJe3vuyABk+vHkWffI3am2kN1HBkvY1ae3QDv0qgK1ciSxL2jBtzgiemXwoS1WxKZ6JRXPeOElCOmg==",
|
||||
"requires": {
|
||||
"@mapbox/node-pre-gyp": "^1.0.5",
|
||||
"async": "^3.0.1",
|
||||
"node-addon-api": "^3.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"node-addon-api": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz",
|
||||
"integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
@@ -13487,6 +13508,7 @@
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
|
||||
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mimic-fn": "^2.1.0"
|
||||
}
|
||||
@@ -13716,7 +13738,8 @@
|
||||
"p-try": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
|
||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||
"dev": true
|
||||
},
|
||||
"pac-proxy-agent": {
|
||||
"version": "5.0.0",
|
||||
@@ -13944,7 +13967,8 @@
|
||||
"path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
|
||||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||
"dev": true
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
@@ -15032,11 +15056,6 @@
|
||||
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
||||
"dev": true
|
||||
},
|
||||
"require-main-filename": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
|
||||
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
|
||||
},
|
||||
"requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
@@ -15114,6 +15133,7 @@
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
|
||||
"integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"onetime": "^5.1.0",
|
||||
"signal-exit": "^3.0.2"
|
||||
@@ -17844,199 +17864,6 @@
|
||||
"resolved": "https://registry.npmjs.org/typeorm-naming-strategies/-/typeorm-naming-strategies-4.1.0.tgz",
|
||||
"integrity": "sha512-vPekJXzZOTZrdDvTl1YoM+w+sUIfQHG4kZTpbFYoTsufyv9NIBRe4Q+PdzhEAFA2std3D9LZHEb1EjE9zhRpiQ=="
|
||||
},
|
||||
"typeorm-seeding": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/typeorm-seeding/-/typeorm-seeding-1.6.1.tgz",
|
||||
"integrity": "sha512-xJIW1pp72hv6npPqbQ7xDvawcDmS60EDUjK++UCfiqT0WE4xTzCn+QK1ZijLkD3GYCqFPuFt4nmeyRJn6VO2Vw==",
|
||||
"requires": {
|
||||
"chalk": "^4.0.0",
|
||||
"faker": "4.1.0",
|
||||
"glob": "7.1.6",
|
||||
"ora": "4.0.3",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"yargs": "15.3.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"cliui": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
||||
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
|
||||
"requires": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"wrap-ansi": "^6.2.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"requires": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
|
||||
},
|
||||
"find-up": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
||||
"requires": {
|
||||
"locate-path": "^5.0.0",
|
||||
"path-exists": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.6",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
||||
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.0.4",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
|
||||
},
|
||||
"locate-path": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
||||
"requires": {
|
||||
"p-locate": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"log-symbols": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
|
||||
"integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==",
|
||||
"requires": {
|
||||
"chalk": "^2.4.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"requires": {
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ora": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/ora/-/ora-4.0.3.tgz",
|
||||
"integrity": "sha512-fnDebVFyz309A73cqCipVL1fBZewq4vwgSHfxh43vVy31mbyoQ8sCH3Oeaog/owYOs/lLlGVPCISQonTneg6Pg==",
|
||||
"requires": {
|
||||
"chalk": "^3.0.0",
|
||||
"cli-cursor": "^3.1.0",
|
||||
"cli-spinners": "^2.2.0",
|
||||
"is-interactive": "^1.0.0",
|
||||
"log-symbols": "^3.0.0",
|
||||
"mute-stream": "0.0.8",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"wcwidth": "^1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
|
||||
"integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"p-limit": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||
"requires": {
|
||||
"p-try": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"p-locate": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
||||
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
||||
"requires": {
|
||||
"p-limit": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
|
||||
"requires": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"y18n": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
||||
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
|
||||
},
|
||||
"yargs": {
|
||||
"version": "15.3.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz",
|
||||
"integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==",
|
||||
"requires": {
|
||||
"cliui": "^6.0.0",
|
||||
"decamelize": "^1.2.0",
|
||||
"find-up": "^4.1.0",
|
||||
"get-caller-file": "^2.0.1",
|
||||
"require-directory": "^2.1.1",
|
||||
"require-main-filename": "^2.0.0",
|
||||
"set-blocking": "^2.0.0",
|
||||
"string-width": "^4.2.0",
|
||||
"which-module": "^2.0.0",
|
||||
"y18n": "^4.0.0",
|
||||
"yargs-parser": "^18.1.1"
|
||||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "18.1.3",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
|
||||
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
|
||||
"requires": {
|
||||
"camelcase": "^5.0.0",
|
||||
"decamelize": "^1.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz",
|
||||
@@ -18475,6 +18302,7 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
|
||||
"integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"defaults": "^1.0.3"
|
||||
}
|
||||
@@ -18562,11 +18390,6 @@
|
||||
"is-symbol": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"which-module": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
|
||||
"integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q=="
|
||||
},
|
||||
"which-pm-runs": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz",
|
||||
|
||||
@@ -44,15 +44,18 @@
|
||||
"class-validator": "^0.13.2",
|
||||
"cookie-parser": "^1.4.5",
|
||||
"cross-env": "^7.0.3",
|
||||
"dylib-node": "^1.0.10",
|
||||
"js-joda": "^1.11.0",
|
||||
"knex": "^2.3.0",
|
||||
"knex-bigquery": "^2.0.3",
|
||||
"knex-db2": "^1.0.0",
|
||||
"knex-schema-inspector": "^2.0.4",
|
||||
"knex-snowflake-dialect": "^1.0.1",
|
||||
"mustache": "^4.2.0",
|
||||
"mysql": "^2.18.1",
|
||||
"mysql2": "^2.3.3",
|
||||
"nest-winston": "^1.7.0",
|
||||
"odbc": "^2.4.6",
|
||||
"oracledb": "^5.5.0",
|
||||
"pg": "^8.8.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
|
||||
@@ -13,8 +13,10 @@ provider:
|
||||
lambdaHashingVersion: 20201221
|
||||
## memorySize: 512 - 기본은 512
|
||||
timeout: 10
|
||||
# layers:
|
||||
# - arn:aws:lambda:${opt:region, self:provider.region}:${AWS::AccountId}:layer:AWSNodeLibs:latest
|
||||
layers:
|
||||
- arn:aws:lambda:${opt:region, self:provider.region}:${AWS::AccountId}:layer:SellerkingApiNodeLibs:latest
|
||||
# - arn:aws:lambda:${opt:region, self:provider.region}:${AWS::AccountId}:layer:FirebaseNodeLibs:latest
|
||||
- arn:aws:lambda:${opt:region, self:provider.region}:${AWS::AccountId}:layer:AWSNodeLibs:latest
|
||||
stage: ${opt:stage, file(./config.serverless.yml):STAGE}
|
||||
region: ${opt:region, file(./config.serverless.yml):AWS_REGION}
|
||||
# deploymentBucket: ${file(./config.serverless.yml):DEPLOYMENT_BUCKET}
|
||||
@@ -48,9 +50,7 @@ custom:
|
||||
- prod
|
||||
STAGE: ${self:provider.stage}
|
||||
DB_CONFIG: ${file(./config.serverless.yml):DB_CONFIG}
|
||||
DOMAINS:
|
||||
prod: vanillameta-api.vanillabrain.com
|
||||
dev: dev-vanillameta-api.vanillabrain.com
|
||||
DOMAINS: ${file(./config.serverless.yml):DOMAINS}
|
||||
customDomain:
|
||||
domainName: ${self:custom.DOMAINS.${self:custom.STAGE}}
|
||||
basePath: 'v1'
|
||||
|
||||
BIN
backend-api/sqlite_sample.sqlite
Normal file
@@ -1,49 +1,47 @@
|
||||
import {Module} from '@nestjs/common';
|
||||
import {AppController} from './app.controller';
|
||||
import {AppService} from './app.service';
|
||||
import {ConfigModule} from '@nestjs/config';
|
||||
import {TypeOrmModule} from '@nestjs/typeorm';
|
||||
import {DatabaseModule} from './database/database.module';
|
||||
import {DatasetModule} from './dataset/dataset.module';
|
||||
import {WidgetModule} from './widget/widget.module';
|
||||
import {DashboardModule} from './dashboard/dashboard.module';
|
||||
import {TemplateModule} from './template/template.module';
|
||||
import {CommonModule} from './common/common.module';
|
||||
import {ComponentModule} from './component/component.module';
|
||||
import {WidgetViewModule} from './widget-view/widget-view.module';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { DatabaseModule } from './database/database.module';
|
||||
import { DatasetModule } from './dataset/dataset.module';
|
||||
import { WidgetModule } from './widget/widget.module';
|
||||
import { DashboardModule } from './dashboard/dashboard.module';
|
||||
import { TemplateModule } from './template/template.module';
|
||||
import { CommonModule } from './common/common.module';
|
||||
import { ComponentModule } from './component/component.module';
|
||||
import { ConnectionModule } from './connection/connection.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
envFilePath: process.env.NODE_ENV == 'dev' ? '.env.dev' : '.env.prod',
|
||||
}),
|
||||
TypeOrmModule.forRoot({
|
||||
type: 'sqlite',
|
||||
// type: process.env.DB_TYPE,
|
||||
// host: process.env.DB_HOST,
|
||||
// port: parseInt(process.env.DB_PORT) || 3306,
|
||||
// username: process.env.DB_USERNAME,
|
||||
// password: process.env.DB_PASSWORD,
|
||||
// database: process.env.DB_NAME,
|
||||
database: 'vanillameta',
|
||||
autoLoadEntities: true,
|
||||
entities: [__dirname + '/**/*.entity{.ts,.js}'],
|
||||
synchronize: true,
|
||||
logging: process.env.NODE_ENV == 'dev',
|
||||
retryAttempts: 1,
|
||||
}),
|
||||
DatabaseModule,
|
||||
DatasetModule,
|
||||
WidgetModule,
|
||||
DashboardModule,
|
||||
TemplateModule,
|
||||
CommonModule,
|
||||
ComponentModule,
|
||||
WidgetViewModule,
|
||||
],
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
envFilePath: process.env.NODE_ENV == 'dev' ? '.env.dev' : '.env.prod',
|
||||
}),
|
||||
TypeOrmModule.forRoot({
|
||||
type: 'mysql',
|
||||
host: process.env.DB_HOST,
|
||||
port: parseInt(process.env.DB_PORT) || 3306,
|
||||
username: process.env.DB_USERNAME,
|
||||
password: process.env.DB_PASSWORD,
|
||||
database: process.env.DB_NAME,
|
||||
autoLoadEntities: true,
|
||||
entities: [__dirname + '/**/*.entity{.ts,.js}'],
|
||||
synchronize: false,
|
||||
logging: process.env.NODE_ENV == 'dev',
|
||||
retryAttempts: 1
|
||||
|
||||
}),
|
||||
DatabaseModule,
|
||||
DatasetModule,
|
||||
WidgetModule,
|
||||
DashboardModule,
|
||||
TemplateModule,
|
||||
CommonModule,
|
||||
ComponentModule,
|
||||
ConnectionModule,
|
||||
],
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
})
|
||||
export class AppModule {
|
||||
}
|
||||
export class AppModule {}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { CreateDateColumn, UpdateDateColumn } from 'typeorm';
|
||||
|
||||
export abstract class BaseEntity {
|
||||
@CreateDateColumn()
|
||||
@CreateDateColumn({comment:'생성일'})
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn()
|
||||
@UpdateDateColumn({comment:'수정일'})
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
7
backend-api/src/common/enum/component-type.enum.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export enum ComponentType {
|
||||
HORIZONTAL = 'HORIZONTAL',
|
||||
VERTICAL = 'VERTICAL',
|
||||
SQUARE = 'SQUARE',
|
||||
SCORE = 'SCORE',
|
||||
TABLE = 'TABLE',
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
export enum DatasetType {
|
||||
DATASET = 'DATASET',
|
||||
WIDGET = 'WIDGET_VIEW'
|
||||
DATASET = 'DATASET',
|
||||
TABLE = 'TABLE',
|
||||
}
|
||||
|
||||
4
backend-api/src/common/enum/response-status.enum.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export enum ResponseStatus {
|
||||
SUCCESS = 'SUCCESS',
|
||||
ERROR = 'ERROR',
|
||||
}
|
||||
@@ -1,39 +1,41 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
|
||||
import { ComponentService } from './component.service';
|
||||
import {Body, Controller, Delete, Get, Param, Patch, Post} from '@nestjs/common';
|
||||
import {ComponentService} from './component.service';
|
||||
import {CreateComponentDto} from "./dto/create-component.dto";
|
||||
import {UpdateComponentDto} from "./dto/update-component.dto";
|
||||
|
||||
@Controller('component')
|
||||
export class ComponentController {
|
||||
constructor(private readonly componentService: ComponentService,
|
||||
) {}
|
||||
constructor(private readonly componentService: ComponentService,
|
||||
) {
|
||||
}
|
||||
|
||||
@Post('/seed')
|
||||
async multipleCreate(@Body() createComponents:CreateComponentDto[]) {
|
||||
return this.componentService.multipleCreate(createComponents);
|
||||
}
|
||||
@Post('/seed')
|
||||
async multipleCreate(@Body() createComponents: CreateComponentDto[]) {
|
||||
return this.componentService.multipleCreate(createComponents);
|
||||
}
|
||||
|
||||
@Post()
|
||||
async create(@Body() createComponent:CreateComponentDto) {
|
||||
return this.componentService.create(createComponent);
|
||||
}
|
||||
@Post()
|
||||
async create(@Body() createComponent: CreateComponentDto) {
|
||||
return this.componentService.create(createComponent);
|
||||
}
|
||||
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.componentService.findAll();
|
||||
}
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.componentService.findAll();
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: number) {
|
||||
return this.componentService.findOne(+id);
|
||||
}
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: number) {
|
||||
return this.componentService.findOne(+id);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
update(@Param('id') id: number, @Body() body: any) {
|
||||
return this.componentService.update(+id, body);
|
||||
}
|
||||
@Patch(':id')
|
||||
update(@Param('id') id: number, @Body() body: UpdateComponentDto) {
|
||||
return this.componentService.update(+id, body);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: number) {
|
||||
return this.componentService.remove(+id);
|
||||
}
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: number) {
|
||||
return this.componentService.remove(+id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,124 +1,105 @@
|
||||
import {Injectable} from '@nestjs/common';
|
||||
import {CreateComponentDto} from './dto/create-component.dto';
|
||||
import {InjectRepository} from "@nestjs/typeorm";
|
||||
import {Component} from "./entities/component.entity";
|
||||
import {Repository} from "typeorm";
|
||||
import {YesNo} from "../common/enum/yn.enum";
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { CreateComponentDto } from './dto/create-component.dto';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Component } from './entities/component.entity';
|
||||
import { Repository } from 'typeorm';
|
||||
import { YesNo } from '../common/enum/yn.enum';
|
||||
import { UpdateComponentDto } from './dto/update-component.dto';
|
||||
|
||||
@Injectable()
|
||||
export class ComponentService {
|
||||
constructor(
|
||||
@InjectRepository(Component)
|
||||
private componentRepository: Repository<Component>) {
|
||||
constructor(
|
||||
@InjectRepository(Component)
|
||||
private componentRepository: Repository<Component>,
|
||||
) {}
|
||||
|
||||
async multipleCreate(createComponents: CreateComponentDto[]) {
|
||||
createComponents.forEach(item => {
|
||||
item.option = JSON.stringify(item.option);
|
||||
});
|
||||
|
||||
return await this.componentRepository.save(createComponents);
|
||||
}
|
||||
|
||||
async create(createComponent: CreateComponentDto) {
|
||||
const find_component = await this.componentRepository.findOne({
|
||||
where: { type: createComponent.type },
|
||||
});
|
||||
if (find_component) {
|
||||
return 'exist same widget';
|
||||
} else {
|
||||
const saveObj: CreateComponentDto = new CreateComponentDto();
|
||||
saveObj.type = createComponent.type;
|
||||
saveObj.title = createComponent.title;
|
||||
saveObj.category = createComponent.category;
|
||||
saveObj.option = JSON.stringify(createComponent.option);
|
||||
|
||||
if (createComponent.seq) saveObj.seq = createComponent.seq;
|
||||
if (createComponent.useYn) saveObj.useYn = createComponent.useYn;
|
||||
if (createComponent.icon) saveObj.icon = createComponent.icon;
|
||||
if (createComponent.description) saveObj.description = createComponent.description;
|
||||
|
||||
return await this.componentRepository.save(saveObj);
|
||||
}
|
||||
}
|
||||
|
||||
async multipleCreate(createComponents: CreateComponentDto[]) {
|
||||
// await this.componentRepository.save(createComponents);
|
||||
// for(let i =0 ; createComponents.length > i; i ++){
|
||||
// const createComponentDto = createComponents[i];
|
||||
// const find_component = await this.componentRepository.findOne({ where: { title: body[i].title }})
|
||||
//
|
||||
// if(!find_component){
|
||||
//
|
||||
// await this.componentRepository.save({
|
||||
// type: body[i].type,
|
||||
// title: body[i].title,
|
||||
// description: body[i].description,
|
||||
// category: body[i].category,
|
||||
// icon: body[i].icon,
|
||||
// option: JSON.stringify(body[i].option),
|
||||
// seq: body[i].seq,
|
||||
// uesYn: body[i].useYn
|
||||
// })
|
||||
// return 'success add a new component'
|
||||
// }
|
||||
// }
|
||||
async findAll() {
|
||||
// const components = await this.componentRepository.find({
|
||||
// select: ['type'],
|
||||
// where: { useYn: YesNo.YES },
|
||||
// });
|
||||
|
||||
createComponents.forEach((item) => {
|
||||
item.option = JSON.stringify(item.option);
|
||||
})
|
||||
const components = await this.componentRepository
|
||||
.createQueryBuilder('component')
|
||||
.select([
|
||||
'id',
|
||||
'type as componentType',
|
||||
'title',
|
||||
'description',
|
||||
'category',
|
||||
'component.option as `option`',
|
||||
'icon',
|
||||
'seq',
|
||||
])
|
||||
.where({ useYn: YesNo.YES })
|
||||
.getRawMany();
|
||||
|
||||
return await this.componentRepository.save(createComponents);
|
||||
components.forEach((component, index) => {
|
||||
component.option = JSON.parse(component.option);
|
||||
});
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
async findOne(id: number) {
|
||||
const find_component_one = await this.componentRepository.findOne({ where: { id: id } });
|
||||
return find_component_one;
|
||||
}
|
||||
|
||||
async update(id: number, updateComponent: UpdateComponentDto) {
|
||||
const find_component = await this.componentRepository.findOne({ where: { id: id } });
|
||||
if (!find_component) {
|
||||
return 'No exist type';
|
||||
} else {
|
||||
const updateObj: UpdateComponentDto = new UpdateComponentDto();
|
||||
|
||||
updateObj.type = updateComponent.type;
|
||||
updateObj.title = updateComponent.title;
|
||||
updateObj.category = updateComponent.category;
|
||||
updateObj.description = updateComponent.description;
|
||||
updateObj.option = JSON.stringify(updateComponent.option);
|
||||
updateObj.icon = updateComponent.icon;
|
||||
updateObj.seq = updateComponent.seq;
|
||||
updateObj.useYn = updateComponent.useYn;
|
||||
|
||||
await this.componentRepository.save(updateObj);
|
||||
|
||||
return 'Success update';
|
||||
}
|
||||
}
|
||||
|
||||
// async shortCreate(body: CreateComponentDto) {
|
||||
// const find_component = await this.componentRepository.findOne({ where: { title: body.title }})
|
||||
// if(find_component){
|
||||
// return 'exist same widget'
|
||||
// } else {
|
||||
// await this.componentRepository.save({
|
||||
// type: body.type,
|
||||
// title: body.title,
|
||||
// description: body.description,
|
||||
// category: body.category,
|
||||
// icon: body.icon,
|
||||
// option: JSON.stringify(body.option),
|
||||
// seq: body.seq,
|
||||
// uesYn: body.useYn
|
||||
// })
|
||||
// }
|
||||
// return 'This action adds a new widget';
|
||||
// }
|
||||
|
||||
async create(createComponent: CreateComponentDto) {
|
||||
const find_component = await this.componentRepository.findOne({where: {type: createComponent.type}})
|
||||
if (find_component) {
|
||||
return 'exist same widget'
|
||||
} else {
|
||||
const saveObj: CreateComponentDto = new CreateComponentDto();
|
||||
saveObj.type = createComponent.type;
|
||||
saveObj.title = createComponent.title;
|
||||
saveObj.category = createComponent.category;
|
||||
saveObj.option = JSON.stringify(createComponent.option);
|
||||
|
||||
if (createComponent.seq) saveObj.seq = createComponent.seq;
|
||||
if (createComponent.useYn) saveObj.useYn = createComponent.useYn;
|
||||
if (createComponent.icon) saveObj.icon = createComponent.icon;
|
||||
if (createComponent.description) saveObj.description = createComponent.description;
|
||||
|
||||
return await this.componentRepository.save(saveObj);
|
||||
}
|
||||
}
|
||||
|
||||
async findAll() {
|
||||
const components = await this.componentRepository.find({where: {useYn: YesNo.YES}})
|
||||
components.forEach((component, index) => {
|
||||
component.option = JSON.parse(component.option);
|
||||
})
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
async findOne(id: number) {
|
||||
const find_component_one = await this.componentRepository.findOne({where: {id: id}})
|
||||
return find_component_one;
|
||||
}
|
||||
|
||||
async update(id: number, body: any) {
|
||||
|
||||
const find_component = await this.componentRepository.findOne({where: {id: id}})
|
||||
if (!find_component) {
|
||||
return 'No exist type'
|
||||
} else {
|
||||
find_component.type = body.type;
|
||||
find_component.title = body.title;
|
||||
find_component.description = body.description;
|
||||
find_component.category = body.category;
|
||||
find_component.option = JSON.stringify(body.option);
|
||||
find_component.icon = body.icon;
|
||||
find_component.seq = body.seq;
|
||||
find_component.useYn = body.useYn;
|
||||
|
||||
await this.componentRepository.save(find_component)
|
||||
|
||||
return 'Success update'
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
async remove(id: number) {
|
||||
await this.componentRepository.delete({id})
|
||||
return `This action removes a #${id} component`;
|
||||
}
|
||||
async remove(id: number) {
|
||||
await this.componentRepository.delete({ id });
|
||||
return `This action removes a #${id} component`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {IsNotEmpty, IsNumber, IsString} from "class-validator";
|
||||
import {IsNotEmpty, IsNumber, IsOptional, IsString} from "class-validator";
|
||||
import {YesNo} from "../../common/enum/yn.enum";
|
||||
|
||||
export class CreateComponentDto {
|
||||
@@ -10,7 +10,7 @@ export class CreateComponentDto {
|
||||
title: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@IsOptional()
|
||||
description: string;
|
||||
|
||||
@IsString()
|
||||
@@ -22,14 +22,15 @@ export class CreateComponentDto {
|
||||
option: string
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@IsOptional()
|
||||
icon: string
|
||||
|
||||
@IsNumber()
|
||||
@IsNotEmpty()
|
||||
@IsOptional()
|
||||
seq: number
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
useYn: YesNo
|
||||
|
||||
}
|
||||
12
backend-api/src/connection/connection.module.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Database } from '../database/entities/database.entity';
|
||||
import { ConnectionService } from './connection.service';
|
||||
import { DatabaseService } from '../database/database.service';
|
||||
import { Dataset } from '../dataset/entities/dataset.entity';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Database, Dataset])],
|
||||
providers: [ConnectionService],
|
||||
})
|
||||
export class ConnectionModule {}
|
||||
192
backend-api/src/connection/connection.service.ts
Normal file
@@ -0,0 +1,192 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { CreateDatabaseDto } from '../database/dto/create-database.dto';
|
||||
import { QueryExecuteDto } from '../database/dto/query-execute.dto';
|
||||
import { Knex, knex } from 'knex';
|
||||
import { FieldTypeUtil } from '../utils/field-type.util';
|
||||
import { Database } from '../database/entities/database.entity';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { ResponseStatus } from '../common/enum/response-status.enum';
|
||||
|
||||
const knexConnections = new Map<number, Knex>();
|
||||
|
||||
@Injectable()
|
||||
export class ConnectionService {
|
||||
constructor(@InjectRepository(Database) private databaseRepository: Repository<Database>) {}
|
||||
|
||||
/**
|
||||
* Knex 객체 생성 후 pool에 추가
|
||||
* @param id
|
||||
* @param options
|
||||
*/
|
||||
addKnex(id: number, options: Knex.Config) {
|
||||
if (!this.hasKnex(id)) {
|
||||
knexConnections.set(id, knex(options));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Knex 객체 pool에서 삭제
|
||||
* @param id
|
||||
*/
|
||||
removeKnex(id: number) {
|
||||
knexConnections.delete(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Knex 객체 pool 안에 존재 유무
|
||||
* @param id
|
||||
*/
|
||||
hasKnex(id: number): boolean {
|
||||
return knexConnections.has(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Knex 객체 가져오기 - 만약 없으면 가져오기
|
||||
* @param id
|
||||
* @param databaseInfo
|
||||
*/
|
||||
async getKnex(id: number): Promise<Knex> {
|
||||
if (!this.hasKnex(id)) {
|
||||
const one = await this.databaseRepository.findOne({ where: { id: id } });
|
||||
one.connectionConfig = JSON.parse(one.connectionConfig);
|
||||
this.addKnex(id, one.connectionConfig as Knex.Config);
|
||||
}
|
||||
return knexConnections.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터베이스 연결 테스트
|
||||
* @param createDatabaseDto
|
||||
*/
|
||||
async testConnection(createDatabaseDto: CreateDatabaseDto) {
|
||||
const connectionConfig = {
|
||||
client: createDatabaseDto.engine,
|
||||
connection: createDatabaseDto.connectionConfig,
|
||||
useNullAsDefault: true,
|
||||
};
|
||||
createDatabaseDto.connectionConfig = JSON.stringify(connectionConfig);
|
||||
let _knex: Knex;
|
||||
let returnObj = {};
|
||||
try {
|
||||
|
||||
_knex = knex(connectionConfig as Knex.Config);
|
||||
} catch (e) {
|
||||
console.log('knex not connected');
|
||||
console.error(e);
|
||||
return { status: ResponseStatus.ERROR, message: 'knex not connected' };
|
||||
}
|
||||
|
||||
try {
|
||||
await _knex.raw('SELECT 1');
|
||||
returnObj = { status: ResponseStatus.SUCCESS, data: { message: 'success' } };
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
returnObj = { status: ResponseStatus.ERROR, message: e.sqlMessage };
|
||||
} finally {
|
||||
await _knex.destroy();
|
||||
}
|
||||
|
||||
return returnObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* 쿼리 실행
|
||||
* @param queryExecuteDto
|
||||
*/
|
||||
async executeQuery(queryExecuteDto: QueryExecuteDto) {
|
||||
const knex = await this.getKnex(queryExecuteDto.id);
|
||||
|
||||
let datas = [];
|
||||
const fields = [];
|
||||
const resultObj = { status: ResponseStatus.SUCCESS, message: 'success', datas: [], fields: [] };
|
||||
|
||||
|
||||
|
||||
try {
|
||||
const queryRes = await knex.raw(queryExecuteDto.query);
|
||||
|
||||
switch (knex.client.config.client) {
|
||||
case 'mysql2':
|
||||
|
||||
if (queryRes && queryRes[0].length > 0) {
|
||||
|
||||
datas = queryRes[0];
|
||||
const tempFields = queryRes[1];
|
||||
tempFields.map(field => {
|
||||
const fieldInfo = {
|
||||
columnName: field.name,
|
||||
columnLength: field._tableLength,
|
||||
columnType: FieldTypeUtil.mysqlFieldType(field.type),
|
||||
};
|
||||
fields.push(fieldInfo);
|
||||
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case 'pg':
|
||||
if (queryRes && queryRes.rows.length > 0) {
|
||||
datas = queryRes.rows;
|
||||
const tempFields = queryRes.fields;
|
||||
tempFields.map(field => {
|
||||
const fieldInfo = {
|
||||
columnName: field.name,
|
||||
columnLength: field.length,
|
||||
columnType: FieldTypeUtil.mysqlFieldType(field.type),
|
||||
};
|
||||
fields.push(fieldInfo);
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case 'mysql':
|
||||
if (queryRes && queryRes[0].length > 0) {
|
||||
|
||||
datas = queryRes[0];
|
||||
const tempFields = queryRes[1];
|
||||
tempFields.map(field => {
|
||||
const fieldInfo = {
|
||||
columnName: field.name,
|
||||
columnLength: field.length,
|
||||
columnType: FieldTypeUtil.mysqlFieldType(field.type),
|
||||
};
|
||||
fields.push(fieldInfo);
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
case 'mssql':
|
||||
if (queryRes && queryRes.length > 0) {
|
||||
|
||||
datas = queryRes;
|
||||
for (let i = 0; i < Object.keys(queryRes[0]).length; i++) {
|
||||
console.log(Object.keys[i]);
|
||||
}
|
||||
const tempFields = queryRes;
|
||||
tempFields.map(field => {
|
||||
const fieldInfo = {
|
||||
columnName: field.name,
|
||||
columnLength: field.length,
|
||||
columnType: FieldTypeUtil.mysqlFieldType(field.type),
|
||||
};
|
||||
fields.push(fieldInfo);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
resultObj.datas = datas;
|
||||
resultObj.fields = fields;
|
||||
} catch (e) {
|
||||
resultObj.status = ResponseStatus.ERROR;
|
||||
resultObj.message = e.sqlMessage;
|
||||
console.log(e);
|
||||
console.log(e.sqlMessage);
|
||||
}
|
||||
|
||||
return resultObj;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { CreateDashboardWidgetDto } from './dto/create-dashboard-widget.dto';
|
||||
import { UpdateDashboardWidgetDto } from './dto/update-dashboard-widget.dto';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { DashboardWidget } from './entities/dashboard-widget.entity';
|
||||
import { In, Repository } from 'typeorm';
|
||||
import { Widget } from '../../widget/entities/widget.entity';
|
||||
import { Component } from '../../component/entities/component.entity';
|
||||
|
||||
@Injectable()
|
||||
export class DashboardWidgetService {
|
||||
constructor(
|
||||
@InjectRepository(DashboardWidget)
|
||||
private dashboardWidgetRepository: Repository<DashboardWidget>,
|
||||
@InjectRepository(Widget)
|
||||
private widgetRepository: Repository<Widget>,
|
||||
@InjectRepository(Component)
|
||||
private componentRepository: Repository<Component>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 대시보드에 해당하는 widget 목록 저장
|
||||
* @param createDashboardWidgetDto
|
||||
*/
|
||||
async create(createDashboardWidgetDto: CreateDashboardWidgetDto) {
|
||||
const { dashboardId, widgetIds } = createDashboardWidgetDto;
|
||||
|
||||
const saveList = [];
|
||||
widgetIds.map(item => {
|
||||
saveList.push({ dashboardId, widgetId: item });
|
||||
});
|
||||
|
||||
return await this.dashboardWidgetRepository.save(saveList);
|
||||
}
|
||||
|
||||
async findWidgets(dashboardId: number) {
|
||||
const widgetList = await this.dashboardWidgetRepository.find({
|
||||
select: {
|
||||
widgetId: true,
|
||||
},
|
||||
where: { dashboardId: dashboardId },
|
||||
});
|
||||
|
||||
const whereInWidgetList = [];
|
||||
widgetList.map(item => {
|
||||
whereInWidgetList.push(item.widgetId);
|
||||
});
|
||||
|
||||
const widgetInfo = this.widgetRepository
|
||||
.createQueryBuilder()
|
||||
.subQuery()
|
||||
.select(['widget.*'])
|
||||
.from(Widget, 'widget')
|
||||
.where('id in (:ids)')
|
||||
.getQuery();
|
||||
|
||||
const result = await this.componentRepository
|
||||
.createQueryBuilder('component')
|
||||
.select(['widgetInfo.*', 'component.type as componentType'])
|
||||
.innerJoin(widgetInfo, 'widgetInfo', 'widgetInfo.componentId = component.id')
|
||||
.setParameter('ids', whereInWidgetList)
|
||||
.getRawMany();
|
||||
|
||||
result.map(el => {
|
||||
el.option = JSON.parse(el.option);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async update(dashboardId: number, updateDashboardWidgetDto: UpdateDashboardWidgetDto) {
|
||||
// 전체 지우고, 다시 insert
|
||||
await this.dashboardWidgetRepository.delete({ dashboardId });
|
||||
|
||||
const saveList = [];
|
||||
updateDashboardWidgetDto.widgetIds.map(item => {
|
||||
saveList.push({ dashboardId, widgetId: item });
|
||||
});
|
||||
|
||||
await this.dashboardWidgetRepository.save(saveList);
|
||||
}
|
||||
|
||||
async remove(dashboardId: number) {
|
||||
await this.dashboardWidgetRepository.delete({ dashboardId });
|
||||
return `This action removes a #${dashboardId} dashboardWidget`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class CreateDashboardWidgetDto {
|
||||
@IsNumber()
|
||||
@IsNotEmpty()
|
||||
dashboardId: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
widgetIds: number[];
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import { PartialType } from '@nestjs/swagger';
|
||||
import { CreateDashboardWidgetDto } from './create-dashboard-widget.dto';
|
||||
|
||||
export class UpdateDashboardWidgetDto extends PartialType(CreateDashboardWidgetDto) {}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Column, Entity, ManyToOne, PrimaryColumn, PrimaryGeneratedColumn } from 'typeorm';
|
||||
import { BaseEntity } from '../../../common/entities/base.entity';
|
||||
|
||||
@Entity()
|
||||
export class DashboardWidget extends BaseEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
dashboardId: number;
|
||||
|
||||
@Column()
|
||||
widgetId: number;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
|
||||
import {Controller, Get, Post, Body, Patch, Param, Delete, Put} from '@nestjs/common';
|
||||
import { DashboardService } from './dashboard.service';
|
||||
import { CreateDashboardDto } from './dto/create-dashboard.dto';
|
||||
import { UpdateDashboardDto } from './dto/update-dashboard.dto';
|
||||
@@ -22,7 +22,7 @@ export class DashboardController {
|
||||
return this.dashboardService.findOne(+id);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
@Put(':id')
|
||||
update(@Param('id') id: string, @Body() updateDashboardDto: UpdateDashboardDto) {
|
||||
return this.dashboardService.update(+id, updateDashboardDto);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { DashboardService } from './dashboard.service';
|
||||
import { DashboardController } from './dashboard.controller';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Dashboard } from './entities/dashboard.entity';
|
||||
import { DashboardWidget } from './dashboard-widget/entities/dashboard-widget.entity';
|
||||
import { DashboardWidgetService } from './dashboard-widget/dashboard-widget.service';
|
||||
import { Widget } from 'src/widget/entities/widget.entity';
|
||||
import { Component } from '../component/entities/component.entity';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Dashboard, DashboardWidget, Widget, Component])],
|
||||
controllers: [DashboardController],
|
||||
providers: [DashboardService],
|
||||
providers: [DashboardService, DashboardWidgetService],
|
||||
})
|
||||
export class DashboardModule {}
|
||||
|
||||
@@ -1,26 +1,108 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { CreateDashboardDto } from './dto/create-dashboard.dto';
|
||||
import { UpdateDashboardDto } from './dto/update-dashboard.dto';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Dashboard } from './entities/dashboard.entity';
|
||||
import { DashboardWidgetService } from './dashboard-widget/dashboard-widget.service';
|
||||
import { ResponseStatus } from '../common/enum/response-status.enum';
|
||||
|
||||
@Injectable()
|
||||
export class DashboardService {
|
||||
create(createDashboardDto: CreateDashboardDto) {
|
||||
return 'This action adds a new dashboard';
|
||||
constructor(
|
||||
@InjectRepository(Dashboard)
|
||||
private dashboardRepository: Repository<Dashboard>,
|
||||
private readonly dashboardWidgetService: DashboardWidgetService,
|
||||
) {}
|
||||
|
||||
async create(createDashboardDto: CreateDashboardDto) {
|
||||
let widgetIds = [];
|
||||
createDashboardDto.layout.map(item => {
|
||||
widgetIds.push(item.i);
|
||||
});
|
||||
|
||||
const saveObj = {
|
||||
title: createDashboardDto.title,
|
||||
layout: JSON.stringify(createDashboardDto.layout),
|
||||
};
|
||||
const newDashboard = await this.dashboardRepository.save(saveObj);
|
||||
|
||||
const saveObjDW = {
|
||||
dashboardId: newDashboard.id,
|
||||
widgetIds: widgetIds,
|
||||
};
|
||||
|
||||
await this.dashboardWidgetService.create(saveObjDW);
|
||||
|
||||
newDashboard.layout = JSON.parse(newDashboard.layout);
|
||||
return { status: ResponseStatus.SUCCESS, data: newDashboard };
|
||||
}
|
||||
|
||||
findAll() {
|
||||
return `This action returns all dashboard`;
|
||||
async findAll() {
|
||||
const find_all = await this.dashboardRepository.find({
|
||||
order: {
|
||||
updatedAt: 'desc',
|
||||
},
|
||||
});
|
||||
find_all.forEach(el => {
|
||||
el.layout = JSON.parse(el.layout);
|
||||
});
|
||||
return { status: ResponseStatus.SUCCESS, data: find_all };
|
||||
}
|
||||
|
||||
findOne(id: number) {
|
||||
return `This action returns a #${id} dashboard`;
|
||||
async findOne(id: number) {
|
||||
const find_dashboard = await this.dashboardRepository.findOne({ where: { id: id } });
|
||||
if (!find_dashboard)
|
||||
return { status: ResponseStatus.ERROR, message: '대시보드가 존재하지 않습니다.' };
|
||||
const widgetList = await this.dashboardWidgetService.findWidgets(id);
|
||||
|
||||
find_dashboard.layout = JSON.parse(find_dashboard.layout);
|
||||
const return_obj = Object.assign(find_dashboard, { widgets: widgetList });
|
||||
|
||||
return { status: ResponseStatus.SUCCESS, data: return_obj };
|
||||
}
|
||||
|
||||
update(id: number, updateDashboardDto: UpdateDashboardDto) {
|
||||
return `This action updates a #${id} dashboard`;
|
||||
async update(id: number, updateDashboardDto: UpdateDashboardDto) {
|
||||
const find_dashboard = await this.dashboardRepository.findOne({ where: { id: id } });
|
||||
|
||||
if (!find_dashboard) {
|
||||
return 'Not exist dashboard';
|
||||
} else {
|
||||
const widgetIds = [];
|
||||
|
||||
if (updateDashboardDto.title) {
|
||||
find_dashboard.title = updateDashboardDto.title;
|
||||
}
|
||||
if (updateDashboardDto.layout) {
|
||||
updateDashboardDto.layout.map(item => {
|
||||
widgetIds.push(item.i);
|
||||
});
|
||||
find_dashboard.layout = JSON.stringify(updateDashboardDto.layout);
|
||||
}
|
||||
|
||||
const saveObjDW = {
|
||||
dashboardId: id,
|
||||
widgetIds: widgetIds,
|
||||
};
|
||||
await this.dashboardWidgetService.update(id, saveObjDW);
|
||||
const updatedDashboard = await this.dashboardRepository.save(find_dashboard);
|
||||
|
||||
updatedDashboard.layout = JSON.parse(updatedDashboard.layout);
|
||||
return { status: ResponseStatus.SUCCESS, data: updatedDashboard };
|
||||
}
|
||||
}
|
||||
|
||||
remove(id: number) {
|
||||
return `This action removes a #${id} dashboard`;
|
||||
async remove(id: number) {
|
||||
const find_dashboard = await this.dashboardRepository.findOne({ where: { id: id } });
|
||||
if (!find_dashboard) {
|
||||
return { status: ResponseStatus.ERROR, message: 'No exist dashboard' };
|
||||
} else {
|
||||
await this.dashboardRepository.delete(id);
|
||||
await this.dashboardWidgetService.remove(id);
|
||||
return {
|
||||
status: ResponseStatus.SUCCESS,
|
||||
data: { message: `This action removes a #${id} dashboard` },
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,19 @@
|
||||
export class CreateDashboardDto {}
|
||||
import { IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class CreateDashboardDto {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
title: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
layout: DashboardLayout[];
|
||||
}
|
||||
|
||||
export class DashboardLayout {
|
||||
x: number;
|
||||
y: number;
|
||||
w: number;
|
||||
h: number;
|
||||
i: number;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreateDashboardDto } from './create-dashboard.dto';
|
||||
import {IsNotEmpty, IsNumber, IsOptional} from "class-validator";
|
||||
|
||||
export class UpdateDashboardDto extends PartialType(CreateDashboardDto) {}
|
||||
export class UpdateDashboardDto extends PartialType(CreateDashboardDto) {
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
dashboardId: number;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import {Column, Entity, ManyToOne, PrimaryGeneratedColumn} from "typeorm";
|
||||
import {BaseEntity} from "../../common/entities/base.entity";
|
||||
import {YesNo} from "../../common/enum/yn.enum";
|
||||
import {Template} from "../../template/entities/template.entity";
|
||||
|
||||
@Entity()
|
||||
export class Dashboard extends BaseEntity {
|
||||
@@ -23,10 +22,4 @@ export class Dashboard extends BaseEntity {
|
||||
@Column({length: 1, default: YesNo.NO, comment: '삭제여부'})
|
||||
delYn: YesNo
|
||||
|
||||
@ManyToOne(
|
||||
(type) => Template,
|
||||
(template) => template.dashboards
|
||||
)
|
||||
template!: Template
|
||||
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ConnectionService } from './connection.service';
|
||||
|
||||
describe('ConnectionService', () => {
|
||||
let service: ConnectionService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [ConnectionService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<ConnectionService>(ConnectionService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -1,79 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { CreateDatabaseDto } from '../dto/create-database.dto';
|
||||
import { QueryExecuteDto } from '../dto/query-execute.dto';
|
||||
import { DatabaseService } from '../database.service';
|
||||
import { Knex, knex } from 'knex';
|
||||
|
||||
const knexConnections = new Map<number, Knex>();
|
||||
|
||||
@Injectable()
|
||||
export class ConnectionService {
|
||||
constructor(private readonly databaseService: DatabaseService) {}
|
||||
|
||||
/**
|
||||
* Knex 객체 생성 후 pool에 추가
|
||||
* @param id
|
||||
* @param options
|
||||
*/
|
||||
addKnex(id: number, options: Knex.Config) {
|
||||
if (!this.hasKnex(id)) {
|
||||
knexConnections.set(id, knex(options));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Knex 객체 pool에서 삭제
|
||||
* @param id
|
||||
*/
|
||||
removeKnex(id: number) {
|
||||
knexConnections.delete(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Knex 객체 pool 안에 존재 유무
|
||||
* @param id
|
||||
*/
|
||||
hasKnex(id: number): boolean {
|
||||
return knexConnections.has(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Knex 객체 가져오기 - 만약 없으면 가져오기
|
||||
* @param id
|
||||
*/
|
||||
async getKnex(id: number): Promise<Knex> {
|
||||
if (!this.hasKnex(id)) {
|
||||
const one = await this.databaseService.findOne(id);
|
||||
this.addKnex(id, one.knexConfig as Knex.Config);
|
||||
}
|
||||
return knexConnections.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터베이스 연결 테스트
|
||||
* @param createDatabaseDto
|
||||
*/
|
||||
async testConnection(createDatabaseDto: CreateDatabaseDto): Promise<boolean> {
|
||||
let _knex = knex(createDatabaseDto.knexConfig as Knex.Config);
|
||||
try {
|
||||
await _knex.raw('SELECT 1');
|
||||
console.log('knex connected');
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.log('knex not connected');
|
||||
console.error(e);
|
||||
return false;
|
||||
} finally {
|
||||
await _knex.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 쿼리 실행
|
||||
* @param queryExecuteDto
|
||||
*/
|
||||
async executeQuery(queryExecuteDto: QueryExecuteDto) {
|
||||
const knex = await this.getKnex(queryExecuteDto.id);
|
||||
return knex.raw(queryExecuteDto.query);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query } from '@nestjs/common';
|
||||
import { DatabaseService } from './database.service';
|
||||
import { CreateDatabaseDto } from './dto/create-database.dto';
|
||||
import { UpdateDatabaseDto } from './dto/update-database.dto';
|
||||
import { QueryExecuteDto } from './dto/query-execute.dto';
|
||||
import { ConnectionService } from './connection/connection.service';
|
||||
import { ConnectionService } from '../connection/connection.service';
|
||||
import { DatasetType } from '../common/enum/dataset-type.enum';
|
||||
|
||||
@Controller('database')
|
||||
export class DatabaseController {
|
||||
@@ -12,32 +13,75 @@ export class DatabaseController {
|
||||
private readonly connectionService: ConnectionService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* database type 목록 조회
|
||||
*/
|
||||
@Get('/type')
|
||||
findTypeList() {
|
||||
return this.databaseService.findTypeList();
|
||||
}
|
||||
|
||||
@Get('/data')
|
||||
async findData(
|
||||
@Query('datasetType') datasetType: DatasetType,
|
||||
@Query('datasetId') datasetId: number,
|
||||
) {
|
||||
const res = await this.databaseService.findData(datasetType, datasetId);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터베이스 생성 ( 데이터소스 생성)
|
||||
* @param createDatabaseDto
|
||||
*/
|
||||
@Post()
|
||||
create(@Body() createDatabaseDto: CreateDatabaseDto) {
|
||||
return this.databaseService.create(createDatabaseDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터베이스 연결 테스트
|
||||
* @param createDatabaseDto
|
||||
*/
|
||||
@Post('test')
|
||||
testConnection(@Body() createDatabaseDto: CreateDatabaseDto) {
|
||||
return this.connectionService.testConnection(createDatabaseDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 쿼리 실행
|
||||
* @param queryExecuteDto
|
||||
*/
|
||||
@Post('execute')
|
||||
executeQuery(@Body() queryExecuteDto: QueryExecuteDto) {
|
||||
return this.connectionService.executeQuery(queryExecuteDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터베이스 목록 조회
|
||||
*/
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.databaseService.findAll();
|
||||
async findAll() {
|
||||
const res = await this.databaseService.findAll();
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터베이스 상세 조회 - 데이터베이스 연결정보, 테이블, 데이터셋 조회
|
||||
* @param id
|
||||
*/
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.databaseService.findOne(+id);
|
||||
async findOne(@Param('id') id: string) {
|
||||
const databaseInfo = await this.databaseService.findOne(+id);
|
||||
return databaseInfo;
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
/**
|
||||
* database update
|
||||
* @param id
|
||||
* @param updateDatabaseDto
|
||||
*/
|
||||
@Put(':id')
|
||||
update(@Param('id') id: string, @Body() updateDatabaseDto: UpdateDatabaseDto) {
|
||||
return this.databaseService.update(+id, updateDatabaseDto);
|
||||
}
|
||||
|
||||
@@ -3,10 +3,13 @@ import { DatabaseService } from './database.service';
|
||||
import { DatabaseController } from './database.controller';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Database } from './entities/database.entity';
|
||||
import { ConnectionService } from './connection/connection.service';
|
||||
import { ConnectionService } from '../connection/connection.service';
|
||||
import { Dataset } from '../dataset/entities/dataset.entity';
|
||||
import { TableQuery } from '../widget/tabel-query/entity/table-query.entity';
|
||||
import {DatabaseType} from "./entities/database_type.entity";
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Database])],
|
||||
imports: [TypeOrmModule.forFeature([Database, Dataset, TableQuery, DatabaseType])],
|
||||
controllers: [DatabaseController],
|
||||
providers: [DatabaseService, ConnectionService],
|
||||
})
|
||||
|
||||
@@ -1,41 +1,170 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { CreateDatabaseDto } from './dto/create-database.dto';
|
||||
import { UpdateDatabaseDto } from './dto/update-database.dto';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Database } from './entities/database.entity';
|
||||
import { Repository } from 'typeorm';
|
||||
import { ConnectionService } from '../connection/connection.service';
|
||||
import { Dataset } from '../dataset/entities/dataset.entity';
|
||||
import { ResponseStatus } from '../common/enum/response-status.enum';
|
||||
import { DatasetType } from '../common/enum/dataset-type.enum';
|
||||
import { TableQuery } from '../widget/tabel-query/entity/table-query.entity';
|
||||
import { QueryExecuteDto } from './dto/query-execute.dto';
|
||||
import {DatabaseType} from "./entities/database_type.entity";
|
||||
import {YesNo} from "../common/enum/yn.enum";
|
||||
|
||||
@Injectable()
|
||||
export class DatabaseService {
|
||||
constructor(@InjectRepository(Database) private databaseRepository: Repository<Database>) {}
|
||||
constructor(
|
||||
@InjectRepository(Database) private databaseRepository: Repository<Database>,
|
||||
@InjectRepository(DatabaseType) private databaseTypeRepository: Repository<DatabaseType>,
|
||||
@InjectRepository(Dataset) private datasetRepository: Repository<Dataset>,
|
||||
@InjectRepository(TableQuery) private tableQueryRepository: Repository<TableQuery>,
|
||||
private readonly connectionService: ConnectionService,
|
||||
) {}
|
||||
|
||||
getHello() {
|
||||
return 'Hello';
|
||||
/**
|
||||
* database type 목록 조회
|
||||
*/
|
||||
async findTypeList(){
|
||||
const result = await this.databaseTypeRepository.find({where:{useYn:YesNo.YES}});
|
||||
return { status: ResponseStatus.SUCCESS, data: result };
|
||||
}
|
||||
|
||||
async create(createDatabaseDto: CreateDatabaseDto): Promise<Database> {
|
||||
const database = Database.toDto(createDatabaseDto);
|
||||
return await this.databaseRepository.save(database);
|
||||
/**
|
||||
* database(데이터소스) 생성
|
||||
* @param createDatabaseDto
|
||||
*/
|
||||
async create(createDatabaseDto: CreateDatabaseDto) {
|
||||
const databaseDto = Database.toDto(createDatabaseDto);
|
||||
|
||||
const connectionConfig = {
|
||||
client: databaseDto.engine,
|
||||
connection: databaseDto.connectionConfig,
|
||||
useNullAsDefault: true,
|
||||
};
|
||||
databaseDto.connectionConfig = JSON.stringify(connectionConfig);
|
||||
databaseDto.timezone = 'Asia/Seoul';
|
||||
|
||||
const saveResult = await this.databaseRepository.save(databaseDto);
|
||||
saveResult.connectionConfig = JSON.parse(saveResult.connectionConfig);
|
||||
return { status: ResponseStatus.SUCCESS, data: saveResult };
|
||||
}
|
||||
|
||||
async findAll(): Promise<Database[]> {
|
||||
return await this.databaseRepository.find();
|
||||
/**
|
||||
* database 목록 조회
|
||||
*/
|
||||
async findAll() {
|
||||
const result = await this.databaseRepository.find();
|
||||
result.forEach(db => {
|
||||
db.connectionConfig = JSON.parse(db.connectionConfig);
|
||||
});
|
||||
return { status: ResponseStatus.SUCCESS, data: result };
|
||||
}
|
||||
|
||||
async findOne(id: number): Promise<Database> {
|
||||
return await this.databaseRepository.findOne({ where: { id } });
|
||||
/**
|
||||
* database 정보 조회
|
||||
* @param id
|
||||
*/
|
||||
async findOne(id: number): Promise<any> {
|
||||
// 연동 db 정보
|
||||
const databaseInfo = await this.databaseRepository.findOne({ where: { id } });
|
||||
databaseInfo.connectionConfig = JSON.parse(databaseInfo.connectionConfig).connection;
|
||||
|
||||
// table 정보 조회
|
||||
const tablesInfo = await this.connectionService.executeQuery({ id: +id, query: 'show tables' });
|
||||
const tables = [];
|
||||
if (tablesInfo && tablesInfo.datas.length > 0) {
|
||||
tablesInfo.datas.map(tableObj => {
|
||||
tables.push({ id: Object.values(tableObj)[0], tableName: Object.values(tableObj)[0], databaseId: id, datasetType:DatasetType.TABLE });
|
||||
});
|
||||
}
|
||||
|
||||
// dataset 정보 조회
|
||||
const tempDatasets = await this.datasetRepository.find({ where: { databaseId: id } });
|
||||
const datasets = [];
|
||||
tempDatasets.map(item => {
|
||||
datasets.push(Object.assign({datasetType:DatasetType.DATASET}, item));
|
||||
})
|
||||
|
||||
return { status: ResponseStatus.SUCCESS, data: { databaseInfo, tables, datasets } };
|
||||
}
|
||||
|
||||
/**
|
||||
* db config 정보 단순 조회
|
||||
* @param id
|
||||
*/
|
||||
private async findDB(id: number) {
|
||||
// 연동 db 정보
|
||||
const databaseInfo = await this.databaseRepository.findOne({ where: { id } });
|
||||
databaseInfo.connectionConfig = JSON.parse(databaseInfo.connectionConfig);
|
||||
return databaseInfo;
|
||||
}
|
||||
|
||||
async update(id: number, updateDatabaseDto: UpdateDatabaseDto) {
|
||||
const one = await this.findOne(id);
|
||||
if (!one) throw new NotFoundException(`조건에 맞는 데이터베이스를 찾지 못했습니다. id:${id}`);
|
||||
one.name = updateDatabaseDto.name;
|
||||
return await this.databaseRepository.save(one);
|
||||
const one = await this.findDB(id);
|
||||
if (!one)
|
||||
return {
|
||||
status: ResponseStatus.ERROR,
|
||||
message: `조건에 맞는 데이터베이스를 찾지 못했습니다. id:${id}`,
|
||||
};
|
||||
|
||||
const connectionConfig = {
|
||||
client: one.engine,
|
||||
connection: Object(one.connectionConfig).connection,
|
||||
useNullAsDefault: true,
|
||||
};
|
||||
updateDatabaseDto.connectionConfig = JSON.stringify(connectionConfig);
|
||||
|
||||
const saveResult = await this.databaseRepository.update(
|
||||
{ id },
|
||||
{ name: updateDatabaseDto.name, connectionConfig: updateDatabaseDto.connectionConfig },
|
||||
);
|
||||
|
||||
if (saveResult.affected === 1) {
|
||||
return { status: ResponseStatus.SUCCESS, data: { message: `${id} 수정 완료` } };
|
||||
} else {
|
||||
return { status: ResponseStatus.ERROR, message: '수정 실패' };
|
||||
}
|
||||
}
|
||||
|
||||
async remove(id: number) {
|
||||
const one = await this.findOne(id);
|
||||
if (!one) throw new NotFoundException(`조건에 맞는 데이터베이스를 찾지 못했습니다. id:${id}`);
|
||||
return this.databaseRepository.remove(one);
|
||||
const one = await this.findDB(id);
|
||||
if (!one)
|
||||
return {
|
||||
status: ResponseStatus.ERROR,
|
||||
message: `조건에 맞는 데이터베이스를 찾지 못했습니다. id:${id}`,
|
||||
};
|
||||
await this.databaseRepository.remove(one);
|
||||
return { status: ResponseStatus.SUCCESS, data: { message: `${id} 삭제 완료` } };
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터 조회
|
||||
* @param datasetType
|
||||
* @param datasetId
|
||||
*/
|
||||
async findData(datasetType: DatasetType, datasetId: number) {
|
||||
if (!datasetType || !datasetId)
|
||||
return {
|
||||
status: ResponseStatus.ERROR,
|
||||
message: 'datasetType, datasetId는 필수 입력 param 입니다',
|
||||
};
|
||||
|
||||
const queryExecuteDto = new QueryExecuteDto();
|
||||
if (datasetType === DatasetType.DATASET) {
|
||||
const datasetItem = await this.datasetRepository.findOne({ where: { id: datasetId } });
|
||||
queryExecuteDto.id = datasetItem.databaseId;
|
||||
queryExecuteDto.query = datasetItem.query;
|
||||
} else if (datasetType === DatasetType.TABLE) {
|
||||
const datasetItem = await this.tableQueryRepository.findOne({ where: { id: datasetId } });
|
||||
queryExecuteDto.id = datasetItem.databaseId;
|
||||
queryExecuteDto.query = datasetItem.query;
|
||||
}
|
||||
const queryResult = await this.connectionService.executeQuery(queryExecuteDto);
|
||||
return {
|
||||
status: queryResult.status,
|
||||
data: { datas: queryResult.datas, fields: queryResult.fields },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export class CreateDatabaseDto {
|
||||
name: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@IsOptional()
|
||||
@ApiProperty({
|
||||
example: '상세 내용',
|
||||
description: '데이터베이스 상세 내용',
|
||||
@@ -24,7 +24,7 @@ export class CreateDatabaseDto {
|
||||
example: '{}',
|
||||
description: '설정 JSON 상세',
|
||||
})
|
||||
knexConfig: string;
|
||||
connectionConfig: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
|
||||
@@ -14,7 +14,7 @@ export class Database extends BaseEntity {
|
||||
description: string;
|
||||
|
||||
@Column({ type: 'text', comment: '속성' })
|
||||
knexConfig: string; // 기타 속성 json으로 .. host, schema, filePath...
|
||||
connectionConfig: string; // 기타 속성 json으로 .. host, schema, filePath...
|
||||
|
||||
@Column({ length: 100, comment: '데이터베이스 구분' })
|
||||
engine: string;
|
||||
@@ -32,14 +32,14 @@ export class Database extends BaseEntity {
|
||||
const obj = new Database();
|
||||
obj.name = name;
|
||||
obj.description = description;
|
||||
obj.knexConfig = details;
|
||||
obj.connectionConfig = details;
|
||||
obj.engine = engine;
|
||||
obj.timezone = timezone;
|
||||
return obj;
|
||||
}
|
||||
|
||||
static toDto(dto: CreateDatabaseDto): Database {
|
||||
return Database.of(dto.name, dto.description, dto.knexConfig, dto.engine, dto.timezone);
|
||||
return Database.of(dto.name, dto.description, dto.connectionConfig, dto.engine, dto.timezone);
|
||||
}
|
||||
|
||||
getFullDescription(): string {
|
||||
|
||||
22
backend-api/src/database/entities/database_type.entity.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import {Column, Entity, PrimaryGeneratedColumn} from "typeorm";
|
||||
import {BaseEntity} from "../../common/entities/base.entity";
|
||||
import {YesNo} from "../../common/enum/yn.enum";
|
||||
|
||||
@Entity('databaseType')
|
||||
export class DatabaseType extends BaseEntity {
|
||||
@PrimaryGeneratedColumn({comment: 'database type ID'})
|
||||
id: number
|
||||
|
||||
@Column({unique: true, length: 45, comment: '타입 코드'})
|
||||
type: string
|
||||
|
||||
@Column({length: 100, comment: '타입명'})
|
||||
title: string
|
||||
|
||||
@Column({comment: '순서', nullable: true})
|
||||
seq: number
|
||||
|
||||
@Column({length: 1, comment: '사용여부', default: YesNo.YES})
|
||||
useYn: YesNo
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete, Put } from '@nestjs/common';
|
||||
import { DatasetService } from './dataset.service';
|
||||
import { CreateDatasetDto } from './dto/create-dataset.dto';
|
||||
import { UpdateDatasetDto } from './dto/update-dataset.dto';
|
||||
@@ -7,26 +7,46 @@ import { UpdateDatasetDto } from './dto/update-dataset.dto';
|
||||
export class DatasetController {
|
||||
constructor(private readonly datasetService: DatasetService) {}
|
||||
|
||||
/**
|
||||
* 데이터셋 생성
|
||||
* @param createDatasetDto
|
||||
*/
|
||||
@Post()
|
||||
create(@Body() createDatasetDto: CreateDatasetDto) {
|
||||
return this.datasetService.create(createDatasetDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터셋 목록 조회
|
||||
*/
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.datasetService.findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터셋 단건 조회
|
||||
* @param id
|
||||
*/
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.datasetService.findOne(+id);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
update(@Param('id') id: string, @Body() updateDatasetDto: UpdateDatasetDto) {
|
||||
/**
|
||||
* 데이터셋 수정
|
||||
* @param id
|
||||
* @param updateDatasetDto
|
||||
*/
|
||||
@Put(':id')
|
||||
update(@Param('id') id: number, @Body() updateDatasetDto: UpdateDatasetDto) {
|
||||
return this.datasetService.update(+id, updateDatasetDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터셋 제거
|
||||
* @param id
|
||||
*/
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: string) {
|
||||
return this.datasetService.remove(+id);
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { DatasetService } from './dataset.service';
|
||||
import { DatasetController } from './dataset.controller';
|
||||
import {TypeOrmModule} from "@nestjs/typeorm";
|
||||
import {Dataset} from "./entities/dataset.entity";
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Dataset } from './entities/dataset.entity';
|
||||
import { ConnectionService } from '../connection/connection.service';
|
||||
import { Database } from '../database/entities/database.entity';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Dataset])],
|
||||
imports: [TypeOrmModule.forFeature([Dataset, Database])],
|
||||
controllers: [DatasetController],
|
||||
providers: [DatasetService],
|
||||
providers: [DatasetService, ConnectionService],
|
||||
})
|
||||
export class DatasetModule {}
|
||||
|
||||
@@ -1,26 +1,95 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { CreateDatasetDto } from './dto/create-dataset.dto';
|
||||
import { UpdateDatasetDto } from './dto/update-dataset.dto';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Dataset } from './entities/dataset.entity';
|
||||
import { ConnectionService } from '../connection/connection.service';
|
||||
import { ResponseStatus } from '../common/enum/response-status.enum';
|
||||
|
||||
@Injectable()
|
||||
export class DatasetService {
|
||||
create(createDatasetDto: CreateDatasetDto) {
|
||||
return 'This action adds a new dataset';
|
||||
constructor(
|
||||
@InjectRepository(Dataset)
|
||||
private datasetRepository: Repository<Dataset>,
|
||||
private readonly connectionService: ConnectionService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 데이터셋 추가
|
||||
* @param createDatasetDto
|
||||
*/
|
||||
async create(createDataset: CreateDatasetDto) {
|
||||
// 쿼리를 돌려보고 문제가 있으면 저장 불가 안내
|
||||
const queryResult = await this.connectionService.executeQuery({
|
||||
id: createDataset.databaseId,
|
||||
query: createDataset.query,
|
||||
});
|
||||
if (queryResult.status === ResponseStatus.ERROR) {
|
||||
return { status: ResponseStatus.ERROR, message: queryResult.message };
|
||||
}
|
||||
|
||||
// 쿼리에 문제가 없으면 저장
|
||||
return {
|
||||
status: ResponseStatus.SUCCESS,
|
||||
data: await this.datasetRepository.save(createDataset),
|
||||
};
|
||||
}
|
||||
|
||||
findAll() {
|
||||
return `This action returns all dataset`;
|
||||
/**
|
||||
* 데이터셋 전체 조회
|
||||
*/
|
||||
async findAll() {
|
||||
return await this.datasetRepository.find();
|
||||
}
|
||||
|
||||
findOne(id: number) {
|
||||
return `This action returns a #${id} dataset`;
|
||||
/**
|
||||
* 데이터셋 단건 조회
|
||||
* @param id
|
||||
*/
|
||||
async findOne(id: number) {
|
||||
let returnObj: any;
|
||||
const dataObj = await this.datasetRepository.findOne({ where: { id: id } });
|
||||
|
||||
if (!dataObj) returnObj = {status: ResponseStatus.ERROR, message: `id ${id}의 값이 존재하지 않습니다.`};
|
||||
else returnObj = {status:ResponseStatus.SUCCESS, data: dataObj};
|
||||
return returnObj;
|
||||
}
|
||||
|
||||
update(id: number, updateDatasetDto: UpdateDatasetDto) {
|
||||
return `This action updates a #${id} dataset`;
|
||||
async update(id: number, updateDataset: UpdateDatasetDto) {
|
||||
const find_dataset = await this.datasetRepository.findOne({ where: { id: id } });
|
||||
if (!find_dataset) {
|
||||
return { status: ResponseStatus.ERROR, message: 'No exist dataset' };
|
||||
} else {
|
||||
// 변경할 쿼리 날려보고, 문제 없으면 저장
|
||||
const queryResult = await this.connectionService.executeQuery({
|
||||
id: find_dataset.databaseId,
|
||||
query: updateDataset.query,
|
||||
});
|
||||
if (queryResult.status === ResponseStatus.ERROR) {
|
||||
return { status: ResponseStatus.ERROR, message: queryResult.message };
|
||||
} else {
|
||||
if (updateDataset.title) {
|
||||
find_dataset.title = updateDataset.title;
|
||||
}
|
||||
if (updateDataset.query) {
|
||||
find_dataset.query = updateDataset.query;
|
||||
}
|
||||
|
||||
const saveResult = await this.datasetRepository.save(find_dataset);
|
||||
return { status: ResponseStatus.SUCCESS, data: saveResult };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
remove(id: number) {
|
||||
async remove(id: number) {
|
||||
const find_dataset = await this.datasetRepository.findOne({ where: { id: id } });
|
||||
|
||||
if (!find_dataset) {
|
||||
return 'Not exist dataset';
|
||||
} else {
|
||||
await this.datasetRepository.delete(find_dataset.id);
|
||||
}
|
||||
return `This action removes a #${id} dataset`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,13 @@
|
||||
export class CreateDatasetDto {}
|
||||
import {IsNumber, IsString} from "class-validator";
|
||||
|
||||
export class CreateDatasetDto {
|
||||
@IsString()
|
||||
title: string;
|
||||
|
||||
@IsNumber()
|
||||
databaseId: number;
|
||||
|
||||
@IsString()
|
||||
query:string;
|
||||
// readonly setting before
|
||||
}
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreateDatasetDto } from './create-dataset.dto';
|
||||
import { IsString } from 'class-validator';
|
||||
|
||||
export class UpdateDatasetDto extends PartialType(CreateDatasetDto) {}
|
||||
export class UpdateDatasetDto {
|
||||
@IsString()
|
||||
title: string;
|
||||
|
||||
@IsString()
|
||||
query: string;
|
||||
// readonly setting before
|
||||
}
|
||||
|
||||
@@ -17,24 +17,10 @@ export class Dataset extends BaseEntity {
|
||||
@Column({type: 'text', comment: '조회 sql'})
|
||||
query: string
|
||||
|
||||
@ManyToMany(type => Database)
|
||||
@JoinTable({
|
||||
name: 'database_dataset',
|
||||
joinColumn: {
|
||||
name: 'datasetId',
|
||||
referencedColumnName: 'databaseId'
|
||||
},
|
||||
inverseJoinColumn: {
|
||||
name: 'databaseId',
|
||||
referencedColumnName: 'id'
|
||||
}
|
||||
})
|
||||
databases: Database[];
|
||||
|
||||
@OneToMany(
|
||||
(type) => Widget,
|
||||
(widget) => widget.datasetId
|
||||
)
|
||||
widgets!: Widget
|
||||
// @OneToMany(
|
||||
// (type) => Widget,
|
||||
// (widget) => widget.datasetId
|
||||
// )
|
||||
// widgets!: Widget
|
||||
|
||||
}
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
import {NestFactory} from '@nestjs/core';
|
||||
import {AppModule} from './app.module';
|
||||
import express from "express";
|
||||
import {ExpressAdapter} from "@nestjs/platform-express";
|
||||
import {HttpExceptionFilter} from "./nest-utils/http-exception.filter";
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
import express from 'express';
|
||||
import { ExpressAdapter } from '@nestjs/platform-express';
|
||||
import { HttpExceptionFilter } from './nest-utils/http-exception.filter';
|
||||
|
||||
async function bootstrap() {
|
||||
const expressApp = express();
|
||||
const app = await NestFactory.create(AppModule, new ExpressAdapter(expressApp));
|
||||
app.useGlobalFilters(new HttpExceptionFilter());
|
||||
// const app = await NestFactory.create(AppModule);
|
||||
await app.listen(3000);
|
||||
const expressApp = express();
|
||||
const app = await NestFactory.create(AppModule, new ExpressAdapter(expressApp), {
|
||||
logger: console,
|
||||
cors: {
|
||||
origin: process.env.CORS_ORIGIN,
|
||||
preflightContinue: false,
|
||||
optionsSuccessStatus: 200,
|
||||
exposedHeaders: ['Content-Disposition'],
|
||||
},
|
||||
});
|
||||
app.useGlobalFilters(new HttpExceptionFilter());
|
||||
// const app = await NestFactory.create(AppModule);
|
||||
await app.listen(4000);
|
||||
}
|
||||
|
||||
bootstrap();
|
||||
bootstrap();
|
||||
@@ -1,4 +1,5 @@
|
||||
import {IsOptional, IsString} from "class-validator";
|
||||
import {ItemInfoDto} from "./item-info.dto";
|
||||
|
||||
export class CreateTemplateDto {
|
||||
@IsString()
|
||||
@@ -6,4 +7,5 @@ export class CreateTemplateDto {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
readonly description: string;
|
||||
readonly layout: ItemInfoDto[];
|
||||
}
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
import {TemplateItem} from "../entities/template-item.entity";
|
||||
import { TemplateItem } from '../entities/template-item.entity';
|
||||
import { IsOptional } from 'class-validator';
|
||||
|
||||
export class ItemInfoDto {
|
||||
x: number;
|
||||
y: number;
|
||||
w: number;
|
||||
h: number;
|
||||
category: string;
|
||||
type: string;
|
||||
x: number;
|
||||
y: number;
|
||||
w: number;
|
||||
h: number;
|
||||
category: string;
|
||||
type: string;
|
||||
|
||||
constructor(templateItem:TemplateItem) {
|
||||
this.x = templateItem.x;
|
||||
this.y = templateItem.y;
|
||||
this.w = templateItem.width;
|
||||
this.h = templateItem.height;
|
||||
this.category = templateItem.recommendCategory;
|
||||
this.type = templateItem.recommendType;
|
||||
};
|
||||
@IsOptional()
|
||||
i: number;
|
||||
|
||||
constructor(templateItem: TemplateItem) {
|
||||
this.x = templateItem.x;
|
||||
this.y = templateItem.y;
|
||||
this.w = templateItem.width;
|
||||
this.h = templateItem.height;
|
||||
this.category = templateItem.recommendCategory;
|
||||
this.type = templateItem.recommendType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
import {Template} from "../entities/template.entity";
|
||||
import {ItemInfoDto} from "./item-info.dto";
|
||||
import { Template } from '../entities/template.entity';
|
||||
import { ItemInfoDto } from './item-info.dto';
|
||||
import { IsOptional } from 'class-validator';
|
||||
|
||||
export class TemplateInfoDto {
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
layout: ItemInfoDto[];
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
layout: ItemInfoDto[];
|
||||
|
||||
constructor(template: Template) {
|
||||
this.id = template.id;
|
||||
this.title = template.title;
|
||||
this.description = template.description;
|
||||
};
|
||||
@IsOptional()
|
||||
widgets: any[];
|
||||
|
||||
constructor(template: Template) {
|
||||
this.id = template.id;
|
||||
this.title = template.title;
|
||||
this.description = template.description;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,11 +21,7 @@ export class Template extends BaseEntity {
|
||||
@Column({length: 1, default: YesNo.YES, comment: '사용여부'})
|
||||
useYn: YesNo
|
||||
|
||||
@OneToMany(
|
||||
(type => Dashboard),
|
||||
(dashboard) => dashboard.template
|
||||
)
|
||||
dashboards!: Dashboard[]
|
||||
|
||||
|
||||
@OneToMany(
|
||||
(type => TemplateItem),
|
||||
|
||||
@@ -1,88 +1,77 @@
|
||||
import {Body, Controller, Delete, Get, HttpStatus, Param, Post, Put, Res} from '@nestjs/common';
|
||||
import {TemplateService} from './template.service';
|
||||
import {CreateTemplateDto} from './dto/create-template.dto';
|
||||
import {UpdateTemplateDto} from './dto/update-template.dto';
|
||||
import {CreateTemplateItemDto} from "./dto/create-template-item.dto";
|
||||
import {UpdateTemplateItemDto} from "./dto/update-template-item.dto";
|
||||
import {TemplateInfoDto} from "./dto/template-info.dto";
|
||||
import { Body, Controller, Delete, Get, HttpStatus, Param, Post, Put, Res } from '@nestjs/common';
|
||||
import { TemplateService } from './template.service';
|
||||
import { CreateTemplateDto } from './dto/create-template.dto';
|
||||
import { UpdateTemplateDto } from './dto/update-template.dto';
|
||||
import { CreateTemplateItemDto } from './dto/create-template-item.dto';
|
||||
import { UpdateTemplateItemDto } from './dto/update-template-item.dto';
|
||||
import { TemplateInfoDto } from './dto/template-info.dto';
|
||||
|
||||
@Controller('template')
|
||||
export class TemplateController {
|
||||
constructor(private readonly templateService: TemplateService) {
|
||||
}
|
||||
constructor(private readonly templateService: TemplateService) {}
|
||||
|
||||
/**
|
||||
* 템플릿 생성
|
||||
* @param createTemplateDto
|
||||
*/
|
||||
@Post()
|
||||
create(@Body() createTemplateDto: CreateTemplateDto) {
|
||||
return this.templateService.create(createTemplateDto);
|
||||
}
|
||||
/**
|
||||
* 템플릿 생성
|
||||
* @param createTemplateDto
|
||||
*/
|
||||
@Post()
|
||||
create(@Body() createTemplateDto: CreateTemplateDto) {
|
||||
return this.templateService.create(createTemplateDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 템플릿 상세 아이템 생성
|
||||
* @param createTemplateItemDto
|
||||
*/
|
||||
@Post('/item')
|
||||
createItem(@Body() createTemplateItemDto: CreateTemplateItemDto) {
|
||||
return this.templateService.createItem(createTemplateItemDto);
|
||||
}
|
||||
/**
|
||||
* 템플릿 목록 조회
|
||||
*/
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.templateService.findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* 템플릿 목록 조회
|
||||
*/
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.templateService.findAll();
|
||||
}
|
||||
/**
|
||||
* 템플릿 단건 조회
|
||||
* @param id
|
||||
*/
|
||||
@Get(':id')
|
||||
async findOne(@Res() res, @Param('id') id: number) {
|
||||
return await this.templateService.findOne(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 템플릿 단건 조회
|
||||
* @param id
|
||||
*/
|
||||
@Get(':id')
|
||||
async findOne(@Res() res, @Param('id') id: number) {
|
||||
const resultTemplate: TemplateInfoDto = await this.templateService.findOne(id);
|
||||
return res.status(HttpStatus.OK).json(resultTemplate);
|
||||
}
|
||||
/**
|
||||
* 템플릿 단건 업데이트
|
||||
* @param id
|
||||
* @param updateTemplateDto
|
||||
*/
|
||||
@Put(':id')
|
||||
update(@Param('id') id: string, @Body() updateTemplateDto: UpdateTemplateDto) {
|
||||
return this.templateService.update(+id, updateTemplateDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 템플릿 단건 업데이트
|
||||
* @param id
|
||||
* @param updateTemplateDto
|
||||
*/
|
||||
@Put(':id')
|
||||
update(@Param('id') id: string, @Body() updateTemplateDto: UpdateTemplateDto) {
|
||||
return this.templateService.update(+id, updateTemplateDto);
|
||||
}
|
||||
/**
|
||||
* 템플릿 삭제 (사용여부 N 처리)
|
||||
* @param id
|
||||
*/
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: string) {
|
||||
return this.templateService.remove(+id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 템플릿 상세 아이템 단건 업데이트
|
||||
* @param id
|
||||
* @param updateTemplateDto
|
||||
*/
|
||||
@Put('/item/:id')
|
||||
updateItem(@Param('id') id: string, @Body() updateTemplateItemDto: UpdateTemplateItemDto) {
|
||||
return this.templateService.updateItem(+id, updateTemplateItemDto);
|
||||
}
|
||||
/**
|
||||
* 템플릿 추천 목록 조회
|
||||
* @param widgets
|
||||
*/
|
||||
//todo:: yhs:: 추천 알고리즘 적용해서 조회해 와야함
|
||||
@Post('/recommend')
|
||||
findRecommendAll(@Body() body) {
|
||||
return this.templateService.findRecommendTemplates(body.widgets);
|
||||
}
|
||||
|
||||
/**
|
||||
* 템플릿 삭제 (사용여부 N 처리)
|
||||
* @param id
|
||||
*/
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: string) {
|
||||
return this.templateService.remove(+id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 템플릿 추천 목록 조회
|
||||
* @param widgets
|
||||
*/
|
||||
//todo:: yhs:: 추천 알고리즘 적용해서 조회해 와야함
|
||||
@Post('/recommend')
|
||||
findRecommendAll(@Body() widgets: any[]) {
|
||||
return this.templateService.findRecommendTemplates(widgets);
|
||||
}
|
||||
/**
|
||||
* 선택된 템플릿에 맞게 대시보드 레아이웃을 정해서 값을 보내줘야 한다.
|
||||
* @param widgets
|
||||
* @param templateId
|
||||
*/
|
||||
@Post('/dashboard')
|
||||
getTemplateDashboardLayout(@Body() data) {
|
||||
return this.templateService.getTemplateDashboardLayout(data.widgets, data.templateId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import {Module} from '@nestjs/common';
|
||||
import {TemplateService} from './template.service';
|
||||
import {TemplateController} from './template.controller';
|
||||
import {TypeOrmModule} from "@nestjs/typeorm";
|
||||
import {Template} from "./entities/template.entity";
|
||||
import {TemplateItem} from "./entities/template-item.entity";
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TemplateService } from './template.service';
|
||||
import { TemplateController } from './template.controller';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Template } from './entities/template.entity';
|
||||
import { TemplateItem } from './entities/template-item.entity';
|
||||
import { Widget } from '../widget/entities/widget.entity';
|
||||
import { Component } from '../component/entities/component.entity';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Template, TemplateItem])],
|
||||
controllers: [TemplateController],
|
||||
providers: [TemplateService],
|
||||
imports: [TypeOrmModule.forFeature([Template, TemplateItem, Widget, Component])],
|
||||
controllers: [TemplateController],
|
||||
providers: [TemplateService],
|
||||
})
|
||||
export class TemplateModule {
|
||||
}
|
||||
export class TemplateModule {}
|
||||
|
||||
@@ -1,191 +1,453 @@
|
||||
import {Injectable} from '@nestjs/common';
|
||||
import {CreateTemplateDto} from './dto/create-template.dto';
|
||||
import {UpdateTemplateDto} from './dto/update-template.dto';
|
||||
import {InjectRepository} from "@nestjs/typeorm";
|
||||
import {Template} from "./entities/template.entity";
|
||||
import {Repository} from "typeorm";
|
||||
import {YesNo} from "../common/enum/yn.enum";
|
||||
import {TemplateItem} from "./entities/template-item.entity";
|
||||
import {CreateTemplateItemDto} from "./dto/create-template-item.dto";
|
||||
import {UpdateTemplateItemDto} from "./dto/update-template-item.dto";
|
||||
import {TemplateInfoDto} from "./dto/template-info.dto";
|
||||
import {ItemInfoDto} from "./dto/item-info.dto";
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { CreateTemplateDto } from './dto/create-template.dto';
|
||||
import { UpdateTemplateDto } from './dto/update-template.dto';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Template } from './entities/template.entity';
|
||||
import { In, Repository } from 'typeorm';
|
||||
import { YesNo } from '../common/enum/yn.enum';
|
||||
import { TemplateItem } from './entities/template-item.entity';
|
||||
import { CreateTemplateItemDto } from './dto/create-template-item.dto';
|
||||
import { UpdateTemplateItemDto } from './dto/update-template-item.dto';
|
||||
import { TemplateInfoDto } from './dto/template-info.dto';
|
||||
import { ItemInfoDto } from './dto/item-info.dto';
|
||||
import { async } from 'rxjs';
|
||||
import { ResponseStatus } from '../common/enum/response-status.enum';
|
||||
import { Widget } from '../widget/entities/widget.entity';
|
||||
import { Component } from '../component/entities/component.entity';
|
||||
import { ComponentType } from '../common/enum/component-type.enum';
|
||||
|
||||
@Injectable()
|
||||
export class TemplateService {
|
||||
constructor(
|
||||
@InjectRepository(Template)
|
||||
private readonly templateRepository: Repository<Template>,
|
||||
@InjectRepository(TemplateItem)
|
||||
private readonly templateItemRepository: Repository<TemplateItem>
|
||||
) {
|
||||
constructor(
|
||||
@InjectRepository(Template)
|
||||
private readonly templateRepository: Repository<Template>,
|
||||
@InjectRepository(TemplateItem)
|
||||
private readonly templateItemRepository: Repository<TemplateItem>,
|
||||
@InjectRepository(Widget)
|
||||
private readonly widgetRepository: Repository<Widget>,
|
||||
@InjectRepository(Component)
|
||||
private readonly componentRepository: Repository<Component>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 템플릿 추가
|
||||
* @param createTemplate
|
||||
*/
|
||||
async create(createTemplate: CreateTemplateDto) {
|
||||
const insertTemplate = await this.templateRepository.save({
|
||||
title: createTemplate.title,
|
||||
description: createTemplate.description,
|
||||
});
|
||||
|
||||
// 템플릿 상세
|
||||
const insertItems = [];
|
||||
createTemplate.layout.map(item => {
|
||||
const tempObj = {
|
||||
templateId: insertTemplate.id,
|
||||
x: item.x,
|
||||
y: item.y,
|
||||
width: item.w,
|
||||
height: item.h,
|
||||
recommendCategory: item.category,
|
||||
};
|
||||
insertItems.push(tempObj);
|
||||
});
|
||||
await this.templateItemRepository.save(insertItems);
|
||||
|
||||
return { status: ResponseStatus.SUCCESS, data: insertTemplate };
|
||||
}
|
||||
|
||||
/**
|
||||
* 템플릿 목록 조회
|
||||
*/
|
||||
async findAll() {
|
||||
const result = await this.templateRepository.find({
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
description: true,
|
||||
},
|
||||
where: {
|
||||
useYn: YesNo.YES,
|
||||
},
|
||||
});
|
||||
|
||||
return { status: ResponseStatus.SUCCESS, data: result };
|
||||
}
|
||||
|
||||
/**
|
||||
* 템플릿 단건 조회
|
||||
* @param id
|
||||
*/
|
||||
async findOne(id: number) {
|
||||
let returnObj: TemplateInfoDto;
|
||||
|
||||
// 템플릿 기본 정보 조회
|
||||
const templateInfo = await this.templateRepository.findOne({
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
description: true,
|
||||
},
|
||||
where: {
|
||||
useYn: YesNo.YES,
|
||||
id: id,
|
||||
},
|
||||
});
|
||||
|
||||
if (templateInfo.id) {
|
||||
returnObj = new TemplateInfoDto(templateInfo);
|
||||
// 템플릿 상세 아이템 조회(layout 조회 및 가공)
|
||||
const layoutList = await this.templateItemRepository.find({
|
||||
where: {
|
||||
templateId: templateInfo.id,
|
||||
},
|
||||
});
|
||||
const layout = [];
|
||||
layoutList.map(item => {
|
||||
const itemInfo: ItemInfoDto = new ItemInfoDto(item);
|
||||
layout.push(itemInfo);
|
||||
});
|
||||
returnObj.layout = layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* 템플릿 추가
|
||||
* @param createTemplate
|
||||
*/
|
||||
async create(createTemplate: CreateTemplateDto): Promise<Template> {
|
||||
const insertItem = await this.templateRepository.save({
|
||||
title: createTemplate.title,
|
||||
description: createTemplate.description,
|
||||
return { status: ResponseStatus.SUCCESS, data: returnObj };
|
||||
}
|
||||
|
||||
})
|
||||
return insertItem;
|
||||
/**
|
||||
* 템플릿 업데이트
|
||||
* @param id
|
||||
* @param updateTemplate
|
||||
*/
|
||||
async update(id: number, updateTemplate: UpdateTemplateDto) {
|
||||
const updateItem = await this.templateRepository.update(
|
||||
{
|
||||
id: id,
|
||||
},
|
||||
{
|
||||
title: updateTemplate.title,
|
||||
description: updateTemplate.description,
|
||||
},
|
||||
);
|
||||
|
||||
// 템플릿 상세
|
||||
await this.templateItemRepository.delete({ templateId: id });
|
||||
const insertItems = [];
|
||||
updateTemplate.layout.map(item => {
|
||||
const tempObj = {
|
||||
templateId: id,
|
||||
x: item.x,
|
||||
y: item.y,
|
||||
width: item.w,
|
||||
height: item.h,
|
||||
recommendCategory: item.category,
|
||||
};
|
||||
insertItems.push(tempObj);
|
||||
});
|
||||
await this.templateItemRepository.save(insertItems);
|
||||
|
||||
if (updateItem.affected < 1) {
|
||||
return {
|
||||
status: ResponseStatus.ERROR,
|
||||
message: '변동사항 없음',
|
||||
};
|
||||
} else if (updateItem.affected > 1) {
|
||||
return {
|
||||
status: ResponseStatus.ERROR,
|
||||
message: '여러개 바뀜',
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
status: ResponseStatus.SUCCESS,
|
||||
data: { message: `This action updates a #${id} template` },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 템플릿 비활성화(useYn='N')
|
||||
* @param id
|
||||
*/
|
||||
async remove(id: number) {
|
||||
const deleteItem = await this.templateRepository.update(
|
||||
{
|
||||
id: id,
|
||||
},
|
||||
{
|
||||
useYn: YesNo.NO,
|
||||
},
|
||||
);
|
||||
|
||||
if (deleteItem.affected < 1) {
|
||||
return {
|
||||
status: ResponseStatus.ERROR,
|
||||
message: '변동사항 없음',
|
||||
};
|
||||
} else if (deleteItem.affected > 1) {
|
||||
return {
|
||||
status: ResponseStatus.ERROR,
|
||||
message: '여러개 바뀜',
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
status: ResponseStatus.SUCCESS,
|
||||
data: { message: `#${id} template useYn='N' 변경완료 ` },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 선택된 위젯목록으로 추천될 template 목록 가져오기
|
||||
* @param widgets
|
||||
*/
|
||||
async findRecommendTemplates(widgets: number[]) {
|
||||
const widgetInfo = this.widgetRepository
|
||||
.createQueryBuilder()
|
||||
.subQuery()
|
||||
.select(['widget.*'])
|
||||
.from(Widget, 'widget')
|
||||
.where('id in (:id)')
|
||||
.getQuery();
|
||||
|
||||
const widgetList = await this.componentRepository
|
||||
.createQueryBuilder('component')
|
||||
.select([
|
||||
'widget.*',
|
||||
'component.category as componentCategory',
|
||||
'component.type as componentType',
|
||||
])
|
||||
// .select([
|
||||
// `sum(case when component.category = 'HORIZONTAL' then 1 else 0 end) as horizontalCnt`,
|
||||
// `sum(case when component.category = 'VERTICAL' then 1 else 0 end) as verticalCnt`,
|
||||
// `sum(case when component.category = 'SQUARE' then 1 else 0 end) as squareCnt`,
|
||||
// `sum(case when component.category = 'SCORE' then 1 else 0 end) as scoreCnt`,
|
||||
// `sum(1) as widgetCnt`,
|
||||
// ])
|
||||
.innerJoin(widgetInfo, 'widget', 'widget.componentId = component.id')
|
||||
.setParameter('id', widgets)
|
||||
.getRawMany();
|
||||
// .getRawOne();
|
||||
|
||||
// 템플릿 추천 알고리즘
|
||||
await this.getRecommendTemplates(widgetList);
|
||||
|
||||
const returnArr = [];
|
||||
const tempTemplateInfo = new TemplateItem();
|
||||
returnArr.push(tempTemplateInfo);
|
||||
|
||||
const templateList = await this.templateRepository.find({
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
description: true,
|
||||
},
|
||||
where: {
|
||||
useYn: YesNo.YES,
|
||||
},
|
||||
});
|
||||
|
||||
return { status: ResponseStatus.SUCCESS, data: templateList };
|
||||
}
|
||||
|
||||
/**
|
||||
* 선택된 템플릿에 맞게 대시보드 레아이웃을 정해서 값을 보내줘야 한다.
|
||||
* @param widgets
|
||||
* @param templateId
|
||||
*/
|
||||
async getTemplateDashboardLayout(widgets: number[], templateId: number) {
|
||||
const widgetInfo = this.widgetRepository
|
||||
.createQueryBuilder()
|
||||
.subQuery()
|
||||
.select(['widget.*'])
|
||||
.from(Widget, 'widget')
|
||||
.where('id in (:ids)')
|
||||
.getQuery();
|
||||
|
||||
const widgetList = await this.componentRepository
|
||||
.createQueryBuilder('component')
|
||||
.select(['widgetInfo.*', 'component.type as componentType'])
|
||||
.innerJoin(widgetInfo, 'widgetInfo', 'widgetInfo.componentId = component.id')
|
||||
.setParameter('ids', widgets)
|
||||
.getRawMany();
|
||||
|
||||
// const widgetList = await this.widgetRepository.find({ where: { id: In(widgets) } });
|
||||
|
||||
let templateInfo: TemplateInfoDto;
|
||||
|
||||
// 템플릿 기본 정보 조회
|
||||
const template = await this.templateRepository.findOne({
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
description: true,
|
||||
},
|
||||
where: {
|
||||
useYn: YesNo.YES,
|
||||
id: templateId,
|
||||
},
|
||||
});
|
||||
|
||||
if (template.id) {
|
||||
templateInfo = new TemplateInfoDto(template);
|
||||
// 템플릿 상세 아이템 조회(layout 조회 및 가공)
|
||||
const layoutList = await this.templateItemRepository.find({
|
||||
where: {
|
||||
templateId: templateInfo.id,
|
||||
},
|
||||
});
|
||||
const layout = [];
|
||||
layoutList.map(item => {
|
||||
const itemInfo: ItemInfoDto = new ItemInfoDto(item);
|
||||
layout.push(itemInfo);
|
||||
});
|
||||
templateInfo.layout = layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* 템플릿 상세 아이템 추가
|
||||
* @param createTemplateItem
|
||||
*/
|
||||
async createItem(createTemplateItem: CreateTemplateItemDto): Promise<TemplateItem> {
|
||||
const insertItem = await this.templateItemRepository.save({
|
||||
templateId: createTemplateItem.templateId,
|
||||
x: createTemplateItem.x,
|
||||
y: createTemplateItem.y,
|
||||
width: createTemplateItem.width,
|
||||
height: createTemplateItem.height,
|
||||
recommendCategory: createTemplateItem.recommendCategory,
|
||||
})
|
||||
return insertItem;
|
||||
}
|
||||
widgetList.forEach((item, i) => {
|
||||
item.option = JSON.parse(item.option);
|
||||
// templateInfo.widgets.push(item);
|
||||
if (templateInfo.layout.length > i && template) templateInfo.layout[i].i = item.id;
|
||||
});
|
||||
|
||||
/**
|
||||
* 템플릿 목록 조회
|
||||
*/
|
||||
async findAll() {
|
||||
const result = await this.templateRepository.find({
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
description: true,
|
||||
},
|
||||
where: {
|
||||
useYn: YesNo.YES
|
||||
}
|
||||
});
|
||||
templateInfo.widgets = widgetList;
|
||||
|
||||
return result;
|
||||
}
|
||||
//
|
||||
// widgetList[0].option = JSON.parse(widgetList[0].option);
|
||||
// widgetList[1].option = JSON.parse(widgetList[1].option);
|
||||
//
|
||||
// templateInfo.widgets = [widgetList[0], widgetList[1]];
|
||||
// templateInfo.layout[0].i = widgetList[0].id;
|
||||
// templateInfo.layout[1].i = widgetList[1].id;
|
||||
return { status: ResponseStatus.SUCCESS, data: templateInfo };
|
||||
}
|
||||
|
||||
/**
|
||||
* 템플릿 단건 조회
|
||||
* @param id
|
||||
*/
|
||||
async findOne(id: number): Promise<TemplateInfoDto> {
|
||||
let returnObj: TemplateInfoDto;
|
||||
/**
|
||||
* 템플릿 추천 목록 계산
|
||||
* @private
|
||||
* @param widgetList
|
||||
*/
|
||||
private async getRecommendTemplates(widgetList) {
|
||||
// widget component별 개수 정리
|
||||
const widgetComponentInfo = {
|
||||
horizontalCnt: 0,
|
||||
verticalCnt: 0,
|
||||
squareCnt: 0,
|
||||
scoreCnt: 0,
|
||||
tableCnt: 0,
|
||||
widgetCnt: widgetList.length,
|
||||
};
|
||||
|
||||
// 템플릿 기본 정보 조회
|
||||
const templateInfo = await this.templateRepository.findOne({
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
description: true,
|
||||
},
|
||||
where: {
|
||||
useYn: YesNo.YES,
|
||||
id: id,
|
||||
}
|
||||
});
|
||||
widgetList.map(item => {
|
||||
switch (item.componentCategory) {
|
||||
case ComponentType.HORIZONTAL:
|
||||
widgetComponentInfo.horizontalCnt += 1;
|
||||
break;
|
||||
case ComponentType.VERTICAL:
|
||||
widgetComponentInfo.verticalCnt += 1;
|
||||
break;
|
||||
case ComponentType.SCORE:
|
||||
widgetComponentInfo.scoreCnt += 1;
|
||||
break;
|
||||
case ComponentType.SQUARE:
|
||||
widgetComponentInfo.squareCnt += 1;
|
||||
break;
|
||||
case ComponentType.TABLE:
|
||||
widgetComponentInfo.tableCnt += 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
if(templateInfo.id){
|
||||
returnObj = new TemplateInfoDto(templateInfo);
|
||||
// 템플릿 상세 아이템 조회(layout 조회 및 가공)
|
||||
const layoutList = await this.templateItemRepository.find({
|
||||
where: {
|
||||
templateId: templateInfo.id
|
||||
}
|
||||
});
|
||||
const layout = [];
|
||||
layoutList.map((item) => {
|
||||
const itemInfo:ItemInfoDto = new ItemInfoDto(item);
|
||||
layout.push(itemInfo);
|
||||
})
|
||||
returnObj.layout = layout;
|
||||
const templates = this.templateRepository
|
||||
.createQueryBuilder()
|
||||
.subQuery()
|
||||
.select(['template.id as id'])
|
||||
.from(Template, 'template')
|
||||
.where(`useYn = 'Y'`)
|
||||
.getQuery();
|
||||
|
||||
const templateComponentInfo = await this.templateItemRepository
|
||||
.createQueryBuilder('templateItems')
|
||||
.select([
|
||||
'template.id as id',
|
||||
`sum(case when templateItems.recommendCategory = 'HORIZONTAL' then 1 else 0 end) as horizontalCnt`,
|
||||
`sum(case when templateItems.recommendCategory = 'VERTICAL' then 1 else 0 end) as verticalCnt`,
|
||||
`sum(case when templateItems.recommendCategory = 'SQUARE' then 1 else 0 end) as squareCnt`,
|
||||
`sum(case when templateItems.recommendCategory = 'SCORE' then 1 else 0 end) as scoreCnt`,
|
||||
'sum(1) as totalCnt',
|
||||
])
|
||||
.innerJoin(templates, 'template', 'template.id = templateItems.templateId')
|
||||
.groupBy('template.id')
|
||||
.getRawMany();
|
||||
|
||||
templateComponentInfo.forEach(item => {
|
||||
// 갯수로 점수 산출
|
||||
let cntScore = 0;
|
||||
if (item.totalCnt === widgetComponentInfo.widgetCnt) {
|
||||
// 갯수가 같을 때
|
||||
cntScore = 100;
|
||||
} else if (item.totalCnt > widgetComponentInfo.widgetCnt) {
|
||||
// 템플릿의 item이 더 많을 때
|
||||
cntScore = 100 - (item.totalCnt - widgetComponentInfo.widgetCnt) * 5;
|
||||
} else {
|
||||
// 템플릿의 item이 적을 때
|
||||
cntScore = 100 - (widgetComponentInfo.widgetCnt - item.totalCnt) * 10;
|
||||
}
|
||||
item.cntScore = cntScore;
|
||||
|
||||
// 컴포넌트 타입으로 점수 산출
|
||||
item.recommendScore = this.calRecommendScore(item, widgetComponentInfo);
|
||||
});
|
||||
|
||||
// console.log(templateComponentInfo);
|
||||
}
|
||||
|
||||
private async calRecommendScore(templateInfo, widgetInfo) {
|
||||
let recommendScore = 0;
|
||||
|
||||
let templateCount = {
|
||||
HORIZONTAL: Number(templateInfo.horizontalCnt),
|
||||
VERTICAL: Number(templateInfo.verticalCnt),
|
||||
SQUARE: Number(templateInfo.squareCnt),
|
||||
SCORE: Number(templateInfo.scoreCnt),
|
||||
TABLE: 0,
|
||||
};
|
||||
|
||||
let widgetCount = {
|
||||
HORIZONTAL: Number(widgetInfo.horizontalCnt),
|
||||
VERTICAL: Number(widgetInfo.verticalCnt),
|
||||
SQUARE: Number(widgetInfo.squareCnt),
|
||||
SCORE: Number(widgetInfo.scoreCnt),
|
||||
TABLE: Number(widgetInfo.tableCnt),
|
||||
};
|
||||
|
||||
// 일치하는 경우 계산 (horizontal, vertical, square, score)
|
||||
for (let i = 0; i < Object.keys(templateCount).length; i++) {
|
||||
const componentType = Object.keys(templateCount)[i];
|
||||
if (widgetCount[componentType] > 0 && templateCount[componentType] > 0) {
|
||||
let equalCnt = 0;
|
||||
if (templateCount[componentType] >= widgetCount[componentType]) {
|
||||
equalCnt = widgetCount[componentType];
|
||||
} else if (templateCount[componentType] < widgetCount[componentType]) {
|
||||
equalCnt = templateCount[componentType];
|
||||
}
|
||||
|
||||
return returnObj;
|
||||
templateCount[componentType] -= equalCnt;
|
||||
widgetCount[componentType] -= equalCnt;
|
||||
recommendScore += 100 * equalCnt;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 템플릿 업데이트
|
||||
* @param id
|
||||
* @param updateTemplateDto
|
||||
*/
|
||||
async update(id: number, updateTemplateDto: UpdateTemplateDto) {
|
||||
// 남은 것중에 table 100점 짜리 제거
|
||||
// restWigetList.map(widget => {
|
||||
// if (widget.componentCategory === ComponentType.TABLE) {
|
||||
// console.log('test');
|
||||
// // componentTypeCount.
|
||||
// }
|
||||
// });
|
||||
|
||||
const updateItem = await this.templateRepository.update({
|
||||
id: id
|
||||
}, {
|
||||
title: updateTemplateDto.title,
|
||||
description: updateTemplateDto.description,
|
||||
});
|
||||
console.log(templateInfo);
|
||||
|
||||
let msg = `This action updates a #${id} template`
|
||||
if (updateItem.affected < 1) {
|
||||
msg = '변동사항 없음';
|
||||
} else if (updateItem.affected > 1) {
|
||||
msg = '여러개 바뀜';
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 템플릿 상세 아이템 수정
|
||||
* @param id
|
||||
* @param updateTemplateItem
|
||||
*/
|
||||
async updateItem(id: number,updateTemplateItem: UpdateTemplateItemDto) {
|
||||
const updateItem = await this.templateItemRepository.update({
|
||||
id: id
|
||||
}, {
|
||||
x: updateTemplateItem.x,
|
||||
y: updateTemplateItem.y,
|
||||
width: updateTemplateItem.width,
|
||||
height: updateTemplateItem.height,
|
||||
recommendCategory: updateTemplateItem.recommendCategory
|
||||
});
|
||||
|
||||
let msg = `This action updates a #${id} templateItem`
|
||||
if (updateItem.affected < 1) {
|
||||
msg = '변동사항 없음';
|
||||
} else if (updateItem.affected > 1) {
|
||||
msg = '여러개 바뀜';
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 템플릿 비활성화(useYn='Y')
|
||||
* @param id
|
||||
*/
|
||||
async remove(id: number) {
|
||||
const deleteItem = await this.templateRepository.update({
|
||||
id: id
|
||||
}, {
|
||||
useYn: YesNo.YES
|
||||
});
|
||||
|
||||
let msg = `#${id} template useYn='Y' 변경완료 `
|
||||
if (deleteItem.affected < 1) {
|
||||
msg = '변동사항 없음';
|
||||
} else if (deleteItem.affected > 1) {
|
||||
msg = '여러개 바뀜';
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 선택된 위젯목록으로 추천될 template 목록
|
||||
* @param widgets
|
||||
*/
|
||||
async findRecommendTemplates(widgets: any[]): Promise<TemplateInfoDto[]> {
|
||||
const returnArr = [];
|
||||
const tempTemplateInfo = new TemplateItem();
|
||||
returnArr.push(tempTemplateInfo);
|
||||
|
||||
|
||||
|
||||
return returnArr;
|
||||
}
|
||||
return recommendScore;
|
||||
}
|
||||
}
|
||||
|
||||
45
backend-api/src/utils/field-type.util.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { convert, DateTimeFormatter, LocalDate, LocalDateTime } from 'js-joda';
|
||||
|
||||
export class FieldTypeUtil {
|
||||
private static FIELD_TYPE_NUMBER = 'number';
|
||||
private static FIELD_TYPE_STRING = 'string';
|
||||
private static FIELD_TYPE_DATE = 'date';
|
||||
|
||||
static mysqlFieldType(fieldType: string): string {
|
||||
switch (+fieldType) {
|
||||
case 0: //'DECIMAL', // aka DECIMAL
|
||||
case 1: //'TINY', // aka TINYINT, 1 byte
|
||||
case 2: //'SHORT', // aka SMALLINT, 2 bytes
|
||||
case 3: //'LONG', // aka INT, 4 bytes
|
||||
case 4: //'FLOAT', // aka FLOAT, 4-8 bytes
|
||||
case 5: //'DOUBLE', // aka DOUBLE, 8 bytes
|
||||
case 8: //'LONGLONG', // aka BIGINT, 8 bytes
|
||||
case 9: //'INT24', // aka MEDIUMINT, 3 bytes
|
||||
case 16: //'BIT', // aka BIT, 1-8 byte
|
||||
case 246:
|
||||
return this.FIELD_TYPE_NUMBER; //'NEWDECIMAL', // aka DECIMAL
|
||||
case 7: //'TIMESTAMP', // aka TIMESTAMP
|
||||
case 10: //'DATE', // aka DATE
|
||||
case 11: //'TIME', // aka TIME
|
||||
case 12: //'DATETIME', // aka DATETIME
|
||||
case 13: //'YEAR', // aka YEAR, 1 byte (don't ask)
|
||||
case 14:
|
||||
return this.FIELD_TYPE_DATE; //'NEWDATE', // aka ?
|
||||
case 6: // NULL (used for prepared statements, I think)
|
||||
case 15: //'VARCHAR', // aka VARCHAR (?)
|
||||
case 245: //'JSON',
|
||||
case 247: //'ENUM', // aka ENUM
|
||||
case 248: //'SET', // aka SET
|
||||
case 249: //'TINY_BLOB', // aka TINYBLOB, TINYTEXT
|
||||
case 250: //'MEDIUM_BLOB', // aka MEDIUMBLOB, MEDIUMTEXT
|
||||
case 251: //'LONG_BLOB', // aka LONGBLOG, LONGTEXT
|
||||
case 252: //'BLOB', // aka BLOB, TEXT
|
||||
case 253: //'VAR_STRING', // aka VARCHAR, VARBINARY
|
||||
case 254: //'STRING', // aka CHAR, BINARY
|
||||
case 255:
|
||||
return this.FIELD_TYPE_STRING; //'GEOMETRY' // aka GEOMETRY
|
||||
default:
|
||||
return this.FIELD_TYPE_STRING;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export class CreateWidgetViewDto {}
|
||||
@@ -1,4 +0,0 @@
|
||||
import { PartialType } from '@nestjs/swagger';
|
||||
import { CreateWidgetViewDto } from './create-widget-view.dto';
|
||||
|
||||
export class UpdateWidgetViewDto extends PartialType(CreateWidgetViewDto) {}
|
||||
@@ -1,36 +0,0 @@
|
||||
import {Column, Entity, JoinTable, ManyToMany, OneToMany, PrimaryGeneratedColumn} from "typeorm";
|
||||
import {Database} from "../../database/entities/database.entity";
|
||||
import {BaseEntity} from "../../common/entities/base.entity";
|
||||
import {Widget} from "../../widget/entities/widget.entity";
|
||||
|
||||
@Entity()
|
||||
export class WidgetView extends BaseEntity {
|
||||
@PrimaryGeneratedColumn({comment: '위젯 데이터셋 ID'})
|
||||
id: number
|
||||
|
||||
@Column({comment: '데이터베이스 ID'})
|
||||
databaseId: number
|
||||
|
||||
@Column({type: 'text', comment: '조회 sql'})
|
||||
query: string
|
||||
|
||||
@ManyToMany(type => Database)
|
||||
@JoinTable({
|
||||
name: 'database_widget_view',
|
||||
joinColumn: {
|
||||
name: 'widgetViewId',
|
||||
referencedColumnName: 'databaseId'
|
||||
},
|
||||
inverseJoinColumn: {
|
||||
name: 'databaseId',
|
||||
referencedColumnName: 'id'
|
||||
}
|
||||
})
|
||||
databases: Database[];
|
||||
|
||||
@OneToMany(
|
||||
(type) => Widget,
|
||||
(widget) => widget.datasetId
|
||||
)
|
||||
widgets!: Widget
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { WidgetViewController } from './widget-view.controller';
|
||||
import { WidgetViewService } from './widget-view.service';
|
||||
|
||||
describe('WidgetViewController', () => {
|
||||
let controller: WidgetViewController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [WidgetViewController],
|
||||
providers: [WidgetViewService],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<WidgetViewController>(WidgetViewController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -1,34 +0,0 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
|
||||
import { WidgetViewService } from './widget-view.service';
|
||||
import { CreateWidgetViewDto } from './dto/create-widget-view.dto';
|
||||
import { UpdateWidgetViewDto } from './dto/update-widget-view.dto';
|
||||
|
||||
@Controller('widget-view')
|
||||
export class WidgetViewController {
|
||||
constructor(private readonly widgetViewService: WidgetViewService) {}
|
||||
|
||||
@Post()
|
||||
create(@Body() createWidgetViewDto: CreateWidgetViewDto) {
|
||||
return this.widgetViewService.create(createWidgetViewDto);
|
||||
}
|
||||
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.widgetViewService.findAll();
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.widgetViewService.findOne(+id);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
update(@Param('id') id: string, @Body() updateWidgetViewDto: UpdateWidgetViewDto) {
|
||||
return this.widgetViewService.update(+id, updateWidgetViewDto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: string) {
|
||||
return this.widgetViewService.remove(+id);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { WidgetViewService } from './widget-view.service';
|
||||
import { WidgetViewController } from './widget-view.controller';
|
||||
|
||||
@Module({
|
||||
controllers: [WidgetViewController],
|
||||
providers: [WidgetViewService]
|
||||
})
|
||||
export class WidgetViewModule {}
|
||||
@@ -1,18 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { WidgetViewService } from './widget-view.service';
|
||||
|
||||
describe('WidgetViewService', () => {
|
||||
let service: WidgetViewService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [WidgetViewService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<WidgetViewService>(WidgetViewService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -1,28 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { CreateWidgetViewDto } from './dto/create-widget-view.dto';
|
||||
import { UpdateWidgetViewDto } from './dto/update-widget-view.dto';
|
||||
|
||||
@Injectable()
|
||||
export class WidgetViewService {
|
||||
create(createWidgetViewDto: CreateWidgetViewDto) {
|
||||
|
||||
|
||||
return 'This action adds a new widgetView';
|
||||
}
|
||||
|
||||
findAll() {
|
||||
return `This action returns all widgetView`;
|
||||
}
|
||||
|
||||
findOne(id: number) {
|
||||
return `This action returns a #${id} widgetView`;
|
||||
}
|
||||
|
||||
update(id: number, updateWidgetViewDto: UpdateWidgetViewDto) {
|
||||
return `This action updates a #${id} widgetView`;
|
||||
}
|
||||
|
||||
remove(id: number) {
|
||||
return `This action removes a #${id} widgetView`;
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,41 @@
|
||||
import {IsNotEmpty, IsNumber, IsString} from "class-validator";
|
||||
import {DatasetType} from "../../common/enum/dataset-type.enum";
|
||||
import {YesNo} from "../../common/enum/yn.enum";
|
||||
import { IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator';
|
||||
import { DatasetType } from '../../common/enum/dataset-type.enum';
|
||||
import { YesNo } from '../../common/enum/yn.enum';
|
||||
|
||||
export class CreateWidgetDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
title: string;
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
title: string;
|
||||
|
||||
@IsString()
|
||||
description: string;
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
description: string;
|
||||
|
||||
@IsNumber()
|
||||
@IsNotEmpty()
|
||||
componentId: number;
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
databaseId: number;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
datasetType: DatasetType
|
||||
@IsNumber()
|
||||
@IsNotEmpty()
|
||||
componentId: number;
|
||||
|
||||
@IsNumber()
|
||||
@IsNotEmpty()
|
||||
datasetId: number
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
datasetType: DatasetType;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
option: string
|
||||
@IsNumber()
|
||||
@IsNotEmpty()
|
||||
datasetId: number;
|
||||
|
||||
@IsString()
|
||||
delYn: YesNo
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
tableName: string;
|
||||
|
||||
@IsNumber()
|
||||
widgetViewId: number
|
||||
}
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
option: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
delYn: YesNo;
|
||||
}
|
||||
|
||||
@@ -1,73 +1,50 @@
|
||||
import {Column, Entity, JoinTable, ManyToMany, ManyToOne, PrimaryGeneratedColumn} from "typeorm";
|
||||
import {BaseEntity} from "../../common/entities/base.entity";
|
||||
import {DatasetType} from "../../common/enum/dataset-type.enum";
|
||||
import {YesNo} from "../../common/enum/yn.enum";
|
||||
import {Component} from "../../component/entities/component.entity";
|
||||
import {Dashboard} from "../../dashboard/entities/dashboard.entity";
|
||||
import {Dataset} from "../../dataset/entities/dataset.entity";
|
||||
import {WidgetView} from "../../widget-view/entities/widget-view.entity";
|
||||
import { Column, Entity, JoinTable, ManyToMany, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
|
||||
import { BaseEntity } from '../../common/entities/base.entity';
|
||||
import { DatasetType } from '../../common/enum/dataset-type.enum';
|
||||
import { YesNo } from '../../common/enum/yn.enum';
|
||||
import { Component } from '../../component/entities/component.entity';
|
||||
import { Dashboard } from '../../dashboard/entities/dashboard.entity';
|
||||
|
||||
@Entity()
|
||||
export class Widget extends BaseEntity {
|
||||
@PrimaryGeneratedColumn({comment: '위젯 ID'})
|
||||
id: number
|
||||
@PrimaryGeneratedColumn({ comment: '위젯 ID' })
|
||||
id: number;
|
||||
|
||||
@Column({length: 300, nullable: true, comment: '위젯명'})
|
||||
title: string
|
||||
@Column({ length: 300, nullable: true, comment: '위젯명' })
|
||||
title: string;
|
||||
|
||||
@Column({length: 1000, nullable: true, comment: '설명'})
|
||||
description: string
|
||||
@Column({ length: 1000, nullable: true, comment: '설명' })
|
||||
description: string;
|
||||
|
||||
@Column({comment: '컴포넌트 ID'})
|
||||
componentId: number
|
||||
@Column({ comment: '컴포넌트 ID' })
|
||||
componentId: number;
|
||||
|
||||
@Column({comment: '데이터셋 구분(데이터셋, 위젯 데이터셋)', default: DatasetType.WIDGET})
|
||||
datasetType: DatasetType
|
||||
@Column({ comment: '데이터셋 구분(데이터셋, 위젯 뷰)', default: DatasetType.TABLE })
|
||||
datasetType: DatasetType;
|
||||
|
||||
@Column({comment: '데이터셋 ID'})
|
||||
datasetId: number
|
||||
@Column({ comment: '데이터셋 ID' })
|
||||
datasetId: number;
|
||||
|
||||
@Column({type: "text", comment: '위젯 속성'})
|
||||
option: string
|
||||
@Column({ type: 'text', comment: '위젯 속성' })
|
||||
option: string;
|
||||
|
||||
@Column({length: 1, default: YesNo.NO, comment: '삭제여부'})
|
||||
delYn: YesNo
|
||||
@Column({ length: 1, default: YesNo.NO, comment: '삭제여부' })
|
||||
delYn: YesNo;
|
||||
|
||||
@Column({ comment: '위젯뷰 ID'})
|
||||
widgetViewId: number
|
||||
|
||||
|
||||
|
||||
@ManyToMany(type => Dashboard)
|
||||
@JoinTable({
|
||||
name: 'dashboard_widget',
|
||||
joinColumn: {
|
||||
name: 'widgetId',
|
||||
referencedColumnName: 'id'
|
||||
},
|
||||
inverseJoinColumn: {
|
||||
name: 'dashboardId',
|
||||
referencedColumnName: 'id'
|
||||
}
|
||||
})
|
||||
databases: Dashboard[];
|
||||
|
||||
@ManyToOne(
|
||||
(type) => Component,
|
||||
(component) => component
|
||||
)
|
||||
component!: Component
|
||||
|
||||
@ManyToOne(
|
||||
(type => Dataset),
|
||||
(dataset) => dataset
|
||||
)
|
||||
dataset!: Dataset[]
|
||||
|
||||
@ManyToOne(
|
||||
(type => WidgetView),
|
||||
(widgetView) => widgetView
|
||||
)
|
||||
widgetView!: WidgetView[]
|
||||
@ManyToMany(type => Dashboard)
|
||||
@JoinTable({
|
||||
name: 'dashboard_widget',
|
||||
// joinColumn: {
|
||||
// name: 'widgetId',
|
||||
// referencedColumnName: 'id'
|
||||
// },
|
||||
inverseJoinColumn: {
|
||||
name: 'dashboardId',
|
||||
referencedColumnName: 'id',
|
||||
},
|
||||
})
|
||||
databases: Dashboard[];
|
||||
|
||||
@ManyToOne(type => Component, component => component)
|
||||
component!: Component;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
||||
import { BaseEntity } from '../../../common/entities/base.entity';
|
||||
|
||||
@Entity()
|
||||
export class TableQuery extends BaseEntity {
|
||||
@PrimaryGeneratedColumn({ comment: '테이블 쿼리 ID' })
|
||||
id: number;
|
||||
|
||||
@Column({ comment: '데이터베이스 ID' })
|
||||
databaseId: number;
|
||||
|
||||
@Column({ type: 'text', comment: '조회 sql' })
|
||||
query: string;
|
||||
}
|
||||
39
backend-api/src/widget/tabel-query/table-query.service.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { TableQuery } from './entity/table-query.entity';
|
||||
|
||||
@Injectable()
|
||||
export class TableQueryService {
|
||||
constructor(
|
||||
@InjectRepository(TableQuery)
|
||||
private tableQueryRepository: Repository<TableQuery>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* tableQuery 생성
|
||||
* @param databaseId
|
||||
* @param tableName
|
||||
*/
|
||||
async create(databaseId: number, tableName: string) {
|
||||
return await this.tableQueryRepository.save({
|
||||
databaseId,
|
||||
query: `SELECT * FROM ${tableName}`,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* tableQuery 삭제
|
||||
* @param id
|
||||
*/
|
||||
async remove(id: number) {
|
||||
const tableQuery = await this.tableQueryRepository.findOne({ where: { id: id } });
|
||||
if (!tableQuery) {
|
||||
return 'No exist';
|
||||
} else {
|
||||
await this.tableQueryRepository.delete(tableQuery.id);
|
||||
}
|
||||
|
||||
return `This action removes a #${id} widgetView`;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
|
||||
import {Controller, Get, Post, Body, Patch, Param, Delete, Put} from '@nestjs/common';
|
||||
import { WidgetService } from './widget.service';
|
||||
import { CreateWidgetDto } from './dto/create-widget.dto';
|
||||
import { UpdateWidgetDto } from './dto/update-widget.dto';
|
||||
@@ -7,26 +7,46 @@ import { UpdateWidgetDto } from './dto/update-widget.dto';
|
||||
export class WidgetController {
|
||||
constructor(private readonly widgetService: WidgetService) {}
|
||||
|
||||
/**
|
||||
* 위젯 생성
|
||||
* @param createWidgetDto
|
||||
*/
|
||||
@Post()
|
||||
create(@Body() createWidgetDto: CreateWidgetDto) {
|
||||
return this.widgetService.create(createWidgetDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 위젯 목록 조회
|
||||
*/
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.widgetService.findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* 위젯 단건 조회
|
||||
* @param id
|
||||
*/
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.widgetService.findOne(+id);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
/**
|
||||
* 위젯 수정
|
||||
* @param id
|
||||
* @param updateWidgetDto
|
||||
*/
|
||||
@Put(':id')
|
||||
update(@Param('id') id: string, @Body() updateWidgetDto: UpdateWidgetDto) {
|
||||
return this.widgetService.update(+id, updateWidgetDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 위젯 삭제
|
||||
* @param id
|
||||
*/
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: string) {
|
||||
return this.widgetService.remove(+id);
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { WidgetService } from './widget.service';
|
||||
import { WidgetController } from './widget.controller';
|
||||
import {TypeOrmModule} from "@nestjs/typeorm";
|
||||
import {Widget} from "./entities/widget.entity";
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Widget } from './entities/widget.entity';
|
||||
import { Component } from '../component/entities/component.entity';
|
||||
import { TableQueryService } from './tabel-query/table-query.service';
|
||||
import { TableQuery } from './tabel-query/entity/table-query.entity';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Widget])],
|
||||
imports: [TypeOrmModule.forFeature([Widget, Component, TableQuery])],
|
||||
controllers: [WidgetController],
|
||||
providers: [WidgetService],
|
||||
providers: [WidgetService, TableQueryService],
|
||||
})
|
||||
export class WidgetModule {}
|
||||
|
||||
@@ -1,68 +1,140 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { CreateWidgetDto } from './dto/create-widget.dto';
|
||||
import { UpdateWidgetDto } from './dto/update-widget.dto';
|
||||
import { Repository } from "typeorm";
|
||||
import { Widget } from "./entities/widget.entity";
|
||||
import { InjectRepository } from "@nestjs/typeorm";
|
||||
import { Repository } from 'typeorm';
|
||||
import { Widget } from './entities/widget.entity';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { DatasetType } from '../common/enum/dataset-type.enum';
|
||||
import { Component } from '../component/entities/component.entity';
|
||||
import { ResponseStatus } from '../common/enum/response-status.enum';
|
||||
import { TableQueryService } from './tabel-query/table-query.service';
|
||||
|
||||
@Injectable()
|
||||
export class WidgetService {
|
||||
constructor(
|
||||
@InjectRepository(Widget)
|
||||
private widgetRepository: Repository<Widget>) {}
|
||||
@InjectRepository(Widget)
|
||||
private widgetRepository: Repository<Widget>,
|
||||
@InjectRepository(Component)
|
||||
private componentRepository: Repository<Component>,
|
||||
private tableQueryService: TableQueryService,
|
||||
) {}
|
||||
|
||||
async create(body: CreateWidgetDto) {
|
||||
|
||||
const find_widget = await this.widgetRepository.findOne({where: { title: body.title }})
|
||||
if(find_widget){
|
||||
return 'exist same widget'
|
||||
} else {
|
||||
// await this.widgetRepository.save({
|
||||
// title: body.title,
|
||||
// description: body.description,
|
||||
// componentId: body.componentId,
|
||||
// datasetType: body.datasetType,
|
||||
// datasetId: body.datasetId,
|
||||
// option: JSON.stringify(body.option),
|
||||
// delYn: body.delYn
|
||||
// })
|
||||
/**
|
||||
* 위젯 생성
|
||||
* @param createWidget
|
||||
*/
|
||||
async create(createWidget: CreateWidgetDto) {
|
||||
if (
|
||||
createWidget.datasetType === DatasetType.TABLE &&
|
||||
createWidget.tableName &&
|
||||
String(createWidget.tableName).length <= 0
|
||||
) {
|
||||
return { status: ResponseStatus.ERROR, message: '필수 입력사항::::선택한 테이블명 ' };
|
||||
}
|
||||
return 'This action adds a new widget';
|
||||
|
||||
// 테이블 선택해서 생성할 경우(DatasetType : TABLE), tablequery 아이템을 추가하고 추가된 id값을 넣어줘야 한다.
|
||||
if (createWidget.datasetType === DatasetType.TABLE) {
|
||||
const res = await this.tableQueryService.create(
|
||||
createWidget.databaseId,
|
||||
createWidget.tableName,
|
||||
);
|
||||
createWidget.datasetId = res.id;
|
||||
}
|
||||
// 데이터셋 선택해서 생성할 경우(DatasetType : DATASET) 그대로 insert
|
||||
createWidget.option = JSON.stringify(createWidget.option);
|
||||
|
||||
const saveResult = await this.widgetRepository.save(createWidget);
|
||||
saveResult.option = JSON.parse(saveResult.option);
|
||||
return { status: ResponseStatus.SUCCESS, data: saveResult };
|
||||
}
|
||||
|
||||
/**
|
||||
* 위젯 목록 조회
|
||||
*/
|
||||
async findAll() {
|
||||
const find_all = await this.widgetRepository.find()
|
||||
return find_all
|
||||
const find_all = await this.widgetRepository
|
||||
.createQueryBuilder('widget')
|
||||
.innerJoin(Component, 'component', 'component.id = widget.componentId')
|
||||
.select(['widget.*', 'component.type as componentType'])
|
||||
.orderBy('widget.updatedAt', 'DESC')
|
||||
.getRawMany();
|
||||
|
||||
find_all.forEach(el => {
|
||||
el.option = JSON.parse(el.option);
|
||||
});
|
||||
return { status: ResponseStatus.SUCCESS, data: find_all };
|
||||
}
|
||||
|
||||
async findOne(id: number) {
|
||||
const widgetInfo = this.widgetRepository
|
||||
.createQueryBuilder()
|
||||
.subQuery()
|
||||
.select(['widget.*'])
|
||||
.from(Widget, 'widget')
|
||||
.where('id=:id')
|
||||
.getQuery();
|
||||
|
||||
const find_widget = await this.widgetRepository.findOne({ where: { id: id }})
|
||||
return find_widget;
|
||||
}
|
||||
const find_widget = await this.componentRepository
|
||||
.createQueryBuilder('component')
|
||||
.select(['widgetInfo.*', 'component.type as componentType'])
|
||||
.innerJoin(widgetInfo, 'widgetInfo', 'widgetInfo.componentId = component.id')
|
||||
.setParameter('id', id)
|
||||
.getRawOne();
|
||||
|
||||
async update(id: number, body: UpdateWidgetDto) {
|
||||
const find_widget = await this.widgetRepository.findOne({ where: { id: id}})
|
||||
if(!find_widget){
|
||||
return 'No exist widget'
|
||||
} else {
|
||||
|
||||
find_widget.title = body.title;
|
||||
find_widget.componentId = body.componentId;
|
||||
find_widget.datasetType = body.datasetType;
|
||||
find_widget.datasetId = body.datasetId;
|
||||
find_widget.option = JSON.stringify(body.option);
|
||||
find_widget.delYn = body.delYn;
|
||||
find_widget.widgetViewId = body.widgetViewId;
|
||||
|
||||
await this.widgetRepository.save(find_widget)
|
||||
|
||||
return `This action updates a #${id} widget`;
|
||||
let resultObj = {};
|
||||
if (!find_widget)
|
||||
resultObj = { status: ResponseStatus.ERROR, message: `${id} 위젯이 존재하지 않습니다.` };
|
||||
else {
|
||||
find_widget.option = JSON.parse(find_widget.option);
|
||||
resultObj = { status: ResponseStatus.SUCCESS, data: find_widget };
|
||||
}
|
||||
|
||||
return resultObj;
|
||||
}
|
||||
|
||||
remove(id: number) {
|
||||
return `This action removes a #${id} widget`;
|
||||
/**
|
||||
* 위젯 수정
|
||||
* @param id
|
||||
* @param updateWidget
|
||||
*/
|
||||
async update(id: number, updateWidget: UpdateWidgetDto) {
|
||||
const find_widget = await this.widgetRepository.findOne({ where: { id: id } });
|
||||
if (!find_widget) {
|
||||
return { status: ResponseStatus.ERROR, message: `${id} 위젯이 존재하지 않습니다.` };
|
||||
} else {
|
||||
// 변경 가능한 항목 넣어주기
|
||||
find_widget.option = JSON.stringify(updateWidget.option);
|
||||
if (updateWidget.delYn) {
|
||||
find_widget.delYn = updateWidget.delYn;
|
||||
}
|
||||
if (updateWidget.title) {
|
||||
find_widget.title = updateWidget.title;
|
||||
}
|
||||
if (updateWidget.componentId) {
|
||||
find_widget.componentId = updateWidget.componentId;
|
||||
}
|
||||
|
||||
const saveResult = await this.widgetRepository.save(find_widget);
|
||||
saveResult.option = JSON.parse(saveResult.option);
|
||||
|
||||
return { status: ResponseStatus.SUCCESS, data: saveResult };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* widget 삭제
|
||||
* @param id
|
||||
*/
|
||||
async remove(id: number) {
|
||||
const find_widget = await this.widgetRepository.findOne({ where: { id: id } });
|
||||
|
||||
if (!find_widget) {
|
||||
return { status: ResponseStatus.ERROR, message: 'No exist' };
|
||||
} else {
|
||||
// dataset이 아닐경우
|
||||
if (find_widget.datasetType === DatasetType.TABLE)
|
||||
await this.tableQueryService.remove(find_widget.datasetId);
|
||||
await this.widgetRepository.delete(id);
|
||||
return { status: ResponseStatus.SUCCESS, message: `This action removes a #${id} widget` };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
134
docs/backup/DDL.sql
Normal file
@@ -0,0 +1,134 @@
|
||||
create table if not exists component
|
||||
(
|
||||
createdAt datetime(6) default (CURRENT_TIMESTAMP(6)) not null comment '생성일',
|
||||
updatedAt datetime(6) default (CURRENT_TIMESTAMP(6)) not null on update CURRENT_TIMESTAMP(6) comment '수정일',
|
||||
id int auto_increment comment '컴포넌트 ID'
|
||||
primary key,
|
||||
type varchar(45) not null comment '타입 코드',
|
||||
title varchar(100) not null comment '타입명',
|
||||
description varchar(300) null comment '설명',
|
||||
category varchar(45) null comment '분류',
|
||||
`option` text not null comment '컴포넌트 속성',
|
||||
icon varchar(45) null comment '컴포넌트 아이콘 경로',
|
||||
seq int null comment '순서',
|
||||
useYn varchar(1) default 'Y' not null comment '사용여부',
|
||||
constraint IDX_9fe1f6a769df8035b25b0d8070
|
||||
unique (type)
|
||||
);
|
||||
|
||||
create table if not exists dashboard
|
||||
(
|
||||
createdAt datetime(6) default (CURRENT_TIMESTAMP(6)) not null comment '생성일',
|
||||
updatedAt datetime(6) default (CURRENT_TIMESTAMP(6)) not null on update CURRENT_TIMESTAMP(6) comment '수정일',
|
||||
id int auto_increment comment '대시보드 ID'
|
||||
primary key,
|
||||
title varchar(300) not null comment '대시보드명',
|
||||
templateId int null comment '템플릿 ID',
|
||||
layout text null comment '레이아웃 정보',
|
||||
seq int null comment '순서',
|
||||
delYn varchar(1) default 'N' not null comment '삭제여부'
|
||||
);
|
||||
|
||||
create table if not exists dashboard_widget
|
||||
(
|
||||
dashboardId int not null,
|
||||
createdAt datetime(6) default (CURRENT_TIMESTAMP(6)) not null comment '생성일',
|
||||
updatedAt datetime(6) default (CURRENT_TIMESTAMP(6)) not null on update CURRENT_TIMESTAMP(6) comment '수정일',
|
||||
id int auto_increment
|
||||
primary key,
|
||||
widgetId int not null
|
||||
);
|
||||
|
||||
create table if not exists `database`
|
||||
(
|
||||
createdAt datetime(6) default (CURRENT_TIMESTAMP(6)) not null comment '생성일',
|
||||
updatedAt datetime(6) default (CURRENT_TIMESTAMP(6)) not null on update CURRENT_TIMESTAMP(6) comment '수정일',
|
||||
id int auto_increment comment '데이터베이스 ID'
|
||||
primary key,
|
||||
name varchar(300) not null comment '데이터베이스명',
|
||||
description varchar(1000) null comment '설명',
|
||||
connectionConfig text not null comment '속성',
|
||||
engine varchar(100) not null comment '데이터베이스 구분',
|
||||
timezone varchar(100) null comment '타임존'
|
||||
);
|
||||
|
||||
create table if not exists databaseType
|
||||
(
|
||||
createdAt datetime(6) default (now(6)) not null comment '생성일',
|
||||
updatedAt datetime(6) default (now(6)) not null on update CURRENT_TIMESTAMP(6) comment '수정일',
|
||||
id int auto_increment comment '데이터베이스 타입 ID'
|
||||
primary key,
|
||||
type varchar(45) not null comment '타입 코드',
|
||||
title varchar(100) not null comment '타입명',
|
||||
seq int null comment '순서',
|
||||
useYn varchar(1) default 'Y' not null comment '사용여부',
|
||||
constraint type
|
||||
unique (type)
|
||||
);
|
||||
|
||||
create table if not exists dataset
|
||||
(
|
||||
createdAt datetime(6) default (CURRENT_TIMESTAMP(6)) not null comment '생성일',
|
||||
updatedAt datetime(6) default (CURRENT_TIMESTAMP(6)) not null on update CURRENT_TIMESTAMP(6) comment '수정일',
|
||||
id int auto_increment comment '데이터셋 ID'
|
||||
primary key,
|
||||
title varchar(255) null comment '데이터셋명',
|
||||
databaseId int not null comment '데이터베이스 ID',
|
||||
query text not null comment '조회 sql'
|
||||
);
|
||||
|
||||
create table if not exists table_query
|
||||
(
|
||||
createdAt datetime(6) default (now(6)) not null comment '생성일',
|
||||
updatedAt datetime(6) default (now(6)) not null on update CURRENT_TIMESTAMP(6) comment '수정일',
|
||||
id int auto_increment comment '위젯 데이터셋 ID'
|
||||
primary key,
|
||||
databaseId int not null comment '데이터베이스 ID',
|
||||
query text not null comment '조회 sql'
|
||||
);
|
||||
|
||||
create table if not exists template
|
||||
(
|
||||
createdAt datetime(6) default (CURRENT_TIMESTAMP(6)) not null comment '생성일',
|
||||
updatedAt datetime(6) default (CURRENT_TIMESTAMP(6)) not null on update CURRENT_TIMESTAMP(6) comment '수정일',
|
||||
id int auto_increment comment '템플릿 ID'
|
||||
primary key,
|
||||
title varchar(300) null comment '템플릿명',
|
||||
description varchar(1000) null comment '템플릿 설명',
|
||||
useYn varchar(1) default 'Y' not null comment '사용여부'
|
||||
);
|
||||
|
||||
create table if not exists template_item
|
||||
(
|
||||
createdAt datetime(6) default (CURRENT_TIMESTAMP(6)) not null comment '생성일',
|
||||
updatedAt datetime(6) default (CURRENT_TIMESTAMP(6)) not null on update CURRENT_TIMESTAMP(6) comment '수정일',
|
||||
id int auto_increment comment '템플릿 아이템 ID'
|
||||
primary key,
|
||||
templateId int not null comment '템플릿 ID',
|
||||
x int not null comment 'x좌표 값',
|
||||
y int not null comment 'y좌표 값',
|
||||
width int not null comment '너비',
|
||||
height int not null comment '높이',
|
||||
recommendCategory varchar(45) null comment '컴포넌트 분류',
|
||||
recommendType varchar(45) null comment '컴포넌트 타입 코드',
|
||||
constraint FK_cce2beaf0ceb340c67c602206e1
|
||||
foreign key (templateId) references template (id)
|
||||
);
|
||||
|
||||
create table if not exists widget
|
||||
(
|
||||
createdAt datetime(6) default (CURRENT_TIMESTAMP(6)) not null comment '생성일',
|
||||
updatedAt datetime(6) default (CURRENT_TIMESTAMP(6)) not null on update CURRENT_TIMESTAMP(6) comment '수정일',
|
||||
id int auto_increment comment '위젯 ID'
|
||||
primary key,
|
||||
title varchar(300) null comment '위젯명',
|
||||
description varchar(1000) null comment '설명',
|
||||
componentId int not null comment '컴포넌트 ID',
|
||||
datasetType varchar(255) default 'WIDGET_VIEW' not null comment '데이터셋 구분(데이터셋, 위젯 뷰)',
|
||||
datasetId int not null comment '데이터셋 ID',
|
||||
`option` text not null comment '위젯 속성',
|
||||
delYn varchar(1) default 'N' not null comment '삭제여부',
|
||||
constraint FK_30bbe9afcbb39f1c40b74bbb320
|
||||
foreign key (componentId) references component (id)
|
||||
);
|
||||
|
||||
40
docs/backup/vanillameta_component.sql
Normal file
@@ -0,0 +1,40 @@
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-09-28 19:55:38', '2022-10-11 11:03:28.774378', 1, 'CHART_LINE', '선형 차트', 'Line Chart', 'LINE', '{"title":"","xField":"","series":[{"field":"","color":"#2870c5","aggregation":"sum"}],"legendPosition":""}', 'icon/ct-line.svg', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-09-28 15:16:18.111257', '2022-10-11 11:03:29.012271', 2, 'CHART_AREA', '영역형 차트', 'Area Chart', 'AREA', '{"title":"","xField":"","series":[{"field":"","color":"#2870c5","aggregation":"sum"}],"legendPosition":""}', 'icon/ct-area.svg', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-09-28 15:16:18.129862', '2022-10-14 16:08:35.880890', 3, 'CHART_BAR', '세로 막대형 차트', 'Bar Chart', 'BAR', '{"title":"","xField":"","series":[{"field":"","color":"#2870c5","aggregation":"sum"}],"legendPosition":""}', 'icon/ct-bar.svg', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-09-28 15:16:18.147718', '2022-10-14 16:08:35.903814', 4, 'CHART_COLUMN', '가로 막대형 차트', 'Column Chart', 'BAR', '{"title":"","yField":"","series":[{"field":"","color":"#2870c5","aggregation":"sum"}],"legendPosition":""}', 'icon/ct-column.svg', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-09-28 15:16:18.165671', '2022-10-11 11:03:28.903132', 5, 'CHART_MIXED_LINE_BAR', '복합형 차트', 'Mixed Chart', 'MIXED', '{"title":"","xField":"","series":[{"field":"","color":"#2870c5","aggregation":"sum","type":"line"},{"field":"","color":"#47a8ea","aggregation":"sum","type":"bar"}],"legendPosition":""}', 'icon/ct-bar-line.svg', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-09-28 15:16:18.183978', '2022-10-11 11:03:28.937662', 6, 'CHART_PIE', '원형 차트', 'Pie Chart', 'PIE', '{"title":"","series":{"field":"","color":[],"aggregation":"sum","name":""},"legendPosition":""}', 'icon/ct-pie.svg', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-09-28 15:16:18.201009', '2022-10-11 11:03:28.964198', 7, 'CHART_NIGHTINGALE', '나이팅게일 차트', 'Nightingale Chart', 'PIE', '{"title":"","series":{"field":"","color":[],"aggregation":"sum","name":"","radius":["10%","90%"]},"legendPosition":""}', 'icon/ct-nightingale.svg', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-09-28 15:16:18.218438', '2022-10-11 11:03:28.853730', 8, 'CHART_BUBBLE', '거품형 차트', 'Bubble Chart', 'SCATTER', '{"title":"","series":[{"title":"","xField":"","yField":"","symbolSize":"","color":"#2870c5"}],"legendPosition":""}', 'icon/ct-bubble.svg', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-09-28 15:16:18.235656', '2022-10-11 11:03:28.815733', 9, 'CHART_RADAR', '방사형 차트', 'Radar Chart', 'RADAR', '{"title":"","field":"","series":[{"field":"","color":"#2870c5","aggregation":"sum"}],"legendPosition":""}', 'icon/ct-radar.svg', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-09-28 15:16:18.253510', '2022-10-11 11:03:29.043597', 10, 'CHART_SCATTER', '분산형 차트', 'Scatter Chart', 'SCATTER', '{"title":"","series":[{"title":"","xField":"","yField":"","symbolSize":"20","color":"#2870c5"}],"legendPosition":""}', 'icon/ct-scatter.svg', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-09-28 19:48:20', '2022-10-11 11:07:01.313797', 11, 'CHART_DONUT', '도넛형 차트', 'Donut Chart', 'PIE', '{"title":"","series":{"field":"","color":[],"aggregation":"sum","name":"","radius":["30%","75%"]},"legendPosition":""}', 'icon/ct-donut.svg', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-09-29 10:51:13.436536', '2022-10-11 11:07:01.053555', 12, 'BOARD_NUMERIC', '숫자판', 'Score Board', 'ETC', '{"header":{"title":"타이틀을 입력하세요","fontSize":20,"color":"#4A4A4A"},"content":{"field":null,"aggregation":"sum","fontSize":20,"color":"#4A4A4A","prefix":"","suffix":""}}', 'icon/ct-score.svg', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-09-29 10:51:34.693945', '2022-10-11 11:07:01.072979', 13, 'BOARD_TABLE', '표', 'Data Grid', 'ETC', '{"columns":[]}', 'icon/ct-data.svg', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-09-29 10:51:34.783042', '2022-10-11 11:07:01.176117', 14, 'CHART_STACKED_LINE', '누적 선형 차트', 'Stacked Line Chart', 'LINE', '{"title":"","xField":"","series":[{"field":"","color":"#2870c5","aggregation":"sum"}],"legendPosition":""}', 'icon/ct-stacked-line.svg', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-09-29 10:51:35.033840', '2022-10-11 11:07:01.335155', 15, 'CHART_STACKED_AREA', '누적 영역형 차트', 'Stacked Area Chart', 'AREA', '{"title":"","xField":"","series":[{"field":"","color":"#2870c5","aggregation":"sum"}],"legendPosition":""}', 'icon/ct-stacked-area.svg', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-09-29 10:58:46.125790', '2022-10-14 16:08:35.852453', 16, 'CHART_STACKED_COLUMN', '누적 가로 막대형 차트', 'Stacked Column Chart', 'BAR', '{"title":"","yField":"","series":[{"field":"","color":"#2870c5","aggregation":"sum"}],"legendPosition":""}', 'icon/ct-stacked-column.svg', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-09-29 11:02:11.889462', '2022-10-14 16:08:35.927216', 17, 'CHART_STACKED_BAR', '누적 세로 막대형 차트', 'Stacked Bar Chart', 'BAR', '{"title":"","xField":"","series":[{"field":"","color":"#2870c5","aggregation":"sum"}],"legendPosition":""}', 'icon/ct-stacked-bar.svg', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-09-30 14:26:41.497188', '2022-10-14 15:46:08.941388', 19, 'CHART_TREEMAP', '트리맵 차트', 'Treemap Chart', 'TREEMAP', '{"title":"","series":{"field":"","color":["#2870c4","#4ecef6","#ffd43b","#fa5a5a"],"aggregation":"sum","name":""}}', 'icon/ct-treemap.svg', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-09-30 14:27:25.459299', '2022-10-11 11:07:25.204108', 20, 'CHART_CANDLESTICK', '캔들스틱 차트', 'Candlestick Chart', 'CANDLESTICK', '{"title":"","xField":"","series":[{"field":"","color":"#FA5A5A","aggregation":"sum"},{"field":"","color":"#2870C4","aggregation":"sum"},{"field":"","color":"#E03B3B","aggregation":"sum"},{"field":"","color":"#215DA3","aggregation":"sum"}],"legendPosition":""}', 'icon/ct-candle.svg', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-09-30 14:30:15.042412', '2022-10-11 11:07:01.246008', 21, 'CHART_GAUGE', '계기판 차트', 'Gauge Chart', 'GAUGE', '{"title":"","field":"","color":"#2870c5","aggregation":"sum","suffix":""}', 'icon/ct-gauge.svg', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-10-04 16:53:01.371721', '2022-10-14 15:46:08.909344', 22, 'CHART_SUNBURST', '선버스트 차트', 'Sunburst Chart', 'TREEMAP', '{"title":"","series":{"field":"","color":["#2870c4","#4ecef6","#ffd43b","#fa5a5a"],"aggregation":"sum","name":""}}', 'icon/ct-sunburst.svg', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-10-05 14:45:50.362534', '2022-10-14 15:46:09.026073', 23, 'CHART_HEATMAP', '히트맵 차트', 'Heatmap Chart', 'HEATMAP', '{"title":"","xField":"","yField":"","series":"","color":["#2870c4","#4ecef6","#ffd43b","#fa5a5a"],"aggregation":"sum"}', 'icon/ct-heatmap.svg', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-10-05 15:06:18.415655', '2022-10-11 11:07:01.221918', 24, 'CHART_FUNNEL', '깔때기형 차트', 'Funnel Chart', 'FUNNEL', '{"title":"","series":{"field":"","color":[],"aggregation":"sum","name":""},"legendPosition":""}', 'icon/ct-funnel.svg', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-10-07 18:02:26.568254', '2022-10-14 16:09:49.915372', 25, 'CHART_3D_BAR', '3D 막대형 차트', '3D Bar Chart', '3D', '{"title":"","xField":"","series":[{"field":"","aggregation":"sum"}],"color":["#2870c4","#4ecef6","#ffd43b","#fa5a5a"]}', '', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-10-12 16:45:23.767491', '2022-10-14 16:09:49.965900', 26, 'CHART_3D_LINE', '3D 선형 차트', '3D Line Chart', '3D', '{"title":"","xField":"","series":[{"field":"","color":"#2870c5","aggregation":"sum"}],"legendPosition":""}', '', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-10-13 11:23:31.980147', '2022-10-14 16:09:49.943901', 27, 'CHART_3D_SCATTER', '3D 분산형 차트', '3D Scatter Chart', '3D', '{"title":"","series":[{"title":"","xField":"","yField":"","zField":"","symbolSize":"20","color":"#2870c5"}],"legendPosition":""}', '', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-10-13 21:23:06.474435', '2022-10-14 16:09:50.015379', 28, 'CHART_3D_BUBBLE', '3D 거품형 차트', '3D Bubble Chart', '3D', '{"title":"","series":[{"title":"","xField":"","yField":"","zField":"","symbolSize":"","color":"#2870c5"}],"legendPosition":""}', '', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-10-14 14:50:41.667043', '2022-10-14 21:39:29.874649', 29, 'CHART_WATERFALL_BAR', '폭포수 세로 차트', 'Waterfall Bar Chart', 'BAR', '{"title":"","xField":"","series":[{"field":"","aggregation":"sum"}],"color":["#2870c4","#fa5a5a"],"legendPosition":""}', '', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-10-14 14:53:02.853952', '2022-10-14 21:39:29.899883', 30, 'CHART_WATERFALL_COLUMN', '폭포수 가로 차트', 'Waterfall Column Chart', 'BAR', '{"title":"","yField":"","series":[{"field":"","aggregation":"sum"}],"color":["#2870c4","#fa5a5a"],"legendPosition":""}', '', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-10-14 15:21:20.186899', '2022-10-14 23:40:31.490277', 31, 'CHART_POLAR_BAR', '극좌표 막대형 차트', 'Polar Bar Chart', 'BAR', '{"title":"","axisField":"","series":[{"field":"","color":"#2870c5","aggregation":"sum"}],"radius":["10%","75%"],"legendPosition":""}', '', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-10-14 16:07:18.347330', '2022-10-15 20:50:59.453903', 32, 'CHART_MIXED_LINE_PIE', '선형과 원형 복합 차트', 'Mixed Line and Pie Chart', 'MIXED', '{"title":"","xField":"","series":[{"field":"","color":"#2870c5","aggregation":"sum"}],"pie":{"field":"","color":[],"center":["75%","35%"],"radius":"20%","name":"","aggregation":"sum"},"legendPosition":""}', '', null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-10-15 20:09:47.754250', '2022-10-15 20:09:47.754250', 33, 'CHART_POLAR_STACKED_BAR', '극좌표 누적 막대형 차트', 'Polar Stacked Bar Chart', 'BAR', '{"title":"","axisField":"","series":[{"field":"","color":"#2870c5","aggregation":"sum"}],"radius":["10%","75%"],"legendPosition":""}', null, null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-10-17 16:22:43.859648', '2022-10-17 16:22:43.859648', 34, 'CHART_MIXED_AREA_PIE', '영역형과 원형 복합 차트', 'Mixed Area and Pie Chart', 'MIXED', '{"title":"","xField":"","series":[{"field":"","color":"#2870c5","aggregation":"sum"}],"pie":{"field":"","color":[],"center":["75%","35%"],"radius":"20%","name":"","aggregation":"sum"},"legendPosition":""}', null, null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-10-17 16:23:33.681125', '2022-10-17 16:55:23.797335', 35, 'CHART_MIXED_BAR_PIE', '세로 막대형과 원형 복합 차트', 'Mixed Bar and Pie Chart', 'MIXED', '{"title":"","xField":"","series":[{"field":"","color":"#2870c5","aggregation":"sum"}],"pie":{"field":"","color":[],"center":["75%","35%"],"radius":"20%","name":"","aggregation":"sum"},"legendPosition":""}', null, null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-10-17 16:24:04.783798', '2022-10-17 16:24:04.783798', 36, 'CHART_MIXED_COLUMN_PIE', '가로 막대형과 원형 복합 차트', 'Mixed Column and Pie Chart', 'MIXED', '{"title":"","xField":"","series":[{"field":"","color":"#2870c5","aggregation":"sum"}],"pie":{"field":"","color":[],"center":["75%","35%"],"radius":"20%","name":"","aggregation":"sum"},"legendPosition":""}', null, null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-10-17 16:34:32.081057', '2022-10-17 16:34:32.081057', 37, 'CHART_MIXED_STACKED_BAR_PIE', '누적 세로 막대형과 원형 복합 차트', 'Mixed Stacked-Bar and Pie Chart', 'MIXED', '{"title":"","xField":"","series":[{"field":"","color":"#2870c5","aggregation":"sum"}],"pie":{"field":"","color":[],"center":["75%","35%"],"radius":"20%","name":"","aggregation":"sum"},"legendPosition":""}', null, null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-10-17 16:35:01.816820', '2022-10-17 16:35:01.816820', 38, 'CHART_MIXED_STACKED_COLUMN_PIE', '누적 가로 막대형과 원형 복합 차트', 'Mixed Stacked-Column and Pie Chart', 'MIXED', '{"title":"","xField":"","series":[{"field":"","color":"#2870c5","aggregation":"sum"}],"pie":{"field":"","color":[],"center":["75%","35%"],"radius":"20%","name":"","aggregation":"sum"},"legendPosition":""}', null, null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-10-17 17:00:01.525246', '2022-10-17 17:00:01.525246', 39, 'CHART_MIXED_STACKED_LINE_PIE', '누적 선형과 원형 복합 차트', 'Mixed Stacked-Line and Pie Chart', 'MIXED', '{"title":"","xField":"","series":[{"field":"","color":"#2870c5","aggregation":"sum"}],"pie":{"field":"","color":[],"center":["75%","35%"],"radius":"20%","name":"","aggregation":"sum"},"legendPosition":""}', null, null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-10-17 17:00:18.251377', '2022-10-17 17:00:18.251377', 40, 'CHART_MIXED_STACKED_AREA_PIE', '누적 영역형과 원형 복합 차트', 'Mixed Stacked-Area and Pie Chart', 'MIXED', '{"title":"","xField":"","series":[{"field":"","color":"#2870c5","aggregation":"sum"}],"pie":{"field":"","color":[],"center":["75%","35%"],"radius":"20%","name":"","aggregation":"sum"},"legendPosition":""}', null, null, 'Y');
|
||||
INSERT INTO vanillameta.component (createdAt, updatedAt, id, type, title, description, category, `option`, icon, seq, useYn) VALUES ('2022-10-17 17:36:30.244205', '2022-10-18 15:19:10.964257', 41, 'CHART_MIXED_DONUT_PIE', '도넛형과 원형 복합 차트', 'Mixed Donut and Pie Chart', 'MIXED', '{"title":"","series":{"field":"","color":[],"aggregation":"sum","name":"","radius":["45%","60%"]},"pie":{"field":"","color":[],"radius":"30%","name":"","aggregation":"sum"},"legendPosition":""}', null, null, 'Y');
|
||||
20
docs/backup/vanillameta_dashboard_widget.sql
Normal file
@@ -0,0 +1,20 @@
|
||||
INSERT INTO vanillameta.dashboard_widget (dashboardId, createdAt, updatedAt, id, widgetId) VALUES (10, '2022-10-15 19:06:49.096950', '2022-10-15 19:06:49.096950', 30, 52);
|
||||
INSERT INTO vanillameta.dashboard_widget (dashboardId, createdAt, updatedAt, id, widgetId) VALUES (15, '2022-10-16 21:51:01.963619', '2022-10-16 21:51:01.963619', 61, 49);
|
||||
INSERT INTO vanillameta.dashboard_widget (dashboardId, createdAt, updatedAt, id, widgetId) VALUES (15, '2022-10-16 21:51:01.981934', '2022-10-16 21:51:01.981934', 62, 51);
|
||||
INSERT INTO vanillameta.dashboard_widget (dashboardId, createdAt, updatedAt, id, widgetId) VALUES (15, '2022-10-16 21:51:01.999992', '2022-10-16 21:51:01.999992', 63, 58);
|
||||
INSERT INTO vanillameta.dashboard_widget (dashboardId, createdAt, updatedAt, id, widgetId) VALUES (17, '2022-10-16 23:07:45.141190', '2022-10-16 23:07:45.141190', 66, 49);
|
||||
INSERT INTO vanillameta.dashboard_widget (dashboardId, createdAt, updatedAt, id, widgetId) VALUES (17, '2022-10-16 23:07:45.159313', '2022-10-16 23:07:45.159313', 67, 51);
|
||||
INSERT INTO vanillameta.dashboard_widget (dashboardId, createdAt, updatedAt, id, widgetId) VALUES (17, '2022-10-16 23:07:45.177186', '2022-10-16 23:07:45.177186', 68, 53);
|
||||
INSERT INTO vanillameta.dashboard_widget (dashboardId, createdAt, updatedAt, id, widgetId) VALUES (17, '2022-10-16 23:07:45.194393', '2022-10-16 23:07:45.194393', 69, 67);
|
||||
INSERT INTO vanillameta.dashboard_widget (dashboardId, createdAt, updatedAt, id, widgetId) VALUES (18, '2022-10-16 23:08:10.295314', '2022-10-16 23:08:10.295314', 70, 51);
|
||||
INSERT INTO vanillameta.dashboard_widget (dashboardId, createdAt, updatedAt, id, widgetId) VALUES (18, '2022-10-16 23:08:10.316567', '2022-10-16 23:08:10.316567', 71, 58);
|
||||
INSERT INTO vanillameta.dashboard_widget (dashboardId, createdAt, updatedAt, id, widgetId) VALUES (19, '2022-10-16 23:08:18.711423', '2022-10-16 23:08:18.711423', 72, 51);
|
||||
INSERT INTO vanillameta.dashboard_widget (dashboardId, createdAt, updatedAt, id, widgetId) VALUES (19, '2022-10-16 23:08:18.735048', '2022-10-16 23:08:18.735048', 73, 53);
|
||||
INSERT INTO vanillameta.dashboard_widget (dashboardId, createdAt, updatedAt, id, widgetId) VALUES (21, '2022-10-16 23:08:40.854938', '2022-10-16 23:08:40.854938', 78, 52);
|
||||
INSERT INTO vanillameta.dashboard_widget (dashboardId, createdAt, updatedAt, id, widgetId) VALUES (21, '2022-10-16 23:08:40.880936', '2022-10-16 23:08:40.880936', 79, 53);
|
||||
INSERT INTO vanillameta.dashboard_widget (dashboardId, createdAt, updatedAt, id, widgetId) VALUES (21, '2022-10-16 23:08:40.896782', '2022-10-16 23:08:40.896782', 80, 58);
|
||||
INSERT INTO vanillameta.dashboard_widget (dashboardId, createdAt, updatedAt, id, widgetId) VALUES (21, '2022-10-16 23:08:40.912810', '2022-10-16 23:08:40.912810', 81, 67);
|
||||
INSERT INTO vanillameta.dashboard_widget (dashboardId, createdAt, updatedAt, id, widgetId) VALUES (22, '2022-10-16 23:08:50.033491', '2022-10-16 23:08:50.033491', 82, 49);
|
||||
INSERT INTO vanillameta.dashboard_widget (dashboardId, createdAt, updatedAt, id, widgetId) VALUES (22, '2022-10-16 23:08:50.052862', '2022-10-16 23:08:50.052862', 83, 51);
|
||||
INSERT INTO vanillameta.dashboard_widget (dashboardId, createdAt, updatedAt, id, widgetId) VALUES (22, '2022-10-16 23:08:50.073046', '2022-10-16 23:08:50.073046', 84, 52);
|
||||
INSERT INTO vanillameta.dashboard_widget (dashboardId, createdAt, updatedAt, id, widgetId) VALUES (22, '2022-10-16 23:08:50.088055', '2022-10-16 23:08:50.088055', 85, 53);
|
||||
9
docs/backup/vanillameta_database.sql
Normal file
@@ -0,0 +1,9 @@
|
||||
INSERT INTO vanillameta.`database` (createdAt, updatedAt, id, name, description, connectionConfig, engine, timezone) VALUES ('2022-09-28 17:30:45.141496', '2022-10-13 15:52:10.235477', 1, 'mysql database', 'vanilla mysql - dev', '{"client":"mysql","connection":{"host":"dev-sellerking-service.cpxfjdgipx1h.ap-northeast-2.rds.amazonaws.com","port":3306,"user":"vanillabrain","password":"qkslffk123123","database":"sellerking_service"},"useNullAsDefault":true}', 'mysql', 'Asia/Seoul');
|
||||
INSERT INTO vanillameta.`database` (createdAt, updatedAt, id, name, description, connectionConfig, engine, timezone) VALUES ('2022-10-06 11:51:38.845216', '2022-10-12 18:36:39.916867', 2, 'mysql database2', 'vanilla mysql - dev', '{"client":"mysql","connection":{"host":"dev-sellerking-service.cpxfjdgipx1h.ap-northeast-2.rds.amazonaws.com","port":3306,"user":"vanillabrain","password":"qkslffk123123","database":"sellerking_service"},"useNullAsDefault":true}', 'mysql', 'Asia/Seoul');
|
||||
INSERT INTO vanillameta.`database` (createdAt, updatedAt, id, name, description, connectionConfig, engine, timezone) VALUES ('2022-10-13 11:06:59.245390', '2022-10-13 11:06:59.245390', 3, 'sqlite database', 'vanilla sqlite - dev', '{"client":"sqlite3","connection":{"filename":"./vanillameta"}}', 'sqlite', 'Asia/Seoul');
|
||||
INSERT INTO vanillameta.`database` (createdAt, updatedAt, id, name, description, connectionConfig, engine, timezone) VALUES ('2022-10-16 22:40:07.273593', '2022-10-16 22:40:07.273593', 4, 'mysql database', 'vanilla mysql - dev', '{"client":"mysql","connection":{"host":"dev-sellerking-service.cpxfjdgipx1h.ap-northeast-2.rds.amazonaws.com","port":3306,"user":"vanillabrain","password":"qkslffk123123","database":"sellerking_service"},"useNullAsDefault":true}', 'mysql', 'Asia/Seoul');
|
||||
INSERT INTO vanillameta.`database` (createdAt, updatedAt, id, name, description, connectionConfig, engine, timezone) VALUES ('2022-10-16 22:40:47.985547', '2022-10-16 23:56:49', 5, 'update test', 'vanilla mysql - dev', '{"client":"mysql","connection":{"client":"mysql","connection":{"client":"mysql","connection":{"host":"dev-sellerking-service.cpxfjdgipx1h.ap-northeast-2.rds.amazonaws.com","port":3306,"user":"vanillabrain","password":"qkslffk123123","database":"sellerking_service"},"useNullAsDefault":true},"useNullAsDefault":true},"useNullAsDefault":true}', 'mysql', 'Asia/Seoul');
|
||||
INSERT INTO vanillameta.`database` (createdAt, updatedAt, id, name, description, connectionConfig, engine, timezone) VALUES ('2022-10-17 17:15:29.047964', '2022-10-17 17:15:29.047964', 7, 'mysql database', 'vanilla mysql - dev', '{"host":"dev-sellerking-service.cpxfjdgipx1h.ap-northeast-2.rds.amazonaws.com","port":3306,"username":"vanillabrain","password":"qkslffk123123","database":"sellerking_service"}', 'mysql', null);
|
||||
INSERT INTO vanillameta.`database` (createdAt, updatedAt, id, name, description, connectionConfig, engine, timezone) VALUES ('2022-10-17 17:18:40.975070', '2022-10-17 17:18:40.975070', 8, 'mysql database', 'vanilla mysql - dev', '{"host":"dev-sellerking-service.cpxfjdgipx1h.ap-northeast-2.rds.amazonaws.com","port":3306,"username":"vanillabrain","password":"qkslffk123123","database":"sellerking_service"}', 'mysql', null);
|
||||
INSERT INTO vanillameta.`database` (createdAt, updatedAt, id, name, description, connectionConfig, engine, timezone) VALUES ('2022-10-17 17:21:19.729032', '2022-10-17 17:21:19.729032', 9, 'mysql database', 'vanilla mysql - dev', '{"host":"dev-sellerking-service.cpxfjdgipx1h.ap-northeast-2.rds.amazonaws.com","port":3306,"username":"vanillabrain","password":"qkslffk123123","database":"sellerking_service"}', 'mysql', null);
|
||||
INSERT INTO vanillameta.`database` (createdAt, updatedAt, id, name, description, connectionConfig, engine, timezone) VALUES ('2022-10-17 17:22:30.380400', '2022-10-17 17:22:30.380400', 10, 'mysql database', 'vanilla mysql - dev', '{"host":"dev-sellerking-service.cpxfjdgipx1h.ap-northeast-2.rds.amazonaws.com","port":3306,"username":"vanillabrain","password":"qkslffk123123","database":"sellerking_service"}', 'mysql', null);
|
||||
10
docs/backup/vanillameta_databaseType.sql
Normal file
@@ -0,0 +1,10 @@
|
||||
INSERT INTO vanillameta.databaseType (createdAt, updatedAt, id, type, title, seq, useYn) VALUES ('2022-10-18 17:44:18.946447', '2022-10-18 17:44:18.946447', 1, 'mysql', 'MySQL', 1, 'Y');
|
||||
INSERT INTO vanillameta.databaseType (createdAt, updatedAt, id, type, title, seq, useYn) VALUES ('2022-10-18 17:46:14.700394', '2022-10-18 17:46:14.700394', 2, 'maria', 'MariaDB', 2, 'Y');
|
||||
INSERT INTO vanillameta.databaseType (createdAt, updatedAt, id, type, title, seq, useYn) VALUES ('2022-10-18 17:49:21.842039', '2022-10-18 17:49:21.842039', 3, 'postgres', 'PostgreSQL', 3, 'Y');
|
||||
INSERT INTO vanillameta.databaseType (createdAt, updatedAt, id, type, title, seq, useYn) VALUES ('2022-10-18 17:49:22.361833', '2022-10-18 17:49:22.361833', 4, 'oracle', 'Oracle', 4, 'Y');
|
||||
INSERT INTO vanillameta.databaseType (createdAt, updatedAt, id, type, title, seq, useYn) VALUES ('2022-10-18 17:49:22.982368', '2022-10-18 17:49:22.982368', 5, 'db2', 'DB2', 5, 'Y');
|
||||
INSERT INTO vanillameta.databaseType (createdAt, updatedAt, id, type, title, seq, useYn) VALUES ('2022-10-18 17:49:23.378836', '2022-10-18 17:49:23.378836', 6, 'redshift', 'Amazon Redshift', 6, 'Y');
|
||||
INSERT INTO vanillameta.databaseType (createdAt, updatedAt, id, type, title, seq, useYn) VALUES ('2022-10-18 17:49:23.784827', '2022-10-18 17:49:23.784827', 7, 'bigquery', 'Google Cloud BigQuery', 7, 'Y');
|
||||
INSERT INTO vanillameta.databaseType (createdAt, updatedAt, id, type, title, seq, useYn) VALUES ('2022-10-18 17:49:23.966790', '2022-10-18 17:49:23.966790', 8, 'sqlite', 'SQLite', 8, 'Y');
|
||||
INSERT INTO vanillameta.databaseType (createdAt, updatedAt, id, type, title, seq, useYn) VALUES ('2022-10-18 17:49:24.300902', '2022-10-18 17:49:24.300902', 9, 'mssql', 'MSSQL', 9, 'Y');
|
||||
INSERT INTO vanillameta.databaseType (createdAt, updatedAt, id, type, title, seq, useYn) VALUES ('2022-10-18 17:49:24.684788', '2022-10-18 17:49:24.684788', 10, 'snowflake', 'Snowflake', 10, 'Y');
|
||||
3
docs/backup/vanillameta_template.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
INSERT INTO vanillameta.template (createdAt, updatedAt, id, title, description, useYn) VALUES ('2022-09-28 15:17:06.963629', '2022-09-28 16:01:09', 1, 'template1', 'template1 description aa...', 'Y');
|
||||
INSERT INTO vanillameta.template (createdAt, updatedAt, id, title, description, useYn) VALUES ('2022-09-28 15:17:06.985930', '2022-09-28 15:17:06.985930', 2, 'template2', 'template2 description ㄱㄴㄷㄹ', 'Y');
|
||||
INSERT INTO vanillameta.template (createdAt, updatedAt, id, title, description, useYn) VALUES ('2022-09-28 15:17:07.011120', '2022-09-28 15:17:07.011120', 3, 'template3', 'template3 description ㄱㄴㄷㄹ', 'Y');
|
||||
12
docs/backup/vanillameta_template_item.sql
Normal file
@@ -0,0 +1,12 @@
|
||||
INSERT INTO vanillameta.template_item (createdAt, updatedAt, id, templateId, x, y, width, height, recommendCategory, recommendType) VALUES ('2022-09-28 15:17:31.001596', '2022-09-28 15:17:31.001596', 1, 1, 0, 0, 6, 6, 'LINE', null);
|
||||
INSERT INTO vanillameta.template_item (createdAt, updatedAt, id, templateId, x, y, width, height, recommendCategory, recommendType) VALUES ('2022-09-28 15:17:31.024090', '2022-09-28 15:17:31.024090', 2, 1, 6, 0, 6, 6, 'LINE', null);
|
||||
INSERT INTO vanillameta.template_item (createdAt, updatedAt, id, templateId, x, y, width, height, recommendCategory, recommendType) VALUES ('2022-09-28 15:17:31.050109', '2022-09-28 15:17:31.050109', 3, 1, 0, 6, 6, 6, 'LINE', null);
|
||||
INSERT INTO vanillameta.template_item (createdAt, updatedAt, id, templateId, x, y, width, height, recommendCategory, recommendType) VALUES ('2022-09-28 15:17:31.073522', '2022-09-28 15:17:31.073522', 4, 1, 6, 6, 6, 6, 'LINE', null);
|
||||
INSERT INTO vanillameta.template_item (createdAt, updatedAt, id, templateId, x, y, width, height, recommendCategory, recommendType) VALUES ('2022-09-28 15:17:31.094919', '2022-09-28 15:17:31.094919', 5, 2, 0, 0, 12, 4, 'LINE', null);
|
||||
INSERT INTO vanillameta.template_item (createdAt, updatedAt, id, templateId, x, y, width, height, recommendCategory, recommendType) VALUES ('2022-09-28 15:17:31.118104', '2022-09-28 15:17:31.118104', 6, 2, 0, 4, 4, 4, 'LINE', null);
|
||||
INSERT INTO vanillameta.template_item (createdAt, updatedAt, id, templateId, x, y, width, height, recommendCategory, recommendType) VALUES ('2022-09-28 15:17:31.141382', '2022-09-28 15:17:31.141382', 7, 2, 4, 4, 4, 4, 'LINE', null);
|
||||
INSERT INTO vanillameta.template_item (createdAt, updatedAt, id, templateId, x, y, width, height, recommendCategory, recommendType) VALUES ('2022-09-28 15:17:31.161634', '2022-09-28 15:17:31.161634', 8, 2, 8, 4, 4, 4, 'LINE', null);
|
||||
INSERT INTO vanillameta.template_item (createdAt, updatedAt, id, templateId, x, y, width, height, recommendCategory, recommendType) VALUES ('2022-09-28 15:17:31.183596', '2022-09-28 15:17:31.183596', 9, 3, 0, 0, 4, 4, 'LINE', null);
|
||||
INSERT INTO vanillameta.template_item (createdAt, updatedAt, id, templateId, x, y, width, height, recommendCategory, recommendType) VALUES ('2022-09-28 15:17:31.205220', '2022-09-28 15:17:31.205220', 10, 3, 4, 0, 4, 4, 'LINE', null);
|
||||
INSERT INTO vanillameta.template_item (createdAt, updatedAt, id, templateId, x, y, width, height, recommendCategory, recommendType) VALUES ('2022-09-28 15:17:31.225541', '2022-09-28 15:17:31.225541', 11, 3, 8, 0, 4, 4, 'LINE', null);
|
||||
INSERT INTO vanillameta.template_item (createdAt, updatedAt, id, templateId, x, y, width, height, recommendCategory, recommendType) VALUES ('2022-09-28 15:17:31.246820', '2022-09-28 15:17:31.246820', 12, 3, 0, 4, 12, 4, 'LINE', null);
|
||||
@@ -1 +1 @@
|
||||
REACT_APP_API_URL='http://localhost:3000'
|
||||
REACT_APP_API_URL='http://localhost:4000'
|
||||
@@ -1 +1 @@
|
||||
REACT_APP_API_URL='http://localhost:3000'
|
||||
REACT_APP_API_URL='http://localhost:4000'
|
||||
1
frontend-web/.env.local
Normal file
@@ -0,0 +1 @@
|
||||
REACT_APP_API_URL='http://localhost:4000'
|
||||
6
frontend-web/.idea/vcs.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
11
frontend-web/global.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
declare module '*.svg' {
|
||||
import React = require('react');
|
||||
export const ReactComponent: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.gif' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
1153
frontend-web/package-lock.json
generated
@@ -25,8 +25,10 @@
|
||||
"axios": "^0.27.2",
|
||||
"echarts": "^5.3.3",
|
||||
"echarts-for-react": "^3.0.2",
|
||||
"echarts-gl": "^2.0.9",
|
||||
"react": "^18.2.0",
|
||||
"react-ace": "^10.1.0",
|
||||
"react-alert": "^7.0.3",
|
||||
"react-color": "^2.19.3",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-grid-layout": "^1.3.4",
|
||||
@@ -34,6 +36,7 @@
|
||||
"react-intersection-observer": "^9.4.0",
|
||||
"react-router-dom": "^6.3.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"save": "^2.9.0",
|
||||
"styled-components": "^5.3.5",
|
||||
"swiper": "^8.3.2",
|
||||
"web-vitals": "^2.1.4"
|
||||
@@ -41,6 +44,7 @@
|
||||
"scripts": {
|
||||
"start": "craco start",
|
||||
"start:dev": "env-cmd -f .env.development craco start",
|
||||
"start:local": "env-cmd -f .env.local craco start",
|
||||
"build": "craco build",
|
||||
"test": "craco test",
|
||||
"eject": "craco eject",
|
||||
|
||||
|
Before Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 824 B |
|
Before Width: | Height: | Size: 10 KiB |
10782
frontend-web/public/data/sample/chart3d.json
Normal file
@@ -3,122 +3,542 @@
|
||||
{
|
||||
"id": 1,
|
||||
"month": 1,
|
||||
"name": "january",
|
||||
"color": "#00BFFF",
|
||||
"high": 18,
|
||||
"low": 2,
|
||||
"avg": 8.9,
|
||||
"cDate": "2022-09-14T08:06:11+09:00"
|
||||
"monthName": "january",
|
||||
"high": 100,
|
||||
"low": -41,
|
||||
"avg": 50,
|
||||
"cDate": "2022-09-14T08:06:11+09:00",
|
||||
"open": 2320,
|
||||
"close": 2320.26,
|
||||
"lowest": 2287.3,
|
||||
"highest": 2362.94,
|
||||
"weekDay": "thur",
|
||||
"day": "4"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"month": 1,
|
||||
"name": "february",
|
||||
"color": "#4682B4",
|
||||
"high": 23,
|
||||
"low": -2,
|
||||
"avg": 9.9,
|
||||
"cDate": "2022-09-14T08:06:11+09:00"
|
||||
"monthName": "february",
|
||||
"high": 101,
|
||||
"low": -42,
|
||||
"avg": 51,
|
||||
"cDate": "2022-09-14T08:06:11+09:00",
|
||||
"open": 2300,
|
||||
"close": 2291.3,
|
||||
"lowest": 2288.26,
|
||||
"highest": 2308.38,
|
||||
"weekDay": "tues",
|
||||
"day": "4"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"month": 1,
|
||||
"name": "march",
|
||||
"color": "#00CED1",
|
||||
"high": 25,
|
||||
"low": -1,
|
||||
"avg": 11.3,
|
||||
"cDate": "2022-09-14T08:06:11+09:00"
|
||||
"monthName": "march",
|
||||
"high": 102,
|
||||
"low": -43,
|
||||
"avg": 52,
|
||||
"cDate": "2022-09-14T08:06:11+09:00",
|
||||
"open": 2295,
|
||||
"close": 2346.5,
|
||||
"lowest": 2295.35,
|
||||
"highest": 2346.92,
|
||||
"weekDay": "fri",
|
||||
"day": "10"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"month": 2,
|
||||
"name": "april",
|
||||
"color": "#3CB371",
|
||||
"high": 23,
|
||||
"low": 1,
|
||||
"avg": 13,
|
||||
"cDate": "2022-09-14T08:06:11+09:00"
|
||||
"monthName": "april",
|
||||
"high": 103,
|
||||
"low": -44,
|
||||
"avg": 53,
|
||||
"cDate": "2022-09-14T08:06:11+09:00",
|
||||
"open": 2347,
|
||||
"close": 2358.98,
|
||||
"lowest": 2337.35,
|
||||
"highest": 2363.8,
|
||||
"weekDay": "wed",
|
||||
"day": "17"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"month": 2,
|
||||
"name": "may",
|
||||
"color": "#9ACD32",
|
||||
"high": 26,
|
||||
"low": 5,
|
||||
"avg": 16.2,
|
||||
"cDate": "2022-09-14T08:06:11+09:00"
|
||||
"monthName": "may",
|
||||
"high": 104,
|
||||
"low": -45,
|
||||
"avg": 54,
|
||||
"cDate": "2022-09-14T08:06:11+09:00",
|
||||
"open": 2360,
|
||||
"close": 2382.48,
|
||||
"lowest": 2347.89,
|
||||
"highest": 2383.76,
|
||||
"weekDay": "sat",
|
||||
"day": "8"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"month": 3,
|
||||
"name": "june",
|
||||
"color": "#BDB76B",
|
||||
"high": 32,
|
||||
"low": 7,
|
||||
"avg": 19.9,
|
||||
"cDate": "2022-09-14T08:06:11+09:00"
|
||||
"monthName": "june",
|
||||
"high": 105,
|
||||
"low": -46,
|
||||
"avg": 55,
|
||||
"cDate": "2022-09-14T08:06:11+09:00",
|
||||
"open": 2383,
|
||||
"close": 2385.42,
|
||||
"lowest": 2371.23,
|
||||
"highest": 2391.82,
|
||||
"weekDay": "wed",
|
||||
"day": "18"
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"month": 3,
|
||||
"name": "july",
|
||||
"color": "#FFD700",
|
||||
"high": 33,
|
||||
"low": 12,
|
||||
"avg": 23,
|
||||
"cDate": "2022-09-14T08:06:11+09:00"
|
||||
"monthName": "july",
|
||||
"high": 106,
|
||||
"low": -47,
|
||||
"avg": 56,
|
||||
"cDate": "2022-09-14T08:06:11+09:00",
|
||||
"open": 2377,
|
||||
"close": 2419.02,
|
||||
"lowest": 2369.57,
|
||||
"highest": 2421.15,
|
||||
"weekDay": "mon",
|
||||
"day": "24"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"month": 4,
|
||||
"name": "august",
|
||||
"color": "#FF8C00",
|
||||
"high": 33,
|
||||
"low": 12,
|
||||
"avg": 23.6,
|
||||
"cDate": "2022-09-14T08:06:11+09:00"
|
||||
"monthName": "august",
|
||||
"high": 107,
|
||||
"low": -48,
|
||||
"avg": 57,
|
||||
"cDate": "2022-09-14T08:06:11+09:00",
|
||||
"open": 2425,
|
||||
"close": 2428.15,
|
||||
"lowest": 2417.58,
|
||||
"highest": 2440.38,
|
||||
"weekDay": "fri",
|
||||
"day": "2"
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"month": 5,
|
||||
"name": "september",
|
||||
"color": "#FF7F50",
|
||||
"high": 32,
|
||||
"low": 7,
|
||||
"avg": 21.1,
|
||||
"cDate": "2022-09-14T08:06:11+09:00"
|
||||
"monthName": "september",
|
||||
"high": 108,
|
||||
"low": -49,
|
||||
"avg": 58,
|
||||
"cDate": "2022-09-14T08:06:11+09:00",
|
||||
"open": 2411,
|
||||
"close": 2433.13,
|
||||
"lowest": 2403.3,
|
||||
"highest": 2437.42,
|
||||
"weekDay": "wed",
|
||||
"day": "21"
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"month": 5,
|
||||
"name": "october",
|
||||
"color": "#F4A460",
|
||||
"high": 28,
|
||||
"low": 2,
|
||||
"avg": 17,
|
||||
"cDate": "2022-09-14T08:06:11+09:00"
|
||||
"monthName": "october",
|
||||
"high": 109,
|
||||
"low": -50,
|
||||
"avg": 59,
|
||||
"cDate": "2022-09-14T08:06:11+09:00",
|
||||
"open": 2432,
|
||||
"close": 2434.48,
|
||||
"lowest": 2427.7,
|
||||
"highest": 2441.73,
|
||||
"weekDay": "thur",
|
||||
"day": "13"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"month": 6,
|
||||
"name": "november",
|
||||
"color": "#8B4513",
|
||||
"high": 25,
|
||||
"low": -2,
|
||||
"avg": 12.5,
|
||||
"cDate": "2022-09-14T08:06:11+09:00"
|
||||
"monthName": "november",
|
||||
"high": 110,
|
||||
"low": -51,
|
||||
"avg": 60,
|
||||
"cDate": "2022-09-14T08:06:11+09:00",
|
||||
"open": 2430,
|
||||
"close": 2418.53,
|
||||
"lowest": 2394.22,
|
||||
"highest": 2433.89,
|
||||
"weekDay": "mon",
|
||||
"day": "29"
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"month": 6,
|
||||
"name": "december",
|
||||
"color": "#778899",
|
||||
"high": 22,
|
||||
"low": -2,
|
||||
"avg": 10,
|
||||
"cDate": "2022-09-14T08:06:11+09:00"
|
||||
"monthName": "december",
|
||||
"high": 111,
|
||||
"low": -52,
|
||||
"avg": 61,
|
||||
"cDate": "2022-09-14T08:06:11+09:00",
|
||||
"open": 2416,
|
||||
"close": 2432.4,
|
||||
"lowest": 2414.4,
|
||||
"highest": 2443.03,
|
||||
"weekDay": "sat",
|
||||
"day": "13"
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"month": 5,
|
||||
"monthName": "january",
|
||||
"high": 112,
|
||||
"low": -53,
|
||||
"avg": 62,
|
||||
"cDate": "2022-09-05T19:15:52+09:00",
|
||||
"open": 1930,
|
||||
"close": 3462,
|
||||
"lowest": 7897,
|
||||
"highest": 2475,
|
||||
"weekDay": "mon",
|
||||
"day": "12"
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"month": 5,
|
||||
"monthName": "february",
|
||||
"high": 113,
|
||||
"low": -54,
|
||||
"avg": 63,
|
||||
"cDate": "2022-02-22T11:27:02+09:00",
|
||||
"open": 5521,
|
||||
"close": 8168,
|
||||
"lowest": 6605,
|
||||
"highest": 1002,
|
||||
"weekDay": "fri",
|
||||
"day": "25"
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"month": 5,
|
||||
"monthName": "march",
|
||||
"high": 114,
|
||||
"low": -55,
|
||||
"avg": 64,
|
||||
"cDate": "2022-07-25T14:34:18+09:00",
|
||||
"open": 7364,
|
||||
"close": 1981,
|
||||
"lowest": 8150,
|
||||
"highest": 4864,
|
||||
"weekDay": "sat",
|
||||
"day": "13"
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"month": 9,
|
||||
"monthName": "april",
|
||||
"high": 115,
|
||||
"low": -20,
|
||||
"avg": 65,
|
||||
"cDate": "2022-08-24T16:38:58+09:00",
|
||||
"open": 8331,
|
||||
"close": 5074,
|
||||
"lowest": 1986,
|
||||
"highest": 1861,
|
||||
"weekDay": "sun",
|
||||
"day": "23"
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"month": 6,
|
||||
"monthName": "may",
|
||||
"high": 116,
|
||||
"low": -21,
|
||||
"avg": 66,
|
||||
"cDate": "2022-06-11T02:13:13+09:00",
|
||||
"open": 1486,
|
||||
"close": 6726,
|
||||
"lowest": 1871,
|
||||
"highest": 8166,
|
||||
"weekDay": "thur",
|
||||
"day": "18"
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"month": 6,
|
||||
"monthName": "june",
|
||||
"high": 117,
|
||||
"low": -22,
|
||||
"avg": 67,
|
||||
"cDate": "2022-10-17T15:47:44+09:00",
|
||||
"open": 6154,
|
||||
"close": 6328,
|
||||
"lowest": 6317,
|
||||
"highest": 1773,
|
||||
"weekDay": "tues",
|
||||
"day": "12"
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"month": 1,
|
||||
"monthName": "july",
|
||||
"high": 118,
|
||||
"low": -23,
|
||||
"avg": 68,
|
||||
"cDate": "2022-06-15T07:04:42+09:00",
|
||||
"open": 7430,
|
||||
"close": 4066,
|
||||
"lowest": 2569,
|
||||
"highest": 1529,
|
||||
"weekDay": "tues",
|
||||
"day": "27"
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"month": 2,
|
||||
"monthName": "august",
|
||||
"high": 119,
|
||||
"low": -24,
|
||||
"avg": 69,
|
||||
"cDate": "2022-12-30T15:45:47+09:00",
|
||||
"open": 8200,
|
||||
"close": 5570,
|
||||
"lowest": 5144,
|
||||
"highest": 5647,
|
||||
"weekDay": "mon",
|
||||
"day": "9"
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"month": 5,
|
||||
"monthName": "september",
|
||||
"high": 120,
|
||||
"low": -25,
|
||||
"avg": 70,
|
||||
"cDate": "2022-01-21T13:01:12+09:00",
|
||||
"open": 8762,
|
||||
"close": 1786,
|
||||
"lowest": 8783,
|
||||
"highest": 3567,
|
||||
"weekDay": "thur",
|
||||
"day": "6"
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
"month": 1,
|
||||
"monthName": "october",
|
||||
"high": 121,
|
||||
"low": -26,
|
||||
"avg": 71,
|
||||
"cDate": "2022-06-14T01:43:43+09:00",
|
||||
"open": 3891,
|
||||
"close": 2533,
|
||||
"lowest": 4303,
|
||||
"highest": 1551,
|
||||
"weekDay": "mon",
|
||||
"day": "12"
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"month": 8,
|
||||
"monthName": "november",
|
||||
"high": 122,
|
||||
"low": -27,
|
||||
"avg": 72,
|
||||
"cDate": "2022-01-12T06:57:56+09:00",
|
||||
"open": 9271,
|
||||
"close": 4471,
|
||||
"lowest": 3705,
|
||||
"highest": 5830,
|
||||
"weekDay": "wed",
|
||||
"day": "18"
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"month": 7,
|
||||
"monthName": "december",
|
||||
"high": 123,
|
||||
"low": -28,
|
||||
"avg": 73,
|
||||
"cDate": "2022-07-28T20:25:26+09:00",
|
||||
"open": 834,
|
||||
"close": 8914,
|
||||
"lowest": 2339,
|
||||
"highest": 8354,
|
||||
"weekDay": "thur",
|
||||
"day": "24"
|
||||
},
|
||||
{
|
||||
"id": 25,
|
||||
"month": 5,
|
||||
"monthName": "january",
|
||||
"high": 124,
|
||||
"low": -29,
|
||||
"avg": 74,
|
||||
"cDate": "2022-01-22T03:11:51+09:00",
|
||||
"open": 8904,
|
||||
"close": 4056,
|
||||
"lowest": 1931,
|
||||
"highest": 8530,
|
||||
"weekDay": "wed",
|
||||
"day": "22"
|
||||
},
|
||||
{
|
||||
"id": 26,
|
||||
"month": 5,
|
||||
"monthName": "february",
|
||||
"high": 125,
|
||||
"low": -30,
|
||||
"avg": 75,
|
||||
"cDate": "2022-02-20T22:11:22+09:00",
|
||||
"open": 2854,
|
||||
"close": 6825,
|
||||
"lowest": 2349,
|
||||
"highest": 6066,
|
||||
"weekDay": "tues",
|
||||
"day": "28"
|
||||
},
|
||||
{
|
||||
"id": 27,
|
||||
"month": 4,
|
||||
"monthName": "march",
|
||||
"high": 126,
|
||||
"low": 10,
|
||||
"avg": 76,
|
||||
"cDate": "2022-11-20T00:09:15+09:00",
|
||||
"open": 901,
|
||||
"close": 2407,
|
||||
"lowest": 6395,
|
||||
"highest": 7580,
|
||||
"weekDay": "tues",
|
||||
"day": "25"
|
||||
},
|
||||
{
|
||||
"id": 28,
|
||||
"month": 4,
|
||||
"monthName": "april",
|
||||
"high": 127,
|
||||
"low": 9,
|
||||
"avg": 77,
|
||||
"cDate": "2022-10-06T19:56:38+09:00",
|
||||
"open": 1021,
|
||||
"close": 4735,
|
||||
"lowest": 7062,
|
||||
"highest": 9247,
|
||||
"weekDay": "fri",
|
||||
"day": "23"
|
||||
},
|
||||
{
|
||||
"id": 29,
|
||||
"month": 5,
|
||||
"monthName": "may",
|
||||
"high": 128,
|
||||
"low": 8,
|
||||
"avg": 78,
|
||||
"cDate": "2022-09-15T00:09:23+09:00",
|
||||
"open": 2215,
|
||||
"close": 856,
|
||||
"lowest": 3935,
|
||||
"highest": 6171,
|
||||
"weekDay": "fri",
|
||||
"day": "14"
|
||||
},
|
||||
{
|
||||
"id": 30,
|
||||
"month": 5,
|
||||
"monthName": "june",
|
||||
"high": 129,
|
||||
"low": 7,
|
||||
"avg": 79,
|
||||
"cDate": "2022-11-08T19:13:52+09:00",
|
||||
"open": 6801,
|
||||
"close": 6320,
|
||||
"lowest": 604,
|
||||
"highest": 8857,
|
||||
"weekDay": "mon",
|
||||
"day": "23"
|
||||
},
|
||||
{
|
||||
"id": 31,
|
||||
"month": 7,
|
||||
"monthName": "july",
|
||||
"high": 130,
|
||||
"low": 6,
|
||||
"avg": 80,
|
||||
"cDate": "2022-02-18T16:27:21+09:00",
|
||||
"open": 9226,
|
||||
"close": 8752,
|
||||
"lowest": 450,
|
||||
"highest": 8468,
|
||||
"weekDay": "thur",
|
||||
"day": "25"
|
||||
},
|
||||
{
|
||||
"id": 32,
|
||||
"month": 6,
|
||||
"monthName": "august",
|
||||
"high": 131,
|
||||
"low": 5,
|
||||
"avg": 81,
|
||||
"cDate": "2022-06-28T01:19:17+09:00",
|
||||
"open": 1234,
|
||||
"close": 9424,
|
||||
"lowest": 4807,
|
||||
"highest": 3242,
|
||||
"weekDay": "mon",
|
||||
"day": "20"
|
||||
},
|
||||
{
|
||||
"id": 33,
|
||||
"month": 6,
|
||||
"monthName": "september",
|
||||
"high": 132,
|
||||
"low": 4,
|
||||
"avg": 82,
|
||||
"cDate": "2022-02-24T17:46:34+09:00",
|
||||
"open": 1880,
|
||||
"close": 6353,
|
||||
"lowest": 7890,
|
||||
"highest": 618,
|
||||
"weekDay": "sat",
|
||||
"day": "16"
|
||||
},
|
||||
{
|
||||
"id": 34,
|
||||
"month": 6,
|
||||
"monthName": "october",
|
||||
"high": 133,
|
||||
"low": 3,
|
||||
"avg": 83,
|
||||
"cDate": "2022-11-03T18:39:16+09:00",
|
||||
"open": 9727,
|
||||
"close": 4352,
|
||||
"lowest": 9473,
|
||||
"highest": 2138,
|
||||
"weekDay": "tues",
|
||||
"day": "2"
|
||||
},
|
||||
{
|
||||
"id": 35,
|
||||
"month": 1,
|
||||
"monthName": "november",
|
||||
"high": 134,
|
||||
"low": 2,
|
||||
"avg": 84,
|
||||
"cDate": "2022-03-07T22:41:53+09:00",
|
||||
"open": 4942,
|
||||
"close": 4078,
|
||||
"lowest": 6696,
|
||||
"highest": 3365,
|
||||
"weekDay": "mon",
|
||||
"day": "17"
|
||||
},
|
||||
{
|
||||
"id": 36,
|
||||
"month": 8,
|
||||
"monthName": "december",
|
||||
"high": 135,
|
||||
"low": 1,
|
||||
"avg": 85,
|
||||
"cDate": "2022-12-14T15:39:43+09:00",
|
||||
"open": 5218,
|
||||
"close": 8383,
|
||||
"lowest": 6945,
|
||||
"highest": 645,
|
||||
"weekDay": "mon",
|
||||
"day": "3"
|
||||
}
|
||||
],
|
||||
"spec": [
|
||||
@@ -127,17 +547,13 @@
|
||||
"columnType": "STRING"
|
||||
},
|
||||
{
|
||||
"columnName": "name",
|
||||
"columnName": "monthName",
|
||||
"columnType": "STRING"
|
||||
},
|
||||
{
|
||||
"columnName": "month",
|
||||
"columnType": "NUMBER"
|
||||
},
|
||||
{
|
||||
"columnName": "color",
|
||||
"columnType": "STRING"
|
||||
},
|
||||
{
|
||||
"columnName": "high",
|
||||
"columnType": "NUMBER"
|
||||
@@ -153,6 +569,30 @@
|
||||
{
|
||||
"columnName": "cDate",
|
||||
"columnType": "DATE"
|
||||
},
|
||||
{
|
||||
"columnName": "open",
|
||||
"columnType": "NUMBER"
|
||||
},
|
||||
{
|
||||
"columnName": "close",
|
||||
"columnType": "NUMBER"
|
||||
},
|
||||
{
|
||||
"columnName": "lowest",
|
||||
"columnType": "NUMBER"
|
||||
},
|
||||
{
|
||||
"columnName": "highest",
|
||||
"columnType": "NUMBER"
|
||||
},
|
||||
{
|
||||
"columnName": "weekDay",
|
||||
"columnType": "STRING"
|
||||
},
|
||||
{
|
||||
"columnName": "day",
|
||||
"columnType": "NUMBER"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
BIN
frontend-web/public/static/images/icon/area-chart.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
BIN
frontend-web/public/static/images/icon/btn-icon-default.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
frontend-web/public/static/images/icon/btn-plus.png
Normal file
|
After Width: | Height: | Size: 303 B |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
BIN
frontend-web/public/static/images/icon/candlestick-chart.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |