mirror of
https://github.com/20kaushik02/spotify-manager.git
synced 2025-12-06 08:04:06 +00:00
dockerize!
changes to sequelize, env loading, ts workflow, minor improvements
This commit is contained in:
parent
8c909929d1
commit
57c82dd71c
9
.dockerignore
Normal file
9
.dockerignore
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
.git
|
||||||
|
.github
|
||||||
|
.dockerignore
|
||||||
|
.gitignore
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
logs
|
||||||
|
Dockerfile*
|
||||||
|
.env*
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -109,4 +109,4 @@ dist
|
|||||||
|
|
||||||
# production
|
# production
|
||||||
/build
|
/build
|
||||||
/tsout
|
/dist
|
||||||
|
|||||||
@ -1,6 +0,0 @@
|
|||||||
import _ from "./config/dotenv.ts";
|
|
||||||
|
|
||||||
import { resolve } from "path";
|
|
||||||
export default {
|
|
||||||
config: resolve("config", "sequelize.ts"),
|
|
||||||
};
|
|
||||||
32
Dockerfile
Normal file
32
Dockerfile
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
ARG APP_USER_DEFAULT=node
|
||||||
|
ARG NODE_ENV_DEFAULT=production
|
||||||
|
|
||||||
|
FROM node:lts-alpine AS base
|
||||||
|
|
||||||
|
ARG NODE_ENV_DEFAULT
|
||||||
|
ENV NODE_ENV=${NODE_ENV_DEFAULT}
|
||||||
|
|
||||||
|
ARG APP_USER_DEFAULT
|
||||||
|
ENV APP_USER=${APP_USER_DEFAULT}
|
||||||
|
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
FROM base AS build
|
||||||
|
|
||||||
|
COPY --chown=${APP_USER}:${APP_USER} package.json package-lock.json ./
|
||||||
|
RUN npm ci --include=prod --include=dev
|
||||||
|
|
||||||
|
COPY --chown=${APP_USER}:${APP_USER} . .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
|
||||||
|
COPY --chown=${APP_USER}:${APP_USER} package.json package-lock.json ./
|
||||||
|
RUN npm ci --include=prod
|
||||||
|
|
||||||
|
COPY --from=build --chown=${APP_USER}:${APP_USER} /usr/src/app/dist dist
|
||||||
|
RUN mkdir dist/logs && chown -R ${APP_USER} dist/logs
|
||||||
|
|
||||||
|
USER ${APP_USER}
|
||||||
|
|
||||||
|
CMD [ "npm", "start" ]
|
||||||
@ -1,6 +1,7 @@
|
|||||||
// TODO: rate limit module is busted (CJS types), do something for rate limiting
|
// TODO: rate limit module is busted (CJS types), do something for rate limiting
|
||||||
// bottleneck (https://npmjs.com/package/bottleneck) looks nice
|
// bottleneck (https://npmjs.com/package/bottleneck) looks nice
|
||||||
import axios, { type AxiosInstance } from "axios";
|
import axios from "axios";
|
||||||
|
import type { AxiosInstance } from "axios";
|
||||||
import { baseAPIURL, accountsAPIURL } from "../constants.ts";
|
import { baseAPIURL, accountsAPIURL } from "../constants.ts";
|
||||||
import logger from "../utils/logger.ts";
|
import logger from "../utils/logger.ts";
|
||||||
|
|
||||||
|
|||||||
45
compose-core.yaml
Normal file
45
compose-core.yaml
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
container_name: spotify-manager-postgres
|
||||||
|
image: postgres
|
||||||
|
restart: on-failure
|
||||||
|
environment:
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
POSTGRES_DB: spotify-manager
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
user: postgres
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U postgres -d spotify-manager"]
|
||||||
|
interval: 1s
|
||||||
|
retries: 5
|
||||||
|
timeout: 5s
|
||||||
|
redis:
|
||||||
|
container_name: spotify-manager-redis
|
||||||
|
image: redis/redis-stack-server:latest
|
||||||
|
restart: on-failure
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
|
||||||
|
interval: 1s
|
||||||
|
retries: 5
|
||||||
|
timeout: 3s
|
||||||
|
web:
|
||||||
|
container_name: spotify-manager
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
init: true
|
||||||
|
restart: on-failure
|
||||||
|
ports:
|
||||||
|
- 127.0.0.1:9001:9001
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: true
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: true
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
|
redis_data:
|
||||||
9
compose-dev.yaml
Normal file
9
compose-dev.yaml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
services:
|
||||||
|
web:
|
||||||
|
environment:
|
||||||
|
NODE_ENV: development
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
- .env.local
|
||||||
|
- .env.development
|
||||||
|
- .env.development.local
|
||||||
9
compose-prod.yaml
Normal file
9
compose-prod.yaml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
services:
|
||||||
|
web:
|
||||||
|
environment:
|
||||||
|
NODE_ENV: production
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
- .env.local
|
||||||
|
- .env.production
|
||||||
|
- .env.production.local
|
||||||
@ -1,18 +0,0 @@
|
|||||||
// https://github.com/motdotla/dotenv/issues/133#issuecomment-255298822
|
|
||||||
// explanation: in ESM, import statements execute first, unlike CJS where it's line order
|
|
||||||
// so if placed directly in index.ts, the .config gets called after all other imports in index.ts
|
|
||||||
// and one of those imports is the sequelize loader, which depends on env being loaded
|
|
||||||
// soln: raise the priority of dotenv to match by placing it in a separate module like this
|
|
||||||
|
|
||||||
import { config, type DotenvConfigOutput } from "dotenv";
|
|
||||||
|
|
||||||
const result: DotenvConfigOutput = config({
|
|
||||||
path: [
|
|
||||||
`.env.${process.env["NODE_ENV"]}.local`,
|
|
||||||
`.env.${process.env["NODE_ENV"]}`,
|
|
||||||
".env.local",
|
|
||||||
".env",
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
export default result;
|
|
||||||
@ -2,13 +2,21 @@ import type { SequelizeOptions } from "sequelize-typescript";
|
|||||||
|
|
||||||
import logger from "../utils/logger.ts";
|
import logger from "../utils/logger.ts";
|
||||||
|
|
||||||
type ConnConfigs = Record<string, SequelizeOptions>;
|
interface SeqOptsWithURI extends SequelizeOptions {
|
||||||
|
use_env_variable: string;
|
||||||
|
}
|
||||||
|
type ConnConfigs = Record<string, SeqOptsWithURI>;
|
||||||
|
|
||||||
// env-specific config
|
// env-specific config
|
||||||
const connConfigs: ConnConfigs = {
|
const connConfigs: ConnConfigs = {
|
||||||
development: {},
|
development: {
|
||||||
test: {},
|
use_env_variable: "DB_URI",
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
use_env_variable: "DB_URI",
|
||||||
|
},
|
||||||
production: {
|
production: {
|
||||||
|
use_env_variable: "DB_URI",
|
||||||
// dialectOptions: {
|
// dialectOptions: {
|
||||||
// ssl: true,
|
// ssl: true,
|
||||||
// },
|
// },
|
||||||
|
|||||||
2
index.ts
2
index.ts
@ -1,5 +1,3 @@
|
|||||||
import _ from "./config/dotenv.ts";
|
|
||||||
|
|
||||||
import { promisify } from "util";
|
import { promisify } from "util";
|
||||||
import express from "express";
|
import express from "express";
|
||||||
import session from "express-session";
|
import session from "express-session";
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
import { type Migration } from "sequelize-cli";
|
import type { Migration } from "sequelize-cli";
|
||||||
export default {
|
export default {
|
||||||
up: async function (queryInterface, Sequelize) {
|
up: async function (queryInterface, Sequelize) {
|
||||||
await queryInterface.createTable("playlists", {
|
await queryInterface.createTable("playlists", {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
import { type Migration } from "sequelize-cli";
|
import type { Migration } from "sequelize-cli";
|
||||||
export default {
|
export default {
|
||||||
up: async function (queryInterface, Sequelize) {
|
up: async function (queryInterface, Sequelize) {
|
||||||
await queryInterface.createTable("links", {
|
await queryInterface.createTable("links", {
|
||||||
|
|||||||
@ -8,14 +8,16 @@ import playlists from "./playlists.ts";
|
|||||||
|
|
||||||
import logger from "../utils/logger.ts";
|
import logger from "../utils/logger.ts";
|
||||||
|
|
||||||
|
// Initialize
|
||||||
if (!process.env["NODE_ENV"])
|
if (!process.env["NODE_ENV"])
|
||||||
throw new TypeError("Node environment not defined");
|
throw new TypeError("Node environment not defined");
|
||||||
if (!process.env["DB_URI"])
|
|
||||||
throw new TypeError("Database connection URI not defined");
|
|
||||||
|
|
||||||
// Initialize
|
|
||||||
const config = seqConfig[process.env["NODE_ENV"]];
|
const config = seqConfig[process.env["NODE_ENV"]];
|
||||||
const seqConn: Sequelize = new Sequelize(process.env["DB_URI"], config);
|
if (!config) throw new TypeError("Unknown environment");
|
||||||
|
|
||||||
|
const dbURI = process.env[config.use_env_variable];
|
||||||
|
if (!dbURI) throw new TypeError("Database connection URI not defined");
|
||||||
|
|
||||||
|
const seqConn: Sequelize = new Sequelize(dbURI, config);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await seqConn.authenticate();
|
await seqConn.authenticate();
|
||||||
|
|||||||
1039
package-lock.json
generated
1039
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
13
package.json
13
package.json
@ -5,10 +5,9 @@
|
|||||||
"exports": "./index.ts",
|
"exports": "./index.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "cross-env NODE_ENV=development tsx watch index.ts",
|
"build": "tsc",
|
||||||
"test_setup": "npm i && cross-env NODE_ENV=test npx sequelize-cli db:migrate",
|
"migrate": "npx sequelize-cli db:migrate --config dist/config/sequelize.js --models-path dist/models --migrations-path dist/migrations",
|
||||||
"test": "NODE_ENV=test tsx index.ts",
|
"start": "npm run migrate && node dist/index.js"
|
||||||
"prod": "NODE_ENV=production tsx index.ts"
|
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -26,7 +25,6 @@
|
|||||||
"connect-redis": "^8.0.1",
|
"connect-redis": "^8.0.1",
|
||||||
"cookie-parser": "^1.4.7",
|
"cookie-parser": "^1.4.7",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.4.7",
|
|
||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
"express-session": "^1.18.1",
|
"express-session": "^1.18.1",
|
||||||
"express-validator": "^7.2.0",
|
"express-validator": "^7.2.0",
|
||||||
@ -36,6 +34,7 @@
|
|||||||
"redis": "^4.7.0",
|
"redis": "^4.7.0",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"sequelize": "^6.37.6",
|
"sequelize": "^6.37.6",
|
||||||
|
"sequelize-cli": "^6.6.2",
|
||||||
"sequelize-typescript": "^2.1.6",
|
"sequelize-typescript": "^2.1.6",
|
||||||
"serializr": "^3.0.3",
|
"serializr": "^3.0.3",
|
||||||
"winston": "^3.17.0"
|
"winston": "^3.17.0"
|
||||||
@ -49,10 +48,6 @@
|
|||||||
"@types/node": "^22.13.10",
|
"@types/node": "^22.13.10",
|
||||||
"@types/sequelize": "^4.28.20",
|
"@types/sequelize": "^4.28.20",
|
||||||
"@types/validator": "^13.12.2",
|
"@types/validator": "^13.12.2",
|
||||||
"cross-env": "^7.0.3",
|
|
||||||
"nodemon": "^3.1.9",
|
|
||||||
"sequelize-cli": "^6.6.2",
|
|
||||||
"tsx": "^4.19.3",
|
|
||||||
"typescript": "^5.8.2"
|
"typescript": "^5.8.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"boilerplates",
|
"boilerplates",
|
||||||
"tsout"
|
"dist"
|
||||||
],
|
],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||||
/* Projects */
|
/* Projects */
|
||||||
"incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||||
"composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||||
@ -63,7 +63,7 @@
|
|||||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||||
"outDir": "./tsout", /* Specify an output folder for all emitted files. */
|
"outDir": "./dist", /* Specify an output folder for all emitted files. */
|
||||||
// "removeComments": true, /* Disable emitting comments. */
|
// "removeComments": true, /* Disable emitting comments. */
|
||||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
import { createLogger, transports, config, format, type Logger } from "winston";
|
import { createLogger, transports, config, format } from "winston";
|
||||||
|
import type { Logger } from "winston";
|
||||||
|
|
||||||
const { combine, timestamp, printf } = format;
|
const { combine, timestamp, printf } = format;
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user