mirror of
https://github.com/20kaushik02/spotify-manager.git
synced 2025-12-06 06:34: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
|
||||
/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
|
||||
// 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 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";
|
||||
|
||||
type ConnConfigs = Record<string, SequelizeOptions>;
|
||||
interface SeqOptsWithURI extends SequelizeOptions {
|
||||
use_env_variable: string;
|
||||
}
|
||||
type ConnConfigs = Record<string, SeqOptsWithURI>;
|
||||
|
||||
// env-specific config
|
||||
const connConfigs: ConnConfigs = {
|
||||
development: {},
|
||||
test: {},
|
||||
development: {
|
||||
use_env_variable: "DB_URI",
|
||||
},
|
||||
test: {
|
||||
use_env_variable: "DB_URI",
|
||||
},
|
||||
production: {
|
||||
use_env_variable: "DB_URI",
|
||||
// dialectOptions: {
|
||||
// ssl: true,
|
||||
// },
|
||||
|
||||
2
index.ts
2
index.ts
@ -1,5 +1,3 @@
|
||||
import _ from "./config/dotenv.ts";
|
||||
|
||||
import { promisify } from "util";
|
||||
import express from "express";
|
||||
import session from "express-session";
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
"use strict";
|
||||
import { type Migration } from "sequelize-cli";
|
||||
import type { Migration } from "sequelize-cli";
|
||||
export default {
|
||||
up: async function (queryInterface, Sequelize) {
|
||||
await queryInterface.createTable("playlists", {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
"use strict";
|
||||
import { type Migration } from "sequelize-cli";
|
||||
import type { Migration } from "sequelize-cli";
|
||||
export default {
|
||||
up: async function (queryInterface, Sequelize) {
|
||||
await queryInterface.createTable("links", {
|
||||
|
||||
@ -8,14 +8,16 @@ import playlists from "./playlists.ts";
|
||||
|
||||
import logger from "../utils/logger.ts";
|
||||
|
||||
// Initialize
|
||||
if (!process.env["NODE_ENV"])
|
||||
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 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 {
|
||||
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",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "cross-env NODE_ENV=development tsx watch index.ts",
|
||||
"test_setup": "npm i && cross-env NODE_ENV=test npx sequelize-cli db:migrate",
|
||||
"test": "NODE_ENV=test tsx index.ts",
|
||||
"prod": "NODE_ENV=production tsx index.ts"
|
||||
"build": "tsc",
|
||||
"migrate": "npx sequelize-cli db:migrate --config dist/config/sequelize.js --models-path dist/models --migrations-path dist/migrations",
|
||||
"start": "npm run migrate && node dist/index.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -26,7 +25,6 @@
|
||||
"connect-redis": "^8.0.1",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.4.7",
|
||||
"express": "^4.21.2",
|
||||
"express-session": "^1.18.1",
|
||||
"express-validator": "^7.2.0",
|
||||
@ -36,6 +34,7 @@
|
||||
"redis": "^4.7.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"sequelize": "^6.37.6",
|
||||
"sequelize-cli": "^6.6.2",
|
||||
"sequelize-typescript": "^2.1.6",
|
||||
"serializr": "^3.0.3",
|
||||
"winston": "^3.17.0"
|
||||
@ -49,10 +48,6 @@
|
||||
"@types/node": "^22.13.10",
|
||||
"@types/sequelize": "^4.28.20",
|
||||
"@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"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
{
|
||||
"exclude": [
|
||||
"boilerplates",
|
||||
"tsout"
|
||||
"dist"
|
||||
],
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
/* 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. */
|
||||
// "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. */
|
||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||
// "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. */
|
||||
@ -63,7 +63,7 @@
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "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. */
|
||||
"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. */
|
||||
// "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. */
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
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;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user