issue # nestjs 로 초기화
This commit is contained in:
8
.env
8
.env
@@ -4,8 +4,14 @@ PGPORT=15454
|
|||||||
PGDATABASE=scheduler
|
PGDATABASE=scheduler
|
||||||
PGUSER=baekyangdan
|
PGUSER=baekyangdan
|
||||||
PGPASSWORD=qwas745478!
|
PGPASSWORD=qwas745478!
|
||||||
|
PG_DATABASE_URL=postgres://baekyangdan:qwas745478!@bkdhome.p-e.kr:15454/scheduler
|
||||||
|
|
||||||
# Fastify 서버 포트
|
# Redis 설정
|
||||||
|
RD_HOST=bkdhome.p-e.kr
|
||||||
|
RD_PORT=16779
|
||||||
|
RD_URL=redis://bkdhome.p-e.kr:16779
|
||||||
|
|
||||||
|
# Express 서버 포트
|
||||||
PORT=3000
|
PORT=3000
|
||||||
|
|
||||||
# Gmail SMTP 설정
|
# Gmail SMTP 설정
|
||||||
|
|||||||
13
.gitignore
vendored
13
.gitignore
vendored
@@ -5,14 +5,24 @@ yarn-error.log*
|
|||||||
package-lock.json
|
package-lock.json
|
||||||
yarn.lock
|
yarn.lock
|
||||||
|
|
||||||
|
.pnp.*
|
||||||
|
.pnp.loader.mjs
|
||||||
|
.yarn/install-state.gz
|
||||||
|
|
||||||
# TypeScript
|
# TypeScript
|
||||||
dist/
|
dist/
|
||||||
|
build/
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
|
|
||||||
# 환경 변수
|
# 환경 변수
|
||||||
# .env
|
# .env
|
||||||
# .env.*.local
|
# .env.*.local
|
||||||
|
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
# IDE / OS
|
# IDE / OS
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
@@ -31,3 +41,6 @@ Thumbs.db
|
|||||||
# Logs
|
# Logs
|
||||||
logs/
|
logs/
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
|
.cache/
|
||||||
|
.eslintcache
|
||||||
@@ -1,43 +1,43 @@
|
|||||||
# This file is a template, and might need editing before it works on your project.
|
# # This file is a template, and might need editing before it works on your project.
|
||||||
# This is a sample GitLab CI/CD configuration file that should run without any modifications.
|
# # This is a sample GitLab CI/CD configuration file that should run without any modifications.
|
||||||
# It demonstrates a basic 3 stage CI/CD pipeline. Instead of real tests or scripts,
|
# # It demonstrates a basic 3 stage CI/CD pipeline. Instead of real tests or scripts,
|
||||||
# it uses echo commands to simulate the pipeline execution.
|
# # it uses echo commands to simulate the pipeline execution.
|
||||||
#
|
# #
|
||||||
# A pipeline is composed of independent jobs that run scripts, grouped into stages.
|
# # A pipeline is composed of independent jobs that run scripts, grouped into stages.
|
||||||
# Stages run in sequential order, but jobs within stages run in parallel.
|
# # Stages run in sequential order, but jobs within stages run in parallel.
|
||||||
#
|
# #
|
||||||
# For more information, see: https://docs.gitlab.com/ee/ci/yaml/#stages
|
# # For more information, see: https://docs.gitlab.com/ee/ci/yaml/#stages
|
||||||
#
|
# #
|
||||||
# You can copy and paste this template into a new `.gitlab-ci.yml` file.
|
# # You can copy and paste this template into a new `.gitlab-ci.yml` file.
|
||||||
# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword.
|
# # You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword.
|
||||||
#
|
# #
|
||||||
# To contribute improvements to CI/CD templates, please follow the Development guide at:
|
# # To contribute improvements to CI/CD templates, please follow the Development guide at:
|
||||||
# https://docs.gitlab.com/development/cicd/templates/
|
# # https://docs.gitlab.com/development/cicd/templates/
|
||||||
# This specific template is located at:
|
# # This specific template is located at:
|
||||||
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Getting-Started.gitlab-ci.yml
|
# # https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Getting-Started.gitlab-ci.yml
|
||||||
|
|
||||||
stages: # List of stages for jobs, and their order of execution
|
# stages: # List of stages for jobs, and their order of execution
|
||||||
- build
|
# - build
|
||||||
|
|
||||||
cache:
|
# cache:
|
||||||
key:
|
# key:
|
||||||
files:
|
# files:
|
||||||
- package-lock.json
|
# - package-lock.json
|
||||||
paths:
|
# paths:
|
||||||
- node_modules/
|
# - node_modules/
|
||||||
|
|
||||||
build: # This job runs in the build stage, which runs first.
|
# build: # This job runs in the build stage, which runs first.
|
||||||
stage: build
|
# stage: build
|
||||||
tags:
|
# tags:
|
||||||
- local-runner
|
# - local-runner
|
||||||
before_script:
|
# before_script:
|
||||||
script:
|
# script:
|
||||||
- echo "Compiling the code..."
|
# - echo "Compiling the code..."
|
||||||
- echo $DOCKER_VOLUME
|
# - echo $DOCKER_VOLUME
|
||||||
- echo $DOCKER_COMPOSE_VOLUME
|
# - echo $DOCKER_COMPOSE_VOLUME
|
||||||
- npm install
|
# - npm install
|
||||||
- npm run build
|
# - npm run build
|
||||||
- sudo cp -r $PWD/dist/. $DOCKER_VOLUME/scheduler/back/dist
|
# - sudo cp -r $PWD/dist/. $DOCKER_VOLUME/scheduler/back/dist
|
||||||
- sudo cp $PWD/package.json $DOCKER_VOLUME/scheduler/back/dist
|
# - sudo cp $PWD/package.json $DOCKER_VOLUME/scheduler/back/dist
|
||||||
- docker compose -f $DOCKER_COMPOSE_VOLUME/scheduler/docker-compose.yaml up -d back
|
# - docker compose -f $DOCKER_COMPOSE_VOLUME/scheduler/docker-compose.yaml up -d back
|
||||||
- echo "Compile complete."
|
# - echo "Compile complete."
|
||||||
4
.prettierrc
Normal file
4
.prettierrc
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "all"
|
||||||
|
}
|
||||||
32
.yarn/sdks/eslint/bin/eslint.js
vendored
Normal file
32
.yarn/sdks/eslint/bin/eslint.js
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire, register} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
const {pathToFileURL} = require(`url`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||||
|
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require eslint/bin/eslint.js
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
if (isPnpLoaderEnabled && register) {
|
||||||
|
register(pathToFileURL(absPnpLoaderPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
|
? exports => absRequire(absUserWrapperPath)(exports)
|
||||||
|
: exports => exports;
|
||||||
|
|
||||||
|
// Defer to the real eslint/bin/eslint.js your application uses
|
||||||
|
module.exports = wrapWithUserWrapper(absRequire(`eslint/bin/eslint.js`));
|
||||||
32
.yarn/sdks/eslint/lib/api.js
vendored
Normal file
32
.yarn/sdks/eslint/lib/api.js
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire, register} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
const {pathToFileURL} = require(`url`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||||
|
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require eslint
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
if (isPnpLoaderEnabled && register) {
|
||||||
|
register(pathToFileURL(absPnpLoaderPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
|
? exports => absRequire(absUserWrapperPath)(exports)
|
||||||
|
: exports => exports;
|
||||||
|
|
||||||
|
// Defer to the real eslint your application uses
|
||||||
|
module.exports = wrapWithUserWrapper(absRequire(`eslint`));
|
||||||
32
.yarn/sdks/eslint/lib/config-api.js
vendored
Normal file
32
.yarn/sdks/eslint/lib/config-api.js
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire, register} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
const {pathToFileURL} = require(`url`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||||
|
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require eslint/config
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
if (isPnpLoaderEnabled && register) {
|
||||||
|
register(pathToFileURL(absPnpLoaderPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
|
? exports => absRequire(absUserWrapperPath)(exports)
|
||||||
|
: exports => exports;
|
||||||
|
|
||||||
|
// Defer to the real eslint/config your application uses
|
||||||
|
module.exports = wrapWithUserWrapper(absRequire(`eslint/config`));
|
||||||
32
.yarn/sdks/eslint/lib/types/config-api.d.ts
vendored
Normal file
32
.yarn/sdks/eslint/lib/types/config-api.d.ts
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire, register} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
const {pathToFileURL} = require(`url`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||||
|
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require eslint/config
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
if (isPnpLoaderEnabled && register) {
|
||||||
|
register(pathToFileURL(absPnpLoaderPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
|
? exports => absRequire(absUserWrapperPath)(exports)
|
||||||
|
: exports => exports;
|
||||||
|
|
||||||
|
// Defer to the real eslint/config your application uses
|
||||||
|
module.exports = wrapWithUserWrapper(absRequire(`eslint/config`));
|
||||||
32
.yarn/sdks/eslint/lib/types/index.d.ts
vendored
Normal file
32
.yarn/sdks/eslint/lib/types/index.d.ts
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire, register} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
const {pathToFileURL} = require(`url`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||||
|
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require eslint
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
if (isPnpLoaderEnabled && register) {
|
||||||
|
register(pathToFileURL(absPnpLoaderPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
|
? exports => absRequire(absUserWrapperPath)(exports)
|
||||||
|
: exports => exports;
|
||||||
|
|
||||||
|
// Defer to the real eslint your application uses
|
||||||
|
module.exports = wrapWithUserWrapper(absRequire(`eslint`));
|
||||||
32
.yarn/sdks/eslint/lib/types/rules.d.ts
vendored
Normal file
32
.yarn/sdks/eslint/lib/types/rules.d.ts
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire, register} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
const {pathToFileURL} = require(`url`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||||
|
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require eslint/rules
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
if (isPnpLoaderEnabled && register) {
|
||||||
|
register(pathToFileURL(absPnpLoaderPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
|
? exports => absRequire(absUserWrapperPath)(exports)
|
||||||
|
: exports => exports;
|
||||||
|
|
||||||
|
// Defer to the real eslint/rules your application uses
|
||||||
|
module.exports = wrapWithUserWrapper(absRequire(`eslint/rules`));
|
||||||
32
.yarn/sdks/eslint/lib/types/universal.d.ts
vendored
Normal file
32
.yarn/sdks/eslint/lib/types/universal.d.ts
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire, register} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
const {pathToFileURL} = require(`url`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||||
|
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require eslint/universal
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
if (isPnpLoaderEnabled && register) {
|
||||||
|
register(pathToFileURL(absPnpLoaderPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
|
? exports => absRequire(absUserWrapperPath)(exports)
|
||||||
|
: exports => exports;
|
||||||
|
|
||||||
|
// Defer to the real eslint/universal your application uses
|
||||||
|
module.exports = wrapWithUserWrapper(absRequire(`eslint/universal`));
|
||||||
32
.yarn/sdks/eslint/lib/types/use-at-your-own-risk.d.ts
vendored
Normal file
32
.yarn/sdks/eslint/lib/types/use-at-your-own-risk.d.ts
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire, register} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
const {pathToFileURL} = require(`url`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||||
|
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require eslint/use-at-your-own-risk
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
if (isPnpLoaderEnabled && register) {
|
||||||
|
register(pathToFileURL(absPnpLoaderPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
|
? exports => absRequire(absUserWrapperPath)(exports)
|
||||||
|
: exports => exports;
|
||||||
|
|
||||||
|
// Defer to the real eslint/use-at-your-own-risk your application uses
|
||||||
|
module.exports = wrapWithUserWrapper(absRequire(`eslint/use-at-your-own-risk`));
|
||||||
32
.yarn/sdks/eslint/lib/universal.js
vendored
Normal file
32
.yarn/sdks/eslint/lib/universal.js
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire, register} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
const {pathToFileURL} = require(`url`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||||
|
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require eslint/universal
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
if (isPnpLoaderEnabled && register) {
|
||||||
|
register(pathToFileURL(absPnpLoaderPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
|
? exports => absRequire(absUserWrapperPath)(exports)
|
||||||
|
: exports => exports;
|
||||||
|
|
||||||
|
// Defer to the real eslint/universal your application uses
|
||||||
|
module.exports = wrapWithUserWrapper(absRequire(`eslint/universal`));
|
||||||
32
.yarn/sdks/eslint/lib/unsupported-api.js
vendored
Normal file
32
.yarn/sdks/eslint/lib/unsupported-api.js
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire, register} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
const {pathToFileURL} = require(`url`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||||
|
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require eslint/use-at-your-own-risk
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
if (isPnpLoaderEnabled && register) {
|
||||||
|
register(pathToFileURL(absPnpLoaderPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
|
? exports => absRequire(absUserWrapperPath)(exports)
|
||||||
|
: exports => exports;
|
||||||
|
|
||||||
|
// Defer to the real eslint/use-at-your-own-risk your application uses
|
||||||
|
module.exports = wrapWithUserWrapper(absRequire(`eslint/use-at-your-own-risk`));
|
||||||
31
.yarn/sdks/eslint/package.json
vendored
Normal file
31
.yarn/sdks/eslint/package.json
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"name": "eslint",
|
||||||
|
"version": "9.39.1-sdk",
|
||||||
|
"main": "./lib/api.js",
|
||||||
|
"type": "commonjs",
|
||||||
|
"bin": {
|
||||||
|
"eslint": "./bin/eslint.js"
|
||||||
|
},
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./lib/types/index.d.ts",
|
||||||
|
"default": "./lib/api.js"
|
||||||
|
},
|
||||||
|
"./config": {
|
||||||
|
"types": "./lib/types/config-api.d.ts",
|
||||||
|
"default": "./lib/config-api.js"
|
||||||
|
},
|
||||||
|
"./package.json": "./package.json",
|
||||||
|
"./use-at-your-own-risk": {
|
||||||
|
"types": "./lib/types/use-at-your-own-risk.d.ts",
|
||||||
|
"default": "./lib/unsupported-api.js"
|
||||||
|
},
|
||||||
|
"./rules": {
|
||||||
|
"types": "./lib/types/rules.d.ts"
|
||||||
|
},
|
||||||
|
"./universal": {
|
||||||
|
"types": "./lib/types/universal.d.ts",
|
||||||
|
"default": "./lib/universal.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
.yarn/sdks/integrations.yml
vendored
Normal file
5
.yarn/sdks/integrations.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# This file is automatically generated by @yarnpkg/sdks.
|
||||||
|
# Manual changes might be lost!
|
||||||
|
|
||||||
|
integrations:
|
||||||
|
- vscode
|
||||||
32
.yarn/sdks/prettier/bin/prettier.cjs
vendored
Normal file
32
.yarn/sdks/prettier/bin/prettier.cjs
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire, register} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
const {pathToFileURL} = require(`url`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||||
|
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require prettier/bin/prettier.cjs
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
if (isPnpLoaderEnabled && register) {
|
||||||
|
register(pathToFileURL(absPnpLoaderPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
|
? exports => absRequire(absUserWrapperPath)(exports)
|
||||||
|
: exports => exports;
|
||||||
|
|
||||||
|
// Defer to the real prettier/bin/prettier.cjs your application uses
|
||||||
|
module.exports = wrapWithUserWrapper(absRequire(`prettier/bin/prettier.cjs`));
|
||||||
32
.yarn/sdks/prettier/index.cjs
vendored
Normal file
32
.yarn/sdks/prettier/index.cjs
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire, register} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
const {pathToFileURL} = require(`url`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||||
|
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require prettier
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
if (isPnpLoaderEnabled && register) {
|
||||||
|
register(pathToFileURL(absPnpLoaderPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
|
? exports => absRequire(absUserWrapperPath)(exports)
|
||||||
|
: exports => exports;
|
||||||
|
|
||||||
|
// Defer to the real prettier your application uses
|
||||||
|
module.exports = wrapWithUserWrapper(absRequire(`prettier`));
|
||||||
7
.yarn/sdks/prettier/package.json
vendored
Normal file
7
.yarn/sdks/prettier/package.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"name": "prettier",
|
||||||
|
"version": "3.6.2-sdk",
|
||||||
|
"main": "./index.cjs",
|
||||||
|
"type": "commonjs",
|
||||||
|
"bin": "./bin/prettier.cjs"
|
||||||
|
}
|
||||||
32
.yarn/sdks/typescript/bin/tsc
vendored
Normal file
32
.yarn/sdks/typescript/bin/tsc
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire, register} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
const {pathToFileURL} = require(`url`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||||
|
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require typescript/bin/tsc
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
if (isPnpLoaderEnabled && register) {
|
||||||
|
register(pathToFileURL(absPnpLoaderPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
|
? exports => absRequire(absUserWrapperPath)(exports)
|
||||||
|
: exports => exports;
|
||||||
|
|
||||||
|
// Defer to the real typescript/bin/tsc your application uses
|
||||||
|
module.exports = wrapWithUserWrapper(absRequire(`typescript/bin/tsc`));
|
||||||
32
.yarn/sdks/typescript/bin/tsserver
vendored
Normal file
32
.yarn/sdks/typescript/bin/tsserver
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire, register} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
const {pathToFileURL} = require(`url`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||||
|
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require typescript/bin/tsserver
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
if (isPnpLoaderEnabled && register) {
|
||||||
|
register(pathToFileURL(absPnpLoaderPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
|
? exports => absRequire(absUserWrapperPath)(exports)
|
||||||
|
: exports => exports;
|
||||||
|
|
||||||
|
// Defer to the real typescript/bin/tsserver your application uses
|
||||||
|
module.exports = wrapWithUserWrapper(absRequire(`typescript/bin/tsserver`));
|
||||||
32
.yarn/sdks/typescript/lib/tsc.js
vendored
Normal file
32
.yarn/sdks/typescript/lib/tsc.js
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire, register} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
const {pathToFileURL} = require(`url`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||||
|
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require typescript/lib/tsc.js
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
if (isPnpLoaderEnabled && register) {
|
||||||
|
register(pathToFileURL(absPnpLoaderPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
|
? exports => absRequire(absUserWrapperPath)(exports)
|
||||||
|
: exports => exports;
|
||||||
|
|
||||||
|
// Defer to the real typescript/lib/tsc.js your application uses
|
||||||
|
module.exports = wrapWithUserWrapper(absRequire(`typescript/lib/tsc.js`));
|
||||||
248
.yarn/sdks/typescript/lib/tsserver.js
vendored
Normal file
248
.yarn/sdks/typescript/lib/tsserver.js
vendored
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire, register} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
const {pathToFileURL} = require(`url`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||||
|
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require typescript/lib/tsserver.js
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
if (isPnpLoaderEnabled && register) {
|
||||||
|
register(pathToFileURL(absPnpLoaderPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
|
? exports => absRequire(absUserWrapperPath)(exports)
|
||||||
|
: exports => exports;
|
||||||
|
|
||||||
|
const moduleWrapper = exports => {
|
||||||
|
return wrapWithUserWrapper(moduleWrapperFn(exports));
|
||||||
|
};
|
||||||
|
|
||||||
|
const moduleWrapperFn = tsserver => {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
return tsserver;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {isAbsolute} = require(`path`);
|
||||||
|
const pnpApi = require(`pnpapi`);
|
||||||
|
|
||||||
|
const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//);
|
||||||
|
const isPortal = str => str.startsWith("portal:/");
|
||||||
|
const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
|
||||||
|
|
||||||
|
const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => {
|
||||||
|
return `${locator.name}@${locator.reference}`;
|
||||||
|
}));
|
||||||
|
|
||||||
|
// VSCode sends the zip paths to TS using the "zip://" prefix, that TS
|
||||||
|
// doesn't understand. This layer makes sure to remove the protocol
|
||||||
|
// before forwarding it to TS, and to add it back on all returned paths.
|
||||||
|
|
||||||
|
function toEditorPath(str) {
|
||||||
|
// We add the `zip:` prefix to both `.zip/` paths and virtual paths
|
||||||
|
if (isAbsolute(str) && !str.match(/^\^?(zip:|\/zip\/)/) && (str.match(/\.zip\//) || isVirtual(str))) {
|
||||||
|
// We also take the opportunity to turn virtual paths into physical ones;
|
||||||
|
// this makes it much easier to work with workspaces that list peer
|
||||||
|
// dependencies, since otherwise Ctrl+Click would bring us to the virtual
|
||||||
|
// file instances instead of the real ones.
|
||||||
|
//
|
||||||
|
// We only do this to modules owned by the the dependency tree roots.
|
||||||
|
// This avoids breaking the resolution when jumping inside a vendor
|
||||||
|
// with peer dep (otherwise jumping into react-dom would show resolution
|
||||||
|
// errors on react).
|
||||||
|
//
|
||||||
|
const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str;
|
||||||
|
if (resolved) {
|
||||||
|
const locator = pnpApi.findPackageLocator(resolved);
|
||||||
|
if (locator && (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference))) {
|
||||||
|
str = resolved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
str = normalize(str);
|
||||||
|
|
||||||
|
if (str.match(/\.zip\//)) {
|
||||||
|
switch (hostInfo) {
|
||||||
|
// Absolute VSCode `Uri.fsPath`s need to start with a slash.
|
||||||
|
// VSCode only adds it automatically for supported schemes,
|
||||||
|
// so we have to do it manually for the `zip` scheme.
|
||||||
|
// The path needs to start with a caret otherwise VSCode doesn't handle the protocol
|
||||||
|
//
|
||||||
|
// Ref: https://github.com/microsoft/vscode/issues/105014#issuecomment-686760910
|
||||||
|
//
|
||||||
|
// 2021-10-08: VSCode changed the format in 1.61.
|
||||||
|
// Before | ^zip:/c:/foo/bar.zip/package.json
|
||||||
|
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||||
|
//
|
||||||
|
// 2022-04-06: VSCode changed the format in 1.66.
|
||||||
|
// Before | ^/zip//c:/foo/bar.zip/package.json
|
||||||
|
// After | ^/zip/c:/foo/bar.zip/package.json
|
||||||
|
//
|
||||||
|
// 2022-05-06: VSCode changed the format in 1.68
|
||||||
|
// Before | ^/zip/c:/foo/bar.zip/package.json
|
||||||
|
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||||
|
//
|
||||||
|
case `vscode <1.61`: {
|
||||||
|
str = `^zip:${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `vscode <1.66`: {
|
||||||
|
str = `^/zip/${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `vscode <1.68`: {
|
||||||
|
str = `^/zip${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `vscode`: {
|
||||||
|
str = `^/zip/${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// To make "go to definition" work,
|
||||||
|
// We have to resolve the actual file system path from virtual path
|
||||||
|
// and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip)
|
||||||
|
case `coc-nvim`: {
|
||||||
|
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||||
|
str = resolve(`zipfile:${str}`);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server)
|
||||||
|
// We have to resolve the actual file system path from virtual path,
|
||||||
|
// everything else is up to neovim
|
||||||
|
case `neovim`: {
|
||||||
|
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||||
|
str = `zipfile://${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
str = `zip:${str}`;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
str = str.replace(/^\/?/, process.platform === `win32` ? `` : `/`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fromEditorPath(str) {
|
||||||
|
switch (hostInfo) {
|
||||||
|
case `coc-nvim`: {
|
||||||
|
str = str.replace(/\.zip::/, `.zip/`);
|
||||||
|
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
|
||||||
|
// So in order to convert it back, we use .* to match all the thing
|
||||||
|
// before `zipfile:`
|
||||||
|
return process.platform === `win32`
|
||||||
|
? str.replace(/^.*zipfile:\//, ``)
|
||||||
|
: str.replace(/^.*zipfile:/, ``);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `neovim`: {
|
||||||
|
str = str.replace(/\.zip::/, `.zip/`);
|
||||||
|
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
|
||||||
|
return str.replace(/^zipfile:\/\//, ``);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `vscode`:
|
||||||
|
default: {
|
||||||
|
return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`)
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force enable 'allowLocalPluginLoads'
|
||||||
|
// TypeScript tries to resolve plugins using a path relative to itself
|
||||||
|
// which doesn't work when using the global cache
|
||||||
|
// https://github.com/microsoft/TypeScript/blob/1b57a0395e0bff191581c9606aab92832001de62/src/server/project.ts#L2238
|
||||||
|
// VSCode doesn't want to enable 'allowLocalPluginLoads' due to security concerns but
|
||||||
|
// TypeScript already does local loads and if this code is running the user trusts the workspace
|
||||||
|
// https://github.com/microsoft/vscode/issues/45856
|
||||||
|
const ConfiguredProject = tsserver.server.ConfiguredProject;
|
||||||
|
const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype;
|
||||||
|
ConfiguredProject.prototype.enablePluginsWithOptions = function() {
|
||||||
|
this.projectService.allowLocalPluginLoads = true;
|
||||||
|
return originalEnablePluginsWithOptions.apply(this, arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
// And here is the point where we hijack the VSCode <-> TS communications
|
||||||
|
// by adding ourselves in the middle. We locate everything that looks
|
||||||
|
// like an absolute path of ours and normalize it.
|
||||||
|
|
||||||
|
const Session = tsserver.server.Session;
|
||||||
|
const {onMessage: originalOnMessage, send: originalSend} = Session.prototype;
|
||||||
|
let hostInfo = `unknown`;
|
||||||
|
|
||||||
|
Object.assign(Session.prototype, {
|
||||||
|
onMessage(/** @type {string | object} */ message) {
|
||||||
|
const isStringMessage = typeof message === 'string';
|
||||||
|
const parsedMessage = isStringMessage ? JSON.parse(message) : message;
|
||||||
|
|
||||||
|
if (
|
||||||
|
parsedMessage != null &&
|
||||||
|
typeof parsedMessage === `object` &&
|
||||||
|
parsedMessage.arguments &&
|
||||||
|
typeof parsedMessage.arguments.hostInfo === `string`
|
||||||
|
) {
|
||||||
|
hostInfo = parsedMessage.arguments.hostInfo;
|
||||||
|
if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK) {
|
||||||
|
const [, major, minor] = (process.env.VSCODE_IPC_HOOK.match(
|
||||||
|
// The RegExp from https://semver.org/ but without the caret at the start
|
||||||
|
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
|
||||||
|
) ?? []).map(Number)
|
||||||
|
|
||||||
|
if (major === 1) {
|
||||||
|
if (minor < 61) {
|
||||||
|
hostInfo += ` <1.61`;
|
||||||
|
} else if (minor < 66) {
|
||||||
|
hostInfo += ` <1.66`;
|
||||||
|
} else if (minor < 68) {
|
||||||
|
hostInfo += ` <1.68`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => {
|
||||||
|
return typeof value === 'string' ? fromEditorPath(value) : value;
|
||||||
|
});
|
||||||
|
|
||||||
|
return originalOnMessage.call(
|
||||||
|
this,
|
||||||
|
isStringMessage ? processedMessageJSON : JSON.parse(processedMessageJSON)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
send(/** @type {any} */ msg) {
|
||||||
|
return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => {
|
||||||
|
return typeof value === `string` ? toEditorPath(value) : value;
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return tsserver;
|
||||||
|
};
|
||||||
|
|
||||||
|
const [major, minor] = absRequire(`typescript/package.json`).version.split(`.`, 2).map(value => parseInt(value, 10));
|
||||||
|
// In TypeScript@>=5.5 the tsserver uses the public TypeScript API so that needs to be patched as well.
|
||||||
|
// Ref https://github.com/microsoft/TypeScript/pull/55326
|
||||||
|
if (major > 5 || (major === 5 && minor >= 5)) {
|
||||||
|
moduleWrapper(absRequire(`typescript`));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer to the real typescript/lib/tsserver.js your application uses
|
||||||
|
module.exports = moduleWrapper(absRequire(`typescript/lib/tsserver.js`));
|
||||||
248
.yarn/sdks/typescript/lib/tsserverlibrary.js
vendored
Normal file
248
.yarn/sdks/typescript/lib/tsserverlibrary.js
vendored
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire, register} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
const {pathToFileURL} = require(`url`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||||
|
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require typescript/lib/tsserverlibrary.js
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
if (isPnpLoaderEnabled && register) {
|
||||||
|
register(pathToFileURL(absPnpLoaderPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
|
? exports => absRequire(absUserWrapperPath)(exports)
|
||||||
|
: exports => exports;
|
||||||
|
|
||||||
|
const moduleWrapper = exports => {
|
||||||
|
return wrapWithUserWrapper(moduleWrapperFn(exports));
|
||||||
|
};
|
||||||
|
|
||||||
|
const moduleWrapperFn = tsserver => {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
return tsserver;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {isAbsolute} = require(`path`);
|
||||||
|
const pnpApi = require(`pnpapi`);
|
||||||
|
|
||||||
|
const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//);
|
||||||
|
const isPortal = str => str.startsWith("portal:/");
|
||||||
|
const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
|
||||||
|
|
||||||
|
const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => {
|
||||||
|
return `${locator.name}@${locator.reference}`;
|
||||||
|
}));
|
||||||
|
|
||||||
|
// VSCode sends the zip paths to TS using the "zip://" prefix, that TS
|
||||||
|
// doesn't understand. This layer makes sure to remove the protocol
|
||||||
|
// before forwarding it to TS, and to add it back on all returned paths.
|
||||||
|
|
||||||
|
function toEditorPath(str) {
|
||||||
|
// We add the `zip:` prefix to both `.zip/` paths and virtual paths
|
||||||
|
if (isAbsolute(str) && !str.match(/^\^?(zip:|\/zip\/)/) && (str.match(/\.zip\//) || isVirtual(str))) {
|
||||||
|
// We also take the opportunity to turn virtual paths into physical ones;
|
||||||
|
// this makes it much easier to work with workspaces that list peer
|
||||||
|
// dependencies, since otherwise Ctrl+Click would bring us to the virtual
|
||||||
|
// file instances instead of the real ones.
|
||||||
|
//
|
||||||
|
// We only do this to modules owned by the the dependency tree roots.
|
||||||
|
// This avoids breaking the resolution when jumping inside a vendor
|
||||||
|
// with peer dep (otherwise jumping into react-dom would show resolution
|
||||||
|
// errors on react).
|
||||||
|
//
|
||||||
|
const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str;
|
||||||
|
if (resolved) {
|
||||||
|
const locator = pnpApi.findPackageLocator(resolved);
|
||||||
|
if (locator && (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference))) {
|
||||||
|
str = resolved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
str = normalize(str);
|
||||||
|
|
||||||
|
if (str.match(/\.zip\//)) {
|
||||||
|
switch (hostInfo) {
|
||||||
|
// Absolute VSCode `Uri.fsPath`s need to start with a slash.
|
||||||
|
// VSCode only adds it automatically for supported schemes,
|
||||||
|
// so we have to do it manually for the `zip` scheme.
|
||||||
|
// The path needs to start with a caret otherwise VSCode doesn't handle the protocol
|
||||||
|
//
|
||||||
|
// Ref: https://github.com/microsoft/vscode/issues/105014#issuecomment-686760910
|
||||||
|
//
|
||||||
|
// 2021-10-08: VSCode changed the format in 1.61.
|
||||||
|
// Before | ^zip:/c:/foo/bar.zip/package.json
|
||||||
|
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||||
|
//
|
||||||
|
// 2022-04-06: VSCode changed the format in 1.66.
|
||||||
|
// Before | ^/zip//c:/foo/bar.zip/package.json
|
||||||
|
// After | ^/zip/c:/foo/bar.zip/package.json
|
||||||
|
//
|
||||||
|
// 2022-05-06: VSCode changed the format in 1.68
|
||||||
|
// Before | ^/zip/c:/foo/bar.zip/package.json
|
||||||
|
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||||
|
//
|
||||||
|
case `vscode <1.61`: {
|
||||||
|
str = `^zip:${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `vscode <1.66`: {
|
||||||
|
str = `^/zip/${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `vscode <1.68`: {
|
||||||
|
str = `^/zip${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `vscode`: {
|
||||||
|
str = `^/zip/${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// To make "go to definition" work,
|
||||||
|
// We have to resolve the actual file system path from virtual path
|
||||||
|
// and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip)
|
||||||
|
case `coc-nvim`: {
|
||||||
|
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||||
|
str = resolve(`zipfile:${str}`);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server)
|
||||||
|
// We have to resolve the actual file system path from virtual path,
|
||||||
|
// everything else is up to neovim
|
||||||
|
case `neovim`: {
|
||||||
|
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||||
|
str = `zipfile://${str}`;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
str = `zip:${str}`;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
str = str.replace(/^\/?/, process.platform === `win32` ? `` : `/`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fromEditorPath(str) {
|
||||||
|
switch (hostInfo) {
|
||||||
|
case `coc-nvim`: {
|
||||||
|
str = str.replace(/\.zip::/, `.zip/`);
|
||||||
|
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
|
||||||
|
// So in order to convert it back, we use .* to match all the thing
|
||||||
|
// before `zipfile:`
|
||||||
|
return process.platform === `win32`
|
||||||
|
? str.replace(/^.*zipfile:\//, ``)
|
||||||
|
: str.replace(/^.*zipfile:/, ``);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `neovim`: {
|
||||||
|
str = str.replace(/\.zip::/, `.zip/`);
|
||||||
|
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
|
||||||
|
return str.replace(/^zipfile:\/\//, ``);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case `vscode`:
|
||||||
|
default: {
|
||||||
|
return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`)
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force enable 'allowLocalPluginLoads'
|
||||||
|
// TypeScript tries to resolve plugins using a path relative to itself
|
||||||
|
// which doesn't work when using the global cache
|
||||||
|
// https://github.com/microsoft/TypeScript/blob/1b57a0395e0bff191581c9606aab92832001de62/src/server/project.ts#L2238
|
||||||
|
// VSCode doesn't want to enable 'allowLocalPluginLoads' due to security concerns but
|
||||||
|
// TypeScript already does local loads and if this code is running the user trusts the workspace
|
||||||
|
// https://github.com/microsoft/vscode/issues/45856
|
||||||
|
const ConfiguredProject = tsserver.server.ConfiguredProject;
|
||||||
|
const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype;
|
||||||
|
ConfiguredProject.prototype.enablePluginsWithOptions = function() {
|
||||||
|
this.projectService.allowLocalPluginLoads = true;
|
||||||
|
return originalEnablePluginsWithOptions.apply(this, arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
// And here is the point where we hijack the VSCode <-> TS communications
|
||||||
|
// by adding ourselves in the middle. We locate everything that looks
|
||||||
|
// like an absolute path of ours and normalize it.
|
||||||
|
|
||||||
|
const Session = tsserver.server.Session;
|
||||||
|
const {onMessage: originalOnMessage, send: originalSend} = Session.prototype;
|
||||||
|
let hostInfo = `unknown`;
|
||||||
|
|
||||||
|
Object.assign(Session.prototype, {
|
||||||
|
onMessage(/** @type {string | object} */ message) {
|
||||||
|
const isStringMessage = typeof message === 'string';
|
||||||
|
const parsedMessage = isStringMessage ? JSON.parse(message) : message;
|
||||||
|
|
||||||
|
if (
|
||||||
|
parsedMessage != null &&
|
||||||
|
typeof parsedMessage === `object` &&
|
||||||
|
parsedMessage.arguments &&
|
||||||
|
typeof parsedMessage.arguments.hostInfo === `string`
|
||||||
|
) {
|
||||||
|
hostInfo = parsedMessage.arguments.hostInfo;
|
||||||
|
if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK) {
|
||||||
|
const [, major, minor] = (process.env.VSCODE_IPC_HOOK.match(
|
||||||
|
// The RegExp from https://semver.org/ but without the caret at the start
|
||||||
|
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
|
||||||
|
) ?? []).map(Number)
|
||||||
|
|
||||||
|
if (major === 1) {
|
||||||
|
if (minor < 61) {
|
||||||
|
hostInfo += ` <1.61`;
|
||||||
|
} else if (minor < 66) {
|
||||||
|
hostInfo += ` <1.66`;
|
||||||
|
} else if (minor < 68) {
|
||||||
|
hostInfo += ` <1.68`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => {
|
||||||
|
return typeof value === 'string' ? fromEditorPath(value) : value;
|
||||||
|
});
|
||||||
|
|
||||||
|
return originalOnMessage.call(
|
||||||
|
this,
|
||||||
|
isStringMessage ? processedMessageJSON : JSON.parse(processedMessageJSON)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
send(/** @type {any} */ msg) {
|
||||||
|
return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => {
|
||||||
|
return typeof value === `string` ? toEditorPath(value) : value;
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return tsserver;
|
||||||
|
};
|
||||||
|
|
||||||
|
const [major, minor] = absRequire(`typescript/package.json`).version.split(`.`, 2).map(value => parseInt(value, 10));
|
||||||
|
// In TypeScript@>=5.5 the tsserver uses the public TypeScript API so that needs to be patched as well.
|
||||||
|
// Ref https://github.com/microsoft/TypeScript/pull/55326
|
||||||
|
if (major > 5 || (major === 5 && minor >= 5)) {
|
||||||
|
moduleWrapper(absRequire(`typescript`));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer to the real typescript/lib/tsserverlibrary.js your application uses
|
||||||
|
module.exports = moduleWrapper(absRequire(`typescript/lib/tsserverlibrary.js`));
|
||||||
32
.yarn/sdks/typescript/lib/typescript.js
vendored
Normal file
32
.yarn/sdks/typescript/lib/typescript.js
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const {existsSync} = require(`fs`);
|
||||||
|
const {createRequire, register} = require(`module`);
|
||||||
|
const {resolve} = require(`path`);
|
||||||
|
const {pathToFileURL} = require(`url`);
|
||||||
|
|
||||||
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
|
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||||
|
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||||
|
const absRequire = createRequire(absPnpApiPath);
|
||||||
|
|
||||||
|
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||||
|
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||||
|
|
||||||
|
if (existsSync(absPnpApiPath)) {
|
||||||
|
if (!process.versions.pnp) {
|
||||||
|
// Setup the environment to be able to require typescript
|
||||||
|
require(absPnpApiPath).setup();
|
||||||
|
if (isPnpLoaderEnabled && register) {
|
||||||
|
register(pathToFileURL(absPnpLoaderPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
|
? exports => absRequire(absUserWrapperPath)(exports)
|
||||||
|
: exports => exports;
|
||||||
|
|
||||||
|
// Defer to the real typescript your application uses
|
||||||
|
module.exports = wrapWithUserWrapper(absRequire(`typescript`));
|
||||||
10
.yarn/sdks/typescript/package.json
vendored
Normal file
10
.yarn/sdks/typescript/package.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"name": "typescript",
|
||||||
|
"version": "5.9.3-sdk",
|
||||||
|
"main": "./lib/typescript.js",
|
||||||
|
"type": "commonjs",
|
||||||
|
"bin": {
|
||||||
|
"tsc": "./bin/tsc",
|
||||||
|
"tsserver": "./bin/tsserver"
|
||||||
|
}
|
||||||
|
}
|
||||||
98
README.md
Normal file
98
README.md
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<p align="center">
|
||||||
|
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
|
||||||
|
[circleci-url]: https://circleci.com/gh/nestjs/nest
|
||||||
|
|
||||||
|
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
|
||||||
|
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
|
||||||
|
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
|
||||||
|
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
|
||||||
|
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
|
||||||
|
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
|
||||||
|
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
|
||||||
|
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg" alt="Donate us"/></a>
|
||||||
|
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
|
||||||
|
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow" alt="Follow us on Twitter"></a>
|
||||||
|
</p>
|
||||||
|
<!--[](https://opencollective.com/nest#backer)
|
||||||
|
[](https://opencollective.com/nest#sponsor)-->
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
|
||||||
|
|
||||||
|
## Project setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ yarn install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Compile and run the project
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# development
|
||||||
|
$ yarn run start
|
||||||
|
|
||||||
|
# watch mode
|
||||||
|
$ yarn run start:dev
|
||||||
|
|
||||||
|
# production mode
|
||||||
|
$ yarn run start:prod
|
||||||
|
```
|
||||||
|
|
||||||
|
## Run tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# unit tests
|
||||||
|
$ yarn run test
|
||||||
|
|
||||||
|
# e2e tests
|
||||||
|
$ yarn run test:e2e
|
||||||
|
|
||||||
|
# test coverage
|
||||||
|
$ yarn run test:cov
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
When you're ready to deploy your NestJS application to production, there are some key steps you can take to ensure it runs as efficiently as possible. Check out the [deployment documentation](https://docs.nestjs.com/deployment) for more information.
|
||||||
|
|
||||||
|
If you are looking for a cloud-based platform to deploy your NestJS application, check out [Mau](https://mau.nestjs.com), our official platform for deploying NestJS applications on AWS. Mau makes deployment straightforward and fast, requiring just a few simple steps:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ yarn install -g @nestjs/mau
|
||||||
|
$ mau deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
With Mau, you can deploy your application in just a few clicks, allowing you to focus on building features rather than managing infrastructure.
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
Check out a few resources that may come in handy when working with NestJS:
|
||||||
|
|
||||||
|
- Visit the [NestJS Documentation](https://docs.nestjs.com) to learn more about the framework.
|
||||||
|
- For questions and support, please visit our [Discord channel](https://discord.gg/G7Qnnhy).
|
||||||
|
- To dive deeper and get more hands-on experience, check out our official video [courses](https://courses.nestjs.com/).
|
||||||
|
- Deploy your application to AWS with the help of [NestJS Mau](https://mau.nestjs.com) in just a few clicks.
|
||||||
|
- Visualize your application graph and interact with the NestJS application in real-time using [NestJS Devtools](https://devtools.nestjs.com).
|
||||||
|
- Need help with your project (part-time to full-time)? Check out our official [enterprise support](https://enterprise.nestjs.com).
|
||||||
|
- To stay in the loop and get updates, follow us on [X](https://x.com/nestframework) and [LinkedIn](https://linkedin.com/company/nestjs).
|
||||||
|
- Looking for a job, or have a job to offer? Check out our official [Jobs board](https://jobs.nestjs.com).
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
||||||
|
|
||||||
|
## Stay in touch
|
||||||
|
|
||||||
|
- Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec)
|
||||||
|
- Website - [https://nestjs.com](https://nestjs.com/)
|
||||||
|
- Twitter - [@nestframework](https://twitter.com/nestframework)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE).
|
||||||
13
drizzle.config.ts
Normal file
13
drizzle.config.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { defineConfig } from 'drizzle-kit';
|
||||||
|
import dotenv from 'dotenv';
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
dialect: "postgresql",
|
||||||
|
schema: "./src/db/schema",
|
||||||
|
out: "./drizzle",
|
||||||
|
dbCredentials: {
|
||||||
|
url: process.env.PG_DATABASE_URL!
|
||||||
|
}
|
||||||
|
});
|
||||||
79
drizzle/0000_parallel_rocket_racer.sql
Normal file
79
drizzle/0000_parallel_rocket_racer.sql
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
-- Current sql file was generated after introspecting the database
|
||||||
|
-- If you want to run this migration please uncomment this code before executing migrations
|
||||||
|
/*
|
||||||
|
CREATE TABLE "comment" (
|
||||||
|
"id" uuid PRIMARY KEY NOT NULL,
|
||||||
|
"content" text,
|
||||||
|
"created_at" date,
|
||||||
|
"is_deleted" boolean DEFAULT false,
|
||||||
|
"writer_id" uuid,
|
||||||
|
"parent_id" uuid
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE "schedule" (
|
||||||
|
"id" uuid PRIMARY KEY NOT NULL,
|
||||||
|
"name" varchar,
|
||||||
|
"start_at" date,
|
||||||
|
"end_at" date,
|
||||||
|
"status" varchar,
|
||||||
|
"content" text,
|
||||||
|
"is_deleted" boolean DEFAULT false,
|
||||||
|
"type" varchar,
|
||||||
|
"created_at" date,
|
||||||
|
"owner" uuid
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE "account" (
|
||||||
|
"name" varchar NOT NULL,
|
||||||
|
"email" varchar NOT NULL,
|
||||||
|
"password" varchar NOT NULL,
|
||||||
|
"birthday" date,
|
||||||
|
"account_id" varchar NOT NULL,
|
||||||
|
"nickname" varchar NOT NULL,
|
||||||
|
"status" varchar DEFAULT 'wait' NOT NULL,
|
||||||
|
"is_deleted" boolean DEFAULT false NOT NULL,
|
||||||
|
"created_at" date DEFAULT now() NOT NULL,
|
||||||
|
"id" uuid PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE "participant" (
|
||||||
|
"participant_id" uuid NOT NULL,
|
||||||
|
"schedule_id" uuid NOT NULL,
|
||||||
|
"is_deleted" boolean DEFAULT false
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE "favorite" (
|
||||||
|
"is_deleted" boolean DEFAULT false,
|
||||||
|
"created_at" date,
|
||||||
|
"user_id" uuid NOT NULL,
|
||||||
|
"schedule_id" uuid NOT NULL,
|
||||||
|
CONSTRAINT "favorite_pk" PRIMARY KEY("user_id","schedule_id")
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE "follow" (
|
||||||
|
"is_deleted" boolean DEFAULT false,
|
||||||
|
"is_accepted" boolean DEFAULT false,
|
||||||
|
"is_linked" boolean DEFAULT false,
|
||||||
|
"created_at" date,
|
||||||
|
"following" uuid NOT NULL,
|
||||||
|
"follower" uuid NOT NULL,
|
||||||
|
CONSTRAINT "follow_pk" PRIMARY KEY("following","follower")
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
ALTER TABLE "comment" ADD CONSTRAINT "writer_id" FOREIGN KEY ("writer_id") REFERENCES "public"."account"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE "comment" ADD CONSTRAINT "parent_id" FOREIGN KEY ("parent_id") REFERENCES "public"."comment"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE "schedule" ADD CONSTRAINT "schedule_user_fk" FOREIGN KEY ("owner") REFERENCES "public"."account"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE "participant" ADD CONSTRAINT "schedule_id" FOREIGN KEY ("schedule_id") REFERENCES "public"."schedule"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE "participant" ADD CONSTRAINT "participant_id" FOREIGN KEY ("participant_id") REFERENCES "public"."account"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE "favorite" ADD CONSTRAINT "schedule_id" FOREIGN KEY ("schedule_id") REFERENCES "public"."schedule"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE "favorite" ADD CONSTRAINT "user_id" FOREIGN KEY ("user_id") REFERENCES "public"."account"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE "follow" ADD CONSTRAINT "follower_id" FOREIGN KEY ("follower") REFERENCES "public"."account"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE "follow" ADD CONSTRAINT "following_id" FOREIGN KEY ("following") REFERENCES "public"."account"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
CREATE INDEX "schedule_enddatetime_idx" ON "schedule" USING btree ("end_at" date_ops);--> statement-breakpoint
|
||||||
|
CREATE INDEX "schedule_name_idx" ON "schedule" USING btree ("name" text_ops,"content" text_ops);--> statement-breakpoint
|
||||||
|
CREATE INDEX "schedule_startdatetime_idx" ON "schedule" USING btree ("start_at" date_ops);--> statement-breakpoint
|
||||||
|
CREATE INDEX "schedule_status_idx" ON "schedule" USING btree ("status" text_ops);--> statement-breakpoint
|
||||||
|
CREATE INDEX "schedule_type_idx" ON "schedule" USING btree ("type" text_ops);--> statement-breakpoint
|
||||||
|
CREATE INDEX "participant_participant_id_idx" ON "participant" USING btree ("participant_id" uuid_ops);--> statement-breakpoint
|
||||||
|
CREATE INDEX "participant_schedule_id_idx" ON "participant" USING btree ("schedule_id" uuid_ops);
|
||||||
|
*/
|
||||||
613
drizzle/meta/0000_snapshot.json
Normal file
613
drizzle/meta/0000_snapshot.json
Normal file
@@ -0,0 +1,613 @@
|
|||||||
|
{
|
||||||
|
"id": "00000000-0000-0000-0000-000000000000",
|
||||||
|
"prevId": "",
|
||||||
|
"version": "7",
|
||||||
|
"dialect": "postgresql",
|
||||||
|
"tables": {
|
||||||
|
"public.comment": {
|
||||||
|
"name": "comment",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"name": "content",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "date",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"is_deleted": {
|
||||||
|
"name": "is_deleted",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"writer_id": {
|
||||||
|
"name": "writer_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"parent_id": {
|
||||||
|
"name": "parent_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"writer_id": {
|
||||||
|
"name": "writer_id",
|
||||||
|
"tableFrom": "comment",
|
||||||
|
"tableTo": "account",
|
||||||
|
"schemaTo": "public",
|
||||||
|
"columnsFrom": [
|
||||||
|
"writer_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"parent_id": {
|
||||||
|
"name": "parent_id",
|
||||||
|
"tableFrom": "comment",
|
||||||
|
"tableTo": "comment",
|
||||||
|
"schemaTo": "public",
|
||||||
|
"columnsFrom": [
|
||||||
|
"parent_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.schedule": {
|
||||||
|
"name": "schedule",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "varchar",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"start_at": {
|
||||||
|
"name": "start_at",
|
||||||
|
"type": "date",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"end_at": {
|
||||||
|
"name": "end_at",
|
||||||
|
"type": "date",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "varchar",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"name": "content",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"is_deleted": {
|
||||||
|
"name": "is_deleted",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"name": "type",
|
||||||
|
"type": "varchar",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "date",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"owner": {
|
||||||
|
"name": "owner",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"schedule_enddatetime_idx": {
|
||||||
|
"name": "schedule_enddatetime_idx",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"expression": "end_at",
|
||||||
|
"asc": true,
|
||||||
|
"nulls": "last",
|
||||||
|
"opclass": "date_ops",
|
||||||
|
"isExpression": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isUnique": false,
|
||||||
|
"concurrently": false,
|
||||||
|
"method": "btree",
|
||||||
|
"with": {}
|
||||||
|
},
|
||||||
|
"schedule_name_idx": {
|
||||||
|
"name": "schedule_name_idx",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"expression": "name",
|
||||||
|
"asc": true,
|
||||||
|
"nulls": "last",
|
||||||
|
"opclass": "text_ops",
|
||||||
|
"isExpression": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "content",
|
||||||
|
"asc": true,
|
||||||
|
"nulls": "last",
|
||||||
|
"opclass": "text_ops",
|
||||||
|
"isExpression": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isUnique": false,
|
||||||
|
"concurrently": false,
|
||||||
|
"method": "btree",
|
||||||
|
"with": {}
|
||||||
|
},
|
||||||
|
"schedule_startdatetime_idx": {
|
||||||
|
"name": "schedule_startdatetime_idx",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"expression": "start_at",
|
||||||
|
"asc": true,
|
||||||
|
"nulls": "last",
|
||||||
|
"opclass": "date_ops",
|
||||||
|
"isExpression": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isUnique": false,
|
||||||
|
"concurrently": false,
|
||||||
|
"method": "btree",
|
||||||
|
"with": {}
|
||||||
|
},
|
||||||
|
"schedule_status_idx": {
|
||||||
|
"name": "schedule_status_idx",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"expression": "status",
|
||||||
|
"asc": true,
|
||||||
|
"nulls": "last",
|
||||||
|
"opclass": "text_ops",
|
||||||
|
"isExpression": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isUnique": false,
|
||||||
|
"concurrently": false,
|
||||||
|
"method": "btree",
|
||||||
|
"with": {}
|
||||||
|
},
|
||||||
|
"schedule_type_idx": {
|
||||||
|
"name": "schedule_type_idx",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"expression": "type",
|
||||||
|
"asc": true,
|
||||||
|
"nulls": "last",
|
||||||
|
"opclass": "text_ops",
|
||||||
|
"isExpression": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isUnique": false,
|
||||||
|
"concurrently": false,
|
||||||
|
"method": "btree",
|
||||||
|
"with": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {
|
||||||
|
"schedule_user_fk": {
|
||||||
|
"name": "schedule_user_fk",
|
||||||
|
"tableFrom": "schedule",
|
||||||
|
"tableTo": "account",
|
||||||
|
"schemaTo": "public",
|
||||||
|
"columnsFrom": [
|
||||||
|
"owner"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.account": {
|
||||||
|
"name": "account",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "varchar",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"name": "email",
|
||||||
|
"type": "varchar",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"name": "password",
|
||||||
|
"type": "varchar",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"birthday": {
|
||||||
|
"name": "birthday",
|
||||||
|
"type": "date",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"account_id": {
|
||||||
|
"name": "account_id",
|
||||||
|
"type": "varchar",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"nickname": {
|
||||||
|
"name": "nickname",
|
||||||
|
"type": "varchar",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "varchar",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'wait'"
|
||||||
|
},
|
||||||
|
"is_deleted": {
|
||||||
|
"name": "is_deleted",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "date",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "uuid_generate_v4()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.participant": {
|
||||||
|
"name": "participant",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"participant_id": {
|
||||||
|
"name": "participant_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"schedule_id": {
|
||||||
|
"name": "schedule_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"is_deleted": {
|
||||||
|
"name": "is_deleted",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"participant_participant_id_idx": {
|
||||||
|
"name": "participant_participant_id_idx",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"expression": "participant_id",
|
||||||
|
"asc": true,
|
||||||
|
"nulls": "last",
|
||||||
|
"opclass": "uuid_ops",
|
||||||
|
"isExpression": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isUnique": false,
|
||||||
|
"concurrently": false,
|
||||||
|
"method": "btree",
|
||||||
|
"with": {}
|
||||||
|
},
|
||||||
|
"participant_schedule_id_idx": {
|
||||||
|
"name": "participant_schedule_id_idx",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"expression": "schedule_id",
|
||||||
|
"asc": true,
|
||||||
|
"nulls": "last",
|
||||||
|
"opclass": "uuid_ops",
|
||||||
|
"isExpression": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isUnique": false,
|
||||||
|
"concurrently": false,
|
||||||
|
"method": "btree",
|
||||||
|
"with": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {
|
||||||
|
"schedule_id": {
|
||||||
|
"name": "schedule_id",
|
||||||
|
"tableFrom": "participant",
|
||||||
|
"tableTo": "schedule",
|
||||||
|
"schemaTo": "public",
|
||||||
|
"columnsFrom": [
|
||||||
|
"schedule_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"participant_id": {
|
||||||
|
"name": "participant_id",
|
||||||
|
"tableFrom": "participant",
|
||||||
|
"tableTo": "account",
|
||||||
|
"schemaTo": "public",
|
||||||
|
"columnsFrom": [
|
||||||
|
"participant_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.favorite": {
|
||||||
|
"name": "favorite",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"is_deleted": {
|
||||||
|
"name": "is_deleted",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "date",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"schedule_id": {
|
||||||
|
"name": "schedule_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"schedule_id": {
|
||||||
|
"name": "schedule_id",
|
||||||
|
"tableFrom": "favorite",
|
||||||
|
"tableTo": "schedule",
|
||||||
|
"schemaTo": "public",
|
||||||
|
"columnsFrom": [
|
||||||
|
"schedule_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"tableFrom": "favorite",
|
||||||
|
"tableTo": "account",
|
||||||
|
"schemaTo": "public",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"favorite_pk": {
|
||||||
|
"name": "favorite_pk",
|
||||||
|
"columns": [
|
||||||
|
"user_id",
|
||||||
|
"schedule_id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.follow": {
|
||||||
|
"name": "follow",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"is_deleted": {
|
||||||
|
"name": "is_deleted",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"is_accepted": {
|
||||||
|
"name": "is_accepted",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"is_linked": {
|
||||||
|
"name": "is_linked",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "date",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"following": {
|
||||||
|
"name": "following",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"follower": {
|
||||||
|
"name": "follower",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"follower_id": {
|
||||||
|
"name": "follower_id",
|
||||||
|
"tableFrom": "follow",
|
||||||
|
"tableTo": "account",
|
||||||
|
"schemaTo": "public",
|
||||||
|
"columnsFrom": [
|
||||||
|
"follower"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"following_id": {
|
||||||
|
"name": "following_id",
|
||||||
|
"tableFrom": "follow",
|
||||||
|
"tableTo": "account",
|
||||||
|
"schemaTo": "public",
|
||||||
|
"columnsFrom": [
|
||||||
|
"following"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"follow_pk": {
|
||||||
|
"name": "follow_pk",
|
||||||
|
"columns": [
|
||||||
|
"following",
|
||||||
|
"follower"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"enums": {},
|
||||||
|
"schemas": {},
|
||||||
|
"sequences": {},
|
||||||
|
"roles": {},
|
||||||
|
"policies": {},
|
||||||
|
"views": {},
|
||||||
|
"_meta": {
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {},
|
||||||
|
"columns": {}
|
||||||
|
},
|
||||||
|
"internal": {
|
||||||
|
"tables": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
drizzle/meta/_journal.json
Normal file
13
drizzle/meta/_journal.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"version": "7",
|
||||||
|
"dialect": "postgresql",
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"idx": 0,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1763702111620,
|
||||||
|
"tag": "0000_parallel_rocket_racer",
|
||||||
|
"breakpoints": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
74
drizzle/relations.ts
Normal file
74
drizzle/relations.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import { relations } from "drizzle-orm/relations";
|
||||||
|
import { account, comment, schedule, participant, favorite, follow } from "./schema";
|
||||||
|
|
||||||
|
export const commentRelations = relations(comment, ({one, many}) => ({
|
||||||
|
account: one(account, {
|
||||||
|
fields: [comment.writerId],
|
||||||
|
references: [account.id]
|
||||||
|
}),
|
||||||
|
comment: one(comment, {
|
||||||
|
fields: [comment.parentId],
|
||||||
|
references: [comment.id],
|
||||||
|
relationName: "comment_parentId_comment_id"
|
||||||
|
}),
|
||||||
|
comments: many(comment, {
|
||||||
|
relationName: "comment_parentId_comment_id"
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const accountRelations = relations(account, ({many}) => ({
|
||||||
|
comments: many(comment),
|
||||||
|
schedules: many(schedule),
|
||||||
|
participants: many(participant),
|
||||||
|
favorites: many(favorite),
|
||||||
|
follows_follower: many(follow, {
|
||||||
|
relationName: "follow_follower_account_id"
|
||||||
|
}),
|
||||||
|
follows_following: many(follow, {
|
||||||
|
relationName: "follow_following_account_id"
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const scheduleRelations = relations(schedule, ({one, many}) => ({
|
||||||
|
account: one(account, {
|
||||||
|
fields: [schedule.owner],
|
||||||
|
references: [account.id]
|
||||||
|
}),
|
||||||
|
participants: many(participant),
|
||||||
|
favorites: many(favorite),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const participantRelations = relations(participant, ({one}) => ({
|
||||||
|
schedule: one(schedule, {
|
||||||
|
fields: [participant.scheduleId],
|
||||||
|
references: [schedule.id]
|
||||||
|
}),
|
||||||
|
account: one(account, {
|
||||||
|
fields: [participant.participantId],
|
||||||
|
references: [account.id]
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const favoriteRelations = relations(favorite, ({one}) => ({
|
||||||
|
schedule: one(schedule, {
|
||||||
|
fields: [favorite.scheduleId],
|
||||||
|
references: [schedule.id]
|
||||||
|
}),
|
||||||
|
account: one(account, {
|
||||||
|
fields: [favorite.userId],
|
||||||
|
references: [account.id]
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const followRelations = relations(follow, ({one}) => ({
|
||||||
|
account_follower: one(account, {
|
||||||
|
fields: [follow.follower],
|
||||||
|
references: [account.id],
|
||||||
|
relationName: "follow_follower_account_id"
|
||||||
|
}),
|
||||||
|
account_following: one(account, {
|
||||||
|
fields: [follow.following],
|
||||||
|
references: [account.id],
|
||||||
|
relationName: "follow_following_account_id"
|
||||||
|
}),
|
||||||
|
}));
|
||||||
120
drizzle/schema.ts
Normal file
120
drizzle/schema.ts
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
import { pgTable, foreignKey, uuid, text, date, boolean, index, varchar, primaryKey } from "drizzle-orm/pg-core"
|
||||||
|
import { sql } from "drizzle-orm"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const comment = pgTable("comment", {
|
||||||
|
id: uuid().primaryKey().notNull(),
|
||||||
|
content: text(),
|
||||||
|
createdAt: date("created_at"),
|
||||||
|
isDeleted: boolean("is_deleted").default(false),
|
||||||
|
writerId: uuid("writer_id"),
|
||||||
|
parentId: uuid("parent_id"),
|
||||||
|
}, (table) => [
|
||||||
|
foreignKey({
|
||||||
|
columns: [table.writerId],
|
||||||
|
foreignColumns: [account.id],
|
||||||
|
name: "writer_id"
|
||||||
|
}),
|
||||||
|
foreignKey({
|
||||||
|
columns: [table.parentId],
|
||||||
|
foreignColumns: [table.id],
|
||||||
|
name: "parent_id"
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const schedule = pgTable("schedule", {
|
||||||
|
id: uuid().primaryKey().notNull(),
|
||||||
|
name: varchar(),
|
||||||
|
startAt: date("start_at"),
|
||||||
|
endAt: date("end_at"),
|
||||||
|
status: varchar(),
|
||||||
|
content: text(),
|
||||||
|
isDeleted: boolean("is_deleted").default(false),
|
||||||
|
type: varchar(),
|
||||||
|
createdAt: date("created_at"),
|
||||||
|
owner: uuid(),
|
||||||
|
}, (table) => [
|
||||||
|
index("schedule_enddatetime_idx").using("btree", table.endAt.asc().nullsLast().op("date_ops")),
|
||||||
|
index("schedule_name_idx").using("btree", table.name.asc().nullsLast().op("text_ops"), table.content.asc().nullsLast().op("text_ops")),
|
||||||
|
index("schedule_startdatetime_idx").using("btree", table.startAt.asc().nullsLast().op("date_ops")),
|
||||||
|
index("schedule_status_idx").using("btree", table.status.asc().nullsLast().op("text_ops")),
|
||||||
|
index("schedule_type_idx").using("btree", table.type.asc().nullsLast().op("text_ops")),
|
||||||
|
foreignKey({
|
||||||
|
columns: [table.owner],
|
||||||
|
foreignColumns: [account.id],
|
||||||
|
name: "schedule_user_fk"
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const account = pgTable("account", {
|
||||||
|
name: varchar().notNull(),
|
||||||
|
email: varchar().notNull(),
|
||||||
|
password: varchar().notNull(),
|
||||||
|
birthday: date(),
|
||||||
|
accountId: varchar("account_id").notNull(),
|
||||||
|
nickname: varchar().notNull(),
|
||||||
|
status: varchar().default('wait').notNull(),
|
||||||
|
isDeleted: boolean("is_deleted").default(false).notNull(),
|
||||||
|
createdAt: date("created_at").defaultNow().notNull(),
|
||||||
|
id: uuid().default(sql`uuid_generate_v4()`).primaryKey().notNull(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const participant = pgTable("participant", {
|
||||||
|
participantId: uuid("participant_id").notNull(),
|
||||||
|
scheduleId: uuid("schedule_id").notNull(),
|
||||||
|
isDeleted: boolean("is_deleted").default(false),
|
||||||
|
}, (table) => [
|
||||||
|
index("participant_participant_id_idx").using("btree", table.participantId.asc().nullsLast().op("uuid_ops")),
|
||||||
|
index("participant_schedule_id_idx").using("btree", table.scheduleId.asc().nullsLast().op("uuid_ops")),
|
||||||
|
foreignKey({
|
||||||
|
columns: [table.scheduleId],
|
||||||
|
foreignColumns: [schedule.id],
|
||||||
|
name: "schedule_id"
|
||||||
|
}),
|
||||||
|
foreignKey({
|
||||||
|
columns: [table.participantId],
|
||||||
|
foreignColumns: [account.id],
|
||||||
|
name: "participant_id"
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const favorite = pgTable("favorite", {
|
||||||
|
isDeleted: boolean("is_deleted").default(false),
|
||||||
|
createdAt: date("created_at"),
|
||||||
|
userId: uuid("user_id").notNull(),
|
||||||
|
scheduleId: uuid("schedule_id").notNull(),
|
||||||
|
}, (table) => [
|
||||||
|
foreignKey({
|
||||||
|
columns: [table.scheduleId],
|
||||||
|
foreignColumns: [schedule.id],
|
||||||
|
name: "schedule_id"
|
||||||
|
}),
|
||||||
|
foreignKey({
|
||||||
|
columns: [table.userId],
|
||||||
|
foreignColumns: [account.id],
|
||||||
|
name: "user_id"
|
||||||
|
}),
|
||||||
|
primaryKey({ columns: [table.userId, table.scheduleId], name: "favorite_pk"}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const follow = pgTable("follow", {
|
||||||
|
isDeleted: boolean("is_deleted").default(false),
|
||||||
|
isAccepted: boolean("is_accepted").default(false),
|
||||||
|
isLinked: boolean("is_linked").default(false),
|
||||||
|
createdAt: date("created_at"),
|
||||||
|
following: uuid().notNull(),
|
||||||
|
follower: uuid().notNull(),
|
||||||
|
}, (table) => [
|
||||||
|
foreignKey({
|
||||||
|
columns: [table.follower],
|
||||||
|
foreignColumns: [account.id],
|
||||||
|
name: "follower_id"
|
||||||
|
}),
|
||||||
|
foreignKey({
|
||||||
|
columns: [table.following],
|
||||||
|
foreignColumns: [account.id],
|
||||||
|
name: "following_id"
|
||||||
|
}),
|
||||||
|
primaryKey({ columns: [table.following, table.follower], name: "follow_pk"}),
|
||||||
|
]);
|
||||||
35
eslint.config.mjs
Normal file
35
eslint.config.mjs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// @ts-check
|
||||||
|
import eslint from '@eslint/js';
|
||||||
|
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
|
||||||
|
import globals from 'globals';
|
||||||
|
import tseslint from 'typescript-eslint';
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
{
|
||||||
|
ignores: ['eslint.config.mjs'],
|
||||||
|
},
|
||||||
|
eslint.configs.recommended,
|
||||||
|
...tseslint.configs.recommendedTypeChecked,
|
||||||
|
eslintPluginPrettierRecommended,
|
||||||
|
{
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
...globals.node,
|
||||||
|
...globals.jest,
|
||||||
|
},
|
||||||
|
sourceType: 'commonjs',
|
||||||
|
parserOptions: {
|
||||||
|
projectService: true,
|
||||||
|
tsconfigRootDir: import.meta.dirname,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
'@typescript-eslint/no-floating-promises': 'warn',
|
||||||
|
'@typescript-eslint/no-unsafe-argument': 'warn',
|
||||||
|
"prettier/prettier": ["error", { endOfLine: "auto" }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
8
nest-cli.json
Normal file
8
nest-cli.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/nest-cli",
|
||||||
|
"collection": "@nestjs/schematics",
|
||||||
|
"sourceRoot": "src",
|
||||||
|
"compilerOptions": {
|
||||||
|
"deleteOutDir": true
|
||||||
|
}
|
||||||
|
}
|
||||||
92
package.json
92
package.json
@@ -1,32 +1,80 @@
|
|||||||
{
|
{
|
||||||
"name": "fastify",
|
"name": "back",
|
||||||
"version": "1.0.0",
|
"version": "0.0.1",
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
|
||||||
"dev": "ts-node src/server.ts",
|
|
||||||
"build": "tsc",
|
|
||||||
"start": "node dist/server.js"
|
|
||||||
},
|
|
||||||
"keywords": [],
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"description": "",
|
"description": "",
|
||||||
|
"author": "",
|
||||||
|
"private": true,
|
||||||
|
"license": "UNLICENSED",
|
||||||
|
"scripts": {
|
||||||
|
"build": "nest build",
|
||||||
|
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||||
|
"start": "nest start",
|
||||||
|
"start:dev": "nest start --watch",
|
||||||
|
"start:debug": "nest start --debug --watch",
|
||||||
|
"start:prod": "node dist/main",
|
||||||
|
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||||
|
"test": "jest",
|
||||||
|
"test:watch": "jest --watch",
|
||||||
|
"test:cov": "jest --coverage",
|
||||||
|
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||||
|
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fastify/swagger": "^9.6.1",
|
"@nestjs/class-transformer": "^0.4.0",
|
||||||
"@fastify/swagger-ui": "^5.2.3",
|
"@nestjs/class-validator": "^0.13.4",
|
||||||
|
"@nestjs/common": "^11.0.1",
|
||||||
|
"@nestjs/core": "^11.0.1",
|
||||||
|
"@nestjs/platform-express": "^11.0.1",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"fastify": "^5.6.2",
|
"drizzle-kit": "^0.31.7",
|
||||||
"fastify-postgres": "^3.6.0",
|
"drizzle-orm": "^0.44.7",
|
||||||
"nodemailer": "^7.0.10",
|
"ioredis": "^5.8.2",
|
||||||
"pg": "^8.16.3"
|
"pg": "^8.16.3",
|
||||||
|
"reflect-metadata": "^0.2.2",
|
||||||
|
"rxjs": "^7.8.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/dotenv": "^6.1.1",
|
"@eslint/eslintrc": "^3.2.0",
|
||||||
"@types/node": "^24.10.1",
|
"@eslint/js": "^9.18.0",
|
||||||
"@types/nodemailer": "^7.0.3",
|
"@nestjs/cli": "^11.0.0",
|
||||||
|
"@nestjs/schematics": "^11.0.0",
|
||||||
|
"@nestjs/testing": "^11.0.1",
|
||||||
|
"@types/express": "^5.0.0",
|
||||||
|
"@types/ioredis": "^5.0.0",
|
||||||
|
"@types/jest": "^30.0.0",
|
||||||
|
"@types/node": "^22.10.7",
|
||||||
"@types/pg": "^8.15.6",
|
"@types/pg": "^8.15.6",
|
||||||
|
"@types/supertest": "^6.0.2",
|
||||||
|
"eslint": "^9.18.0",
|
||||||
|
"eslint-config-prettier": "^10.0.1",
|
||||||
|
"eslint-plugin-prettier": "^5.2.2",
|
||||||
|
"globals": "^16.0.0",
|
||||||
|
"jest": "^30.0.0",
|
||||||
|
"prettier": "^3.4.2",
|
||||||
|
"source-map-support": "^0.5.21",
|
||||||
|
"supertest": "^7.0.0",
|
||||||
|
"ts-jest": "^29.2.5",
|
||||||
|
"ts-loader": "^9.5.2",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.9.3"
|
"tsconfig-paths": "^4.2.0",
|
||||||
|
"typescript": "^5.7.3",
|
||||||
|
"typescript-eslint": "^8.20.0"
|
||||||
|
},
|
||||||
|
"jest": {
|
||||||
|
"moduleFileExtensions": [
|
||||||
|
"js",
|
||||||
|
"json",
|
||||||
|
"ts"
|
||||||
|
],
|
||||||
|
"rootDir": "src",
|
||||||
|
"testRegex": ".*\\.spec\\.ts$",
|
||||||
|
"transform": {
|
||||||
|
"^.+\\.(t|j)s$": "ts-jest"
|
||||||
|
},
|
||||||
|
"collectCoverageFrom": [
|
||||||
|
"**/*.(t|j)s"
|
||||||
|
],
|
||||||
|
"coverageDirectory": "../coverage",
|
||||||
|
"testEnvironment": "node"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
22
src/app.controller.spec.ts
Normal file
22
src/app.controller.spec.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { AppController } from './app.controller';
|
||||||
|
import { AppService } from './app.service';
|
||||||
|
|
||||||
|
describe('AppController', () => {
|
||||||
|
let appController: AppController;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const app: TestingModule = await Test.createTestingModule({
|
||||||
|
controllers: [AppController],
|
||||||
|
providers: [AppService],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
appController = app.get<AppController>(AppController);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('root', () => {
|
||||||
|
it('should return "Hello World!"', () => {
|
||||||
|
expect(appController.getHello()).toBe('Hello World!');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
12
src/app.controller.ts
Normal file
12
src/app.controller.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { Controller, Get } from '@nestjs/common';
|
||||||
|
import { AppService } from './app.service';
|
||||||
|
|
||||||
|
@Controller()
|
||||||
|
export class AppController {
|
||||||
|
constructor(private readonly appService: AppService) {}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
getHello(): string {
|
||||||
|
return this.appService.getHello();
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/app.module.ts
Normal file
12
src/app.module.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { AppController } from './app.controller';
|
||||||
|
import { AppService } from './app.service';
|
||||||
|
import { DbModule } from './db/db.module';
|
||||||
|
import { RedisModule } from './redis/redis.module';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [DbModule, RedisModule],
|
||||||
|
controllers: [AppController],
|
||||||
|
providers: [AppService],
|
||||||
|
})
|
||||||
|
export class AppModule {}
|
||||||
8
src/app.service.ts
Normal file
8
src/app.service.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AppService {
|
||||||
|
getHello(): string {
|
||||||
|
return 'Hello World!';
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/app.ts
17
src/app.ts
@@ -1,17 +0,0 @@
|
|||||||
import Fastify from 'fastify';
|
|
||||||
import fastifyPostgres from 'fastify-postgres';
|
|
||||||
import { registerSwagger } from './utils/plugin/swagger';
|
|
||||||
import dotenv from 'dotenv';
|
|
||||||
|
|
||||||
dotenv.config();
|
|
||||||
|
|
||||||
export async function buildApp() {
|
|
||||||
|
|
||||||
const app = Fastify();
|
|
||||||
await app.register(fastifyPostgres, {
|
|
||||||
connectionString: `postgresql://${process.env.PGUSER}:${process.env.PGPASSWORD}@${process.env.PGHOST}:${process.env.PGPORT}/${process.env.PGDATABASE}`
|
|
||||||
})
|
|
||||||
await registerSwagger(app);
|
|
||||||
|
|
||||||
return app;
|
|
||||||
}
|
|
||||||
22
src/db/db.module.ts
Normal file
22
src/db/db.module.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { Global, Module } from "@nestjs/common";
|
||||||
|
import { Pool } from "pg";
|
||||||
|
import { drizzle, NodePgDatabase } from "drizzle-orm/node-postgres";
|
||||||
|
import * as schema from '../../drizzle/schema';
|
||||||
|
|
||||||
|
@Global()
|
||||||
|
@Module({
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: "DRIZZLE",
|
||||||
|
useFactory: (): NodePgDatabase<typeof schema> => {
|
||||||
|
const pool = new Pool({
|
||||||
|
connectionString: process.env.PG_DATABASE_URL
|
||||||
|
});
|
||||||
|
|
||||||
|
return drizzle(pool, { schema: schema });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
exports: ["DRIZZLE"]
|
||||||
|
})
|
||||||
|
export class DbModule {}
|
||||||
8
src/main.ts
Normal file
8
src/main.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { NestFactory } from '@nestjs/core';
|
||||||
|
import { AppModule } from './app.module';
|
||||||
|
|
||||||
|
async function bootstrap() {
|
||||||
|
const app = await NestFactory.create(AppModule);
|
||||||
|
await app.listen(process.env.PORT ?? 3000);
|
||||||
|
}
|
||||||
|
bootstrap();
|
||||||
14
src/modules/account/account.controller.ts
Normal file
14
src/modules/account/account.controller.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { Controller, Get, Post, Query } from "@nestjs/common";
|
||||||
|
import { CheckDuplicationRequestDto } from "./dto/checkDuplication/check-duplication-request.dto";
|
||||||
|
import { CheckDuplicationResponseDto } from "./dto/checkDuplication/check-duplication-response.dto";
|
||||||
|
import { AccountService } from "./account.service";
|
||||||
|
|
||||||
|
@Controller('account')
|
||||||
|
export class AccountController {
|
||||||
|
constructor(private readonly accountService: AccountService) {}
|
||||||
|
|
||||||
|
@Get('check-duplication')
|
||||||
|
async checkDuplication(@Query() query: CheckDuplicationRequestDto): Promise<CheckDuplicationResponseDto> {
|
||||||
|
return this.accountService.checkDuplication(query);
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/modules/account/account.module.ts
Normal file
10
src/modules/account/account.module.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { Module } from "@nestjs/common";
|
||||||
|
import { AccountController } from "./account.controller";
|
||||||
|
import { AccountRepo } from "./account.repo";
|
||||||
|
import { AccountService } from "./account.service";
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [AccountController],
|
||||||
|
providers: [AccountService, AccountRepo]
|
||||||
|
})
|
||||||
|
export class AccountModule {}
|
||||||
21
src/modules/account/account.repo.ts
Normal file
21
src/modules/account/account.repo.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { Inject, Injectable } from "@nestjs/common";
|
||||||
|
import * as schema from "drizzle/schema";
|
||||||
|
import { countDistinct, and, eq } from 'drizzle-orm';
|
||||||
|
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AccountRepo {
|
||||||
|
constructor(@Inject('DRIZZLE') private readonly db: NodePgDatabase<typeof schema>) {}
|
||||||
|
|
||||||
|
async checkDuplication(type: 'email' | 'accountId', value: string) {
|
||||||
|
const result = await this
|
||||||
|
.db
|
||||||
|
.select({ count: countDistinct(schema.account[type])})
|
||||||
|
.from(schema.account)
|
||||||
|
.where(
|
||||||
|
eq(schema.account[type], value)
|
||||||
|
);
|
||||||
|
|
||||||
|
return result[0].count;
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/modules/account/account.service.ts
Normal file
15
src/modules/account/account.service.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { AccountRepo } from "./account.repo";
|
||||||
|
import { CheckDuplicationRequestDto } from "./dto/checkDuplication/check-duplication-request.dto";
|
||||||
|
import { CheckDuplicationResponseDto } from "./dto/checkDuplication/check-duplication-response.dto";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AccountService {
|
||||||
|
constructor(private readonly accountRepo: AccountRepo) {}
|
||||||
|
|
||||||
|
async checkDuplication(data: CheckDuplicationRequestDto): Promise<CheckDuplicationResponseDto> {
|
||||||
|
const count = await this.accountRepo.checkDuplication(data.type, data.value);
|
||||||
|
|
||||||
|
return { isDuplicated: count > 0 };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { IsString, IsIn } from '@nestjs/class-validator'
|
||||||
|
|
||||||
|
export class CheckDuplicationRequestDto {
|
||||||
|
@IsIn(['email', 'accountId'])
|
||||||
|
type: 'email' | 'accountId';
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export class CheckDuplicationResponseDto {
|
||||||
|
isDuplicated: boolean;
|
||||||
|
}
|
||||||
19
src/redis/redis.module.ts
Normal file
19
src/redis/redis.module.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { Global, Module } from "@nestjs/common";
|
||||||
|
import Redis from "ioredis";
|
||||||
|
|
||||||
|
@Global()
|
||||||
|
@Module({
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: "REDIS",
|
||||||
|
useFactory: () => {
|
||||||
|
return new Redis({
|
||||||
|
host: process.env.RD_HOST!,
|
||||||
|
port: Number(process.env.RD_PORT || 6779)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
exports: ["REDIS"]
|
||||||
|
})
|
||||||
|
export class RedisModule{}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { buildApp } from './app';
|
|
||||||
|
|
||||||
const start = async () => {
|
|
||||||
try {
|
|
||||||
const app = await buildApp();
|
|
||||||
await app.listen({ port: 3000 });
|
|
||||||
console.log(`Server running on port ${process.env.PORT}`);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
start();
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import { FastifyInstance } from "fastify";
|
|
||||||
import fastifySwagger from "@fastify/swagger";
|
|
||||||
import fastifySwaggerUi from "@fastify/swagger-ui";
|
|
||||||
|
|
||||||
export async function registerSwagger(fastify: FastifyInstance) {
|
|
||||||
await fastify.register(fastifySwagger, {
|
|
||||||
openapi: {
|
|
||||||
info: {
|
|
||||||
title: "API Docs",
|
|
||||||
description: "API documentation for Fastify server",
|
|
||||||
version: "1.0.0"
|
|
||||||
},
|
|
||||||
servers: [
|
|
||||||
{
|
|
||||||
url: "http://localhost:3000",
|
|
||||||
description: "Local Development",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: "https://api.scheduler.bkdhome.p-e.kr",
|
|
||||||
description: "Production Server"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await fastify.register(fastifySwaggerUi, {
|
|
||||||
routePrefix: "/docs",
|
|
||||||
uiConfig: {
|
|
||||||
docExpansion: "none",
|
|
||||||
deepLinking: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
25
test/app.e2e-spec.ts
Normal file
25
test/app.e2e-spec.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { INestApplication } from '@nestjs/common';
|
||||||
|
import request from 'supertest';
|
||||||
|
import { App } from 'supertest/types';
|
||||||
|
import { AppModule } from './../src/app.module';
|
||||||
|
|
||||||
|
describe('AppController (e2e)', () => {
|
||||||
|
let app: INestApplication<App>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||||
|
imports: [AppModule],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
app = moduleFixture.createNestApplication();
|
||||||
|
await app.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('/ (GET)', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Hello World!');
|
||||||
|
});
|
||||||
|
});
|
||||||
9
test/jest-e2e.json
Normal file
9
test/jest-e2e.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"moduleFileExtensions": ["js", "json", "ts"],
|
||||||
|
"rootDir": ".",
|
||||||
|
"testEnvironment": "node",
|
||||||
|
"testRegex": ".e2e-spec.ts$",
|
||||||
|
"transform": {
|
||||||
|
"^.+\\.(t|j)s$": "ts-jest"
|
||||||
|
}
|
||||||
|
}
|
||||||
4
tsconfig.build.json
Normal file
4
tsconfig.build.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
|
||||||
|
}
|
||||||
@@ -1,19 +1,25 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "esnext",
|
"module": "nodenext",
|
||||||
"lib": ["ESNext"],
|
"moduleResolution": "nodenext",
|
||||||
"outDir": "dist",
|
"resolvePackageJsonExports": true,
|
||||||
"rootDir": "src",
|
|
||||||
"strict": true,
|
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"module": "NodeNext",
|
"isolatedModules": true,
|
||||||
"moduleResolution": "NodeNext",
|
"declaration": true,
|
||||||
"resolveJsonModule": true,
|
"removeComments": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"emitDecoratorMetadata": true,
|
||||||
"skipLibCheck": true,
|
"experimentalDecorators": true,
|
||||||
// "noEmit": true,
|
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
},
|
"target": "ES2023",
|
||||||
"include": ["src/**/*"],
|
"sourceMap": true,
|
||||||
"exclude": ["node_modules", "dist"]
|
"outDir": "./dist",
|
||||||
|
"baseUrl": "./",
|
||||||
|
"incremental": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"strictBindCallApply": false,
|
||||||
|
"noFallthroughCasesInSwitch": false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user