Merge remote-tracking branch 'upstream/develop' into develop_backend_yds

# Conflicts:
#	.idea/misc.xml
#	.idea/modules.xml
This commit is contained in:
Dosun Yun
2022-10-27 04:45:45 +09:00
372 changed files with 25460 additions and 4807 deletions

7
.gitignore vendored
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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>

View File

@@ -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
```

View File

@@ -7,3 +7,4 @@ DB_NAME='vanillameta'
CORS_ORIGIN='*'
api propertyfile fix

24
backend-api/Dockerfile Normal file
View 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"]

View File

@@ -0,0 +1,7 @@
[client]
default-character-set = utf8mb4
[mysql]
default-character-set = utf8mb4

View 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:

View File

@@ -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",

View File

@@ -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",

View File

@@ -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'

Binary file not shown.

View 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 {}

View File

@@ -1,9 +1,9 @@
import { CreateDateColumn, UpdateDateColumn } from 'typeorm';
export abstract class BaseEntity {
@CreateDateColumn()
@CreateDateColumn({comment:'생성일'})
createdAt: Date;
@UpdateDateColumn()
@UpdateDateColumn({comment:'수정일'})
updatedAt: Date;
}

View File

@@ -0,0 +1,7 @@
export enum ComponentType {
HORIZONTAL = 'HORIZONTAL',
VERTICAL = 'VERTICAL',
SQUARE = 'SQUARE',
SCORE = 'SCORE',
TABLE = 'TABLE',
}

View File

@@ -1,4 +1,4 @@
export enum DatasetType {
DATASET = 'DATASET',
WIDGET = 'WIDGET_VIEW'
DATASET = 'DATASET',
TABLE = 'TABLE',
}

View File

@@ -0,0 +1,4 @@
export enum ResponseStatus {
SUCCESS = 'SUCCESS',
ERROR = 'ERROR',
}

View File

@@ -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);
}
}

View File

@@ -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`;
}
}

View File

@@ -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
}

View 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 {}

View 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;
}
}

View File

@@ -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`;
}
}

View File

@@ -0,0 +1,10 @@
import { IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator';
export class CreateDashboardWidgetDto {
@IsNumber()
@IsNotEmpty()
dashboardId: number;
@IsNotEmpty()
widgetIds: number[];
}

View File

@@ -0,0 +1,4 @@
import { PartialType } from '@nestjs/swagger';
import { CreateDashboardWidgetDto } from './create-dashboard-widget.dto';
export class UpdateDashboardWidgetDto extends PartialType(CreateDashboardWidgetDto) {}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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 {}

View File

@@ -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` },
};
}
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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
}

View File

@@ -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();
});
});

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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],
})

View File

@@ -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 },
};
}
}

View File

@@ -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()

View File

@@ -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 {

View 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
}

View File

@@ -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);

View File

@@ -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 {}

View File

@@ -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`;
}
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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();

View File

@@ -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[];
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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),

View File

@@ -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);
}
}

View File

@@ -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 {}

View File

@@ -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;
}
}

View 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;
}
}
}

View File

@@ -1 +0,0 @@
export class CreateWidgetViewDto {}

View File

@@ -1,4 +0,0 @@
import { PartialType } from '@nestjs/swagger';
import { CreateWidgetViewDto } from './create-widget-view.dto';
export class UpdateWidgetViewDto extends PartialType(CreateWidgetViewDto) {}

View File

@@ -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
}

View File

@@ -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();
});
});

View File

@@ -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);
}
}

View File

@@ -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 {}

View File

@@ -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();
});
});

View File

@@ -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`;
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View 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`;
}
}

View File

@@ -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);

View File

@@ -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 {}

View File

@@ -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
View 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)
);

View 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');

View 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);

View 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);

View 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');

View 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');

View 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);

View File

@@ -1 +1 @@
REACT_APP_API_URL='http://localhost:3000'
REACT_APP_API_URL='http://localhost:4000'

View File

@@ -1 +1 @@
REACT_APP_API_URL='http://localhost:3000'
REACT_APP_API_URL='http://localhost:4000'

1
frontend-web/.env.local Normal file
View File

@@ -0,0 +1 @@
REACT_APP_API_URL='http://localhost:4000'

View File

@@ -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
View 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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -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",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 824 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because it is too large Load Diff

View 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"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 B

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

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